<commit_before>// +build linux

package apparmor // import "github.com/docker/docker/profiles/apparmor"

// baseTemplate defines the default apparmor profile for containers.
const baseTemplate = `
{{range $value := .Imports}}
{{$value}}
{{end}}

profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
{{range $value := .InnerImports}}
  {{$value}}
{{end}}

  network,
  capability,
  file,
  umount,
{{if ge .Version 208096}}
{{/* Allow 'docker kill' to actually send signals to container processes. */}}
  signal (receive) peer={{.DaemonProfile}},
{{/* Allow container processes to send signals amongst themselves. */}}
  signal (send,receive) peer={{.Name}},
{{end}}

  deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
  # deny write to files not in /proc/<number>/** or /proc/sys/**
  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
  deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
  deny @{PROC}/sysrq-trigger rwklx,
  deny @{PROC}/kcore rwklx,

  deny mount,

  deny /sys/[^f]*/** wklx,
  deny /sys/f[^s]*/** wklx,
  deny /sys/fs/[^c]*/** wklx,
  deny /sys/fs/c[^g]*/** wklx,
  deny /sys/fs/cg[^r]*/** wklx,
  deny /sys/firmware/** rwklx,
  deny /sys/kernel/security/** rwklx,

{{if ge .Version 208095}}
  # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
  ptrace (trace,read) peer={{.Name}},
{{end}}
}
`
<commit_msg>apparmor: allow readby and tracedby<commit_after>// +build linux

package apparmor // import "github.com/docker/docker/profiles/apparmor"

// baseTemplate defines the default apparmor profile for containers.
const baseTemplate = `
{{range $value := .Imports}}
{{$value}}
{{end}}

profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
{{range $value := .InnerImports}}
  {{$value}}
{{end}}

  network,
  capability,
  file,
  umount,
{{if ge .Version 208096}}
{{/* Allow 'docker kill' to actually send signals to container processes. */}}
  signal (receive) peer={{.DaemonProfile}},
{{/* Allow container processes to send signals amongst themselves. */}}
  signal (send,receive) peer={{.Name}},
{{end}}

  deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
  # deny write to files not in /proc/<number>/** or /proc/sys/**
  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
  deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
  deny @{PROC}/sysrq-trigger rwklx,
  deny @{PROC}/kcore rwklx,

  deny mount,

  deny /sys/[^f]*/** wklx,
  deny /sys/f[^s]*/** wklx,
  deny /sys/fs/[^c]*/** wklx,
  deny /sys/fs/c[^g]*/** wklx,
  deny /sys/fs/cg[^r]*/** wklx,
  deny /sys/firmware/** rwklx,
  deny /sys/kernel/security/** rwklx,

{{if ge .Version 208095}}
  # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
  ptrace (trace,read,tracedby,readby) peer={{.Name}},
{{end}}
}
`
<|endoftext|>


<commit_before>// Copyright 2010 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 main

import (
	"bytes"
	"exec"
	"flag"
	"http"
	"io"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strconv"
	"template"
)

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)
	// the architecture-identifying character of the tool chain, 5, 6, or 8
	archChar string
)

func main() {
	flag.Parse()

	// set archChar
	switch runtime.GOARCH {
	case "arm":
		archChar = "5"
	case "amd64":
		archChar = "6"
	case "386":
		archChar = "8"
	default:
		log.Fatalln("unrecognized GOARCH:", runtime.GOARCH)
	}

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile", Compile)
	log.Fatal(http.ListenAndServe(*httpListen, nil))
}

// FrontPage is an HTTP handler that renders the goplay interface.
// If a filename is supplied in the path component of the URI,
// its contents will be put in the interface's text area.
// Otherwise, the default "hello, world" program is displayed.
func FrontPage(w http.ResponseWriter, req *http.Request) {
	data, err := ioutil.ReadFile(req.URL.Path[1:])
	if err != nil {
		data = helloWorld
	}
	frontPage.Execute(w, data)
}

// Compile is an HTTP handler that reads Go source code from the request,
// compiles and links the code (returning any errors), runs the program,
// and sends the program's output as the HTTP response.
func Compile(w http.ResponseWriter, req *http.Request) {
	// x is the base name for .go, .6, executable files
	x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq)
	src := x + ".go"
	obj := x + "." + archChar
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// write request Body to x.go
	f, err := os.Create(src)
	if err != nil {
		error(w, nil, err)
		return
	}
	defer os.Remove(src)
	defer f.Close()
	_, err = io.Copy(f, req.Body)
	if err != nil {
		error(w, nil, err)
		return
	}
	f.Close()

	// build x.go, creating x.6
	out, err := run(archChar+"g", "-o", obj, src)
	defer os.Remove(obj)
	if err != nil {
		error(w, out, err)
		return
	}

	// link x.6, creating x (the program binary)
	out, err = run(archChar+"l", "-o", bin, obj)
	defer os.Remove(bin)
	if err != nil {
		error(w, out, err)
		return
	}

	// run x
	out, err = run(bin)
	if err != nil {
		error(w, out, err)
	}

	// write the output of x as the http response
	if *htmlOutput {
		w.Write(out)
	} else {
		output.Execute(w, out)
	}
}

// error writes compile, link, or runtime errors to the HTTP connection.
// The JavaScript interface uses the 404 status code to identify the error.
func error(w http.ResponseWriter, out []byte, err os.Error) {
	w.WriteHeader(404)
	if out != nil {
		output.Execute(w, out)
	} else {
		output.Execute(w, err.String())
	}
}

// run executes the specified command and returns its output and an error.
func run(cmd ...string) ([]byte, os.Error) {
	// find the specified binary
	bin, err := exec.LookPath(cmd[0])
	if err != nil {
		// report binary as well as the error
		return nil, os.NewError(cmd[0] + ": " + err.String())
	}

	// run the binary and read its combined stdout and stderr into a buffer
	p, err := exec.Run(bin, cmd, os.Environ(), "", exec.DevNull, exec.Pipe, exec.MergeWithStdout)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	io.Copy(&buf, p.Stdout)
	w, err := p.Wait(0)
	p.Close()
	if err != nil {
		return nil, err
	}

	// set the error return value if the program had a non-zero exit status
	if !w.Exited() || w.ExitStatus() != 0 {
		err = os.ErrorString("running " + cmd[0] + ": " + w.String())
	}

	return buf.Bytes(), err
}

var frontPage, output *template.Template // HTML templates

func init() {
	frontPage = template.New(nil)
	frontPage.SetDelims("«", "»")
	if err := frontPage.Parse(frontPageText); err != nil {
		panic(err)
	}
	output = template.MustParse(outputText, nil)
}

var outputText = `<pre>{@|html}</pre>`

var frontPageText = `<!doctype html>

<textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler();" onkeyup="autocompile();">«@|html»</textarea>

(Shift-Enter to compile and run.)
<input type="checkbox" id="autocompile" value="checked" /> Compile and run after each keystroke

`

var helloWorld = []byte(`package main

import "fmt"

func main() {
	fmt.Println("hello, world")
}
`)
<commit_msg>misc/goplay: fix Tab and Shift+Enter in Firefox.<commit_after>// Copyright 2010 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 main

import (
	"bytes"
	"exec"
	"flag"
	"http"
	"io"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strconv"
	"template"
)

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)
	// the architecture-identifying character of the tool chain, 5, 6, or 8
	archChar string
)

func main() {
	flag.Parse()

	// set archChar
	switch runtime.GOARCH {
	case "arm":
		archChar = "5"
	case "amd64":
		archChar = "6"
	case "386":
		archChar = "8"
	default:
		log.Fatalln("unrecognized GOARCH:", runtime.GOARCH)
	}

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile", Compile)
	log.Fatal(http.ListenAndServe(*httpListen, nil))
}

// FrontPage is an HTTP handler that renders the goplay interface.
// If a filename is supplied in the path component of the URI,
// its contents will be put in the interface's text area.
// Otherwise, the default "hello, world" program is displayed.
func FrontPage(w http.ResponseWriter, req *http.Request) {
	data, err := ioutil.ReadFile(req.URL.Path[1:])
	if err != nil {
		data = helloWorld
	}
	frontPage.Execute(w, data)
}

// Compile is an HTTP handler that reads Go source code from the request,
// compiles and links the code (returning any errors), runs the program,
// and sends the program's output as the HTTP response.
func Compile(w http.ResponseWriter, req *http.Request) {
	// x is the base name for .go, .6, executable files
	x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq)
	src := x + ".go"
	obj := x + "." + archChar
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// write request Body to x.go
	f, err := os.Create(src)
	if err != nil {
		error(w, nil, err)
		return
	}
	defer os.Remove(src)
	defer f.Close()
	_, err = io.Copy(f, req.Body)
	if err != nil {
		error(w, nil, err)
		return
	}
	f.Close()

	// build x.go, creating x.6
	out, err := run(archChar+"g", "-o", obj, src)
	defer os.Remove(obj)
	if err != nil {
		error(w, out, err)
		return
	}

	// link x.6, creating x (the program binary)
	out, err = run(archChar+"l", "-o", bin, obj)
	defer os.Remove(bin)
	if err != nil {
		error(w, out, err)
		return
	}

	// run x
	out, err = run(bin)
	if err != nil {
		error(w, out, err)
	}

	// write the output of x as the http response
	if *htmlOutput {
		w.Write(out)
	} else {
		output.Execute(w, out)
	}
}

// error writes compile, link, or runtime errors to the HTTP connection.
// The JavaScript interface uses the 404 status code to identify the error.
func error(w http.ResponseWriter, out []byte, err os.Error) {
	w.WriteHeader(404)
	if out != nil {
		output.Execute(w, out)
	} else {
		output.Execute(w, err.String())
	}
}

// run executes the specified command and returns its output and an error.
func run(cmd ...string) ([]byte, os.Error) {
	// find the specified binary
	bin, err := exec.LookPath(cmd[0])
	if err != nil {
		// report binary as well as the error
		return nil, os.NewError(cmd[0] + ": " + err.String())
	}

	// run the binary and read its combined stdout and stderr into a buffer
	p, err := exec.Run(bin, cmd, os.Environ(), "", exec.DevNull, exec.Pipe, exec.MergeWithStdout)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	io.Copy(&buf, p.Stdout)
	w, err := p.Wait(0)
	p.Close()
	if err != nil {
		return nil, err
	}

	// set the error return value if the program had a non-zero exit status
	if !w.Exited() || w.ExitStatus() != 0 {
		err = os.ErrorString("running " + cmd[0] + ": " + w.String())
	}

	return buf.Bytes(), err
}

var frontPage, output *template.Template // HTML templates

func init() {
	frontPage = template.New(nil)
	frontPage.SetDelims("«", "»")
	if err := frontPage.Parse(frontPageText); err != nil {
		panic(err)
	}
	output = template.MustParse(outputText, nil)
}

var outputText = `<pre>{@|html}</pre>`

var frontPageText = `<!doctype html>

<textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler(event);" onkeyup="autocompile();">«@|html»</textarea>

(Shift-Enter to compile and run.)
<input type="checkbox" id="autocompile" value="checked" /> Compile and run after each keystroke

`

var helloWorld = []byte(`package main

import "fmt"

func main() {
	fmt.Println("hello, world")
}
`)
<|endoftext|>


<commit_before>package isumm

import (
	"fmt"
	"log"
	"sort"
	"time"
)

// TimeseriesChart represents a chart where the x-axis is a timeseries.
type TimeseriesChart []timeSeriesPoint

func (t TimeseriesChart) Len() int {
	return len(t)
}

func (t TimeseriesChart) Less(i, j int) bool {
	return t[i].date.Before(t[j].date)
}

func (t TimeseriesChart) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}

// timeSeriesPoint represents a chart point where x-axis is a timeseries.
// All fields are private because interesting result is the String().
type timeSeriesPoint struct {
	date  time.Time
	value float32
}

func (g timeSeriesPoint) String() string {
	return fmt.Sprintf("[%d, %.2f]", g.date.UnixNano()/1000000, g.value)
}

func AmountSummaryChart(invs []*Investment) TimeseriesChart {
	balanceByDate := make(map[time.Time]float32)
	for _, i := range invs {
		for _, s := range i.Ops.Summarize() {
			balanceByDate[s.Date] += s.Balance
		}
	}
	var chart TimeseriesChart
	for t, b := range balanceByDate {
		chart = append(chart, timeSeriesPoint{t, b})
	}
	// Very important because go map deliberate changes the map interation from
	// time to time.
	sort.Sort(chart)
	return chart
}

func InterestRateChart(invs []*Investment) TimeseriesChart {
	summByDate := make(map[time.Time]Summary)
	for _, i := range invs {
		log.Printf("Inv: %v", i)
		for _, s := range i.Ops.Summarize() {
			log.Printf("Summ: %v", s)
			v := summByDate[s.Date]
			v.Date = s.Date
			v.Balance += s.Balance
			v.Change += s.Change
			summByDate[s.Date] = v
		}
	}
	log.Printf("byDate: %v", summByDate)
	var summs Summaries
	for _, s := range summByDate {
		summs = append(summs, s)
	}
	// The formula needs sorted summs. More information at: https://github.com/danielfireman/isumm/issues/2
	sort.Sort(summs)
	var chart TimeseriesChart
	for i, s := range summs {
		t := timeSeriesPoint{date: s.Date}
		switch {
		case i == 0:
			t.value = 0
		default:
			t.value = float32(s.Balance-s.Change-summs[i-1].Balance) / float32(s.Change+summs[i-1].Balance)
		}
		chart = append(chart, t)
	}
	log.Printf("%v", chart)
	return chart
}
<commit_msg>removing log.Printf<commit_after>package isumm

import (
	"fmt"
	"sort"
	"time"
)

// TimeseriesChart represents a chart where the x-axis is a timeseries.
type TimeseriesChart []timeSeriesPoint

func (t TimeseriesChart) Len() int {
	return len(t)
}

func (t TimeseriesChart) Less(i, j int) bool {
	return t[i].date.Before(t[j].date)
}

func (t TimeseriesChart) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}

// timeSeriesPoint represents a chart point where x-axis is a timeseries.
// All fields are private because interesting result is the String().
type timeSeriesPoint struct {
	date  time.Time
	value float32
}

func (g timeSeriesPoint) String() string {
	return fmt.Sprintf("[%d, %.2f]", g.date.UnixNano()/1000000, g.value)
}

func AmountSummaryChart(invs []*Investment) TimeseriesChart {
	balanceByDate := make(map[time.Time]float32)
	for _, i := range invs {
		for _, s := range i.Ops.Summarize() {
			balanceByDate[s.Date] += s.Balance
		}
	}
	var chart TimeseriesChart
	for t, b := range balanceByDate {
		chart = append(chart, timeSeriesPoint{t, b})
	}
	// Very important because go map deliberate changes the map interation from
	// time to time.
	sort.Sort(chart)
	return chart
}

func InterestRateChart(invs []*Investment) TimeseriesChart {
	summByDate := make(map[time.Time]Summary)
	for _, i := range invs {
		for _, s := range i.Ops.Summarize() {
			v := summByDate[s.Date]
			v.Date = s.Date
			v.Balance += s.Balance
			v.Change += s.Change
			summByDate[s.Date] = v
		}
	}
	var summs Summaries
	for _, s := range summByDate {
		summs = append(summs, s)
	}
	// The formula needs sorted summs. More information at: https://github.com/danielfireman/isumm/issues/2
	sort.Sort(summs)
	var chart TimeseriesChart
	for i, s := range summs {
		t := timeSeriesPoint{date: s.Date}
		switch {
		case i == 0:
			t.value = 0
		default:
			t.value = float32(s.Balance-s.Change-summs[i-1].Balance) / float32(s.Change+summs[i-1].Balance)
		}
		chart = append(chart, t)
	}
	return chart
}
<|endoftext|>


<commit_before>package isumm

import (
	"fmt"
	"sort"
	"time"
)

// TimeseriesChart represents a chart where the x-axis is a timeseries.
type TimeseriesChart []timeSeriesPoint

func (t TimeseriesChart) Len() int {
	return len(t)
}

func (t TimeseriesChart) Less(i, j int) bool {
	return t[i].date.Before(t[j].date)
}

func (t TimeseriesChart) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}

// timeSeriesPoint represents a chart point where x-axis is a timeseries.
// All fields are private because interesting result is the String().
type timeSeriesPoint struct {
	date  time.Time
	value float32
}

func (g timeSeriesPoint) String() string {
	return fmt.Sprintf("[%d, %.2f]", g.date.UnixNano()/1000000, g.value)
}

func AmountSummaryChart(invs []*Investment) TimeseriesChart {
	balanceByDate := make(map[time.Time]float32)
	for _, i := range invs {
		for _, s := range i.Ops.Summarize() {
			balanceByDate[s.Date] += s.Balance
		}
	}
	var chart TimeseriesChart
	for t, b := range balanceByDate {
		chart = append(chart, timeSeriesPoint{t, b})
	}
	// Very important because go map deliberate changes the map interation from
	// time to time.
	sort.Sort(chart)
	return chart
}

func InterestRateChart(invs []*Investment) TimeseriesChart {
	summByDate := make(map[time.Time]Summary)
	for _, i := range invs {
		for _, s := range i.Ops.Summarize() {
			v := summByDate[s.Date]
			v.Date = s.Date
			v.Balance += s.Balance
			v.Change += s.Change
			summByDate[s.Date] = v
		}
	}
	var summs Summaries
	for _, s := range summByDate {
		summs = append(summs, s)
	}
	// The formula needs sorted summs. More information at: https://github.com/danielfireman/isumm/issues/2
	sort.Sort(summs)
	var chart TimeseriesChart
	for i, s := range summs {
		t := timeSeriesPoint{date: s.Date}
		switch {
		case i == 0:
			t.value = 0
		default:
			t.value = float32(s.Balance-s.Change-summs[i-1].Balance) / float32(s.Change+summs[i-1].Balance) * 100.0
		}
		chart = append(chart, t)
	}
	return chart
}
<commit_msg>Fixes #34 and removes unused sorting.<commit_after>package isumm

import (
	"fmt"
	"sort"
	"time"
)

// TimeseriesChart represents a chart where the x-axis is a timeseries.
type TimeseriesChart []timeSeriesPoint

// timeSeriesPoint represents a chart point where x-axis is a timeseries.
// All fields are private because interesting result is the String().
type timeSeriesPoint struct {
	date  time.Time
	value float32
}

func (g timeSeriesPoint) String() string {
	return fmt.Sprintf("[%d, %.2f]", g.date.UnixNano()/1000000, g.value)
}

func AmountSummaryChart(invs []*Investment) TimeseriesChart {
	summs := AggregateByDate(invs)
	var chart TimeseriesChart
	for _, s := range summs {
		chart = append(chart, timeSeriesPoint{s.Date, s.Balance})
	}
	return chart
}

func InterestRateChart(invs []*Investment) TimeseriesChart {
	summs := AggregateByDate(invs)
	// Interest rates implies at least two reference points.
	if len(summs) < 2 {
		return TimeseriesChart{}
	}
	var chart TimeseriesChart
	for i, s := range summs {
		t := timeSeriesPoint{date: s.Date}
		switch {
		case i == 0:
			t.value = 0
		default:
			t.value = float32(s.Balance-s.Change-summs[i-1].Balance) / float32(s.Change+summs[i-1].Balance) * 100.0
		}
		chart = append(chart, t)
	}
	return chart
}

// Returns the aggregated summaries sorted by date.
func AggregateByDate(invs []*Investment) Summaries {
	summByDate := make(map[time.Time]Summary)
	for _, i := range invs {
		for _, s := range i.Ops.Summarize() {
			v := summByDate[s.Date]
			v.Date = s.Date
			v.Balance += s.Balance
			v.Change += s.Change
			summByDate[s.Date] = v
		}
	}
	var summs Summaries
	for _, s := range summByDate {
		summs = append(summs, s)
	}
	// More information at: https://github.com/danielfireman/isumm/issues/2
	sort.Sort(summs)
	switch len(summs) {
	case 1:
		if summs[0].Balance == 0 {
			// There only one point and it is zero. This probably means that it's the first month the user
			// and the month hasn't changed.
			return Summaries{}
		}
	default:
		// This prevents the corner case where the last month hasn't ended and there already deposits or
		// widrawals. There is a valid case for that where we have zeroed the balance because of a Widrawal.
		last := summs[len(summs)-1]
		previous := summs[len(summs)-2]
		if last.Balance == 0 && (previous.Balance+last.Change != 0) {
			summs = summs[:len(summs)-2]
		}
	}
	return summs
}
<|endoftext|>


<commit_before>package mocks_test

import (
	"bytes"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"

	"github.com/a8m/expect"
)

const packagePrefix = "package foo\n\n"

func source(expect func(interface{}) *expect.Expect, pkg string, decls []ast.Decl, scope *ast.Scope) string {
	buf := bytes.Buffer{}
	f := &ast.File{
		Name:  &ast.Ident{Name: pkg},
		Decls: decls,
		Scope: scope,
	}
	err := format.Node(&buf, token.NewFileSet(), f)
	expect(err).To.Be.Nil()
	return buf.String()
}

func parse(expect func(interface{}) *expect.Expect, code string) *ast.File {
	f, err := parser.ParseFile(token.NewFileSet(), "", packagePrefix+code, 0)
	expect(err).To.Be.Nil()
	expect(f).Not.To.Be.Nil()
	return f
}

func typeSpec(expect func(interface{}) *expect.Expect, spec string) *ast.TypeSpec {
	f := parse(expect, spec)
	expect(f.Scope.Objects).To.Have.Len(1)
	for _, obj := range f.Scope.Objects {
		spec, ok := obj.Decl.(*ast.TypeSpec)
		expect(ok).To.Be.Ok()
		return spec
	}
	return nil
}
<commit_msg>Add helper for reading a method from a TypeSpec<commit_after>package mocks_test

import (
	"bytes"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"

	"github.com/a8m/expect"
)

const packagePrefix = "package foo\n\n"

func source(expect func(interface{}) *expect.Expect, pkg string, decls []ast.Decl, scope *ast.Scope) string {
	buf := bytes.Buffer{}
	f := &ast.File{
		Name:  &ast.Ident{Name: pkg},
		Decls: decls,
		Scope: scope,
	}
	err := format.Node(&buf, token.NewFileSet(), f)
	expect(err).To.Be.Nil()
	return buf.String()
}

func parse(expect func(interface{}) *expect.Expect, code string) *ast.File {
	f, err := parser.ParseFile(token.NewFileSet(), "", packagePrefix+code, 0)
	expect(err).To.Be.Nil()
	expect(f).Not.To.Be.Nil()
	return f
}

func typeSpec(expect func(interface{}) *expect.Expect, spec string) *ast.TypeSpec {
	f := parse(expect, spec)
	expect(f.Scope.Objects).To.Have.Len(1)
	for _, obj := range f.Scope.Objects {
		spec, ok := obj.Decl.(*ast.TypeSpec)
		expect(ok).To.Be.Ok()
		return spec
	}
	return nil
}

func method(expect func(interface{}) *expect.Expect, spec *ast.TypeSpec) *ast.FuncType {
	inter, ok := spec.Type.(*ast.InterfaceType)
	expect(ok).To.Be.Ok()
	expect(inter.Methods.List).To.Have.Len(1)
	f, ok := inter.Methods.List[0].Type.(*ast.FuncType)
	expect(ok).To.Be.Ok()
	return f
}
<|endoftext|>


<commit_before>package nagiosplugin

import (
	"fmt"
	"os"
	"strings"
)

const (
	MessageSeparator = ", "
)

// Standalone Exit function for simple checks without multiple results
// or perfdata.
func Exit(status Status, message string) {
	fmt.Printf("%v: %s\n", status, message)
	os.Exit(int(status))
}

// Represents the state of a Nagios check.
type Check struct {
	results  []Result
	perfdata []PerfDatum
	status   Status
}

// NewCheck returns a Check object initialized with the given result
// (status and message).
func NewCheck(status Status, message string) *Check {
	c := new(Check)
	c.AddResult(status, message)
	return c
}

// NewDefaultCheck returns a Check object initialized with a placeholder
// UNKNOWN result.
func NewDefaultCheck() *Check {
	c := NewCheck(UNKNOWN, "No check results specified")
	return c
}

// AddResult adds a check result. This will not terminate the check. If
// status is the highest yet reported, this will update the check's
// final return status.
func (c *Check) AddResult(status Status, message string) {
	var result Result
	result.status = status
	result.message = message
	c.results = append(c.results, result)
	if result.status > c.status {
		c.status = result.status
	}
}

// AddPerfDatum adds a metric to the set output by the check. unit must
// be a valid Nagios unit of measurement (UOM): "us", "ms", "s",
// "%", "b", "kb", "mb", "gb", "tb", "c", or the empty string. UOMs are
// not case-sensitive.
//
// Zero or more of the thresholds min, max, warn and crit may be
// supplied; these must be of the same UOM as the value.
//
// A threshold may be positive or negative infinity, in which case it
// will be omitted in the check output. A value may not be either
// infinity.
//
// Returns error on invalid parameters.
func (c *Check) AddPerfDatum(label, unit string, value float64, thresholds ...float64) error {
	datum, err := NewPerfDatum(label, unit, value, thresholds...)
	if err != nil {
		return err
	}
	c.perfdata = append(c.perfdata, *datum)
	return nil
}

// exitInfoText returns the most important result text, formatted for
// the first line of plugin output.
//
// Returns joined string of (MessageSeparator-separated) info text from
// results which have a status of at least c.status.
func (c Check) exitInfoText() string {
	importantMessages := make([]string, 0)
	for _, result := range c.results {
		if result.status == c.status {
			importantMessages = append(importantMessages, result.message)
		}
	}
	return strings.Join(importantMessages, MessageSeparator)
}

// String representation of the check results, suitable for output and
// parsing by Nagios.
func (c Check) String() string {
	value := fmt.Sprintf("%v: %s", c.status, c.exitInfoText())
	value += RenderPerfdata(c.perfdata)
	return value
}

// Finish ends the check, prints its output (to stdout), and exits with
// the correct status.
func (c Check) Finish() {
	fmt.Println(c)
	os.Exit(int(c.status))
}

// ExitWith takes a status plus a format string, and a list of
// parameters to pass to Sprintf. It then immediately outputs and exits.
func (c *Check) Exitf(status Status, format string, v ...interface{}) {
	info := fmt.Sprintf(format, v...)
	c.AddResult(status, info)
	c.Finish()
}
<commit_msg>Exitf is a better name for that function<commit_after>package nagiosplugin

import (
	"fmt"
	"os"
	"strings"
)

const (
	MessageSeparator = ", "
)

// Standalone Exit function for simple checks without multiple results
// or perfdata.
func Exit(status Status, message string) {
	fmt.Printf("%v: %s\n", status, message)
	os.Exit(int(status))
}

// Represents the state of a Nagios check.
type Check struct {
	results  []Result
	perfdata []PerfDatum
	status   Status
}

// NewCheck returns a Check object initialized with the given result
// (status and message).
func NewCheck(status Status, message string) *Check {
	c := new(Check)
	c.AddResult(status, message)
	return c
}

// NewDefaultCheck returns a Check object initialized with a placeholder
// UNKNOWN result.
func NewDefaultCheck() *Check {
	c := NewCheck(UNKNOWN, "No check results specified")
	return c
}

// AddResult adds a check result. This will not terminate the check. If
// status is the highest yet reported, this will update the check's
// final return status.
func (c *Check) AddResult(status Status, message string) {
	var result Result
	result.status = status
	result.message = message
	c.results = append(c.results, result)
	if result.status > c.status {
		c.status = result.status
	}
}

// AddPerfDatum adds a metric to the set output by the check. unit must
// be a valid Nagios unit of measurement (UOM): "us", "ms", "s",
// "%", "b", "kb", "mb", "gb", "tb", "c", or the empty string. UOMs are
// not case-sensitive.
//
// Zero or more of the thresholds min, max, warn and crit may be
// supplied; these must be of the same UOM as the value.
//
// A threshold may be positive or negative infinity, in which case it
// will be omitted in the check output. A value may not be either
// infinity.
//
// Returns error on invalid parameters.
func (c *Check) AddPerfDatum(label, unit string, value float64, thresholds ...float64) error {
	datum, err := NewPerfDatum(label, unit, value, thresholds...)
	if err != nil {
		return err
	}
	c.perfdata = append(c.perfdata, *datum)
	return nil
}

// exitInfoText returns the most important result text, formatted for
// the first line of plugin output.
//
// Returns joined string of (MessageSeparator-separated) info text from
// results which have a status of at least c.status.
func (c Check) exitInfoText() string {
	importantMessages := make([]string, 0)
	for _, result := range c.results {
		if result.status == c.status {
			importantMessages = append(importantMessages, result.message)
		}
	}
	return strings.Join(importantMessages, MessageSeparator)
}

// String representation of the check results, suitable for output and
// parsing by Nagios.
func (c Check) String() string {
	value := fmt.Sprintf("%v: %s", c.status, c.exitInfoText())
	value += RenderPerfdata(c.perfdata)
	return value
}

// Finish ends the check, prints its output (to stdout), and exits with
// the correct status.
func (c Check) Finish() {
	fmt.Println(c)
	os.Exit(int(c.status))
}

// Exitf takes a status plus a format string, and a list of
// parameters to pass to Sprintf. It then immediately outputs and exits.
func (c *Check) Exitf(status Status, format string, v ...interface{}) {
	info := fmt.Sprintf(format, v...)
	c.AddResult(status, info)
	c.Finish()
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package interfacer

import (
	"fmt"
	"go/ast"
	"go/token"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/types"
)

func toDiscard(vr *variable) bool {
	if vr.discard {
		return true
	}
	for to := range vr.assigned {
		if toDiscard(to) {
			return true
		}
	}
	return false
}

func (v *visitor) interfaceMatching(obj types.Object, vr *variable) (string, string) {
	if toDiscard(vr) {
		return "", ""
	}
	allFuncs := doMethoderType(obj.Type())
	called := make(map[string]string, len(vr.calls))
	for fname := range vr.calls {
		called[fname] = allFuncs[fname]
	}
	s := funcMapString(called)
	name := v.ifaceOf(s)
	if name == "" {
		return "", ""
	}
	for t := range vr.usedAs {
		iface, ok := t.(*types.Interface)
		if !ok {
			return "", ""
		}
		asMethods := ifaceFuncMap(iface)
		as := funcMapString(asMethods)
		if !assignable(s, as, called, asMethods) {
			return "", ""
		}
	}
	return name, s
}

func orderedPkgs(prog *loader.Program) ([]*types.Package, error) {
	// InitialPackages() is not in the order that we passed to it
	// via Import() calls.
	// For now, make it deterministic by sorting import paths
	// alphabetically.
	unordered := prog.InitialPackages()
	paths := make([]string, 0, len(unordered))
	byPath := make(map[string]*types.Package, len(unordered))
	for _, info := range unordered {
		if info.Errors != nil {
			return nil, info.Errors[0]
		}
		path := info.Pkg.Path()
		paths = append(paths, path)
		byPath[path] = info.Pkg
	}
	sort.Sort(ByAlph(paths))
	pkgs := make([]*types.Package, 0, len(unordered))
	for _, path := range paths {
		pkgs = append(pkgs, byPath[path])
	}
	return pkgs, nil
}

// relPathErr makes converts errors by go/types and go/loader that use
// absolute paths into errors with relative paths
func relPathErr(err error) error {
	errStr := fmt.Sprintf("%v", err)
	if !strings.HasPrefix(errStr, "/") {
		return err
	}
	wd, err := os.Getwd()
	if err != nil {
		return err
	}
	if strings.HasPrefix(errStr, wd) {
		return fmt.Errorf(errStr[len(wd)+1:])
	}
	return err
}

func CheckArgs(args []string, w io.Writer, verbose bool) error {
	paths, err := recurse(args)
	if err != nil {
		return err
	}
	c := newCache()
	if _, err := c.FromArgs(paths, false); err != nil {
		return err
	}
	prog, err := c.Load()
	if err != nil {
		return err
	}
	pkgs, err := orderedPkgs(prog)
	if err != nil {
		return relPathErr(err)
	}
	c.typesGet(pkgs)
	for _, pkg := range pkgs {
		info := prog.AllPackages[pkg]
		if verbose {
			fmt.Fprintln(w, info.Pkg.Path())
		}
		checkPkg(c, info, prog.Fset, w)
	}
	return nil
}

func checkPkg(c *cache, info *loader.PackageInfo, fset *token.FileSet, w io.Writer) {
	v := &visitor{
		cache:       c,
		PackageInfo: info,
		w:           w,
		fset:        fset,
		vars:        make(map[types.Object]*variable),
	}
	for _, f := range info.Files {
		ast.Walk(v, f)
	}
}

type variable struct {
	calls   map[string]struct{}
	usedAs  map[types.Type]struct{}
	discard bool

	assigned map[*variable]struct{}
}

type visitor struct {
	*cache
	*loader.PackageInfo

	w     io.Writer
	fset  *token.FileSet
	signs []*types.Signature
	warns [][]string
	level int

	vars map[types.Object]*variable

	skipNext bool
}

func paramType(sign *types.Signature, i int) types.Type {
	params := sign.Params()
	extra := sign.Variadic() && i >= params.Len()-1
	if !extra {
		if i >= params.Len() {
			// builtins with multiple signatures
			return nil
		}
		return params.At(i).Type()
	}
	last := params.At(params.Len() - 1)
	switch x := last.Type().(type) {
	case *types.Slice:
		return x.Elem()
	default:
		return x
	}
}

func (v *visitor) variable(id *ast.Ident) *variable {
	obj := v.ObjectOf(id)
	if vr, e := v.vars[obj]; e {
		return vr
	}
	vr := &variable{
		calls:    make(map[string]struct{}),
		usedAs:   make(map[types.Type]struct{}),
		assigned: make(map[*variable]struct{}),
	}
	v.vars[obj] = vr
	return vr
}

func (v *visitor) addUsed(id *ast.Ident, as types.Type) {
	if as == nil {
		return
	}
	vr := v.variable(id)
	vr.usedAs[as.Underlying()] = struct{}{}
}

func (v *visitor) addAssign(to, from *ast.Ident) {
	pto := v.variable(to)
	pfrom := v.variable(from)
	pfrom.assigned[pto] = struct{}{}
}

func (v *visitor) discard(e ast.Expr) {
	id, ok := e.(*ast.Ident)
	if !ok {
		return
	}
	vr := v.variable(id)
	vr.discard = true
}

func (v *visitor) implementsIface(sign *types.Signature) bool {
	s := signString(sign)
	return v.funcOf(s) != ""
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	if v.skipNext {
		v.skipNext = false
		return nil
	}
	var sign *types.Signature
	switch x := node.(type) {
	case *ast.FuncLit:
		sign = v.Types[x].Type.(*types.Signature)
		if v.implementsIface(sign) {
			return nil
		}
	case *ast.FuncDecl:
		sign = v.Defs[x.Name].Type().(*types.Signature)
		if v.implementsIface(sign) {
			return nil
		}
	case *ast.SelectorExpr:
		v.discard(x.X)
	case *ast.UnaryExpr:
		v.discard(x.X)
	case *ast.BinaryExpr:
		v.discard(x.X)
		v.discard(x.Y)
	case *ast.IndexExpr:
		v.discard(x.X)
	case *ast.IncDecStmt:
		v.discard(x.X)
	case *ast.AssignStmt:
		v.onAssign(x)
	case *ast.CallExpr:
		v.onCall(x)
	case nil:
		top := v.signs[len(v.signs)-1]
		if top != nil {
			v.funcEnded(top)
		}
		v.signs = v.signs[:len(v.signs)-1]
	}
	if node != nil {
		v.signs = append(v.signs, sign)
		if sign != nil {
			v.level++
		}
	}
	return v
}

func funcSignature(t types.Type) *types.Signature {
	switch x := t.(type) {
	case *types.Signature:
		return x
	case *types.Named:
		return funcSignature(x.Underlying())
	default:
		return nil
	}
}

func (v *visitor) onAssign(as *ast.AssignStmt) {
	for i, e := range as.Rhs {
		id, ok := e.(*ast.Ident)
		if !ok {
			continue
		}
		left := as.Lhs[i]
		v.addUsed(id, v.Types[left].Type)
		if lid, ok := left.(*ast.Ident); ok {
			v.addAssign(lid, id)
		}
	}
}

func (v *visitor) onCall(ce *ast.CallExpr) {
	switch y := ce.Fun.(type) {
	case *ast.Ident:
		v.skipNext = true
	case *ast.SelectorExpr:
		if _, ok := y.X.(*ast.Ident); ok {
			v.skipNext = true
		}
	}
	sign := funcSignature(v.Types[ce.Fun].Type)
	if sign == nil {
		return
	}
	for i, e := range ce.Args {
		if id, ok := e.(*ast.Ident); ok {
			v.addUsed(id, paramType(sign, i))
		}
	}
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	vr := v.variable(left)
	vr.calls[sel.Sel.Name] = struct{}{}
	return
}

func (v *visitor) funcEnded(sign *types.Signature) {
	v.level--
	v.warns = append(v.warns, v.funcWarns(sign))
	if v.level > 0 {
		return
	}
	for i := len(v.warns) - 1; i >= 0; i-- {
		warns := v.warns[i]
		for _, warn := range warns {
			fmt.Fprintln(v.w, warn)
		}
	}
	v.warns = nil
}

func (v *visitor) funcWarns(sign *types.Signature) []string {
	var warns []string
	params := sign.Params()
	for i := 0; i < params.Len(); i++ {
		obj := params.At(i)
		vr := v.vars[obj]
		if vr == nil {
			continue
		}
		if warn := v.paramWarn(obj, vr); warn != "" {
			warns = append(warns, warn)
		}
	}
	return warns
}

func (v *visitor) paramWarn(obj types.Object, vr *variable) string {
	ifname, iftype := v.interfaceMatching(obj, vr)
	if ifname == "" {
		return ""
	}
	t := obj.Type()
	if _, haveIface := t.Underlying().(*types.Interface); haveIface {
		if ifname == t.String() {
			return ""
		}
		have := funcMapString(doMethoderType(t))
		if have == iftype {
			return ""
		}
	}
	pos := v.fset.Position(obj.Pos())
	fname := pos.Filename
	if fname[0] == '/' {
		fname = filepath.Join(v.Pkg.Path(), filepath.Base(fname))
	}
	pname := v.Pkg.Name()
	if strings.HasPrefix(ifname, pname+".") {
		ifname = ifname[len(pname)+1:]
	}
	return fmt.Sprintf("%s:%d:%d: %s can be %s",
		fname, pos.Line, pos.Column, obj.Name(), ifname)
}
<commit_msg>Small style fixes<commit_after>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package interfacer

import (
	"fmt"
	"go/ast"
	"go/token"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/types"
)

func toDiscard(vr *variable) bool {
	if vr.discard {
		return true
	}
	for to := range vr.assigned {
		if toDiscard(to) {
			return true
		}
	}
	return false
}

func (v *visitor) interfaceMatching(obj types.Object, vr *variable) (string, string) {
	if toDiscard(vr) {
		return "", ""
	}
	allFuncs := doMethoderType(obj.Type())
	called := make(map[string]string, len(vr.calls))
	for fname := range vr.calls {
		called[fname] = allFuncs[fname]
	}
	s := funcMapString(called)
	name := v.ifaceOf(s)
	if name == "" {
		return "", ""
	}
	for t := range vr.usedAs {
		iface, ok := t.(*types.Interface)
		if !ok {
			return "", ""
		}
		asMethods := ifaceFuncMap(iface)
		as := funcMapString(asMethods)
		if !assignable(s, as, called, asMethods) {
			return "", ""
		}
	}
	return name, s
}

func orderedPkgs(prog *loader.Program) ([]*types.Package, error) {
	// InitialPackages() is not in the order that we passed to it
	// via Import() calls.
	// For now, make it deterministic by sorting import paths
	// alphabetically.
	unordered := prog.InitialPackages()
	paths := make([]string, 0, len(unordered))
	byPath := make(map[string]*types.Package, len(unordered))
	for _, info := range unordered {
		if info.Errors != nil {
			return nil, info.Errors[0]
		}
		path := info.Pkg.Path()
		paths = append(paths, path)
		byPath[path] = info.Pkg
	}
	sort.Sort(ByAlph(paths))
	pkgs := make([]*types.Package, 0, len(unordered))
	for _, path := range paths {
		pkgs = append(pkgs, byPath[path])
	}
	return pkgs, nil
}

// relPathErr makes converts errors by go/types and go/loader that use
// absolute paths into errors with relative paths
func relPathErr(err error) error {
	errStr := fmt.Sprintf("%v", err)
	if !strings.HasPrefix(errStr, "/") {
		return err
	}
	wd, err := os.Getwd()
	if err != nil {
		return err
	}
	if strings.HasPrefix(errStr, wd) {
		return fmt.Errorf(errStr[len(wd)+1:])
	}
	return err
}

func CheckArgs(args []string, w io.Writer, verbose bool) error {
	paths, err := recurse(args)
	if err != nil {
		return err
	}
	c := newCache()
	if _, err := c.FromArgs(paths, false); err != nil {
		return err
	}
	prog, err := c.Load()
	if err != nil {
		return err
	}
	pkgs, err := orderedPkgs(prog)
	if err != nil {
		return relPathErr(err)
	}
	c.typesGet(pkgs)
	for _, pkg := range pkgs {
		info := prog.AllPackages[pkg]
		if verbose {
			fmt.Fprintln(w, info.Pkg.Path())
		}
		v := &visitor{
			cache:       c,
			PackageInfo: info,
			w:           w,
			fset:        prog.Fset,
			vars:        make(map[types.Object]*variable),
		}
		for _, f := range info.Files {
			ast.Walk(v, f)
		}
	}
	return nil
}

type variable struct {
	calls   map[string]struct{}
	usedAs  map[types.Type]struct{}
	discard bool

	assigned map[*variable]struct{}
}

type visitor struct {
	*cache
	*loader.PackageInfo

	w     io.Writer
	fset  *token.FileSet
	signs []*types.Signature
	warns [][]string
	level int

	vars map[types.Object]*variable

	skipNext bool
}

func paramType(sign *types.Signature, i int) types.Type {
	params := sign.Params()
	extra := sign.Variadic() && i >= params.Len()-1
	if !extra {
		if i >= params.Len() {
			// builtins with multiple signatures
			return nil
		}
		return params.At(i).Type()
	}
	last := params.At(params.Len() - 1)
	switch x := last.Type().(type) {
	case *types.Slice:
		return x.Elem()
	default:
		return x
	}
}

func (v *visitor) variable(id *ast.Ident) *variable {
	obj := v.ObjectOf(id)
	if vr, e := v.vars[obj]; e {
		return vr
	}
	vr := &variable{
		calls:    make(map[string]struct{}),
		usedAs:   make(map[types.Type]struct{}),
		assigned: make(map[*variable]struct{}),
	}
	v.vars[obj] = vr
	return vr
}

func (v *visitor) addUsed(id *ast.Ident, as types.Type) {
	if as == nil {
		return
	}
	vr := v.variable(id)
	vr.usedAs[as.Underlying()] = struct{}{}
}

func (v *visitor) addAssign(to, from *ast.Ident) {
	pto := v.variable(to)
	pfrom := v.variable(from)
	pfrom.assigned[pto] = struct{}{}
}

func (v *visitor) discard(e ast.Expr) {
	id, ok := e.(*ast.Ident)
	if !ok {
		return
	}
	vr := v.variable(id)
	vr.discard = true
}

func (v *visitor) implementsIface(sign *types.Signature) bool {
	s := signString(sign)
	return v.funcOf(s) != ""
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	if v.skipNext {
		v.skipNext = false
		return nil
	}
	var sign *types.Signature
	switch x := node.(type) {
	case *ast.FuncLit:
		sign = v.Types[x].Type.(*types.Signature)
		if v.implementsIface(sign) {
			return nil
		}
	case *ast.FuncDecl:
		sign = v.Defs[x.Name].Type().(*types.Signature)
		if v.implementsIface(sign) {
			return nil
		}
	case *ast.SelectorExpr:
		v.discard(x.X)
	case *ast.UnaryExpr:
		v.discard(x.X)
	case *ast.BinaryExpr:
		v.discard(x.X)
		v.discard(x.Y)
	case *ast.IndexExpr:
		v.discard(x.X)
	case *ast.IncDecStmt:
		v.discard(x.X)
	case *ast.AssignStmt:
		v.onAssign(x)
	case *ast.CallExpr:
		v.onCall(x)
	case nil:
		if top := v.signs[len(v.signs)-1]; top != nil {
			v.funcEnded(top)
		}
		v.signs = v.signs[:len(v.signs)-1]
	}
	if node != nil {
		v.signs = append(v.signs, sign)
		if sign != nil {
			v.level++
		}
	}
	return v
}

func funcSignature(t types.Type) *types.Signature {
	switch x := t.(type) {
	case *types.Signature:
		return x
	case *types.Named:
		return funcSignature(x.Underlying())
	default:
		return nil
	}
}

func (v *visitor) onAssign(as *ast.AssignStmt) {
	for i, e := range as.Rhs {
		id, ok := e.(*ast.Ident)
		if !ok {
			continue
		}
		left := as.Lhs[i]
		v.addUsed(id, v.Types[left].Type)
		if lid, ok := left.(*ast.Ident); ok {
			v.addAssign(lid, id)
		}
	}
}

func (v *visitor) onCall(ce *ast.CallExpr) {
	switch y := ce.Fun.(type) {
	case *ast.Ident:
		v.skipNext = true
	case *ast.SelectorExpr:
		if _, ok := y.X.(*ast.Ident); ok {
			v.skipNext = true
		}
	}
	sign := funcSignature(v.Types[ce.Fun].Type)
	if sign == nil {
		return
	}
	for i, e := range ce.Args {
		if id, ok := e.(*ast.Ident); ok {
			v.addUsed(id, paramType(sign, i))
		}
	}
	sel, ok := ce.Fun.(*ast.SelectorExpr)
	if !ok {
		return
	}
	left, ok := sel.X.(*ast.Ident)
	if !ok {
		return
	}
	vr := v.variable(left)
	vr.calls[sel.Sel.Name] = struct{}{}
	return
}

func (v *visitor) funcEnded(sign *types.Signature) {
	v.level--
	v.warns = append(v.warns, v.funcWarns(sign))
	if v.level > 0 {
		return
	}
	for i := len(v.warns) - 1; i >= 0; i-- {
		warns := v.warns[i]
		for _, warn := range warns {
			fmt.Fprintln(v.w, warn)
		}
	}
	v.warns = nil
}

func (v *visitor) funcWarns(sign *types.Signature) []string {
	var warns []string
	params := sign.Params()
	for i := 0; i < params.Len(); i++ {
		obj := params.At(i)
		vr := v.vars[obj]
		if vr == nil {
			continue
		}
		if warn := v.paramWarn(obj, vr); warn != "" {
			warns = append(warns, warn)
		}
	}
	return warns
}

func (v *visitor) paramWarn(obj types.Object, vr *variable) string {
	ifname, iftype := v.interfaceMatching(obj, vr)
	if ifname == "" {
		return ""
	}
	t := obj.Type()
	if _, haveIface := t.Underlying().(*types.Interface); haveIface {
		if ifname == t.String() {
			return ""
		}
		have := funcMapString(doMethoderType(t))
		if have == iftype {
			return ""
		}
	}
	pos := v.fset.Position(obj.Pos())
	fname := pos.Filename
	if fname[0] == '/' {
		fname = filepath.Join(v.Pkg.Path(), filepath.Base(fname))
	}
	pname := v.Pkg.Name()
	if strings.HasPrefix(ifname, pname+".") {
		ifname = ifname[len(pname)+1:]
	}
	return fmt.Sprintf("%s:%d:%d: %s can be %s",
		fname, pos.Line, pos.Column, obj.Name(), ifname)
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package etcdutil

import (
	"errors"
	"github.com/coreos/go-etcd/etcd"
	"strings"
)

// Client is a wrapper around the etcd client
type Client struct {
	client EtcdClient
}

type EtcdClient interface {
	Get(key string, sort, recurse bool) (*etcd.Response, error)
}

// NewEtcdClient returns an *etcd.Client with a connection to named machines.
// It returns an error if a connection to the cluster cannot be made.
func NewEtcdClient(machines []string, cert, key string, caCert string) (*Client, error) {
	var c *etcd.Client
	if cert != "" && key != "" {
		c, err := etcd.NewTLSClient(machines, cert, key, caCert)
		if err != nil {
			return &Client{c}, err
		}
	} else {
		c = etcd.NewClient(machines)
	}
	success := c.SetCluster(machines)
	if !success {
		return &Client{c}, errors.New("cannot connect to etcd cluster: " + strings.Join(machines, ","))
	}
	return &Client{c}, nil
}

// GetValues queries etcd for keys prefixed by prefix.
// Etcd paths (keys) are translated into names more suitable for use in
// templates. For example if prefix were set to '/production' and one of the
// keys were '/nginx/port'; the prefixed '/production/nginx/port' key would
// be queried for. If the value for the prefixed key where 80, the returned map
// would contain the entry vars["nginx_port"] = "80".
func (c *Client) GetValues(keys []string) (map[string]interface{}, error) {
	vars := make(map[string]interface{})
	for _, key := range keys {
		resp, err := c.client.Get(key, false, true)
		if err != nil {
			return vars, err
		}
		err = nodeWalk(resp.Node, vars)
		if err != nil {
			return vars, err
		}
	}
	return vars, nil
}

// nodeWalk recursively descends nodes, updating vars.
func nodeWalk(node *etcd.Node, vars map[string]interface{}) error {
	if node != nil {
		key := node.Key
		if !node.Dir {
			vars[key] = node.Value
		} else {
			vars[key] = node.Nodes
			for _, node := range node.Nodes {
				nodeWalk(&node, vars)
			}
		}
	}
	return nil
}
<commit_msg>Fix cannot use &node (type **etcd.Node) error<commit_after>// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package etcdutil

import (
	"errors"
	"github.com/coreos/go-etcd/etcd"
	"strings"
)

// Client is a wrapper around the etcd client
type Client struct {
	client EtcdClient
}

type EtcdClient interface {
	Get(key string, sort, recurse bool) (*etcd.Response, error)
}

// NewEtcdClient returns an *etcd.Client with a connection to named machines.
// It returns an error if a connection to the cluster cannot be made.
func NewEtcdClient(machines []string, cert, key string, caCert string) (*Client, error) {
	var c *etcd.Client
	if cert != "" && key != "" {
		c, err := etcd.NewTLSClient(machines, cert, key, caCert)
		if err != nil {
			return &Client{c}, err
		}
	} else {
		c = etcd.NewClient(machines)
	}
	success := c.SetCluster(machines)
	if !success {
		return &Client{c}, errors.New("cannot connect to etcd cluster: " + strings.Join(machines, ","))
	}
	return &Client{c}, nil
}

// GetValues queries etcd for keys prefixed by prefix.
// Etcd paths (keys) are translated into names more suitable for use in
// templates. For example if prefix were set to '/production' and one of the
// keys were '/nginx/port'; the prefixed '/production/nginx/port' key would
// be queried for. If the value for the prefixed key where 80, the returned map
// would contain the entry vars["nginx_port"] = "80".
func (c *Client) GetValues(keys []string) (map[string]interface{}, error) {
	vars := make(map[string]interface{})
	for _, key := range keys {
		resp, err := c.client.Get(key, false, true)
		if err != nil {
			return vars, err
		}
		err = nodeWalk(resp.Node, vars)
		if err != nil {
			return vars, err
		}
	}
	return vars, nil
}

// nodeWalk recursively descends nodes, updating vars.
func nodeWalk(node *etcd.Node, vars map[string]interface{}) error {
	if node != nil {
		key := node.Key
		if !node.Dir {
			vars[key] = node.Value
		} else {
			vars[key] = node.Nodes
			for _, node := range node.Nodes {
				nodeWalk(node, vars)
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Package clock is a low consumption, low latency support for frequent updates of large capacity timing manager：
//	1、能够添加一次性、重复性任务，并能在其执行前撤销或频繁更改。
//	2、支持同一时间点，多个任务提醒。
//	3、适用于中等密度，大跨度的单次、多次定时任务。
//	4、支持10万次/秒的定时任务执行、提醒、撤销或添加操作，平均延迟10微秒内
//	5、支持注册任务的函数调用，及事件通知。
// 基本处理逻辑：
//	1、重复性任务，流程是：
//		a、注册重复任务
//		b、时间抵达时，控制器调用注册函数，并发送通知
//		c、如果次数达到限制，则撤销；否则，控制器更新该任务的下次执行时间点
//		d、控制器等待下一个最近需要执行的任务
//	2、一次性任务，可以是服务运行时，当前时间点之后的任意事件，流程是：
//		a、注册一次性任务
//		b、时间抵达时，控制器调用注册函数，并发送通知
//		c、控制器释放该任务
//		d、控制器等待下一个最近需要执行的任务
// 使用方式，参见示例代码。
package clock

import (
	"github.com/HuKeping/rbtree"
	"math"
	"sync"
	"time"
)

const _UNTOUCHED = time.Duration(math.MaxInt64)

// Clock is joblist control
type Clock struct {
	mut     sync.Mutex
	seq     uint64
	jobList *rbtree.Rbtree //inner memory storage
	count   uint64         //已执行次数，不得大于times
	timer   *time.Timer    //计时器
}

//NewClock Create a task queue controller
func NewClock() *Clock {
	c := &Clock{
		jobList: rbtree.New(),
		timer:   time.NewTimer(_UNTOUCHED),
	}

	//开启守护协程
	go func() {
		for {
			<-c.timer.C
			c.schedule()
		}
	}()

	return c
}

func (jl *Clock) schedule() {
	jl.mut.Lock()
	defer jl.mut.Unlock()

	jl.count++

	if item := jl.jobList.Min(); item != nil {
		job := item.(*jobItem)
		job.done()
		if job.canContinue() {
			jl.jobList.Delete(job)
			job.actionTime = job.actionTime.Add(job.intervalTime)
			jl.jobList.Insert(job)
		} else {
			jl.removeJob(job)
		}

		jl.timeRefreshAfterDel()
	} else {
		jl.timer.Reset(_UNTOUCHED)
	}
}

// UpdateJobTimeout update a timed task with time duration after now
//	@job:		job identifier
//	@timeout:	new job schedule time
func (jl *Clock) UpdateJobTimeout(job Job, timeout time.Duration) (updated bool) {
	if timeout.Nanoseconds() <= 0 {
		return false
	}
	now := time.Now()

	jl.mut.Lock()
	defer jl.mut.Unlock()

	item, ok := job.(*jobItem)
	if !ok {
		return false
	}
	// update jobitem rbtree node
	jl.jobList.Delete(item)
	item.actionTime = now.Add(timeout)
	jl.jobList.Insert(item)

	jl.timeRefreshAfterAdd(item)

	updated = true
	return
}
func (jl *Clock) timeRefreshAfterDel() {
	if head := jl.jobList.Min(); head != nil {
		item := head.(*jobItem)
		jl.timer.Reset(item.actionTime.Sub(time.Now()))
	}

}
func (jl *Clock) timeRefreshAfterAdd(new *jobItem) {
	if head := jl.jobList.Min(); head != nil && head == new {
		item := head.(*jobItem)
		jl.timer.Reset(item.actionTime.Sub(time.Now()))
	}

}

// AddJobWithInterval insert a timed task with time duration after now
// 	@timeout:	duration after now
//	@jobFunc:	action function
//	return
// 	@job:
func (jl *Clock) AddJobWithInterval(timeout time.Duration, jobFunc func()) (job Job, inserted bool) {
	if timeout.Nanoseconds() <= 0 {
		return nil, false
	}
	now := time.Now()

	jl.mut.Lock()

	newitem, inserted := jl.addJob(now, timeout, 1, jobFunc)
	jl.timeRefreshAfterAdd(newitem)

	jl.mut.Unlock()
	job = newitem
	return
}

// AddJobWithDeadtime insert a timed task with time point after now
//	@timeaction:	Execution start time. must after now,else return false
//	@jobFunc:	Execution function
//	return
// 	@job:		返还注册的任务事件。
func (jl *Clock) AddJobWithDeadtime(timeaction time.Time, jobFunc func()) (job Job, inserted bool) {
	timeout := timeaction.Sub(time.Now())
	if timeout.Nanoseconds() <= 0 {
		return nil, false
	}
	now := time.Now()

	jl.mut.Lock()

	newItem, inserted := jl.addJob(now, timeout, 1, jobFunc)
	jl.timeRefreshAfterAdd(newItem)

	jl.mut.Unlock()

	job = newItem
	return
}

// AddJobRepeat add a repeat task with interval duration
//	@jobInterval:	The two time interval operation
//	@jobTimes:	The number of job execution
//	@jobFunc:	The function of job execution
//	return
// 	@job:		job interface。
//Note：
// when jobTimes==0,the job will be executed without limitation。If you no longer use, be sure to call the DelJob method to release
func (jl *Clock) AddJobRepeat(jobInterval time.Duration, jobTimes uint64, jobFunc func()) (job Job, inserted bool) {
	if jobInterval.Nanoseconds() <= 0 {
		return nil, false
	}
	now := time.Now()

	jl.mut.Lock()
	newItem, inserted := jl.addJob(now, jobInterval, jobTimes, jobFunc)
	jl.timeRefreshAfterAdd(newItem)
	jl.mut.Unlock()

	job = newItem
	return
}

func (jl *Clock) addJob(createTime time.Time, jobInterval time.Duration, jobTimes uint64, jobFunc func()) (job *jobItem, inserted bool) {
	inserted = true
	jl.seq++
	job = &jobItem{
		id:           jl.seq,
		times:        jobTimes,
		createTime:   createTime,
		actionTime:   createTime.Add(jobInterval),
		intervalTime: jobInterval,
		msgChan:      make(chan Job, 10),
		fn:           jobFunc,
	}
	jl.jobList.Insert(job)

	return

}

// DelJob Deletes the task that has been added to the task queue. If the key does not exist, return false.
func (jl *Clock) DelJob(job Job) (deleted bool) {
	if job == nil {
		deleted = false
		return
	}

	jl.mut.Lock()
	defer jl.mut.Unlock()

	item, ok := job.(*jobItem)
	if !ok {
		return false
	}
	jl.removeJob(item)
	deleted = true

	jl.timeRefreshAfterDel()

	return
}

// DelJobs remove jobs from clock schedule list
func (jl *Clock) DelJobs(jobIds []Job) {
	jl.mut.Lock()
	defer jl.mut.Unlock()

	for _, job := range jobIds {
		item, ok := job.(*jobItem)
		if !ok {
			continue
		}
		jl.removeJob(item)
	}

	jl.timeRefreshAfterDel()

	return
}
func (jl *Clock) removeJob(item *jobItem) {
	jl.jobList.Delete(item)
	close(item.msgChan)

	return
}

// Count 已经执行的任务数。对于重复任务，会计算多次
func (jl *Clock) Count() uint64 {
	jl.mut.Lock()
	defer jl.mut.Unlock()

	return jl.count
}

//WaitJobs 待执行任务数
// Note:每次使用，对任务队列会有微秒级的阻塞
func (jl *Clock) WaitJobs() uint {
	jl.mut.Lock()
	defer jl.mut.Unlock()

	return jl.jobList.Len()
}
<commit_msg>add Default clock support<commit_after>// Package clock is a low consumption, low latency support for frequent updates of large capacity timing manager：
//	1、能够添加一次性、重复性任务，并能在其执行前撤销或频繁更改。
//	2、支持同一时间点，多个任务提醒。
//	3、适用于中等密度，大跨度的单次、多次定时任务。
//	4、支持10万次/秒的定时任务执行、提醒、撤销或添加操作，平均延迟10微秒内
//	5、支持注册任务的函数调用，及事件通知。
// 基本处理逻辑：
//	1、重复性任务，流程是：
//		a、注册重复任务
//		b、时间抵达时，控制器调用注册函数，并发送通知
//		c、如果次数达到限制，则撤销；否则，控制器更新该任务的下次执行时间点
//		d、控制器等待下一个最近需要执行的任务
//	2、一次性任务，可以是服务运行时，当前时间点之后的任意事件，流程是：
//		a、注册一次性任务
//		b、时间抵达时，控制器调用注册函数，并发送通知
//		c、控制器释放该任务
//		d、控制器等待下一个最近需要执行的任务
// 使用方式，参见示例代码。
package clock

import (
	"github.com/HuKeping/rbtree"
	"math"
	"sync"
	"time"
)

const _UNTOUCHED = time.Duration(math.MaxInt64)

var (
	defaultClock *Clock
	oncedo       sync.Once
)

//Default return singal default clock
func Default() *Clock {
	oncedo.Do(initClock)
	return defaultClock
}
func initClock() {
	defaultClock = NewClock()
}

// Clock is joblist control
type Clock struct {
	mut     sync.Mutex
	seq     uint64
	jobList *rbtree.Rbtree //inner memory storage
	count   uint64         //已执行次数，不得大于times
	timer   *time.Timer    //计时器
}

//NewClock Create a task queue controller
func NewClock() *Clock {
	c := &Clock{
		jobList: rbtree.New(),
		timer:   time.NewTimer(_UNTOUCHED),
	}

	//开启守护协程
	go func() {
		for {
			<-c.timer.C
			c.schedule()
		}
	}()

	return c
}

func (jl *Clock) schedule() {
	jl.mut.Lock()
	defer jl.mut.Unlock()

	jl.count++

	if item := jl.jobList.Min(); item != nil {
		job := item.(*jobItem)
		job.done()
		if job.canContinue() {
			jl.jobList.Delete(job)
			job.actionTime = job.actionTime.Add(job.intervalTime)
			jl.jobList.Insert(job)
		} else {
			jl.removeJob(job)
		}

		jl.timeRefreshAfterDel()
	} else {
		jl.timer.Reset(_UNTOUCHED)
	}
}

// UpdateJobTimeout update a timed task with time duration after now
//	@job:		job identifier
//	@timeout:	new job schedule time
func (jl *Clock) UpdateJobTimeout(job Job, timeout time.Duration) (updated bool) {
	if timeout.Nanoseconds() <= 0 {
		return false
	}
	now := time.Now()

	jl.mut.Lock()
	defer jl.mut.Unlock()

	item, ok := job.(*jobItem)
	if !ok {
		return false
	}
	// update jobitem rbtree node
	jl.jobList.Delete(item)
	item.actionTime = now.Add(timeout)
	jl.jobList.Insert(item)

	jl.timeRefreshAfterAdd(item)

	updated = true
	return
}
func (jl *Clock) timeRefreshAfterDel() {
	if head := jl.jobList.Min(); head != nil {
		item := head.(*jobItem)
		jl.timer.Reset(item.actionTime.Sub(time.Now()))
	}

}
func (jl *Clock) timeRefreshAfterAdd(new *jobItem) {
	if head := jl.jobList.Min(); head != nil && head == new {
		item := head.(*jobItem)
		jl.timer.Reset(item.actionTime.Sub(time.Now()))
	}

}

// AddJobWithInterval insert a timed task with time duration after now
// 	@timeout:	duration after now
//	@jobFunc:	action function
//	return
// 	@job:
func (jl *Clock) AddJobWithInterval(timeout time.Duration, jobFunc func()) (job Job, inserted bool) {
	if timeout.Nanoseconds() <= 0 {
		return nil, false
	}
	now := time.Now()

	jl.mut.Lock()

	newitem, inserted := jl.addJob(now, timeout, 1, jobFunc)
	jl.timeRefreshAfterAdd(newitem)

	jl.mut.Unlock()
	job = newitem
	return
}

// AddJobWithDeadtime insert a timed task with time point after now
//	@timeaction:	Execution start time. must after now,else return false
//	@jobFunc:	Execution function
//	return
// 	@job:		返还注册的任务事件。
func (jl *Clock) AddJobWithDeadtime(timeaction time.Time, jobFunc func()) (job Job, inserted bool) {
	timeout := timeaction.Sub(time.Now())
	if timeout.Nanoseconds() <= 0 {
		return nil, false
	}
	now := time.Now()

	jl.mut.Lock()

	newItem, inserted := jl.addJob(now, timeout, 1, jobFunc)
	jl.timeRefreshAfterAdd(newItem)

	jl.mut.Unlock()

	job = newItem
	return
}

// AddJobRepeat add a repeat task with interval duration
//	@jobInterval:	The two time interval operation
//	@jobTimes:	The number of job execution
//	@jobFunc:	The function of job execution
//	return
// 	@job:		job interface。
//Note：
// when jobTimes==0,the job will be executed without limitation。If you no longer use, be sure to call the DelJob method to release
func (jl *Clock) AddJobRepeat(jobInterval time.Duration, jobTimes uint64, jobFunc func()) (job Job, inserted bool) {
	if jobInterval.Nanoseconds() <= 0 {
		return nil, false
	}
	now := time.Now()

	jl.mut.Lock()
	newItem, inserted := jl.addJob(now, jobInterval, jobTimes, jobFunc)
	jl.timeRefreshAfterAdd(newItem)
	jl.mut.Unlock()

	job = newItem
	return
}

func (jl *Clock) addJob(createTime time.Time, jobInterval time.Duration, jobTimes uint64, jobFunc func()) (job *jobItem, inserted bool) {
	inserted = true
	jl.seq++
	job = &jobItem{
		id:           jl.seq,
		times:        jobTimes,
		createTime:   createTime,
		actionTime:   createTime.Add(jobInterval),
		intervalTime: jobInterval,
		msgChan:      make(chan Job, 10),
		fn:           jobFunc,
	}
	jl.jobList.Insert(job)

	return

}

// DelJob Deletes the task that has been added to the task queue. If the key does not exist, return false.
func (jl *Clock) DelJob(job Job) (deleted bool) {
	if job == nil {
		deleted = false
		return
	}

	jl.mut.Lock()
	defer jl.mut.Unlock()

	item, ok := job.(*jobItem)
	if !ok {
		return false
	}
	jl.removeJob(item)
	deleted = true

	jl.timeRefreshAfterDel()

	return
}

// DelJobs remove jobs from clock schedule list
func (jl *Clock) DelJobs(jobIds []Job) {
	jl.mut.Lock()
	defer jl.mut.Unlock()

	for _, job := range jobIds {
		item, ok := job.(*jobItem)
		if !ok {
			continue
		}
		jl.removeJob(item)
	}

	jl.timeRefreshAfterDel()

	return
}
func (jl *Clock) removeJob(item *jobItem) {
	jl.jobList.Delete(item)
	close(item.msgChan)

	return
}

// Count 已经执行的任务数。对于重复任务，会计算多次
func (jl *Clock) Count() uint64 {
	jl.mut.Lock()
	defer jl.mut.Unlock()

	return jl.count
}

//WaitJobs 待执行任务数
// Note:每次使用，对任务队列会有微秒级的阻塞
func (jl *Clock) WaitJobs() uint {
	jl.mut.Lock()
	defer jl.mut.Unlock()

	return jl.jobList.Len()
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 cloudprovider

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/informers"
	clientset "k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
)

// ControllerClientBuilder allows you to get clients and configs for controllers
// Please note a copy also exists in pkg/controller/client_builder.go
// TODO: Make this depend on the separate controller utilities repo (issues/68947)
type ControllerClientBuilder interface {
	Config(name string) (*restclient.Config, error)
	ConfigOrDie(name string) *restclient.Config
	Client(name string) (clientset.Interface, error)
	ClientOrDie(name string) clientset.Interface
}

// Interface is an abstract, pluggable interface for cloud providers.
type Interface interface {
	// Initialize provides the cloud with a kubernetes client builder and may spawn goroutines
	// to perform housekeeping or run custom controllers specific to the cloud provider.
	// Any tasks started here should be cleaned up when the stop channel closes.
	Initialize(clientBuilder ControllerClientBuilder, stop <-chan struct{})
	// LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
	LoadBalancer() (LoadBalancer, bool)
	// Instances returns an instances interface. Also returns true if the interface is supported, false otherwise.
	Instances() (Instances, bool)
	// Zones returns a zones interface. Also returns true if the interface is supported, false otherwise.
	Zones() (Zones, bool)
	// Clusters returns a clusters interface.  Also returns true if the interface is supported, false otherwise.
	Clusters() (Clusters, bool)
	// Routes returns a routes interface along with whether the interface is supported.
	Routes() (Routes, bool)
	// ProviderName returns the cloud provider ID.
	ProviderName() string
	// HasClusterID returns true if a ClusterID is required and set
	HasClusterID() bool
}

type InformerUser interface {
	// SetInformers sets the informer on the cloud object.
	SetInformers(informerFactory informers.SharedInformerFactory)
}

// Clusters is an abstract, pluggable interface for clusters of containers.
type Clusters interface {
	// ListClusters lists the names of the available clusters.
	ListClusters(ctx context.Context) ([]string, error)
	// Master gets back the address (either DNS name or IP address) of the master node for the cluster.
	Master(ctx context.Context, clusterName string) (string, error)
}

// (DEPRECATED) DefaultLoadBalancerName is the default load balancer name that is called from
// LoadBalancer.GetLoadBalancerName. Use this method to maintain backward compatible names for
// LoadBalancers that were created prior to Kubernetes v1.12. In the future, each provider should
// replace this method call in GetLoadBalancerName with a provider-specific implementation that
// is less cryptic than the Service's UUID.
func DefaultLoadBalancerName(service *v1.Service) string {
	//GCE requires that the name of a load balancer starts with a lower case letter.
	ret := "a" + string(service.UID)
	ret = strings.Replace(ret, "-", "", -1)
	//AWS requires that the name of a load balancer is shorter than 32 bytes.
	if len(ret) > 32 {
		ret = ret[:32]
	}
	return ret
}

// GetInstanceProviderID builds a ProviderID for a node in a cloud.
func GetInstanceProviderID(ctx context.Context, cloud Interface, nodeName types.NodeName) (string, error) {
	instances, ok := cloud.Instances()
	if !ok {
		return "", fmt.Errorf("failed to get instances from cloud provider")
	}
	instanceID, err := instances.InstanceID(ctx, nodeName)
	if err != nil {
		if err == NotImplemented {
			return "", err
		}

		return "", fmt.Errorf("failed to get instance ID from cloud provider: %v", err)
	}
	return cloud.ProviderName() + "://" + instanceID, nil
}

// LoadBalancer is an abstract, pluggable interface for load balancers.
//
// Cloud provider may chose to implement the logic for
// constructing/destroying specific kinds of load balancers in a
// controller separate from the ServiceController.  If this is the case,
// then {Ensure,Update}LoadBalancer must return the ImplementedElsewhere error.
// For the given LB service, the GetLoadBalancer must return "exists=True" if
// there exists a LoadBalancer instance created by ServiceController.
// In all other cases, GetLoadBalancer must return a NotFound error.
// EnsureLoadBalancerDeleted must not return ImplementedElsewhere to ensure
// proper teardown of resources that were allocated by the ServiceController.
// This can happen if a user changes the type of LB via an update to the resource
// or when migrating from ServiceController to alternate implementation.
// The finalizer on the service will be added and removed by ServiceController
// irrespective of the ImplementedElsewhere error. Additional finalizers for
// LB services must be managed in the alternate implementation.
type LoadBalancer interface {
	// TODO: Break this up into different interfaces (LB, etc) when we have more than one type of service
	// GetLoadBalancer returns whether the specified load balancer exists, and
	// if so, what its status is.
	// Implementations must treat the *v1.Service parameter as read-only and not modify it.
	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
	GetLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) (status *v1.LoadBalancerStatus, exists bool, err error)
	// GetLoadBalancerName returns the name of the load balancer. Implementations must treat the
	// *v1.Service parameter as read-only and not modify it.
	GetLoadBalancerName(ctx context.Context, clusterName string, service *v1.Service) string
	// EnsureLoadBalancer creates a new load balancer 'name', or updates the existing one. Returns the status of the balancer
	// Implementations must treat the *v1.Service and *v1.Node
	// parameters as read-only and not modify them.
	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
	EnsureLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error)
	// UpdateLoadBalancer updates hosts under the specified load balancer.
	// Implementations must treat the *v1.Service and *v1.Node
	// parameters as read-only and not modify them.
	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
	UpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error
	// EnsureLoadBalancerDeleted deletes the specified load balancer if it
	// exists, returning nil if the load balancer specified either didn't exist or
	// was successfully deleted.
	// This construction is useful because many cloud providers' load balancers
	// have multiple underlying components, meaning a Get could say that the LB
	// doesn't exist even if some part of it is still laying around.
	// Implementations must treat the *v1.Service parameter as read-only and not modify it.
	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
	EnsureLoadBalancerDeleted(ctx context.Context, clusterName string, service *v1.Service) error
}

// Instances is an abstract, pluggable interface for sets of instances.
type Instances interface {
	// NodeAddresses returns the addresses of the specified instance.
	// TODO(roberthbailey): This currently is only used in such a way that it
	// returns the address of the calling instance. We should do a rename to
	// make this clearer.
	NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.NodeAddress, error)
	// NodeAddressesByProviderID returns the addresses of the specified instance.
	// The instance is specified using the providerID of the node. The
	// ProviderID is a unique identifier of the node. This will not be called
	// from the node whose nodeaddresses are being queried. i.e. local metadata
	// services cannot be used in this method to obtain nodeaddresses
	NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error)
	// InstanceID returns the cloud provider ID of the node with the specified NodeName.
	// Note that if the instance does not exist, we must return ("", cloudprovider.InstanceNotFound)
	// cloudprovider.InstanceNotFound should NOT be returned for instances that exist but are stopped/sleeping
	InstanceID(ctx context.Context, nodeName types.NodeName) (string, error)
	// InstanceType returns the type of the specified instance.
	InstanceType(ctx context.Context, name types.NodeName) (string, error)
	// InstanceTypeByProviderID returns the type of the specified instance.
	InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error)
	// AddSSHKeyToAllInstances adds an SSH public key as a legal identity for all instances
	// expected format for the key is standard ssh-keygen format: <protocol> <blob>
	AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error
	// CurrentNodeName returns the name of the node we are currently running on
	// On most clouds (e.g. GCE) this is the hostname, so we provide the hostname
	CurrentNodeName(ctx context.Context, hostname string) (types.NodeName, error)
	// InstanceExistsByProviderID returns true if the instance for the given provider exists.
	// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
	// This method should still return true for instances that exist but are stopped/sleeping.
	InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error)
	// InstanceShutdownByProviderID returns true if the instance is shutdown in cloudprovider
	InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error)
}

// Route is a representation of an advanced routing rule.
type Route struct {
	// Name is the name of the routing rule in the cloud-provider.
	// It will be ignored in a Create (although nameHint may influence it)
	Name string
	// TargetNode is the NodeName of the target instance.
	TargetNode types.NodeName
	// DestinationCIDR is the CIDR format IP range that this routing rule
	// applies to.
	DestinationCIDR string
	// Blackhole is set to true if this is a blackhole route
	// The node controller will delete the route if it is in the managed range.
	Blackhole bool
}

// Routes is an abstract, pluggable interface for advanced routing rules.
type Routes interface {
	// ListRoutes lists all managed routes that belong to the specified clusterName
	ListRoutes(ctx context.Context, clusterName string) ([]*Route, error)
	// CreateRoute creates the described managed route
	// route.Name will be ignored, although the cloud-provider may use nameHint
	// to create a more user-meaningful name.
	CreateRoute(ctx context.Context, clusterName string, nameHint string, route *Route) error
	// DeleteRoute deletes the specified managed route
	// Route should be as returned by ListRoutes
	DeleteRoute(ctx context.Context, clusterName string, route *Route) error
}

var (
	DiskNotFound         = errors.New("disk is not found")
	ImplementedElsewhere = errors.New("implemented by alternate to cloud provider")
	InstanceNotFound     = errors.New("instance not found")
	NotImplemented       = errors.New("unimplemented")
)

// Zone represents the location of a particular machine.
type Zone struct {
	FailureDomain string
	Region        string
}

// Zones is an abstract, pluggable interface for zone enumeration.
type Zones interface {
	// GetZone returns the Zone containing the current failure zone and locality region that the program is running in
	// In most cases, this method is called from the kubelet querying a local metadata service to acquire its zone.
	// For the case of external cloud providers, use GetZoneByProviderID or GetZoneByNodeName since GetZone
	// can no longer be called from the kubelets.
	GetZone(ctx context.Context) (Zone, error)

	// GetZoneByProviderID returns the Zone containing the current zone and locality region of the node specified by providerID
	// This method is particularly used in the context of external cloud providers where node initialization must be done
	// outside the kubelets.
	GetZoneByProviderID(ctx context.Context, providerID string) (Zone, error)

	// GetZoneByNodeName returns the Zone containing the current zone and locality region of the node specified by node name
	// This method is particularly used in the context of external cloud providers where node initialization must be done
	// outside the kubelets.
	GetZoneByNodeName(ctx context.Context, nodeName types.NodeName) (Zone, error)
}

// PVLabeler is an abstract, pluggable interface for fetching labels for volumes
type PVLabeler interface {
	GetLabelsForVolume(ctx context.Context, pv *v1.PersistentVolume) (map[string]string, error)
}
<commit_msg>remove TODO item about NodeAddresses<commit_after>/*
Copyright 2014 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 cloudprovider

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/informers"
	clientset "k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
)

// ControllerClientBuilder allows you to get clients and configs for controllers
// Please note a copy also exists in pkg/controller/client_builder.go
// TODO: Make this depend on the separate controller utilities repo (issues/68947)
type ControllerClientBuilder interface {
	Config(name string) (*restclient.Config, error)
	ConfigOrDie(name string) *restclient.Config
	Client(name string) (clientset.Interface, error)
	ClientOrDie(name string) clientset.Interface
}

// Interface is an abstract, pluggable interface for cloud providers.
type Interface interface {
	// Initialize provides the cloud with a kubernetes client builder and may spawn goroutines
	// to perform housekeeping or run custom controllers specific to the cloud provider.
	// Any tasks started here should be cleaned up when the stop channel closes.
	Initialize(clientBuilder ControllerClientBuilder, stop <-chan struct{})
	// LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
	LoadBalancer() (LoadBalancer, bool)
	// Instances returns an instances interface. Also returns true if the interface is supported, false otherwise.
	Instances() (Instances, bool)
	// Zones returns a zones interface. Also returns true if the interface is supported, false otherwise.
	Zones() (Zones, bool)
	// Clusters returns a clusters interface.  Also returns true if the interface is supported, false otherwise.
	Clusters() (Clusters, bool)
	// Routes returns a routes interface along with whether the interface is supported.
	Routes() (Routes, bool)
	// ProviderName returns the cloud provider ID.
	ProviderName() string
	// HasClusterID returns true if a ClusterID is required and set
	HasClusterID() bool
}

type InformerUser interface {
	// SetInformers sets the informer on the cloud object.
	SetInformers(informerFactory informers.SharedInformerFactory)
}

// Clusters is an abstract, pluggable interface for clusters of containers.
type Clusters interface {
	// ListClusters lists the names of the available clusters.
	ListClusters(ctx context.Context) ([]string, error)
	// Master gets back the address (either DNS name or IP address) of the master node for the cluster.
	Master(ctx context.Context, clusterName string) (string, error)
}

// (DEPRECATED) DefaultLoadBalancerName is the default load balancer name that is called from
// LoadBalancer.GetLoadBalancerName. Use this method to maintain backward compatible names for
// LoadBalancers that were created prior to Kubernetes v1.12. In the future, each provider should
// replace this method call in GetLoadBalancerName with a provider-specific implementation that
// is less cryptic than the Service's UUID.
func DefaultLoadBalancerName(service *v1.Service) string {
	//GCE requires that the name of a load balancer starts with a lower case letter.
	ret := "a" + string(service.UID)
	ret = strings.Replace(ret, "-", "", -1)
	//AWS requires that the name of a load balancer is shorter than 32 bytes.
	if len(ret) > 32 {
		ret = ret[:32]
	}
	return ret
}

// GetInstanceProviderID builds a ProviderID for a node in a cloud.
func GetInstanceProviderID(ctx context.Context, cloud Interface, nodeName types.NodeName) (string, error) {
	instances, ok := cloud.Instances()
	if !ok {
		return "", fmt.Errorf("failed to get instances from cloud provider")
	}
	instanceID, err := instances.InstanceID(ctx, nodeName)
	if err != nil {
		if err == NotImplemented {
			return "", err
		}

		return "", fmt.Errorf("failed to get instance ID from cloud provider: %v", err)
	}
	return cloud.ProviderName() + "://" + instanceID, nil
}

// LoadBalancer is an abstract, pluggable interface for load balancers.
//
// Cloud provider may chose to implement the logic for
// constructing/destroying specific kinds of load balancers in a
// controller separate from the ServiceController.  If this is the case,
// then {Ensure,Update}LoadBalancer must return the ImplementedElsewhere error.
// For the given LB service, the GetLoadBalancer must return "exists=True" if
// there exists a LoadBalancer instance created by ServiceController.
// In all other cases, GetLoadBalancer must return a NotFound error.
// EnsureLoadBalancerDeleted must not return ImplementedElsewhere to ensure
// proper teardown of resources that were allocated by the ServiceController.
// This can happen if a user changes the type of LB via an update to the resource
// or when migrating from ServiceController to alternate implementation.
// The finalizer on the service will be added and removed by ServiceController
// irrespective of the ImplementedElsewhere error. Additional finalizers for
// LB services must be managed in the alternate implementation.
type LoadBalancer interface {
	// TODO: Break this up into different interfaces (LB, etc) when we have more than one type of service
	// GetLoadBalancer returns whether the specified load balancer exists, and
	// if so, what its status is.
	// Implementations must treat the *v1.Service parameter as read-only and not modify it.
	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
	GetLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) (status *v1.LoadBalancerStatus, exists bool, err error)
	// GetLoadBalancerName returns the name of the load balancer. Implementations must treat the
	// *v1.Service parameter as read-only and not modify it.
	GetLoadBalancerName(ctx context.Context, clusterName string, service *v1.Service) string
	// EnsureLoadBalancer creates a new load balancer 'name', or updates the existing one. Returns the status of the balancer
	// Implementations must treat the *v1.Service and *v1.Node
	// parameters as read-only and not modify them.
	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
	EnsureLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error)
	// UpdateLoadBalancer updates hosts under the specified load balancer.
	// Implementations must treat the *v1.Service and *v1.Node
	// parameters as read-only and not modify them.
	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
	UpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error
	// EnsureLoadBalancerDeleted deletes the specified load balancer if it
	// exists, returning nil if the load balancer specified either didn't exist or
	// was successfully deleted.
	// This construction is useful because many cloud providers' load balancers
	// have multiple underlying components, meaning a Get could say that the LB
	// doesn't exist even if some part of it is still laying around.
	// Implementations must treat the *v1.Service parameter as read-only and not modify it.
	// Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
	EnsureLoadBalancerDeleted(ctx context.Context, clusterName string, service *v1.Service) error
}

// Instances is an abstract, pluggable interface for sets of instances.
type Instances interface {
	// NodeAddresses returns the addresses of the specified instance.
	NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.NodeAddress, error)
	// NodeAddressesByProviderID returns the addresses of the specified instance.
	// The instance is specified using the providerID of the node. The
	// ProviderID is a unique identifier of the node. This will not be called
	// from the node whose nodeaddresses are being queried. i.e. local metadata
	// services cannot be used in this method to obtain nodeaddresses
	NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error)
	// InstanceID returns the cloud provider ID of the node with the specified NodeName.
	// Note that if the instance does not exist, we must return ("", cloudprovider.InstanceNotFound)
	// cloudprovider.InstanceNotFound should NOT be returned for instances that exist but are stopped/sleeping
	InstanceID(ctx context.Context, nodeName types.NodeName) (string, error)
	// InstanceType returns the type of the specified instance.
	InstanceType(ctx context.Context, name types.NodeName) (string, error)
	// InstanceTypeByProviderID returns the type of the specified instance.
	InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error)
	// AddSSHKeyToAllInstances adds an SSH public key as a legal identity for all instances
	// expected format for the key is standard ssh-keygen format: <protocol> <blob>
	AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error
	// CurrentNodeName returns the name of the node we are currently running on
	// On most clouds (e.g. GCE) this is the hostname, so we provide the hostname
	CurrentNodeName(ctx context.Context, hostname string) (types.NodeName, error)
	// InstanceExistsByProviderID returns true if the instance for the given provider exists.
	// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
	// This method should still return true for instances that exist but are stopped/sleeping.
	InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error)
	// InstanceShutdownByProviderID returns true if the instance is shutdown in cloudprovider
	InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error)
}

// Route is a representation of an advanced routing rule.
type Route struct {
	// Name is the name of the routing rule in the cloud-provider.
	// It will be ignored in a Create (although nameHint may influence it)
	Name string
	// TargetNode is the NodeName of the target instance.
	TargetNode types.NodeName
	// DestinationCIDR is the CIDR format IP range that this routing rule
	// applies to.
	DestinationCIDR string
	// Blackhole is set to true if this is a blackhole route
	// The node controller will delete the route if it is in the managed range.
	Blackhole bool
}

// Routes is an abstract, pluggable interface for advanced routing rules.
type Routes interface {
	// ListRoutes lists all managed routes that belong to the specified clusterName
	ListRoutes(ctx context.Context, clusterName string) ([]*Route, error)
	// CreateRoute creates the described managed route
	// route.Name will be ignored, although the cloud-provider may use nameHint
	// to create a more user-meaningful name.
	CreateRoute(ctx context.Context, clusterName string, nameHint string, route *Route) error
	// DeleteRoute deletes the specified managed route
	// Route should be as returned by ListRoutes
	DeleteRoute(ctx context.Context, clusterName string, route *Route) error
}

var (
	DiskNotFound         = errors.New("disk is not found")
	ImplementedElsewhere = errors.New("implemented by alternate to cloud provider")
	InstanceNotFound     = errors.New("instance not found")
	NotImplemented       = errors.New("unimplemented")
)

// Zone represents the location of a particular machine.
type Zone struct {
	FailureDomain string
	Region        string
}

// Zones is an abstract, pluggable interface for zone enumeration.
type Zones interface {
	// GetZone returns the Zone containing the current failure zone and locality region that the program is running in
	// In most cases, this method is called from the kubelet querying a local metadata service to acquire its zone.
	// For the case of external cloud providers, use GetZoneByProviderID or GetZoneByNodeName since GetZone
	// can no longer be called from the kubelets.
	GetZone(ctx context.Context) (Zone, error)

	// GetZoneByProviderID returns the Zone containing the current zone and locality region of the node specified by providerID
	// This method is particularly used in the context of external cloud providers where node initialization must be done
	// outside the kubelets.
	GetZoneByProviderID(ctx context.Context, providerID string) (Zone, error)

	// GetZoneByNodeName returns the Zone containing the current zone and locality region of the node specified by node name
	// This method is particularly used in the context of external cloud providers where node initialization must be done
	// outside the kubelets.
	GetZoneByNodeName(ctx context.Context, nodeName types.NodeName) (Zone, error)
}

// PVLabeler is an abstract, pluggable interface for fetching labels for volumes
type PVLabeler interface {
	GetLabelsForVolume(ctx context.Context, pv *v1.PersistentVolume) (map[string]string, error)
}
<|endoftext|>