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

package ssagen

import (
	"internal/race"
	"math/rand"
	"sort"
	"sync"
	"time"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/objw"
	"cmd/compile/internal/ssa"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
	"cmd/internal/objabi"
	"cmd/internal/src"
	"cmd/internal/sys"
)

// cmpstackvarlt reports whether the stack variable a sorts before b.
//
// Sort the list of stack variables. Autos after anything else,
// within autos, unused after used, within used, things with
// pointers first, zeroed things first, and then decreasing size.
// Because autos are laid out in decreasing addresses
// on the stack, pointers first, zeroed things first and decreasing size
// really means, in memory, things with pointers needing zeroing at
// the top of the stack and increasing in size.
// Non-autos sort on offset.
func cmpstackvarlt(a, b *ir.Name) bool {
	if (a.Class == ir.PAUTO) != (b.Class == ir.PAUTO) {
		return b.Class == ir.PAUTO
	}

	if a.Class != ir.PAUTO {
		return a.FrameOffset() < b.FrameOffset()
	}

	if a.Used() != b.Used() {
		return a.Used()
	}

	ap := a.Type().HasPointers()
	bp := b.Type().HasPointers()
	if ap != bp {
		return ap
	}

	ap = a.Needzero()
	bp = b.Needzero()
	if ap != bp {
		return ap
	}

	if a.Type().Width != b.Type().Width {
		return a.Type().Width > b.Type().Width
	}

	return a.Sym().Name < b.Sym().Name
}

// byStackvar implements sort.Interface for []*Node using cmpstackvarlt.
type byStackVar []*ir.Name

func (s byStackVar) Len() int           { return len(s) }
func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
func (s byStackVar) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func (s *ssafn) AllocFrame(f *ssa.Func) {
	s.stksize = 0
	s.stkptrsize = 0
	fn := s.curfn

	// Mark the PAUTO's unused.
	for _, ln := range fn.Dcl {
		if ln.Class == ir.PAUTO {
			ln.SetUsed(false)
		}
	}

	for _, l := range f.RegAlloc {
		if ls, ok := l.(ssa.LocalSlot); ok {
			ls.N.SetUsed(true)
		}
	}

	for _, b := range f.Blocks {
		for _, v := range b.Values {
			if n, ok := v.Aux.(*ir.Name); ok {
				switch n.Class {
				case ir.PPARAM, ir.PPARAMOUT:
					// Don't modify RegFP; it is a global.
					if n != ir.RegFP {
						n.SetUsed(true)
					}
				case ir.PAUTO:
					n.SetUsed(true)
				}
			}
		}
	}

	sort.Sort(byStackVar(fn.Dcl))

	// Reassign stack offsets of the locals that are used.
	lastHasPtr := false
	for i, n := range fn.Dcl {
		if n.Op() != ir.ONAME || n.Class != ir.PAUTO {
			continue
		}
		if !n.Used() {
			fn.Dcl = fn.Dcl[:i]
			break
		}

		types.CalcSize(n.Type())
		w := n.Type().Width
		if w >= types.MaxWidth || w < 0 {
			base.Fatalf("bad width")
		}
		if w == 0 && lastHasPtr {
			// Pad between a pointer-containing object and a zero-sized object.
			// This prevents a pointer to the zero-sized object from being interpreted
			// as a pointer to the pointer-containing object (and causing it
			// to be scanned when it shouldn't be). See issue 24993.
			w = 1
		}
		s.stksize += w
		s.stksize = types.Rnd(s.stksize, int64(n.Type().Align))
		if n.Type().HasPointers() {
			s.stkptrsize = s.stksize
			lastHasPtr = true
		} else {
			lastHasPtr = false
		}
		if Arch.LinkArch.InFamily(sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
			s.stksize = types.Rnd(s.stksize, int64(types.PtrSize))
		}
		n.SetFrameOffset(-s.stksize)
	}

	s.stksize = types.Rnd(s.stksize, int64(types.RegSize))
	s.stkptrsize = types.Rnd(s.stkptrsize, int64(types.RegSize))
}

const maxStackSize = 1 << 30

// Compile builds an SSA backend function,
// uses it to generate a plist,
// and flushes that plist to machine code.
// worker indicates which of the backend workers is doing the processing.
func Compile(fn *ir.Func, worker int) {
	f := buildssa(fn, worker)
	// Note: check arg size to fix issue 25507.
	if f.Frontend().(*ssafn).stksize >= maxStackSize || fn.Type().ArgWidth() >= maxStackSize {
		largeStackFramesMu.Lock()
		largeStackFrames = append(largeStackFrames, largeStack{locals: f.Frontend().(*ssafn).stksize, args: fn.Type().ArgWidth(), pos: fn.Pos()})
		largeStackFramesMu.Unlock()
		return
	}
	pp := objw.NewProgs(fn, worker)
	defer pp.Free()
	genssa(f, pp)
	// Check frame size again.
	// The check above included only the space needed for local variables.
	// After genssa, the space needed includes local variables and the callee arg region.
	// We must do this check prior to calling pp.Flush.
	// If there are any oversized stack frames,
	// the assembler may emit inscrutable complaints about invalid instructions.
	if pp.Text.To.Offset >= maxStackSize {
		largeStackFramesMu.Lock()
		locals := f.Frontend().(*ssafn).stksize
		largeStackFrames = append(largeStackFrames, largeStack{locals: locals, args: fn.Type().ArgWidth(), callee: pp.Text.To.Offset - locals, pos: fn.Pos()})
		largeStackFramesMu.Unlock()
		return
	}

	pp.Flush() // assemble, fill in boilerplate, etc.
	// fieldtrack must be called after pp.Flush. See issue 20014.
	fieldtrack(pp.Text.From.Sym, fn.FieldTrack)
}

func init() {
	if race.Enabled {
		rand.Seed(time.Now().UnixNano())
	}
}

// StackOffset returns the stack location of a LocalSlot relative to the
// stack pointer, suitable for use in a DWARF location entry. This has nothing
// to do with its offset in the user variable.
func StackOffset(slot ssa.LocalSlot) int32 {
	n := slot.N
	var off int64
	switch n.Class {
	case ir.PAUTO:
		off = n.FrameOffset()
		if base.Ctxt.FixedFrameSize() == 0 {
			off -= int64(types.PtrSize)
		}
		if objabi.Framepointer_enabled {
			off -= int64(types.PtrSize)
		}
	case ir.PPARAM, ir.PPARAMOUT:
		off = n.FrameOffset() + base.Ctxt.FixedFrameSize()
	}
	return int32(off + slot.Off)
}

// fieldtrack adds R_USEFIELD relocations to fnsym to record any
// struct fields that it used.
func fieldtrack(fnsym *obj.LSym, tracked map[*obj.LSym]struct{}) {
	if fnsym == nil {
		return
	}
	if objabi.Fieldtrack_enabled == 0 || len(tracked) == 0 {
		return
	}

	trackSyms := make([]*obj.LSym, 0, len(tracked))
	for sym := range tracked {
		trackSyms = append(trackSyms, sym)
	}
	sort.Slice(trackSyms, func(i, j int) bool { return trackSyms[i].Name < trackSyms[j].Name })
	for _, sym := range trackSyms {
		r := obj.Addrel(fnsym)
		r.Sym = sym
		r.Type = objabi.R_USEFIELD
	}
}

// largeStack is info about a function whose stack frame is too large (rare).
type largeStack struct {
	locals int64
	args   int64
	callee int64
	pos    src.XPos
}

var (
	largeStackFramesMu sync.Mutex // protects largeStackFrames
	largeStackFrames   []largeStack
)

func CheckLargeStacks() {
	// Check whether any of the functions we have compiled have gigantic stack frames.
	sort.Slice(largeStackFrames, func(i, j int) bool {
		return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
	})
	for _, large := range largeStackFrames {
		if large.callee != 0 {
			base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
		} else {
			base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
		}
	}
}
<commit_msg>cmd/compile: remove 8-byte alignment requirement of stack slot on s390x<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ssagen

import (
	"internal/race"
	"math/rand"
	"sort"
	"sync"
	"time"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/objw"
	"cmd/compile/internal/ssa"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
	"cmd/internal/objabi"
	"cmd/internal/src"
	"cmd/internal/sys"
)

// cmpstackvarlt reports whether the stack variable a sorts before b.
//
// Sort the list of stack variables. Autos after anything else,
// within autos, unused after used, within used, things with
// pointers first, zeroed things first, and then decreasing size.
// Because autos are laid out in decreasing addresses
// on the stack, pointers first, zeroed things first and decreasing size
// really means, in memory, things with pointers needing zeroing at
// the top of the stack and increasing in size.
// Non-autos sort on offset.
func cmpstackvarlt(a, b *ir.Name) bool {
	if (a.Class == ir.PAUTO) != (b.Class == ir.PAUTO) {
		return b.Class == ir.PAUTO
	}

	if a.Class != ir.PAUTO {
		return a.FrameOffset() < b.FrameOffset()
	}

	if a.Used() != b.Used() {
		return a.Used()
	}

	ap := a.Type().HasPointers()
	bp := b.Type().HasPointers()
	if ap != bp {
		return ap
	}

	ap = a.Needzero()
	bp = b.Needzero()
	if ap != bp {
		return ap
	}

	if a.Type().Width != b.Type().Width {
		return a.Type().Width > b.Type().Width
	}

	return a.Sym().Name < b.Sym().Name
}

// byStackvar implements sort.Interface for []*Node using cmpstackvarlt.
type byStackVar []*ir.Name

func (s byStackVar) Len() int           { return len(s) }
func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
func (s byStackVar) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func (s *ssafn) AllocFrame(f *ssa.Func) {
	s.stksize = 0
	s.stkptrsize = 0
	fn := s.curfn

	// Mark the PAUTO's unused.
	for _, ln := range fn.Dcl {
		if ln.Class == ir.PAUTO {
			ln.SetUsed(false)
		}
	}

	for _, l := range f.RegAlloc {
		if ls, ok := l.(ssa.LocalSlot); ok {
			ls.N.SetUsed(true)
		}
	}

	for _, b := range f.Blocks {
		for _, v := range b.Values {
			if n, ok := v.Aux.(*ir.Name); ok {
				switch n.Class {
				case ir.PPARAM, ir.PPARAMOUT:
					// Don't modify RegFP; it is a global.
					if n != ir.RegFP {
						n.SetUsed(true)
					}
				case ir.PAUTO:
					n.SetUsed(true)
				}
			}
		}
	}

	sort.Sort(byStackVar(fn.Dcl))

	// Reassign stack offsets of the locals that are used.
	lastHasPtr := false
	for i, n := range fn.Dcl {
		if n.Op() != ir.ONAME || n.Class != ir.PAUTO {
			continue
		}
		if !n.Used() {
			fn.Dcl = fn.Dcl[:i]
			break
		}

		types.CalcSize(n.Type())
		w := n.Type().Width
		if w >= types.MaxWidth || w < 0 {
			base.Fatalf("bad width")
		}
		if w == 0 && lastHasPtr {
			// Pad between a pointer-containing object and a zero-sized object.
			// This prevents a pointer to the zero-sized object from being interpreted
			// as a pointer to the pointer-containing object (and causing it
			// to be scanned when it shouldn't be). See issue 24993.
			w = 1
		}
		s.stksize += w
		s.stksize = types.Rnd(s.stksize, int64(n.Type().Align))
		if n.Type().HasPointers() {
			s.stkptrsize = s.stksize
			lastHasPtr = true
		} else {
			lastHasPtr = false
		}
		if Arch.LinkArch.InFamily(sys.ARM, sys.ARM64, sys.PPC64) {
			s.stksize = types.Rnd(s.stksize, int64(types.PtrSize))
		}
		n.SetFrameOffset(-s.stksize)
	}

	s.stksize = types.Rnd(s.stksize, int64(types.RegSize))
	s.stkptrsize = types.Rnd(s.stkptrsize, int64(types.RegSize))
}

const maxStackSize = 1 << 30

// Compile builds an SSA backend function,
// uses it to generate a plist,
// and flushes that plist to machine code.
// worker indicates which of the backend workers is doing the processing.
func Compile(fn *ir.Func, worker int) {
	f := buildssa(fn, worker)
	// Note: check arg size to fix issue 25507.
	if f.Frontend().(*ssafn).stksize >= maxStackSize || fn.Type().ArgWidth() >= maxStackSize {
		largeStackFramesMu.Lock()
		largeStackFrames = append(largeStackFrames, largeStack{locals: f.Frontend().(*ssafn).stksize, args: fn.Type().ArgWidth(), pos: fn.Pos()})
		largeStackFramesMu.Unlock()
		return
	}
	pp := objw.NewProgs(fn, worker)
	defer pp.Free()
	genssa(f, pp)
	// Check frame size again.
	// The check above included only the space needed for local variables.
	// After genssa, the space needed includes local variables and the callee arg region.
	// We must do this check prior to calling pp.Flush.
	// If there are any oversized stack frames,
	// the assembler may emit inscrutable complaints about invalid instructions.
	if pp.Text.To.Offset >= maxStackSize {
		largeStackFramesMu.Lock()
		locals := f.Frontend().(*ssafn).stksize
		largeStackFrames = append(largeStackFrames, largeStack{locals: locals, args: fn.Type().ArgWidth(), callee: pp.Text.To.Offset - locals, pos: fn.Pos()})
		largeStackFramesMu.Unlock()
		return
	}

	pp.Flush() // assemble, fill in boilerplate, etc.
	// fieldtrack must be called after pp.Flush. See issue 20014.
	fieldtrack(pp.Text.From.Sym, fn.FieldTrack)
}

func init() {
	if race.Enabled {
		rand.Seed(time.Now().UnixNano())
	}
}

// StackOffset returns the stack location of a LocalSlot relative to the
// stack pointer, suitable for use in a DWARF location entry. This has nothing
// to do with its offset in the user variable.
func StackOffset(slot ssa.LocalSlot) int32 {
	n := slot.N
	var off int64
	switch n.Class {
	case ir.PAUTO:
		off = n.FrameOffset()
		if base.Ctxt.FixedFrameSize() == 0 {
			off -= int64(types.PtrSize)
		}
		if objabi.Framepointer_enabled {
			off -= int64(types.PtrSize)
		}
	case ir.PPARAM, ir.PPARAMOUT:
		off = n.FrameOffset() + base.Ctxt.FixedFrameSize()
	}
	return int32(off + slot.Off)
}

// fieldtrack adds R_USEFIELD relocations to fnsym to record any
// struct fields that it used.
func fieldtrack(fnsym *obj.LSym, tracked map[*obj.LSym]struct{}) {
	if fnsym == nil {
		return
	}
	if objabi.Fieldtrack_enabled == 0 || len(tracked) == 0 {
		return
	}

	trackSyms := make([]*obj.LSym, 0, len(tracked))
	for sym := range tracked {
		trackSyms = append(trackSyms, sym)
	}
	sort.Slice(trackSyms, func(i, j int) bool { return trackSyms[i].Name < trackSyms[j].Name })
	for _, sym := range trackSyms {
		r := obj.Addrel(fnsym)
		r.Sym = sym
		r.Type = objabi.R_USEFIELD
	}
}

// largeStack is info about a function whose stack frame is too large (rare).
type largeStack struct {
	locals int64
	args   int64
	callee int64
	pos    src.XPos
}

var (
	largeStackFramesMu sync.Mutex // protects largeStackFrames
	largeStackFrames   []largeStack
)

func CheckLargeStacks() {
	// Check whether any of the functions we have compiled have gigantic stack frames.
	sort.Slice(largeStackFrames, func(i, j int) bool {
		return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
	})
	for _, large := range largeStackFrames {
		if large.callee != 0 {
			base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
		} else {
			base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
		}
	}
}
<|endoftext|>


<commit_before>package services

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"time"

	"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
	m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
)

var (
	IoHelper       m.IoUtil = IoUtilImp{}
	HttpClient     http.Client
	grafanaVersion string
)

func Init(version string) {
	grafanaVersion = version

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
	}

	HttpClient = http.Client{
		Timeout:   time.Duration(10 * time.Second),
		Transport: tr,
	}
}

func ListAllPlugins(repoUrl string) (m.PluginRepo, error) {
	body, err := sendRequest(repoUrl, "repo")

	if err != nil {
		logger.Info("Failed to send request", "error", err)
		return m.PluginRepo{}, fmt.Errorf("Failed to send request. error: %v", err)
	}

	if err != nil {
		return m.PluginRepo{}, err
	}

	var data m.PluginRepo
	err = json.Unmarshal(body, &data)
	if err != nil {
		logger.Info("Failed to unmarshal graphite response error: %v", err)
		return m.PluginRepo{}, err
	}

	return data, nil
}

func ReadPlugin(pluginDir, pluginName string) (m.InstalledPlugin, error) {
	distPluginDataPath := path.Join(pluginDir, pluginName, "dist", "plugin.json")

	var data []byte
	var err error
	data, err = IoHelper.ReadFile(distPluginDataPath)

	if err != nil {
		pluginDataPath := path.Join(pluginDir, pluginName, "plugin.json")
		data, err = IoHelper.ReadFile(pluginDataPath)

		if err != nil {
			return m.InstalledPlugin{}, errors.New("Could not find dist/plugin.json or plugin.json on  " + pluginName + " in " + pluginDir)
		}
	}

	res := m.InstalledPlugin{}
	json.Unmarshal(data, &res)

	if res.Info.Version == "" {
		res.Info.Version = "0.0.0"
	}

	if res.Id == "" {
		return m.InstalledPlugin{}, errors.New("could not find plugin " + pluginName + " in " + pluginDir)
	}

	return res, nil
}

func GetLocalPlugins(pluginDir string) []m.InstalledPlugin {
	result := make([]m.InstalledPlugin, 0)
	files, _ := IoHelper.ReadDir(pluginDir)
	for _, f := range files {
		res, err := ReadPlugin(pluginDir, f.Name())
		if err == nil {
			result = append(result, res)
		}
	}

	return result
}

func RemoveInstalledPlugin(pluginPath, pluginName string) error {
	logger.Infof("Removing plugin: %v\n", pluginName)
	pluginDir := path.Join(pluginPath, pluginName)

	_, err := IoHelper.Stat(pluginDir)
	if err != nil {
		return err
	}

	return IoHelper.RemoveAll(pluginDir)
}

func GetPlugin(pluginId, repoUrl string) (m.Plugin, error) {
	body, err := sendRequest(repoUrl, "repo", pluginId)

	if err != nil {
		logger.Info("Failed to send request", "error", err)
		return m.Plugin{}, fmt.Errorf("Failed to send request. error: %v", err)
	}

	if err != nil {
		return m.Plugin{}, err
	}

	var data m.Plugin
	err = json.Unmarshal(body, &data)
	if err != nil {
		logger.Info("Failed to unmarshal graphite response error: %v", err)
		return m.Plugin{}, err
	}

	return data, nil
}

func sendRequest(repoUrl string, subPaths ...string) ([]byte, error) {
	u, _ := url.Parse(repoUrl)
	for _, v := range subPaths {
		u.Path = path.Join(u.Path, v)
	}

	req, err := http.NewRequest(http.MethodGet, u.String(), nil)

	req.Header.Set("grafana-version", grafanaVersion)
	req.Header.Set("User-Agent", "grafana "+grafanaVersion)

	if err != nil {
		return []byte{}, err
	}

	res, err := HttpClient.Do(req)
	if err != nil {
		return []byte{}, err
	}

	if res.StatusCode/100 != 2 {
		return []byte{}, fmt.Errorf("Api returned invalid status: %s", res.Status)
	}

	body, err := ioutil.ReadAll(res.Body)
	defer res.Body.Close()

	return body, err
}
<commit_msg>Take grafana-cli proxy settings from env<commit_after>package services

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"time"

	"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
	m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
)

var (
	IoHelper       m.IoUtil = IoUtilImp{}
	HttpClient     http.Client
	grafanaVersion string
)

func Init(version string) {
	grafanaVersion = version

	tr := &http.Transport{
		Proxy:           http.ProxyFromEnvironment,
		TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
	}

	HttpClient = http.Client{
		Timeout:   time.Duration(10 * time.Second),
		Transport: tr,
	}
}

func ListAllPlugins(repoUrl string) (m.PluginRepo, error) {
	body, err := sendRequest(repoUrl, "repo")

	if err != nil {
		logger.Info("Failed to send request", "error", err)
		return m.PluginRepo{}, fmt.Errorf("Failed to send request. error: %v", err)
	}

	if err != nil {
		return m.PluginRepo{}, err
	}

	var data m.PluginRepo
	err = json.Unmarshal(body, &data)
	if err != nil {
		logger.Info("Failed to unmarshal graphite response error: %v", err)
		return m.PluginRepo{}, err
	}

	return data, nil
}

func ReadPlugin(pluginDir, pluginName string) (m.InstalledPlugin, error) {
	distPluginDataPath := path.Join(pluginDir, pluginName, "dist", "plugin.json")

	var data []byte
	var err error
	data, err = IoHelper.ReadFile(distPluginDataPath)

	if err != nil {
		pluginDataPath := path.Join(pluginDir, pluginName, "plugin.json")
		data, err = IoHelper.ReadFile(pluginDataPath)

		if err != nil {
			return m.InstalledPlugin{}, errors.New("Could not find dist/plugin.json or plugin.json on  " + pluginName + " in " + pluginDir)
		}
	}

	res := m.InstalledPlugin{}
	json.Unmarshal(data, &res)

	if res.Info.Version == "" {
		res.Info.Version = "0.0.0"
	}

	if res.Id == "" {
		return m.InstalledPlugin{}, errors.New("could not find plugin " + pluginName + " in " + pluginDir)
	}

	return res, nil
}

func GetLocalPlugins(pluginDir string) []m.InstalledPlugin {
	result := make([]m.InstalledPlugin, 0)
	files, _ := IoHelper.ReadDir(pluginDir)
	for _, f := range files {
		res, err := ReadPlugin(pluginDir, f.Name())
		if err == nil {
			result = append(result, res)
		}
	}

	return result
}

func RemoveInstalledPlugin(pluginPath, pluginName string) error {
	logger.Infof("Removing plugin: %v\n", pluginName)
	pluginDir := path.Join(pluginPath, pluginName)

	_, err := IoHelper.Stat(pluginDir)
	if err != nil {
		return err
	}

	return IoHelper.RemoveAll(pluginDir)
}

func GetPlugin(pluginId, repoUrl string) (m.Plugin, error) {
	body, err := sendRequest(repoUrl, "repo", pluginId)

	if err != nil {
		logger.Info("Failed to send request", "error", err)
		return m.Plugin{}, fmt.Errorf("Failed to send request. error: %v", err)
	}

	if err != nil {
		return m.Plugin{}, err
	}

	var data m.Plugin
	err = json.Unmarshal(body, &data)
	if err != nil {
		logger.Info("Failed to unmarshal graphite response error: %v", err)
		return m.Plugin{}, err
	}

	return data, nil
}

func sendRequest(repoUrl string, subPaths ...string) ([]byte, error) {
	u, _ := url.Parse(repoUrl)
	for _, v := range subPaths {
		u.Path = path.Join(u.Path, v)
	}

	req, err := http.NewRequest(http.MethodGet, u.String(), nil)

	req.Header.Set("grafana-version", grafanaVersion)
	req.Header.Set("User-Agent", "grafana "+grafanaVersion)

	if err != nil {
		return []byte{}, err
	}

	res, err := HttpClient.Do(req)
	if err != nil {
		return []byte{}, err
	}

	if res.StatusCode/100 != 2 {
		return []byte{}, fmt.Errorf("Api returned invalid status: %s", res.Status)
	}

	body, err := ioutil.ReadAll(res.Body)
	defer res.Body.Close()

	return body, err
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 certificates

import (
	"testing"
	"time"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	"github.com/jetstack/cert-manager/pkg/util"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

func TestCertificateMatchesSpec(t *testing.T) {
	baseCert := gen.Certificate("test",
		gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: "ca-issuer", Kind: "Issuer", Group: "not-empty"}),
		gen.SetCertificateSecretName("output"),
		gen.SetCertificateRenewBefore(time.Hour*36),
	)

	exampleBundle := mustCreateCryptoBundle(t, gen.CertificateFrom(baseCert,
		gen.SetCertificateDNSNames("a.example.com"),
		gen.SetCertificateCommonName("common.name.example.com"),
		gen.SetCertificateURIs("spiffe://cluster.local/ns/sandbox/sa/foo"),
	))

	secret := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Annotations: map[string]string{
				cmapi.IssuerNameAnnotationKey: "ca-issuer",
				cmapi.IssuerKindAnnotationKey: "Issuer",
			},
		},
	}

	type testT struct {
		cb          cryptoBundle
		certificate *cmapi.Certificate
		secret      *corev1.Secret
		expMatch    bool
		expErrors   []string
	}

	for name, test := range map[string]testT{
		"if all match then return matched": {
			cb:          exampleBundle,
			certificate: exampleBundle.certificate,
			secret:      gen.SecretFrom(secret),
			expMatch:    true,
			expErrors:   nil,
		},

		"if no common name but DNS and all match then return matched": {
			cb: mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateCommonName(""),
			)),
			certificate: gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateCommonName(""),
			),
			secret:    gen.SecretFrom(secret),
			expMatch:  true,
			expErrors: nil,
		},

		"if common name empty but requested common name in DNS names then match": {
			cb: mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateDNSNames("a.example.com", "common.name.example.com"),
				gen.SetCertificateCommonName(""),
			)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret:      gen.SecretFrom(secret),
			expMatch:    true,
			expErrors:   nil,
		},

		"if common name random string but requested common name in DNS names then match": {
			cb: mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateDNSNames("a.example.com", "common.name.example.com"),
				gen.SetCertificateCommonName("foobar"),
			)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret:      gen.SecretFrom(secret),
			expMatch:    true,
			expErrors:   nil,
		},

		"if common name random string and no request DNS names but request common name then error missing common name": {
			cb: mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateDNSNames(),
				gen.SetCertificateCommonName("foobar"),
			)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret:      gen.SecretFrom(secret),
			expMatch:    false,
			expErrors: []string{
				`Common Name on TLS certificate not up to date ("common.name.example.com"): [foobar]`,
				"DNS names on TLS certificate not up to date: []",
			},
		},

		"if the issuer name and kind uses v1alpha2 annotation then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.IssuerNameAnnotationKey: "ca-issuer",
					cmapi.IssuerKindAnnotationKey: "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name uses v1alpha2 annotation but kind uses depreicated then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name uses deprecated annotation but kind uses v1alpha2 then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name and kind uses the deprecated annotation then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name uses v1alpha2 and kind uses both the deprecated and v1alpha2 annotation then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name both the deprecated and v1alpha2 annotation and kind uses deprecated then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name and kind uses both the deprecated and v1alpha2 annotation then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name and kind uses both the deprecated and v1alpha2 annotation but no values in deprecated annotations then should match spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "foo",
					cmapi.DeprecatedIssuerKindAnnotationKey: "bar",
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name and kind deprecated annotations are correct but v1alpha2 values are wrong then should not match spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "foo",
					cmapi.IssuerKindAnnotationKey:           "bar",
				})),
			expMatch: false,
			expErrors: []string{
				`Issuer "cert-manager.io/issuer-name" of the certificate is not up to date: "foo"`,
				`Issuer "cert-manager.io/issuer-kind" of the certificate is not up to date: "bar"`,
			},
		},

		"if the issuer name and kind deprecated annotations are correct but v1alpha2 values are empty but exist then should not match spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "",
					cmapi.IssuerKindAnnotationKey:           "",
				})),
			expMatch: false,
			expErrors: []string{
				`Issuer "cert-manager.io/issuer-name" of the certificate is not up to date: ""`,
				`Issuer "cert-manager.io/issuer-kind" of the certificate is not up to date: ""`,
			},
		},
		"if the issuer name and kind deprecated annotations are wrong and no v1alpha2 values then should not match spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "foo",
					cmapi.DeprecatedIssuerKindAnnotationKey: "bar",
				})),
			expMatch: false,
			expErrors: []string{
				`Issuer "certmanager.k8s.io/issuer-name" of the certificate is not up to date: "foo"`,
				`Issuer "certmanager.k8s.io/issuer-kind" of the certificate is not up to date: "bar"`,
			},
		},
	} {
		t.Run(name, func(t *testing.T) {
			match, errs := certificateMatchesSpec(
				test.certificate, test.cb.privateKey, test.cb.cert, test.secret)

			if match != test.expMatch {
				t.Errorf("got unexpected match bool, exp=%t got=%t",
					test.expMatch, match)
			}

			if !util.EqualSorted(test.expErrors, errs) {
				t.Errorf("got unexpected errors, exp=%s got=%s",
					test.expErrors, errs)
			}
		})
	}

}
<commit_msg>spelling: deprecated<commit_after>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 certificates

import (
	"testing"
	"time"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	"github.com/jetstack/cert-manager/pkg/util"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

func TestCertificateMatchesSpec(t *testing.T) {
	baseCert := gen.Certificate("test",
		gen.SetCertificateIssuer(cmmeta.ObjectReference{Name: "ca-issuer", Kind: "Issuer", Group: "not-empty"}),
		gen.SetCertificateSecretName("output"),
		gen.SetCertificateRenewBefore(time.Hour*36),
	)

	exampleBundle := mustCreateCryptoBundle(t, gen.CertificateFrom(baseCert,
		gen.SetCertificateDNSNames("a.example.com"),
		gen.SetCertificateCommonName("common.name.example.com"),
		gen.SetCertificateURIs("spiffe://cluster.local/ns/sandbox/sa/foo"),
	))

	secret := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Annotations: map[string]string{
				cmapi.IssuerNameAnnotationKey: "ca-issuer",
				cmapi.IssuerKindAnnotationKey: "Issuer",
			},
		},
	}

	type testT struct {
		cb          cryptoBundle
		certificate *cmapi.Certificate
		secret      *corev1.Secret
		expMatch    bool
		expErrors   []string
	}

	for name, test := range map[string]testT{
		"if all match then return matched": {
			cb:          exampleBundle,
			certificate: exampleBundle.certificate,
			secret:      gen.SecretFrom(secret),
			expMatch:    true,
			expErrors:   nil,
		},

		"if no common name but DNS and all match then return matched": {
			cb: mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateCommonName(""),
			)),
			certificate: gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateCommonName(""),
			),
			secret:    gen.SecretFrom(secret),
			expMatch:  true,
			expErrors: nil,
		},

		"if common name empty but requested common name in DNS names then match": {
			cb: mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateDNSNames("a.example.com", "common.name.example.com"),
				gen.SetCertificateCommonName(""),
			)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret:      gen.SecretFrom(secret),
			expMatch:    true,
			expErrors:   nil,
		},

		"if common name random string but requested common name in DNS names then match": {
			cb: mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateDNSNames("a.example.com", "common.name.example.com"),
				gen.SetCertificateCommonName("foobar"),
			)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret:      gen.SecretFrom(secret),
			expMatch:    true,
			expErrors:   nil,
		},

		"if common name random string and no request DNS names but request common name then error missing common name": {
			cb: mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate,
				gen.SetCertificateDNSNames(),
				gen.SetCertificateCommonName("foobar"),
			)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret:      gen.SecretFrom(secret),
			expMatch:    false,
			expErrors: []string{
				`Common Name on TLS certificate not up to date ("common.name.example.com"): [foobar]`,
				"DNS names on TLS certificate not up to date: []",
			},
		},

		"if the issuer name and kind uses v1alpha2 annotation then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.IssuerNameAnnotationKey: "ca-issuer",
					cmapi.IssuerKindAnnotationKey: "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name uses v1alpha2 annotation but kind uses deprecated then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name uses deprecated annotation but kind uses v1alpha2 then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name and kind uses the deprecated annotation then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name uses v1alpha2 and kind uses both the deprecated and v1alpha2 annotation then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name both the deprecated and v1alpha2 annotation and kind uses deprecated then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name and kind uses both the deprecated and v1alpha2 annotation then it should still match the spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name and kind uses both the deprecated and v1alpha2 annotation but no values in deprecated annotations then should match spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "foo",
					cmapi.DeprecatedIssuerKindAnnotationKey: "bar",
					cmapi.IssuerNameAnnotationKey:           "ca-issuer",
					cmapi.IssuerKindAnnotationKey:           "Issuer",
				})),
			expMatch:  true,
			expErrors: nil,
		},

		"if the issuer name and kind deprecated annotations are correct but v1alpha2 values are wrong then should not match spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "foo",
					cmapi.IssuerKindAnnotationKey:           "bar",
				})),
			expMatch: false,
			expErrors: []string{
				`Issuer "cert-manager.io/issuer-name" of the certificate is not up to date: "foo"`,
				`Issuer "cert-manager.io/issuer-kind" of the certificate is not up to date: "bar"`,
			},
		},

		"if the issuer name and kind deprecated annotations are correct but v1alpha2 values are empty but exist then should not match spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "ca-issuer",
					cmapi.DeprecatedIssuerKindAnnotationKey: "Issuer",
					cmapi.IssuerNameAnnotationKey:           "",
					cmapi.IssuerKindAnnotationKey:           "",
				})),
			expMatch: false,
			expErrors: []string{
				`Issuer "cert-manager.io/issuer-name" of the certificate is not up to date: ""`,
				`Issuer "cert-manager.io/issuer-kind" of the certificate is not up to date: ""`,
			},
		},
		"if the issuer name and kind deprecated annotations are wrong and no v1alpha2 values then should not match spec": {
			cb:          mustCreateCryptoBundle(t, gen.CertificateFrom(exampleBundle.certificate)),
			certificate: gen.CertificateFrom(exampleBundle.certificate),
			secret: gen.SecretFrom(secret,
				gen.SetSecretAnnotations(map[string]string{
					cmapi.DeprecatedIssuerNameAnnotationKey: "foo",
					cmapi.DeprecatedIssuerKindAnnotationKey: "bar",
				})),
			expMatch: false,
			expErrors: []string{
				`Issuer "certmanager.k8s.io/issuer-name" of the certificate is not up to date: "foo"`,
				`Issuer "certmanager.k8s.io/issuer-kind" of the certificate is not up to date: "bar"`,
			},
		},
	} {
		t.Run(name, func(t *testing.T) {
			match, errs := certificateMatchesSpec(
				test.certificate, test.cb.privateKey, test.cb.cert, test.secret)

			if match != test.expMatch {
				t.Errorf("got unexpected match bool, exp=%t got=%t",
					test.expMatch, match)
			}

			if !util.EqualSorted(test.expErrors, errs) {
				t.Errorf("got unexpected errors, exp=%s got=%s",
					test.expErrors, errs)
			}
		})
	}

}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package controller

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"strings"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/coreos/go-etcd/etcd"
)

// ReplicationManager is responsible for synchronizing ReplicationController objects stored in etcd
// with actual running pods.
// TODO: Remove the etcd dependency and re-factor in terms of a generic watch interface
type ReplicationManager struct {
	etcdClient *etcd.Client
	kubeClient client.ClientInterface
	podControl PodControlInterface
	updateLock sync.Mutex
}

// An interface that knows how to add or delete pods
// created as an interface to allow testing.
type PodControlInterface interface {
	createReplica(controllerSpec api.ReplicationController)
	deletePod(podID string) error
}

type RealPodControl struct {
	kubeClient client.ClientInterface
}

func (r RealPodControl) createReplica(controllerSpec api.ReplicationController) {
	labels := controllerSpec.DesiredState.PodTemplate.Labels
	if labels != nil {
		labels["replicationController"] = controllerSpec.ID
	}
	pod := api.Pod{
		JSONBase: api.JSONBase{
			ID: fmt.Sprintf("%x", rand.Int()),
		},
		DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState,
		Labels:       controllerSpec.DesiredState.PodTemplate.Labels,
	}
	_, err := r.kubeClient.CreatePod(pod)
	if err != nil {
		log.Printf("%#v\n", err)
	}
}

func (r RealPodControl) deletePod(podID string) error {
	return r.kubeClient.DeletePod(podID)
}

func MakeReplicationManager(etcdClient *etcd.Client, kubeClient client.ClientInterface) *ReplicationManager {
	return &ReplicationManager{
		kubeClient: kubeClient,
		etcdClient: etcdClient,
		podControl: RealPodControl{
			kubeClient: kubeClient,
		},
	}
}

// Begin watching and syncing.
func (rm *ReplicationManager) Run(period time.Duration) {
	go util.Forever(func() { rm.synchronize() }, period)
	go util.Forever(func() { rm.watchControllers() }, period)
}

func (rm *ReplicationManager) watchControllers() {
	watchChannel := make(chan *etcd.Response)
	go func() {
		defer util.HandleCrash()
		defer func() {
			close(watchChannel)
		}()
		rm.etcdClient.Watch("/registry/controllers", 0, true, watchChannel, nil)
	}()

	for {
		watchResponse, ok := <-watchChannel
		if !ok {
			// watchChannel has been closed. Let the util.Forever() that
			// called us call us again.
			return
		}
		if watchResponse == nil {
			time.Sleep(time.Second * 10)
			continue
		}
		log.Printf("Got watch: %#v", watchResponse)
		controller, err := rm.handleWatchResponse(watchResponse)
		if err != nil {
			log.Printf("Error handling data: %#v, %#v", err, watchResponse)
			continue
		}
		rm.syncReplicationController(*controller)
	}
}

func (rm *ReplicationManager) handleWatchResponse(response *etcd.Response) (*api.ReplicationController, error) {
	if response.Action == "set" {
		if response.Node != nil {
			var controllerSpec api.ReplicationController
			err := json.Unmarshal([]byte(response.Node.Value), &controllerSpec)
			if err != nil {
				return nil, err
			}
			return &controllerSpec, nil
		} else {
			return nil, fmt.Errorf("response node is null %#v", response)
		}
	}
	return nil, nil
}

func (rm *ReplicationManager) filterActivePods(pods []api.Pod) []api.Pod {
	var result []api.Pod
	for _, value := range pods {
		if strings.Index(value.CurrentState.Status, "Exit") == -1 {
			result = append(result, value)
		}
	}
	return result
}

func (rm *ReplicationManager) syncReplicationController(controllerSpec api.ReplicationController) error {
	rm.updateLock.Lock()
	podList, err := rm.kubeClient.ListPods(controllerSpec.DesiredState.ReplicasInSet)
	if err != nil {
		return err
	}
	filteredList := rm.filterActivePods(podList.Items)
	diff := len(filteredList) - controllerSpec.DesiredState.Replicas
	log.Printf("%#v", filteredList)
	if diff < 0 {
		diff *= -1
		log.Printf("Too few replicas, creating %d\n", diff)
		for i := 0; i < diff; i++ {
			rm.podControl.createReplica(controllerSpec)
		}
	} else if diff > 0 {
		log.Print("Too many replicas, deleting")
		for i := 0; i < diff; i++ {
			rm.podControl.deletePod(filteredList[i].ID)
		}
	}
	rm.updateLock.Unlock()
	return nil
}

func (rm *ReplicationManager) synchronize() {
	response, err := rm.etcdClient.Get("/registry/controllers", false, false)
	if err != nil {
		log.Printf("Synchronization error %#v", err)
	}
	// TODO(bburns): There is a race here, if we get a version of the controllers, and then it is
	// updated, its possible that the watch will pick up the change first, and then we will execute
	// using the old version of the controller.
	// Probably the correct thing to do is to use the version number in etcd to detect when
	// we are stale.
	// Punting on this for now, but this could lead to some nasty bugs, so we should really fix it
	// sooner rather than later.
	if response != nil && response.Node != nil && response.Node.Nodes != nil {
		for _, value := range response.Node.Nodes {
			var controllerSpec api.ReplicationController
			err := json.Unmarshal([]byte(value.Value), &controllerSpec)
			if err != nil {
				log.Printf("Unexpected error: %#v", err)
				continue
			}
			log.Printf("Synchronizing %s\n", controllerSpec.ID)
			err = rm.syncReplicationController(controllerSpec)
			if err != nil {
				log.Printf("Error synchronizing: %#v", err)
			}
		}
	}
}
<commit_msg>Fix bad lock handling.<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package controller

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"strings"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/coreos/go-etcd/etcd"
)

// ReplicationManager is responsible for synchronizing ReplicationController objects stored in etcd
// with actual running pods.
// TODO: Remove the etcd dependency and re-factor in terms of a generic watch interface
type ReplicationManager struct {
	etcdClient *etcd.Client
	kubeClient client.ClientInterface
	podControl PodControlInterface
	updateLock sync.Mutex
}

// An interface that knows how to add or delete pods
// created as an interface to allow testing.
type PodControlInterface interface {
	createReplica(controllerSpec api.ReplicationController)
	deletePod(podID string) error
}

type RealPodControl struct {
	kubeClient client.ClientInterface
}

func (r RealPodControl) createReplica(controllerSpec api.ReplicationController) {
	labels := controllerSpec.DesiredState.PodTemplate.Labels
	if labels != nil {
		labels["replicationController"] = controllerSpec.ID
	}
	pod := api.Pod{
		JSONBase: api.JSONBase{
			ID: fmt.Sprintf("%x", rand.Int()),
		},
		DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState,
		Labels:       controllerSpec.DesiredState.PodTemplate.Labels,
	}
	_, err := r.kubeClient.CreatePod(pod)
	if err != nil {
		log.Printf("%#v\n", err)
	}
}

func (r RealPodControl) deletePod(podID string) error {
	return r.kubeClient.DeletePod(podID)
}

func MakeReplicationManager(etcdClient *etcd.Client, kubeClient client.ClientInterface) *ReplicationManager {
	return &ReplicationManager{
		kubeClient: kubeClient,
		etcdClient: etcdClient,
		podControl: RealPodControl{
			kubeClient: kubeClient,
		},
	}
}

// Begin watching and syncing.
func (rm *ReplicationManager) Run(period time.Duration) {
	go util.Forever(func() { rm.synchronize() }, period)
	go util.Forever(func() { rm.watchControllers() }, period)
}

func (rm *ReplicationManager) watchControllers() {
	watchChannel := make(chan *etcd.Response)
	go func() {
		defer util.HandleCrash()
		defer func() {
			close(watchChannel)
		}()
		rm.etcdClient.Watch("/registry/controllers", 0, true, watchChannel, nil)
	}()

	for {
		watchResponse, ok := <-watchChannel
		if !ok {
			// watchChannel has been closed. Let the util.Forever() that
			// called us call us again.
			return
		}
		if watchResponse == nil {
			time.Sleep(time.Second * 10)
			continue
		}
		log.Printf("Got watch: %#v", watchResponse)
		controller, err := rm.handleWatchResponse(watchResponse)
		if err != nil {
			log.Printf("Error handling data: %#v, %#v", err, watchResponse)
			continue
		}
		rm.syncReplicationController(*controller)
	}
}

func (rm *ReplicationManager) handleWatchResponse(response *etcd.Response) (*api.ReplicationController, error) {
	if response.Action == "set" {
		if response.Node != nil {
			var controllerSpec api.ReplicationController
			err := json.Unmarshal([]byte(response.Node.Value), &controllerSpec)
			if err != nil {
				return nil, err
			}
			return &controllerSpec, nil
		} else {
			return nil, fmt.Errorf("response node is null %#v", response)
		}
	}
	return nil, nil
}

func (rm *ReplicationManager) filterActivePods(pods []api.Pod) []api.Pod {
	var result []api.Pod
	for _, value := range pods {
		if strings.Index(value.CurrentState.Status, "Exit") == -1 {
			result = append(result, value)
		}
	}
	return result
}

func (rm *ReplicationManager) syncReplicationController(controllerSpec api.ReplicationController) error {
	rm.updateLock.Lock()
	defer rm.updateLock.Unlock()
	podList, err := rm.kubeClient.ListPods(controllerSpec.DesiredState.ReplicasInSet)
	if err != nil {
		return err
	}
	filteredList := rm.filterActivePods(podList.Items)
	diff := len(filteredList) - controllerSpec.DesiredState.Replicas
	log.Printf("%#v", filteredList)
	if diff < 0 {
		diff *= -1
		log.Printf("Too few replicas, creating %d\n", diff)
		for i := 0; i < diff; i++ {
			rm.podControl.createReplica(controllerSpec)
		}
	} else if diff > 0 {
		log.Print("Too many replicas, deleting")
		for i := 0; i < diff; i++ {
			rm.podControl.deletePod(filteredList[i].ID)
		}
	}
	return nil
}

func (rm *ReplicationManager) synchronize() {
	response, err := rm.etcdClient.Get("/registry/controllers", false, false)
	if err != nil {
		log.Printf("Synchronization error %#v", err)
	}
	// TODO(bburns): There is a race here, if we get a version of the controllers, and then it is
	// updated, its possible that the watch will pick up the change first, and then we will execute
	// using the old version of the controller.
	// Probably the correct thing to do is to use the version number in etcd to detect when
	// we are stale.
	// Punting on this for now, but this could lead to some nasty bugs, so we should really fix it
	// sooner rather than later.
	if response != nil && response.Node != nil && response.Node.Nodes != nil {
		for _, value := range response.Node.Nodes {
			var controllerSpec api.ReplicationController
			err := json.Unmarshal([]byte(value.Value), &controllerSpec)
			if err != nil {
				log.Printf("Unexpected error: %#v", err)
				continue
			}
			log.Printf("Synchronizing %s\n", controllerSpec.ID)
			err = rm.syncReplicationController(controllerSpec)
			if err != nil {
				log.Printf("Error synchronizing: %#v", err)
			}
		}
	}
}
<|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 cpumanager

import (
	"fmt"
	"math"
	"sync"
	"time"

	"github.com/golang/glog"
	cadvisorapi "github.com/google/cadvisor/info/v1"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/wait"

	runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
	"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state"
	"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology"
	"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
	"k8s.io/kubernetes/pkg/kubelet/status"
)

// ActivePodsFunc is a function that returns a list of pods to reconcile.
type ActivePodsFunc func() []*v1.Pod

type runtimeService interface {
	UpdateContainerResources(id string, resources *runtimeapi.LinuxContainerResources) error
}

type policyName string

// Manager interface provides methods for Kubelet to manage pod cpus.
type Manager interface {
	// Start is called during Kubelet initialization.
	Start(activePods ActivePodsFunc, podStatusProvider status.PodStatusProvider, containerRuntime runtimeService)

	// AddContainer is called between container create and container start
	// so that initial CPU affinity settings can be written through to the
	// container runtime before the first process begins to execute.
	AddContainer(p *v1.Pod, c *v1.Container, containerID string) error

	// RemoveContainer is called after Kubelet decides to kill or delete a
	// container. After this call, the CPU manager stops trying to reconcile
	// that container and any CPUs dedicated to the container are freed.
	RemoveContainer(containerID string) error

	// State returns a read-only interface to the internal CPU manager state.
	State() state.Reader
}

type manager struct {
	sync.Mutex
	policy Policy

	// reconcilePeriod is the duration between calls to reconcileState.
	reconcilePeriod time.Duration

	// state allows pluggable CPU assignment policies while sharing a common
	// representation of state for the system to inspect and reconcile.
	state state.State

	// containerRuntime is the container runtime service interface needed
	// to make UpdateContainerResources() calls against the containers.
	containerRuntime runtimeService

	// activePods is a method for listing active pods on the node
	// so all the containers can be updated in the reconciliation loop.
	activePods ActivePodsFunc

	// podStatusProvider provides a method for obtaining pod statuses
	// and the containerID of their containers
	podStatusProvider status.PodStatusProvider

	machineInfo *cadvisorapi.MachineInfo

	nodeAllocatableReservation v1.ResourceList
}

var _ Manager = &manager{}

// NewManager creates new cpu manager based on provided policy
func NewManager(
	cpuPolicyName string,
	reconcilePeriod time.Duration,
	machineInfo *cadvisorapi.MachineInfo,
	nodeAllocatableReservation v1.ResourceList,
) (Manager, error) {
	var policy Policy

	switch policyName(cpuPolicyName) {

	case PolicyNone:
		policy = NewNonePolicy()

	case PolicyStatic:
		topo, err := topology.Discover(machineInfo)
		if err != nil {
			return nil, err
		}
		glog.Infof("[cpumanager] detected CPU topology: %v", topo)
		reservedCPUs, ok := nodeAllocatableReservation[v1.ResourceCPU]
		if !ok {
			// The static policy cannot initialize without this information. Panic!
			panic("[cpumanager] unable to determine reserved CPU resources for static policy")
		}
		if reservedCPUs.IsZero() {
			// Panic!
			//
			// The static policy requires this to be nonzero. Zero CPU reservation
			// would allow the shared pool to be completely exhausted. At that point
			// either we would violate our guarantee of exclusivity or need to evict
			// any pod that has at least one container that requires zero CPUs.
			// See the comments in policy_static.go for more details.
			panic("[cpumanager] the static policy requires systemreserved.cpu + kubereserved.cpu to be greater than zero")
		}

		// Take the ceiling of the reservation, since fractional CPUs cannot be
		// exclusively allocated.
		reservedCPUsFloat := float64(reservedCPUs.MilliValue()) / 1000
		numReservedCPUs := int(math.Ceil(reservedCPUsFloat))
		policy = NewStaticPolicy(topo, numReservedCPUs)

	default:
		glog.Errorf("[cpumanager] Unknown policy \"%s\", falling back to default policy \"%s\"", cpuPolicyName, PolicyNone)
		policy = NewNonePolicy()
	}

	manager := &manager{
		policy:                     policy,
		reconcilePeriod:            reconcilePeriod,
		state:                      state.NewMemoryState(),
		machineInfo:                machineInfo,
		nodeAllocatableReservation: nodeAllocatableReservation,
	}
	return manager, nil
}

func (m *manager) Start(activePods ActivePodsFunc, podStatusProvider status.PodStatusProvider, containerRuntime runtimeService) {
	glog.Infof("[cpumanger] starting with %s policy", m.policy.Name())
	glog.Infof("[cpumanger] reconciling every %v", m.reconcilePeriod)

	m.activePods = activePods
	m.podStatusProvider = podStatusProvider
	m.containerRuntime = containerRuntime

	m.policy.Start(m.state)
	if m.policy.Name() == string(PolicyNone) {
		return
	}
	go wait.Until(func() { m.reconcileState() }, m.reconcilePeriod, wait.NeverStop)
}

func (m *manager) AddContainer(p *v1.Pod, c *v1.Container, containerID string) error {
	m.Lock()
	err := m.policy.AddContainer(m.state, p, c, containerID)
	if err != nil {
		glog.Errorf("[cpumanager] AddContainer error: %v", err)
		m.Unlock()
		return err
	}
	cpus := m.state.GetCPUSetOrDefault(containerID)
	m.Unlock()

	if !cpus.IsEmpty() {
		err = m.updateContainerCPUSet(containerID, cpus)
		if err != nil {
			glog.Errorf("[cpumanager] AddContainer error: %v", err)
			return err
		}
	} else {
		glog.V(5).Infof("[cpumanager] update container resources is skipped due to cpu set is empty")
	}

	return nil
}

func (m *manager) RemoveContainer(containerID string) error {
	m.Lock()
	defer m.Unlock()

	err := m.policy.RemoveContainer(m.state, containerID)
	if err != nil {
		glog.Errorf("[cpumanager] RemoveContainer error: %v", err)
		return err
	}
	return nil
}

func (m *manager) State() state.Reader {
	return m.state
}

type reconciledContainer struct {
	podName       string
	containerName string
	containerID   string
}

func (m *manager) reconcileState() (success []reconciledContainer, failure []reconciledContainer) {
	success = []reconciledContainer{}
	failure = []reconciledContainer{}

	for _, pod := range m.activePods() {
		allContainers := pod.Spec.InitContainers
		allContainers = append(allContainers, pod.Spec.Containers...)
		for _, container := range allContainers {
			status, ok := m.podStatusProvider.GetPodStatus(pod.UID)
			if !ok {
				glog.Warningf("[cpumanager] reconcileState: skipping pod; status not found (pod: %s, container: %s)", pod.Name, container.Name)
				failure = append(failure, reconciledContainer{pod.Name, container.Name, ""})
				break
			}

			containerID, err := findContainerIDByName(&status, container.Name)
			if err != nil {
				glog.Warningf("[cpumanager] reconcileState: skipping container; ID not found in status (pod: %s, container: %s, error: %v)", pod.Name, container.Name, err)
				failure = append(failure, reconciledContainer{pod.Name, container.Name, ""})
				continue
			}

			cset := m.state.GetCPUSetOrDefault(containerID)
			if cset.IsEmpty() {
				// NOTE: This should not happen outside of tests.
				glog.Infof("[cpumanager] reconcileState: skipping container; assigned cpuset is empty (pod: %s, container: %s)", pod.Name, container.Name)
				failure = append(failure, reconciledContainer{pod.Name, container.Name, containerID})
				continue
			}

			glog.Infof("[cpumanager] reconcileState: updating container (pod: %s, container: %s, container id: %s, cpuset: \"%v\")", pod.Name, container.Name, containerID, cset)
			err = m.updateContainerCPUSet(containerID, cset)
			if err != nil {
				glog.Errorf("[cpumanager] reconcileState: failed to update container (pod: %s, container: %s, container id: %s, cpuset: \"%v\", error: %v)", pod.Name, container.Name, containerID, cset, err)
				failure = append(failure, reconciledContainer{pod.Name, container.Name, containerID})
				continue
			}
			success = append(success, reconciledContainer{pod.Name, container.Name, containerID})
		}
	}
	return success, failure
}

func findContainerIDByName(status *v1.PodStatus, name string) (string, error) {
	for _, container := range status.ContainerStatuses {
		if container.Name == name && container.ContainerID != "" {
			cid := &kubecontainer.ContainerID{}
			err := cid.ParseString(container.ContainerID)
			if err != nil {
				return "", err
			}
			return cid.ID, nil
		}
	}
	return "", fmt.Errorf("unable to find ID for container with name %v in pod status (it may not be running)", name)
}

func (m *manager) updateContainerCPUSet(containerID string, cpus cpuset.CPUSet) error {
	// TODO: Consider adding a `ResourceConfigForContainer` helper in
	// helpers_linux.go similar to what exists for pods.
	// It would be better to pass the full container resources here instead of
	// this patch-like partial resources.
	return m.containerRuntime.UpdateContainerResources(
		containerID,
		&runtimeapi.LinuxContainerResources{
			CpusetCpus: cpus.String(),
		})
}
<commit_msg>set leveled logging (v=4) for 'updating container' message<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 cpumanager

import (
	"fmt"
	"math"
	"sync"
	"time"

	"github.com/golang/glog"
	cadvisorapi "github.com/google/cadvisor/info/v1"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/wait"

	runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
	"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state"
	"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology"
	"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
	"k8s.io/kubernetes/pkg/kubelet/status"
)

// ActivePodsFunc is a function that returns a list of pods to reconcile.
type ActivePodsFunc func() []*v1.Pod

type runtimeService interface {
	UpdateContainerResources(id string, resources *runtimeapi.LinuxContainerResources) error
}

type policyName string

// Manager interface provides methods for Kubelet to manage pod cpus.
type Manager interface {
	// Start is called during Kubelet initialization.
	Start(activePods ActivePodsFunc, podStatusProvider status.PodStatusProvider, containerRuntime runtimeService)

	// AddContainer is called between container create and container start
	// so that initial CPU affinity settings can be written through to the
	// container runtime before the first process begins to execute.
	AddContainer(p *v1.Pod, c *v1.Container, containerID string) error

	// RemoveContainer is called after Kubelet decides to kill or delete a
	// container. After this call, the CPU manager stops trying to reconcile
	// that container and any CPUs dedicated to the container are freed.
	RemoveContainer(containerID string) error

	// State returns a read-only interface to the internal CPU manager state.
	State() state.Reader
}

type manager struct {
	sync.Mutex
	policy Policy

	// reconcilePeriod is the duration between calls to reconcileState.
	reconcilePeriod time.Duration

	// state allows pluggable CPU assignment policies while sharing a common
	// representation of state for the system to inspect and reconcile.
	state state.State

	// containerRuntime is the container runtime service interface needed
	// to make UpdateContainerResources() calls against the containers.
	containerRuntime runtimeService

	// activePods is a method for listing active pods on the node
	// so all the containers can be updated in the reconciliation loop.
	activePods ActivePodsFunc

	// podStatusProvider provides a method for obtaining pod statuses
	// and the containerID of their containers
	podStatusProvider status.PodStatusProvider

	machineInfo *cadvisorapi.MachineInfo

	nodeAllocatableReservation v1.ResourceList
}

var _ Manager = &manager{}

// NewManager creates new cpu manager based on provided policy
func NewManager(
	cpuPolicyName string,
	reconcilePeriod time.Duration,
	machineInfo *cadvisorapi.MachineInfo,
	nodeAllocatableReservation v1.ResourceList,
) (Manager, error) {
	var policy Policy

	switch policyName(cpuPolicyName) {

	case PolicyNone:
		policy = NewNonePolicy()

	case PolicyStatic:
		topo, err := topology.Discover(machineInfo)
		if err != nil {
			return nil, err
		}
		glog.Infof("[cpumanager] detected CPU topology: %v", topo)
		reservedCPUs, ok := nodeAllocatableReservation[v1.ResourceCPU]
		if !ok {
			// The static policy cannot initialize without this information. Panic!
			panic("[cpumanager] unable to determine reserved CPU resources for static policy")
		}
		if reservedCPUs.IsZero() {
			// Panic!
			//
			// The static policy requires this to be nonzero. Zero CPU reservation
			// would allow the shared pool to be completely exhausted. At that point
			// either we would violate our guarantee of exclusivity or need to evict
			// any pod that has at least one container that requires zero CPUs.
			// See the comments in policy_static.go for more details.
			panic("[cpumanager] the static policy requires systemreserved.cpu + kubereserved.cpu to be greater than zero")
		}

		// Take the ceiling of the reservation, since fractional CPUs cannot be
		// exclusively allocated.
		reservedCPUsFloat := float64(reservedCPUs.MilliValue()) / 1000
		numReservedCPUs := int(math.Ceil(reservedCPUsFloat))
		policy = NewStaticPolicy(topo, numReservedCPUs)

	default:
		glog.Errorf("[cpumanager] Unknown policy \"%s\", falling back to default policy \"%s\"", cpuPolicyName, PolicyNone)
		policy = NewNonePolicy()
	}

	manager := &manager{
		policy:                     policy,
		reconcilePeriod:            reconcilePeriod,
		state:                      state.NewMemoryState(),
		machineInfo:                machineInfo,
		nodeAllocatableReservation: nodeAllocatableReservation,
	}
	return manager, nil
}

func (m *manager) Start(activePods ActivePodsFunc, podStatusProvider status.PodStatusProvider, containerRuntime runtimeService) {
	glog.Infof("[cpumanger] starting with %s policy", m.policy.Name())
	glog.Infof("[cpumanger] reconciling every %v", m.reconcilePeriod)

	m.activePods = activePods
	m.podStatusProvider = podStatusProvider
	m.containerRuntime = containerRuntime

	m.policy.Start(m.state)
	if m.policy.Name() == string(PolicyNone) {
		return
	}
	go wait.Until(func() { m.reconcileState() }, m.reconcilePeriod, wait.NeverStop)
}

func (m *manager) AddContainer(p *v1.Pod, c *v1.Container, containerID string) error {
	m.Lock()
	err := m.policy.AddContainer(m.state, p, c, containerID)
	if err != nil {
		glog.Errorf("[cpumanager] AddContainer error: %v", err)
		m.Unlock()
		return err
	}
	cpus := m.state.GetCPUSetOrDefault(containerID)
	m.Unlock()

	if !cpus.IsEmpty() {
		err = m.updateContainerCPUSet(containerID, cpus)
		if err != nil {
			glog.Errorf("[cpumanager] AddContainer error: %v", err)
			return err
		}
	} else {
		glog.V(5).Infof("[cpumanager] update container resources is skipped due to cpu set is empty")
	}

	return nil
}

func (m *manager) RemoveContainer(containerID string) error {
	m.Lock()
	defer m.Unlock()

	err := m.policy.RemoveContainer(m.state, containerID)
	if err != nil {
		glog.Errorf("[cpumanager] RemoveContainer error: %v", err)
		return err
	}
	return nil
}

func (m *manager) State() state.Reader {
	return m.state
}

type reconciledContainer struct {
	podName       string
	containerName string
	containerID   string
}

func (m *manager) reconcileState() (success []reconciledContainer, failure []reconciledContainer) {
	success = []reconciledContainer{}
	failure = []reconciledContainer{}

	for _, pod := range m.activePods() {
		allContainers := pod.Spec.InitContainers
		allContainers = append(allContainers, pod.Spec.Containers...)
		for _, container := range allContainers {
			status, ok := m.podStatusProvider.GetPodStatus(pod.UID)
			if !ok {
				glog.Warningf("[cpumanager] reconcileState: skipping pod; status not found (pod: %s, container: %s)", pod.Name, container.Name)
				failure = append(failure, reconciledContainer{pod.Name, container.Name, ""})
				break
			}

			containerID, err := findContainerIDByName(&status, container.Name)
			if err != nil {
				glog.Warningf("[cpumanager] reconcileState: skipping container; ID not found in status (pod: %s, container: %s, error: %v)", pod.Name, container.Name, err)
				failure = append(failure, reconciledContainer{pod.Name, container.Name, ""})
				continue
			}

			cset := m.state.GetCPUSetOrDefault(containerID)
			if cset.IsEmpty() {
				// NOTE: This should not happen outside of tests.
				glog.Infof("[cpumanager] reconcileState: skipping container; assigned cpuset is empty (pod: %s, container: %s)", pod.Name, container.Name)
				failure = append(failure, reconciledContainer{pod.Name, container.Name, containerID})
				continue
			}

			glog.V(4).Infof("[cpumanager] reconcileState: updating container (pod: %s, container: %s, container id: %s, cpuset: \"%v\")", pod.Name, container.Name, containerID, cset)
			err = m.updateContainerCPUSet(containerID, cset)
			if err != nil {
				glog.Errorf("[cpumanager] reconcileState: failed to update container (pod: %s, container: %s, container id: %s, cpuset: \"%v\", error: %v)", pod.Name, container.Name, containerID, cset, err)
				failure = append(failure, reconciledContainer{pod.Name, container.Name, containerID})
				continue
			}
			success = append(success, reconciledContainer{pod.Name, container.Name, containerID})
		}
	}
	return success, failure
}

func findContainerIDByName(status *v1.PodStatus, name string) (string, error) {
	for _, container := range status.ContainerStatuses {
		if container.Name == name && container.ContainerID != "" {
			cid := &kubecontainer.ContainerID{}
			err := cid.ParseString(container.ContainerID)
			if err != nil {
				return "", err
			}
			return cid.ID, nil
		}
	}
	return "", fmt.Errorf("unable to find ID for container with name %v in pod status (it may not be running)", name)
}

func (m *manager) updateContainerCPUSet(containerID string, cpus cpuset.CPUSet) error {
	// TODO: Consider adding a `ResourceConfigForContainer` helper in
	// helpers_linux.go similar to what exists for pods.
	// It would be better to pass the full container resources here instead of
	// this patch-like partial resources.
	return m.containerRuntime.UpdateContainerResources(
		containerID,
		&runtimeapi.LinuxContainerResources{
			CpusetCpus: cpus.String(),
		})
}
<|endoftext|>


<commit_before>/*
Copyright 2018 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 revision

import (
	"context"
	"testing"
	"time"

	"github.com/google/go-containerregistry/pkg/authn/k8schain"
	"golang.org/x/sync/errgroup"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/metrics"
	"knative.dev/pkg/ptr"
	"knative.dev/pkg/system"
	"knative.dev/pkg/tracing"
	tracingconfig "knative.dev/pkg/tracing/config"
	tracetesting "knative.dev/pkg/tracing/testing"
	autoscalingv1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
	"knative.dev/serving/pkg/apis/config"
	"knative.dev/serving/pkg/apis/serving"
	v1 "knative.dev/serving/pkg/apis/serving/v1"
	autoscalerconfig "knative.dev/serving/pkg/autoscaler/config"
	fakeservingclient "knative.dev/serving/pkg/client/injection/client/fake"
	"knative.dev/serving/pkg/deployment"
	"knative.dev/serving/pkg/network"

	. "knative.dev/pkg/reconciler/testing"
)

type nopResolver struct{}

func (r *nopResolver) Resolve(_ string, _ k8schain.Options, _ sets.String) (string, error) {
	return "", nil
}

const (
	testAutoscalerImage = "autoscalerImage"
	testNamespace       = "test"
	testQueueImage      = "queueImage"
)

func getPodSpec() corev1.PodSpec {
	return corev1.PodSpec{
		// corev1.Container has a lot of setting.  We try to pass many
		// of them here to verify that we pass through the settings to
		// derived objects.
		Containers: []corev1.Container{{
			Image:      "gcr.io/repo/image",
			Command:    []string{"echo"},
			Args:       []string{"hello", "world"},
			WorkingDir: "/tmp",
			Env: []corev1.EnvVar{{
				Name:  "EDITOR",
				Value: "emacs",
			}},
			LivenessProbe: &corev1.Probe{
				TimeoutSeconds: 42,
			},
			ReadinessProbe: &corev1.Probe{
				Handler: corev1.Handler{
					HTTPGet: &corev1.HTTPGetAction{
						Path: "health",
					},
				},
				TimeoutSeconds: 43,
			},
			TerminationMessagePath: "/dev/null",
		}},
	}
}

func testRevision(podSpec corev1.PodSpec) *v1.Revision {
	rev := &v1.Revision{
		ObjectMeta: metav1.ObjectMeta{
			SelfLink:  "/apis/serving/v1/namespaces/test/revisions/test-rev",
			Name:      "test-rev",
			Namespace: testNamespace,
			Labels: map[string]string{
				"testLabel1":          "foo",
				"testLabel2":          "bar",
				serving.RouteLabelKey: "test-route",
			},
			Annotations: map[string]string{
				"testAnnotation": "test",
			},
			UID: "test-rev-uid",
		},
		Spec: v1.RevisionSpec{
			PodSpec:        podSpec,
			TimeoutSeconds: ptr.Int64(60),
		},
	}
	rev.SetDefaults(context.Background())
	return rev
}

func getTestDeploymentConfig() *deployment.Config {
	c, _ := deployment.NewConfigFromConfigMap(getTestDeploymentConfigMap())
	// ignoring error as test controller is generated
	return c
}

func getTestDeploymentConfigMap() *corev1.ConfigMap {
	return &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      deployment.ConfigName,
			Namespace: system.Namespace(),
		},
		Data: map[string]string{
			"queueSidecarImage": testQueueImage,
			"autoscalerImage":   testAutoscalerImage,
		},
	}
}

func getTestDefaultsConfigMap() *corev1.ConfigMap {
	return &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      config.DefaultsConfigName,
			Namespace: system.Namespace(),
		},
		Data: map[string]string{
			"container-name-template": "user-container",
		},
	}
}

func newTestController(t *testing.T, opts ...reconcilerOption) (
	context.Context,
	context.CancelFunc,
	[]controller.Informer,
	*controller.Impl,
	*configmap.ManualWatcher) {

	ctx, cancel, informers := SetupFakeContextWithCancel(t)
	configMapWatcher := &configmap.ManualWatcher{Namespace: system.Namespace()}

	// Prepend so that callers can override.
	opts = append([]reconcilerOption{func(r *Reconciler) {
		r.resolver = &nopResolver{}
	}}, opts...)
	controller := newControllerWithOptions(ctx, configMapWatcher, opts...)

	configs := []*corev1.ConfigMap{
		getTestDeploymentConfigMap(),
		getTestDefaultsConfigMap(),
		{
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      network.ConfigName,
			}}, {
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      logging.ConfigMapName(),
			},
			Data: map[string]string{
				"zap-logger-config":   "{\"level\": \"error\",\n\"outputPaths\": [\"stdout\"],\n\"errorOutputPaths\": [\"stderr\"],\n\"encoding\": \"json\"}",
				"loglevel.queueproxy": "info",
			}}, {
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      tracingconfig.ConfigName,
			},
			Data: map[string]string{
				"enable":          "true",
				"debug":           "true",
				"zipkin-endpoint": "http://zipkin.istio-system.svc.cluster.local:9411/api/v2/spans",
			},
		}, {
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      metrics.ConfigMapName(),
			},
			Data: map[string]string{
				"logging.enable-var-log-collection": "true",
			}}, {
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      autoscalerconfig.ConfigName,
			},
			Data: map[string]string{
				"max-scale-up-rate":                       "2.0",
				"container-concurrency-target-percentage": "0.5",
				"container-concurrency-target-default":    "10.0",
				"stable-window":                           "5m",
				"panic-window":                            "10s",
				"scale-to-zero-threshold":                 "10m",
				"tick-interval":                           "2s",
			}},
	}
	for _, configMap := range configs {
		configMapWatcher.OnChange(configMap)
	}

	return ctx, cancel, informers, controller, configMapWatcher
}

func TestNewRevisionCallsSyncHandler(t *testing.T) {
	ctx, cancel, informers, ctrl, _ := newTestController(t)
	// Create tracer with reporter recorder
	reporter, co := tracetesting.FakeZipkinExporter()
	defer reporter.Close()
	oct := tracing.NewOpenCensusTracer(co)
	defer oct.Finish()

	cfg := tracingconfig.Config{
		Backend: tracingconfig.Zipkin,
		Debug:   true,
	}
	if err := oct.ApplyConfig(&cfg); err != nil {
		t.Errorf("Failed to apply tracer config: %v", err)
	}

	eg := errgroup.Group{}

	rev := testRevision(getPodSpec())
	servingClient := fakeservingclient.Get(ctx)

	h := NewHooks()

	// Check for a service created as a signal that syncHandler ran
	h.OnCreate(&servingClient.Fake, "podautoscalers", func(obj runtime.Object) HookResult {
		pa := obj.(*autoscalingv1alpha1.PodAutoscaler)
		t.Logf("PA created: %s", pa.Name)
		return HookComplete
	})

	waitInformers, err := controller.RunInformers(ctx.Done(), informers...)
	if err != nil {
		t.Fatal("Error starting informers:", err)
	}
	defer func() {
		cancel()
		if err := eg.Wait(); err != nil {
			t.Fatal("Error running controller:", err)
		}
		waitInformers()
	}()

	eg.Go(func() error {
		return ctrl.Run(2, ctx.Done())
	})

	if _, err := servingClient.ServingV1().Revisions(rev.Namespace).Create(rev); err != nil {
		t.Fatal("Error creating revision:", err)
	}

	if err := h.WaitForHooks(time.Second * 3); err != nil {
		t.Error(err)
	}
}
<commit_msg>Fix the flake in the revision test (#8289)<commit_after>/*
Copyright 2018 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 revision

import (
	"context"
	"testing"
	"time"

	"github.com/google/go-containerregistry/pkg/authn/k8schain"
	"golang.org/x/sync/errgroup"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/metrics"
	"knative.dev/pkg/ptr"
	"knative.dev/pkg/system"
	"knative.dev/pkg/tracing"
	tracingconfig "knative.dev/pkg/tracing/config"
	tracetesting "knative.dev/pkg/tracing/testing"
	"knative.dev/serving/pkg/apis/config"
	"knative.dev/serving/pkg/apis/serving"
	v1 "knative.dev/serving/pkg/apis/serving/v1"
	autoscalerconfig "knative.dev/serving/pkg/autoscaler/config"
	fakeservingclient "knative.dev/serving/pkg/client/injection/client/fake"
	"knative.dev/serving/pkg/deployment"
	"knative.dev/serving/pkg/network"

	. "knative.dev/pkg/reconciler/testing"
)

type nopResolver struct{}

func (r *nopResolver) Resolve(_ string, _ k8schain.Options, _ sets.String) (string, error) {
	return "", nil
}

const (
	testAutoscalerImage = "autoscalerImage"
	testNamespace       = "test"
	testQueueImage      = "queueImage"
)

func getPodSpec() corev1.PodSpec {
	return corev1.PodSpec{
		// corev1.Container has a lot of setting.  We try to pass many
		// of them here to verify that we pass through the settings to
		// derived objects.
		Containers: []corev1.Container{{
			Image:      "gcr.io/repo/image",
			Command:    []string{"echo"},
			Args:       []string{"hello", "world"},
			WorkingDir: "/tmp",
			Env: []corev1.EnvVar{{
				Name:  "EDITOR",
				Value: "emacs",
			}},
			LivenessProbe: &corev1.Probe{
				TimeoutSeconds: 42,
			},
			ReadinessProbe: &corev1.Probe{
				Handler: corev1.Handler{
					HTTPGet: &corev1.HTTPGetAction{
						Path: "health",
					},
				},
				TimeoutSeconds: 43,
			},
			TerminationMessagePath: "/dev/null",
		}},
	}
}

func testRevision(podSpec corev1.PodSpec) *v1.Revision {
	rev := &v1.Revision{
		ObjectMeta: metav1.ObjectMeta{
			SelfLink:  "/apis/serving/v1/namespaces/test/revisions/test-rev",
			Name:      "test-rev",
			Namespace: testNamespace,
			Labels: map[string]string{
				"testLabel1":          "foo",
				"testLabel2":          "bar",
				serving.RouteLabelKey: "test-route",
			},
			Annotations: map[string]string{
				"testAnnotation": "test",
			},
			UID: "test-rev-uid",
		},
		Spec: v1.RevisionSpec{
			PodSpec:        podSpec,
			TimeoutSeconds: ptr.Int64(60),
		},
	}
	rev.SetDefaults(context.Background())
	return rev
}

func getTestDeploymentConfig() *deployment.Config {
	c, _ := deployment.NewConfigFromConfigMap(getTestDeploymentConfigMap())
	// ignoring error as test controller is generated
	return c
}

func getTestDeploymentConfigMap() *corev1.ConfigMap {
	return &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      deployment.ConfigName,
			Namespace: system.Namespace(),
		},
		Data: map[string]string{
			"queueSidecarImage": testQueueImage,
			"autoscalerImage":   testAutoscalerImage,
		},
	}
}

func getTestDefaultsConfigMap() *corev1.ConfigMap {
	return &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      config.DefaultsConfigName,
			Namespace: system.Namespace(),
		},
		Data: map[string]string{
			"container-name-template": "user-container",
		},
	}
}

func newTestController(t *testing.T, opts ...reconcilerOption) (
	context.Context,
	context.CancelFunc,
	[]controller.Informer,
	*controller.Impl,
	*configmap.ManualWatcher) {

	ctx, cancel, informers := SetupFakeContextWithCancel(t)
	configMapWatcher := &configmap.ManualWatcher{Namespace: system.Namespace()}

	// Prepend so that callers can override.
	opts = append([]reconcilerOption{func(r *Reconciler) {
		r.resolver = &nopResolver{}
	}}, opts...)
	controller := newControllerWithOptions(ctx, configMapWatcher, opts...)

	configs := []*corev1.ConfigMap{
		getTestDeploymentConfigMap(),
		getTestDefaultsConfigMap(),
		{
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      network.ConfigName,
			}}, {
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      logging.ConfigMapName(),
			},
			Data: map[string]string{
				"zap-logger-config":   "{\"level\": \"error\",\n\"outputPaths\": [\"stdout\"],\n\"errorOutputPaths\": [\"stderr\"],\n\"encoding\": \"json\"}",
				"loglevel.queueproxy": "info",
			}}, {
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      tracingconfig.ConfigName,
			},
			Data: map[string]string{
				"enable":          "true",
				"debug":           "true",
				"zipkin-endpoint": "http://zipkin.istio-system.svc.cluster.local:9411/api/v2/spans",
			},
		}, {
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      metrics.ConfigMapName(),
			},
			Data: map[string]string{
				"logging.enable-var-log-collection": "true",
			}}, {
			ObjectMeta: metav1.ObjectMeta{
				Namespace: system.Namespace(),
				Name:      autoscalerconfig.ConfigName,
			},
			Data: map[string]string{
				"max-scale-up-rate":                       "2.0",
				"container-concurrency-target-percentage": "0.5",
				"container-concurrency-target-default":    "10.0",
				"stable-window":                           "5m",
				"panic-window":                            "10s",
				"scale-to-zero-threshold":                 "10m",
				"tick-interval":                           "2s",
			}},
	}
	for _, configMap := range configs {
		configMapWatcher.OnChange(configMap)
	}

	return ctx, cancel, informers, controller, configMapWatcher
}

func TestNewRevisionCallsSyncHandler(t *testing.T) {
	ctx, cancel, informers, ctrl, _ := newTestController(t)
	// Create tracer with reporter recorder
	reporter, co := tracetesting.FakeZipkinExporter()
	defer reporter.Close()
	oct := tracing.NewOpenCensusTracer(co)
	defer oct.Finish()

	cfg := tracingconfig.Config{
		Backend: tracingconfig.Zipkin,
		Debug:   true,
	}
	if err := oct.ApplyConfig(&cfg); err != nil {
		t.Errorf("Failed to apply tracer config: %v", err)
	}

	eg := errgroup.Group{}

	rev := testRevision(getPodSpec())
	servingClient := fakeservingclient.Get(ctx)

	waitInformers, err := controller.RunInformers(ctx.Done(), informers...)
	if err != nil {
		t.Fatal("Error starting informers:", err)
	}
	defer func() {
		cancel()
		if err := eg.Wait(); err != nil {
			t.Fatal("Error running controller:", err)
		}
		waitInformers()
	}()

	eg.Go(func() error {
		return ctrl.Run(2, ctx.Done())
	})

	if _, err := servingClient.ServingV1().Revisions(rev.Namespace).Create(rev); err != nil {
		t.Fatal("Error creating revision:", err)
	}

	// Poll to see PA object to be created.
	if err := wait.PollImmediate(10*time.Millisecond, 5*time.Second, func() (bool, error) {
		pa, _ := servingClient.AutoscalingV1alpha1().PodAutoscalers(rev.Namespace).Get(
			rev.Name, metav1.GetOptions{})
		return pa != nil, nil
	}); err != nil {
		t.Error("Failed to see PA creation")
	}
}
<|endoftext|>


<commit_before>package services

import (
	"fmt"
	"strconv"
	"strings"

	kubeapi "k8s.io/client-go/pkg/api"
	kubev1 "k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/batch"
	metav1 "k8s.io/client-go/pkg/apis/meta/v1"

	"kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/logging"
	"kubevirt.io/kubevirt/pkg/precond"
)

type TemplateService interface {
	RenderLaunchManifest(*v1.VM) (*kubev1.Pod, error)
	RenderMigrationJob(*v1.VM, *kubev1.Node, *kubev1.Node) (*batch.Job, error)
}

type templateService struct {
	launcherImage string
}

//Deprecated: remove the service and just use a builder or contextcless helper function
func (t *templateService) RenderLaunchManifest(vm *v1.VM) (*kubev1.Pod, error) {
	precond.MustNotBeNil(vm)
	domain := precond.MustNotBeEmpty(vm.GetObjectMeta().GetName())
	uid := precond.MustNotBeEmpty(string(vm.GetObjectMeta().GetUID()))

	// VM target container
	container := kubev1.Container{
		Name:            "compute",
		Image:           t.launcherImage,
		ImagePullPolicy: kubev1.PullIfNotPresent,
		Command:         []string{"/virt-launcher", "-qemu-timeout", "60s"},
	}

	// Set up spice ports
	ports := []kubev1.ContainerPort{}
	for i, g := range vm.Spec.Domain.Devices.Graphics {
		if strings.ToLower(g.Type) == "spice" {
			ports = append(ports, kubev1.ContainerPort{
				ContainerPort: g.Port,
				Name:          "spice" + strconv.Itoa(i),
			})
		}
	}
	container.Ports = ports

	// TODO use constants for labels
	pod := kubev1.Pod{
		ObjectMeta: kubev1.ObjectMeta{
			GenerateName: "virt-launcher-" + domain + "-----",
			Labels: map[string]string{
				v1.AppLabel:    "virt-launcher",
				v1.DomainLabel: domain,
				v1.UIDLabel:    uid,
			},
		},
		Spec: kubev1.PodSpec{
			RestartPolicy: kubev1.RestartPolicyNever,
			Containers:    []kubev1.Container{container},
			NodeSelector:  vm.Spec.NodeSelector,
		},
	}

	return &pod, nil
}

func (t *templateService) RenderMigrationJob(vm *v1.VM, sourceNode *kubev1.Node, targetNode *kubev1.Node) (*batch.Job, error) {
	srcAddr := ""
	dstAddr := ""
	for _, addr := range sourceNode.Status.Addresses {
		if addr.Type == kubev1.NodeHostName {
			srcAddr = addr.Address
			break
		}
	}
	if srcAddr == "" {
		for _, addr := range sourceNode.Status.Addresses {
			if addr.Type == kubev1.NodeInternalIP {
				srcAddr = addr.Address
				break
			}
		}
	}
	if srcAddr == "" {
		err := fmt.Errorf("migration source node is unreachable")
		logging.DefaultLogger().Error().Msg("migration target node is unreachable")
		return nil, err
	}
	srcUri := fmt.Sprintf("qemu+tcp://%s", srcAddr)

	for _, addr := range targetNode.Status.Addresses {
		if addr.Type == kubev1.NodeHostName {
			dstAddr = addr.Address
			break
		}
	}
	if dstAddr == "" {
		for _, addr := range targetNode.Status.Addresses {
			if addr.Type == kubev1.NodeInternalIP {
				dstAddr = addr.Address
				break
			}
		}
	}
	if dstAddr == "" {
		err := fmt.Errorf("migration target node is unreachable")
		logging.DefaultLogger().Error().Msg("migration target node is unreachable")
		return nil, err
	}
	destUri := fmt.Sprintf("qemu+tcp://%s", dstAddr)

	job := batch.Job{
		ObjectMeta: kubeapi.ObjectMeta{
			GenerateName: "virt-migration",
		},
		TypeMeta: metav1.TypeMeta{
			Kind: "Job",
		},
		Spec: batch.JobSpec{
			Template: kubeapi.PodTemplateSpec{
				Spec: kubeapi.PodSpec{
					RestartPolicy: kubeapi.RestartPolicyNever,
					Containers: []kubeapi.Container{
						kubeapi.Container{
							Name:  "virt-migration",
							Image: "kubevirt/virt-handler:devel",
							Command: []string{
								"virsh", "migrate", vm.Spec.Domain.Name, destUri, srcUri,
							},
						},
					},
				},
			},
		},
	}

	return &job, nil
}

func NewTemplateService(launcherImage string) (TemplateService, error) {
	precond.MustNotBeEmpty(launcherImage)
	svc := templateService{
		launcherImage: launcherImage,
	}
	return &svc, nil
}
<commit_msg>Check node addresses in a single pass<commit_after>package services

import (
	"fmt"
	"strconv"
	"strings"

	kubeapi "k8s.io/client-go/pkg/api"
	kubev1 "k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/batch"
	metav1 "k8s.io/client-go/pkg/apis/meta/v1"

	"kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/logging"
	"kubevirt.io/kubevirt/pkg/precond"
)

type TemplateService interface {
	RenderLaunchManifest(*v1.VM) (*kubev1.Pod, error)
	RenderMigrationJob(*v1.VM, *kubev1.Node, *kubev1.Node) (*batch.Job, error)
}

type templateService struct {
	launcherImage string
}

//Deprecated: remove the service and just use a builder or contextcless helper function
func (t *templateService) RenderLaunchManifest(vm *v1.VM) (*kubev1.Pod, error) {
	precond.MustNotBeNil(vm)
	domain := precond.MustNotBeEmpty(vm.GetObjectMeta().GetName())
	uid := precond.MustNotBeEmpty(string(vm.GetObjectMeta().GetUID()))

	// VM target container
	container := kubev1.Container{
		Name:            "compute",
		Image:           t.launcherImage,
		ImagePullPolicy: kubev1.PullIfNotPresent,
		Command:         []string{"/virt-launcher", "-qemu-timeout", "60s"},
	}

	// Set up spice ports
	ports := []kubev1.ContainerPort{}
	for i, g := range vm.Spec.Domain.Devices.Graphics {
		if strings.ToLower(g.Type) == "spice" {
			ports = append(ports, kubev1.ContainerPort{
				ContainerPort: g.Port,
				Name:          "spice" + strconv.Itoa(i),
			})
		}
	}
	container.Ports = ports

	// TODO use constants for labels
	pod := kubev1.Pod{
		ObjectMeta: kubev1.ObjectMeta{
			GenerateName: "virt-launcher-" + domain + "-----",
			Labels: map[string]string{
				v1.AppLabel:    "virt-launcher",
				v1.DomainLabel: domain,
				v1.UIDLabel:    uid,
			},
		},
		Spec: kubev1.PodSpec{
			RestartPolicy: kubev1.RestartPolicyNever,
			Containers:    []kubev1.Container{container},
			NodeSelector:  vm.Spec.NodeSelector,
		},
	}

	return &pod, nil
}

func (t *templateService) RenderMigrationJob(vm *v1.VM, sourceNode *kubev1.Node, targetNode *kubev1.Node) (*batch.Job, error) {
	srcAddr := ""
	dstAddr := ""
	for _, addr := range sourceNode.Status.Addresses {
		if addr.Type == kubev1.NodeHostName {
			srcAddr = addr.Address
			break
		}
		if (addr.Type == kubev1.NodeInternalIP) && (srcAddr == "") {
			// record this address, but keep iterating addresses. A NodeHostName record
			// would be preferred if present.
			srcAddr = addr.Address
		}
	}
	if srcAddr == "" {
		err := fmt.Errorf("migration source node is unreachable")
		logging.DefaultLogger().Error().Msg("migration target node is unreachable")
		return nil, err
	}
	srcUri := fmt.Sprintf("qemu+tcp://%s", srcAddr)

	for _, addr := range targetNode.Status.Addresses {
		if addr.Type == kubev1.NodeHostName {
			dstAddr = addr.Address
			break
		}
		if (addr.Type == kubev1.NodeInternalIP) && (dstAddr == "") {
			dstAddr = addr.Address
		}
	}
	if dstAddr == "" {
		err := fmt.Errorf("migration target node is unreachable")
		logging.DefaultLogger().Error().Msg("migration target node is unreachable")
		return nil, err
	}
	destUri := fmt.Sprintf("qemu+tcp://%s", dstAddr)

	job := batch.Job{
		ObjectMeta: kubeapi.ObjectMeta{
			GenerateName: "virt-migration",
		},
		TypeMeta: metav1.TypeMeta{
			Kind: "Job",
		},
		Spec: batch.JobSpec{
			Template: kubeapi.PodTemplateSpec{
				Spec: kubeapi.PodSpec{
					RestartPolicy: kubeapi.RestartPolicyNever,
					Containers: []kubeapi.Container{
						kubeapi.Container{
							Name:  "virt-migration",
							Image: "kubevirt/virt-handler:devel",
							Command: []string{
								"virsh", "migrate", vm.Spec.Domain.Name, destUri, srcUri,
							},
						},
					},
				},
			},
		},
	}

	return &job, nil
}

func NewTemplateService(launcherImage string) (TemplateService, error) {
	precond.MustNotBeEmpty(launcherImage)
	svc := templateService{
		launcherImage: launcherImage,
	}
	return &svc, nil
}
<|endoftext|>