<commit_before>package l10n_handler

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"sync"

	"github.com/nicksnyder/go-i18n/i18n/bundle"
	"golang.org/x/text/language"

	"github.com/flimzy/go-cordova"
	"github.com/flimzy/jqeventrouter"
	"github.com/flimzy/log"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/jquery"

	"github.com/FlashbackSRS/flashback/util"
)

var localeName string
var jQuery = jquery.NewJQuery
var initDone <-chan struct{}

const langTagAttr = "data-lt"
const localeAttr = "data-locale"

var translate *bundle.TranslateFunc
var translateFallback *bundle.TranslateFunc

func MobileInit() {
	initChan := make(chan struct{})
	initDone = initChan
	go func() {
		defer close(initChan)
		langs := preferredLanguages()
		log.Debugf("PREFERRED LANGUAGES: %v\n", langs)
		matcher := language.NewMatcher([]language.Tag{
			language.MustParse("en_US"),
			language.MustParse("es_MX"),
		})
		tag, idx, conf := matcher.Match(langs...)
		log.Debugf("I think we'll go with %s (%d th place choice with %s confidence)\n", tag, idx, conf)
		if locale := tag.String(); locale != "en-us" {
			t, err := loadDictionary(locale)
			if err != nil {
				panic(fmt.Sprintf("Cannot load '%s': %s", locale, err))
			}
			translate = t
		}
		if t, err := loadDictionary("en-us"); err != nil {
			panic(fmt.Sprintf("Cannot load 'en-us': %s", err))
		} else {
			translateFallback = t
		}
	}()
}

func loadDictionary(locale string) (*bundle.TranslateFunc, error) {
	locale = strings.ToLower(locale)
	translations, err := fetchTranslations(locale)
	if err != nil {
		return nil, err
	}
	bdl := bundle.New()
	if err := bdl.ParseTranslationFileBytes(locale+".all.json", translations); err != nil {
		return nil, err
	}
	t, err := bdl.Tfunc(locale, locale) // stupid API, requires a second parameter
	if err != nil {
		return nil, err
	}
	return &t, nil
}

func T(id string, args ...interface{}) string {
	if translate != nil {
		t := *translate
		// If dictionary == nil, it means the requested language is the default
		if result := t(id, args...); result != id {
			return result
		}
		log.Debugf("No result looking up tag '%s'\n", id)
		// TODO: Log the missing translation
	}
	t := *translateFallback
	if result := t(id, args...); result != id {
		return result
	}
	log.Debugf("No result looking up fallback tag '%s'\n", id)
	// TODO: *really* log the missing translation!
	return id
}

func preferredLanguages() []language.Tag {
	var langs []language.Tag
	//	langs = append(langs, language.MustParse("es_MX"))
	nav := js.Global.Get("navigator")
	if cordova.IsMobile() {
		var wg sync.WaitGroup
		wg.Add(1)
		nav.Get("globalization").Call("getPreferredLanguage",
			func(l string) {
				defer wg.Done()
				if tag, err := language.Parse(l); err == nil {
					langs = append(langs, tag)
				}
			}, func() {
				defer wg.Done()
				// ignore any error
			})
		wg.Wait()
	}
	if languages := js.Global.Get("navigator").Get("languages"); languages != nil {
		for i := 0; i < languages.Length(); i++ {
			if tag, err := language.Parse(languages.Index(i).String()); err == nil {
				langs = append(langs, tag)
			}
		}
	}
	if tag, err := language.Parse(js.Global.Get("navigator").Get("language").String()); err == nil {
		langs = append(langs, tag)
	}
	return langs
}

func fetchTranslations(lang string) ([]byte, error) {
	resp, err := http.Get(util.BaseURI() + "translations/" + lang + ".all.json")
	if err != nil {
		return []byte{}, err
	}
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, err
	}
	return content, nil
}

func LocalizePage(h jqeventrouter.Handler) jqeventrouter.Handler {
	return jqeventrouter.HandlerFunc(func(event *jquery.Event, ui *js.Object, p url.Values) bool {
		go localize()
		return h.HandleEvent(event, ui, p)
	})
}

func localize() {
	<-initDone // Make sure the dictionary is ready before we try translating
	elements := jQuery("[" + langTagAttr + "]").Not("[" + localeAttr + "='" + localeName + "']")
	if elements.Length == 0 {
		log.Debugf("Nothing to localize, early exiting\n")
		return
	}
	log.Debugf("I think I have %d items to localize\n", elements.Length)
	for i := 0; i < elements.Length; i++ {
		elem := elements.Get(i)
		text := elem.Call("getAttribute", langTagAttr).String()
		if translated := T(text); translated != text {
			elem.Set("innerHTML", translated)
		} else {
			log.Debugf("%d. %s = %s\n", i, text, translated)
		}
	}
}
<commit_msg>More localized variable<commit_after>package l10n_handler

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"sync"

	"github.com/nicksnyder/go-i18n/i18n/bundle"
	"golang.org/x/text/language"

	"github.com/flimzy/go-cordova"
	"github.com/flimzy/jqeventrouter"
	"github.com/flimzy/log"
	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/jquery"

	"github.com/FlashbackSRS/flashback/util"
)

var localeName string
var jQuery = jquery.NewJQuery
var initDone <-chan struct{}

const langTagAttr = "data-lt"
const localeAttr = "data-locale"

var translate *bundle.TranslateFunc
var translateFallback *bundle.TranslateFunc

func MobileInit() {
	initChan := make(chan struct{})
	initDone = initChan
	go func() {
		defer close(initChan)
		langs := preferredLanguages()
		log.Debugf("PREFERRED LANGUAGES: %v\n", langs)
		matcher := language.NewMatcher([]language.Tag{
			language.MustParse("en_US"),
			language.MustParse("es_MX"),
		})
		tag, idx, conf := matcher.Match(langs...)
		log.Debugf("I think we'll go with %s (%d th place choice with %s confidence)\n", tag, idx, conf)
		if locale := tag.String(); locale != "en-us" {
			t, err := loadDictionary(locale)
			if err != nil {
				panic(fmt.Sprintf("Cannot load '%s': %s", locale, err))
			}
			translate = t
		}
		if t, err := loadDictionary("en-us"); err != nil {
			panic(fmt.Sprintf("Cannot load 'en-us': %s", err))
		} else {
			translateFallback = t
		}
	}()
}

func loadDictionary(locale string) (*bundle.TranslateFunc, error) {
	locale = strings.ToLower(locale)
	translations, err := fetchTranslations(locale)
	if err != nil {
		return nil, err
	}
	bdl := bundle.New()
	if err := bdl.ParseTranslationFileBytes(locale+".all.json", translations); err != nil {
		return nil, err
	}
	t, err := bdl.Tfunc(locale, locale) // stupid API, requires a second parameter
	if err != nil {
		return nil, err
	}
	return &t, nil
}

func T(id string, args ...interface{}) string {
	if translate != nil {
		t := *translate
		// If dictionary == nil, it means the requested language is the default
		if result := t(id, args...); result != id {
			return result
		}
		log.Debugf("No result looking up tag '%s'\n", id)
		// TODO: Log the missing translation
	}
	t := *translateFallback
	if result := t(id, args...); result != id {
		return result
	}
	log.Debugf("No result looking up fallback tag '%s'\n", id)
	// TODO: *really* log the missing translation!
	return id
}

func preferredLanguages() []language.Tag {
	var langs []language.Tag
	//	langs = append(langs, language.MustParse("es_MX"))
	if cordova.IsMobile() {
		var wg sync.WaitGroup
		wg.Add(1)
		nav := js.Global.Get("navigator")
		nav.Get("globalization").Call("getPreferredLanguage",
			func(l string) {
				defer wg.Done()
				if tag, err := language.Parse(l); err == nil {
					langs = append(langs, tag)
				}
			}, func() {
				defer wg.Done()
				// ignore any error
			})
		wg.Wait()
	}
	if languages := js.Global.Get("navigator").Get("languages"); languages != nil {
		for i := 0; i < languages.Length(); i++ {
			if tag, err := language.Parse(languages.Index(i).String()); err == nil {
				langs = append(langs, tag)
			}
		}
	}
	if tag, err := language.Parse(js.Global.Get("navigator").Get("language").String()); err == nil {
		langs = append(langs, tag)
	}
	return langs
}

func fetchTranslations(lang string) ([]byte, error) {
	resp, err := http.Get(util.BaseURI() + "translations/" + lang + ".all.json")
	if err != nil {
		return []byte{}, err
	}
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, err
	}
	return content, nil
}

func LocalizePage(h jqeventrouter.Handler) jqeventrouter.Handler {
	return jqeventrouter.HandlerFunc(func(event *jquery.Event, ui *js.Object, p url.Values) bool {
		go localize()
		return h.HandleEvent(event, ui, p)
	})
}

func localize() {
	<-initDone // Make sure the dictionary is ready before we try translating
	elements := jQuery("[" + langTagAttr + "]").Not("[" + localeAttr + "='" + localeName + "']")
	if elements.Length == 0 {
		log.Debugf("Nothing to localize, early exiting\n")
		return
	}
	log.Debugf("I think I have %d items to localize\n", elements.Length)
	for i := 0; i < elements.Length; i++ {
		elem := elements.Get(i)
		text := elem.Call("getAttribute", langTagAttr).String()
		if translated := T(text); translated != text {
			elem.Set("innerHTML", translated)
		} else {
			log.Debugf("%d. %s = %s\n", i, text, translated)
		}
	}
}
<|endoftext|>


<commit_before>package rest_json

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/Everlane/evan/config"
	"github.com/Everlane/evan/context"
)

type CreateDeploymentRequest struct {
	application string `json:"application"`
	environment string `json:"environment"`
	ref         string `json:"ref"`
}

func (cdr *CreateDeploymentRequest) newDeployment(app *config.Application) (*context.Deployment, error) {
	return context.NewDeployment(app.Wrapper(), cdr.environment, cdr.ref)
}

type CreateDeploymentHandler struct {
	Applications *config.Applications
}

func (handler *CreateDeploymentHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	var deploymentRequest CreateDeploymentRequest
	err := handler.readRequestInto(req, &deploymentRequest)
	if err != nil {
		respondWithError(res, err, http.StatusInternalServerError)
		return
	}

	app := handler.Applications.FindApplicationByName(deploymentRequest.application)
	if app == nil {
		err = fmt.Errorf("Application not found: '%v'", deploymentRequest.application)
		respondWithError(res, err, http.StatusNotFound)
		return
	}

	deployment, err := deploymentRequest.newDeployment(app)
	if err != nil {
		respondWithError(res, err, http.StatusNotFound)
		return
	}

	err = deployment.SetStoreAndSave(handler.Applications.Store)
	if err != nil {
		respondWithError(res, err, http.StatusInternalServerError)
		return
	}

	err = deployment.CheckPreconditions()
	if err != nil {
		respondWithError(res, err, http.StatusPreconditionFailed)
		return
	}

	// Start the party!
	go deployment.RunPhases()

	message := fmt.Sprintf(
		"Deploying %v to %v for %v",
		deploymentRequest.ref,
		deploymentRequest.environment,
		deploymentRequest.application,
	)
	respondWithOk(res, message)
}

func (handler *CreateDeploymentHandler) readRequestInto(req *http.Request, val interface{}) error {
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return err
	}

	return json.Unmarshal(body, val)
}
<commit_msg>Try to fix unmarshalling<commit_after>package rest_json

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/Everlane/evan/config"
	"github.com/Everlane/evan/context"
)

type CreateDeploymentRequest struct {
	application string `json:"application"`
	environment string `json:"environment"`
	ref         string `json:"ref"`
}

func (cdr *CreateDeploymentRequest) newDeployment(app *config.Application) (*context.Deployment, error) {
	return context.NewDeployment(app.Wrapper(), cdr.environment, cdr.ref)
}

type CreateDeploymentHandler struct {
	Applications *config.Applications
}

func (handler *CreateDeploymentHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	var deploymentRequest CreateDeploymentRequest
	err := handler.readRequestInto(req, &deploymentRequest)
	if err != nil {
		respondWithError(res, err, http.StatusInternalServerError)
		return
	}

	fmt.Printf("deploymentRequest: %+v", deploymentRequest)

	app := handler.Applications.FindApplicationByName(deploymentRequest.application)
	if app == nil {
		err = fmt.Errorf("Application not found: '%v'", deploymentRequest.application)
		respondWithError(res, err, http.StatusNotFound)
		return
	}

	deployment, err := deploymentRequest.newDeployment(app)
	if err != nil {
		respondWithError(res, err, http.StatusNotFound)
		return
	}

	err = deployment.SetStoreAndSave(handler.Applications.Store)
	if err != nil {
		respondWithError(res, err, http.StatusInternalServerError)
		return
	}

	err = deployment.CheckPreconditions()
	if err != nil {
		respondWithError(res, err, http.StatusPreconditionFailed)
		return
	}

	// Start the party!
	go deployment.RunPhases()

	message := fmt.Sprintf(
		"Deploying %v to %v for %v",
		deploymentRequest.ref,
		deploymentRequest.environment,
		deploymentRequest.application,
	)
	respondWithOk(res, message)
}

func (handler *CreateDeploymentHandler) readRequestInto(req *http.Request, val interface{}) error {
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return err
	}

	return json.Unmarshal(body, &val)
}
<|endoftext|>


<commit_before>package python

import (
	"github.com/anchore/imgbom/imgbom/cataloger/common"
	"github.com/anchore/imgbom/imgbom/pkg"
	"github.com/anchore/imgbom/imgbom/scope"
	"github.com/anchore/stereoscope/pkg/file"
)

type Cataloger struct {
	cataloger common.GenericCataloger
}

func NewCataloger() *Cataloger {
	globParsers := map[string]common.ParserFn{
		"**/egg-info/PKG-INFO":  parseEggMetadata,
		"**/dist-info/METADATA": parseWheelMetadata,
	}

	return &Cataloger{
		cataloger: common.NewGenericCataloger(nil, globParsers),
	}
}

func (a *Cataloger) Name() string {
	return "python-cataloger"
}

func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
	return a.cataloger.SelectFiles(resolver)
}

func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) {
	return a.cataloger.Catalog(contents, a.Name())
}
<commit_msg>cataloger: add a glob path for parsing requirements.txt<commit_after>package python

import (
	"github.com/anchore/imgbom/imgbom/cataloger/common"
	"github.com/anchore/imgbom/imgbom/pkg"
	"github.com/anchore/imgbom/imgbom/scope"
	"github.com/anchore/stereoscope/pkg/file"
)

type Cataloger struct {
	cataloger common.GenericCataloger
}

func NewCataloger() *Cataloger {
	globParsers := map[string]common.ParserFn{
		"**/egg-info/PKG-INFO":  parseEggMetadata,
		"**/dist-info/METADATA": parseWheelMetadata,
		"**/requirements.txt":   parseRequirementsTxt,
	}

	return &Cataloger{
		cataloger: common.NewGenericCataloger(nil, globParsers),
	}
}

func (a *Cataloger) Name() string {
	return "python-cataloger"
}

func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
	return a.cataloger.SelectFiles(resolver)
}

func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) {
	return a.cataloger.Catalog(contents, a.Name())
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"github.com/namsral/flag"
	"fmt"
	"net"
	"math"
	"strings"
	"strconv"
	"sync"
	"io/ioutil"
	"crypto/x509"
)

const maxChannels = 4

// Config is a type of all the configuration variables after user input is processed
type Config struct {
	source   *net.TCPAddr
	dest     *net.TCPAddr
	user     string
	pass     string
	channels []int
	certs    string
}

// Initialize global variables
var (
	config Config
	wg     sync.WaitGroup
)

func main() {
	config = Config{}

	// Retrieve the command line flags
	fs := flag.NewFlagSetWithEnvPrefix(os.Args[0], "SWANN", 0)
	userInput := fs.String("user", "", "Username to authenticate with")
	passInput := fs.String("pass", "", "Password to authenticate with")
	sourceInput := fs.String("source", "", "The address of the DVR in the format host:port")
	destInput := fs.String("dest", "", "The address of the streaming server in the format host:port")
	channelInput := fs.String("channels", "", "Channel(s) to stream, delimited by commas")
	certsInput := fs.String("certs", "", "Absolute file path to the certificate folder")

	fs.Parse(os.Args[1:])

	// Ensure that the command line flags are not empty
	if *userInput == "" || *passInput == "" || *sourceInput == "" || *destInput == "" ||
		*channelInput == "" || *certsInput == "" {
		fs.PrintDefaults()
		os.Exit(1)
	}
	config.user = *userInput
	config.pass = *passInput

	// Ensure that the certificates exist at the location
	for _, file := range []string{"client.key", "client.pem", "server.key", "server.pem"} {
		if _, err := os.Stat(*certsInput + "/" + file); err != nil {

		}
	}
	config.certs = *certsInput

	// Resolve the TCP addresses
	tcpAddr, err := net.ResolveTCPAddr("tcp", *sourceInput)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ResolveTCPAddr failed: ", err.Error())
		os.Exit(1)
	}
	config.source = tcpAddr
	tcpAddr, err = net.ResolveTCPAddr("tcp", *destInput)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ResolveTCPAddr failed: ", err.Error())
		os.Exit(1)
	}
	config.dest = tcpAddr

	// Retrieve the camera streams
	for _, channel := range config.channels {
		wg.Add(1)
		go StreamToServer(channel)
	}

	wg.Wait()
}
<commit_msg>Reinstate channel flag handling<commit_after>package main

import (
	"os"
	"github.com/namsral/flag"
	"fmt"
	"net"
	"sync"
	"strings"
	"math"
	"strconv"
)

const maxChannels = 4

// Config is a type of all the configuration variables after user input is processed
type Config struct {
	source   *net.TCPAddr
	dest     *net.TCPAddr
	user     string
	pass     string
	channels []int
	certs    string
}

// Initialize global variables
var (
	config Config
	wg     sync.WaitGroup
)

func main() {
	config = Config{}

	// Retrieve the command line flags
	fs := flag.NewFlagSetWithEnvPrefix(os.Args[0], "SWANN", 0)
	userInput := fs.String("user", "", "Username to authenticate with")
	passInput := fs.String("pass", "", "Password to authenticate with")
	sourceInput := fs.String("source", "", "The address of the DVR in the format host:port")
	destInput := fs.String("dest", "", "The address of the streaming server in the format host:port")
	channelInput := fs.String("channels", "", "Channel(s) to stream, delimited by commas")
	certsInput := fs.String("certs", "", "Absolute file path to the certificate folder")

	fs.Parse(os.Args[1:])

	// Ensure that the command line flags are not empty
	if *userInput == "" || *passInput == "" || *sourceInput == "" || *destInput == "" ||
		*channelInput == "" || *certsInput == "" {
		fs.PrintDefaults()
		os.Exit(1)
	}
	config.user = *userInput
	config.pass = *passInput

	// Parse the channel input
	channelSlice := strings.Split(*channelInput, ",")
	for i, channel := range channelSlice {
		intChannel, err := strconv.Atoi(channel)
		if i >= maxChannels {
			fmt.Fprintln(os.Stderr, "You cannot have greater than %d streams", maxChannels)
			os.Exit(1)
		} else if err != nil || intChannel > maxChannels {
			fmt.Fprintln(os.Stderr, "All channels need to be a number between 1 and %d", maxChannels)
			os.Exit(1)
		}
		// Convert channel from 1, 2, 3, 4 to 1, 2, 4, 8 respectively
		parsedChannel := int(math.Exp2(float64(intChannel - 1)))
		if intInSlice(&parsedChannel, &config.channels) {
			fmt.Fprintln(os.Stderr, "All channels need to be unique", maxChannels)
			os.Exit(1)
		}
		config.channels = append(config.channels, parsedChannel)
	}

	// Ensure that the certificates exist at the location
	for _, file := range []string{"client.key", "client.pem", "server.key", "server.pem"} {
		if _, err := os.Stat(*certsInput + "/" + file); err != nil {
			fmt.Fprintln(os.Stderr, "Unable to stat certificates: ", err.Error())
			os.Exit(1)
		}
	}
	config.certs = *certsInput

	// Resolve the TCP addresses
	tcpAddr, err := net.ResolveTCPAddr("tcp", *sourceInput)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ResolveTCPAddr failed: ", err.Error())
		os.Exit(1)
	}
	config.source = tcpAddr
	tcpAddr, err = net.ResolveTCPAddr("tcp", *destInput)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ResolveTCPAddr failed: ", err.Error())
		os.Exit(1)
	}
	config.dest = tcpAddr

	// Retrieve the camera streams
	for _, channel := range config.channels {
		wg.Add(1)
		go StreamToServer(channel)
	}

	wg.Wait()
}
<|endoftext|>


<commit_before>// Package jsonp impements JSONp middleware
//
// Copyright (C) Philip Schlump, 2015
// License LICENSE.apache.txt or LICENSE.mit.txt
//

//
// Directive
//
// jsonp /some/url
// jsonp /some/other-url
//

package jsonp

import (
	"net/http"
	"net/url"

	"github.com/mholt/caddy/config/setup"
	"github.com/mholt/caddy/middleware"
	"github.com/pschlump/caddy-jsonp/bufferhtml"
)

// Jsonp configures a new Jsonp middleware instance.
func Setup(c *setup.Controller) (middleware.Middleware, error) {
	paths, err := jsonpParse(c)
	if err != nil {
		return nil, err
	}

	return func(next middleware.Handler) middleware.Handler {
		return JsonPHandlerType{
			Next:  next,
			Paths: paths,
		}
	}, nil
}

func jsonpParse(c *setup.Controller) ([]string, error) {
	var paths []string

	for c.Next() {
		if !c.NextArg() {
			return paths, c.ArgErr()
		}
		paths = append(paths, c.Val())
	}

	return paths, nil
}

type JsonPHandlerType struct {
	Paths []string
	Next  middleware.Handler
}

func (jph JsonPHandlerType) ServeHTTP(www http.ResponseWriter, req *http.Request) (int, error) {
	for _, prefix := range jph.Paths {
		if middleware.Path(req.URL.Path).Matches(prefix) {
			ResponseBodyRecorder := bufferhtml.NewBufferHTML()
			status, err := jph.Next.ServeHTTP(ResponseBodyRecorder, req)
			if status == 200 {
				// if there is a "callback" argument then use that to format the JSONp response.
				Prefix := ""
				Postfix := ""
				u, err := url.ParseRequestURI(req.RequestURI)
				if err != nil {
					ResponseBodyRecorder.FlushAtEnd(www, "", "")
					return status, nil
				}
				m, err := url.ParseQuery(u.RawQuery)
				if err != nil {
					ResponseBodyRecorder.FlushAtEnd(www, "", "")
					return status, nil
				}
				callback := m.Get("callback")
				if callback != "" {
					www.Header().Set("Content-Type", "application/javascript")
					Prefix = callback + "("
					Postfix = ");"
				}
				ResponseBodyRecorder.FlushAtEnd(www, Prefix, Postfix)
			} else {
				ResponseBodyRecorder.FlushAtEnd(www, "", "")
			}
			return status, err
		}
	}
	return jph.Next.ServeHTTP(www, req)
}
<commit_msg>Changes to allow use of JSONP with proxy<commit_after>// Package jsonp impements JSONp middleware
//
// Copyright (C) Philip Schlump, 2015
// License LICENSE.apache.txt or LICENSE.mit.txt
//

//
// Directive
//
// jsonp /some/url
// jsonp /some/other-url
//

package jsonp

import (
	"net/http"
	"net/url"

	"github.com/mholt/caddy/config/setup"
	"github.com/mholt/caddy/middleware"
	"github.com/pschlump/caddy-jsonp/bufferhtml"
)

// Jsonp configures a new Jsonp middleware instance.
func Setup(c *setup.Controller) (middleware.Middleware, error) {
	paths, err := jsonpParse(c)
	if err != nil {
		return nil, err
	}

	// fmt.Printf("Setup called, paths=%s\n", paths)

	return func(next middleware.Handler) middleware.Handler {
		return JsonPHandlerType{
			Next:  next,
			Paths: paths,
		}
	}, nil
}

func jsonpParse(c *setup.Controller) ([]string, error) {
	var paths []string

	// fmt.Printf("jsonpParse called\n")

	for c.Next() {
		if !c.NextArg() {
			return paths, c.ArgErr()
		}
		paths = append(paths, c.Val())
	}

	return paths, nil
}

type JsonPHandlerType struct {
	Paths []string
	Next  middleware.Handler
}

func (jph JsonPHandlerType) ServeHTTP(www http.ResponseWriter, req *http.Request) (int, error) {

	// fmt.Printf("JsonPHandlerType.ServeHTTP called\n")

	for _, prefix := range jph.Paths {
		if middleware.Path(req.URL.Path).Matches(prefix) {
			ResponseBodyRecorder := bufferhtml.NewBufferHTML()
			status, err := jph.Next.ServeHTTP(ResponseBodyRecorder, req)
			if status == 200 || status == 0 {
				// if there is a "callback" argument then use that to format the JSONp response.
				Prefix := ""
				Postfix := ""
				u, err := url.ParseRequestURI(req.RequestURI)
				if err != nil {
					ResponseBodyRecorder.FlushAtEnd(www, "", "")
					return status, nil
				}
				m, err := url.ParseQuery(u.RawQuery)
				if err != nil {
					ResponseBodyRecorder.FlushAtEnd(www, "", "")
					return status, nil
				}
				callback := m.Get("callback")
				if callback != "" {
					www.Header().Set("Content-Type", "application/javascript")
					Prefix = callback + "("
					Postfix = ");"
				}
				ResponseBodyRecorder.FlushAtEnd(www, Prefix, Postfix)
			} else {
				ResponseBodyRecorder.FlushAtEnd(www, "", "")
			}
			return status, err
		}
	}
	return jph.Next.ServeHTTP(www, req)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 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 route

import (
	"context"
	"fmt"
	"net"
	"sync"
	"time"

	"k8s.io/klog"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/types"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	coreinformers "k8s.io/client-go/informers/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	corelisters "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	clientretry "k8s.io/client-go/util/retry"
	cloudprovider "k8s.io/cloud-provider"
	"k8s.io/kubernetes/pkg/controller"
	nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
	"k8s.io/kubernetes/pkg/util/metrics"
	utilnode "k8s.io/kubernetes/pkg/util/node"
)

const (
	// Maximal number of concurrent CreateRoute API calls.
	// TODO: This should be per-provider.
	maxConcurrentRouteCreations int = 200
)

var updateNetworkConditionBackoff = wait.Backoff{
	Steps:    5, // Maximum number of retries.
	Duration: 100 * time.Millisecond,
	Jitter:   1.0,
}

type RouteController struct {
	routes           cloudprovider.Routes
	kubeClient       clientset.Interface
	clusterName      string
	clusterCIDR      *net.IPNet
	nodeLister       corelisters.NodeLister
	nodeListerSynced cache.InformerSynced
	broadcaster      record.EventBroadcaster
	recorder         record.EventRecorder
}

func New(routes cloudprovider.Routes, kubeClient clientset.Interface, nodeInformer coreinformers.NodeInformer, clusterName string, clusterCIDR *net.IPNet) *RouteController {
	if kubeClient != nil && kubeClient.CoreV1().RESTClient().GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("route_controller", kubeClient.CoreV1().RESTClient().GetRateLimiter())
	}

	if clusterCIDR == nil {
		klog.Fatal("RouteController: Must specify clusterCIDR.")
	}

	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(klog.Infof)
	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "route_controller"})

	rc := &RouteController{
		routes:           routes,
		kubeClient:       kubeClient,
		clusterName:      clusterName,
		clusterCIDR:      clusterCIDR,
		nodeLister:       nodeInformer.Lister(),
		nodeListerSynced: nodeInformer.Informer().HasSynced,
		broadcaster:      eventBroadcaster,
		recorder:         recorder,
	}

	return rc
}

func (rc *RouteController) Run(stopCh <-chan struct{}, syncPeriod time.Duration) {
	defer utilruntime.HandleCrash()

	klog.Info("Starting route controller")
	defer klog.Info("Shutting down route controller")

	if !controller.WaitForCacheSync("route", stopCh, rc.nodeListerSynced) {
		return
	}

	if rc.broadcaster != nil {
		rc.broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: rc.kubeClient.CoreV1().Events("")})
	}

	// TODO: If we do just the full Resync every 5 minutes (default value)
	// that means that we may wait up to 5 minutes before even starting
	// creating a route for it. This is bad.
	// We should have a watch on node and if we observe a new node (with CIDR?)
	// trigger reconciliation for that node.
	go wait.NonSlidingUntil(func() {
		if err := rc.reconcileNodeRoutes(); err != nil {
			klog.Errorf("Couldn't reconcile node routes: %v", err)
		}
	}, syncPeriod, stopCh)

	<-stopCh
}

func (rc *RouteController) reconcileNodeRoutes() error {
	routeList, err := rc.routes.ListRoutes(context.TODO(), rc.clusterName)
	if err != nil {
		return fmt.Errorf("error listing routes: %v", err)
	}
	nodes, err := rc.nodeLister.List(labels.Everything())
	if err != nil {
		return fmt.Errorf("error listing nodes: %v", err)
	}
	return rc.reconcile(nodes, routeList)
}

func (rc *RouteController) reconcile(nodes []*v1.Node, routes []*cloudprovider.Route) error {
	// nodeCIDRs maps nodeName->nodeCIDR
	nodeCIDRs := make(map[types.NodeName]string)
	// routeMap maps routeTargetNode->route
	routeMap := make(map[types.NodeName]*cloudprovider.Route)
	for _, route := range routes {
		if route.TargetNode != "" {
			routeMap[route.TargetNode] = route
		}
	}

	wg := sync.WaitGroup{}
	rateLimiter := make(chan struct{}, maxConcurrentRouteCreations)

	for _, node := range nodes {
		// Skip if the node hasn't been assigned a CIDR yet.
		if node.Spec.PodCIDR == "" {
			continue
		}
		nodeName := types.NodeName(node.Name)
		// Check if we have a route for this node w/ the correct CIDR.
		r := routeMap[nodeName]
		if r == nil || r.DestinationCIDR != node.Spec.PodCIDR {
			// If not, create the route.
			route := &cloudprovider.Route{
				TargetNode:      nodeName,
				DestinationCIDR: node.Spec.PodCIDR,
			}
			nameHint := string(node.UID)
			wg.Add(1)
			go func(nodeName types.NodeName, nameHint string, route *cloudprovider.Route) {
				defer wg.Done()
				err := clientretry.RetryOnConflict(updateNetworkConditionBackoff, func() error {
					startTime := time.Now()
					// Ensure that we don't have more than maxConcurrentRouteCreations
					// CreateRoute calls in flight.
					rateLimiter <- struct{}{}
					klog.Infof("Creating route for node %s %s with hint %s, throttled %v", nodeName, route.DestinationCIDR, nameHint, time.Since(startTime))
					err := rc.routes.CreateRoute(context.TODO(), rc.clusterName, nameHint, route)
					<-rateLimiter

					rc.updateNetworkingCondition(nodeName, err == nil)
					if err != nil {
						msg := fmt.Sprintf("Could not create route %s %s for node %s after %v: %v", nameHint, route.DestinationCIDR, nodeName, time.Since(startTime), err)
						if rc.recorder != nil {
							rc.recorder.Eventf(
								&v1.ObjectReference{
									Kind:      "Node",
									Name:      string(nodeName),
									UID:       types.UID(nodeName),
									Namespace: "",
								}, v1.EventTypeWarning, "FailedToCreateRoute", msg)
						}
						klog.V(4).Infof(msg)
						return err
					}
					klog.Infof("Created route for node %s %s with hint %s after %v", nodeName, route.DestinationCIDR, nameHint, time.Since(startTime))
					return nil
				})
				if err != nil {
					klog.Errorf("Could not create route %s %s for node %s: %v", nameHint, route.DestinationCIDR, nodeName, err)
				}
			}(nodeName, nameHint, route)
		} else {
			// Update condition only if it doesn't reflect the current state.
			_, condition := nodeutil.GetNodeCondition(&node.Status, v1.NodeNetworkUnavailable)
			if condition == nil || condition.Status != v1.ConditionFalse {
				rc.updateNetworkingCondition(types.NodeName(node.Name), true)
			}
		}
		nodeCIDRs[nodeName] = node.Spec.PodCIDR
	}
	for _, route := range routes {
		if rc.isResponsibleForRoute(route) {
			// Check if this route is a blackhole, or applies to a node we know about & has an incorrect CIDR.
			if route.Blackhole || (nodeCIDRs[route.TargetNode] != route.DestinationCIDR) {
				wg.Add(1)
				// Delete the route.
				go func(route *cloudprovider.Route, startTime time.Time) {
					defer wg.Done()
					klog.Infof("Deleting route %s %s", route.Name, route.DestinationCIDR)
					if err := rc.routes.DeleteRoute(context.TODO(), rc.clusterName, route); err != nil {
						klog.Errorf("Could not delete route %s %s after %v: %v", route.Name, route.DestinationCIDR, time.Since(startTime), err)
					} else {
						klog.Infof("Deleted route %s %s after %v", route.Name, route.DestinationCIDR, time.Since(startTime))
					}
				}(route, time.Now())
			}
		}
	}
	wg.Wait()
	return nil
}

func (rc *RouteController) updateNetworkingCondition(nodeName types.NodeName, routeCreated bool) error {
	err := clientretry.RetryOnConflict(updateNetworkConditionBackoff, func() error {
		var err error
		// Patch could also fail, even though the chance is very slim. So we still do
		// patch in the retry loop.
		currentTime := metav1.Now()
		if routeCreated {
			err = utilnode.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
				Type:               v1.NodeNetworkUnavailable,
				Status:             v1.ConditionFalse,
				Reason:             "RouteCreated",
				Message:            "RouteController created a route",
				LastTransitionTime: currentTime,
			})
		} else {
			err = utilnode.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
				Type:               v1.NodeNetworkUnavailable,
				Status:             v1.ConditionTrue,
				Reason:             "NoRouteCreated",
				Message:            "RouteController failed to create a route",
				LastTransitionTime: currentTime,
			})
		}
		if err != nil {
			klog.V(4).Infof("Error updating node %s, retrying: %v", nodeName, err)
		}
		return err
	})

	if err != nil {
		klog.Errorf("Error updating node %s: %v", nodeName, err)
	}

	return err
}

func (rc *RouteController) isResponsibleForRoute(route *cloudprovider.Route) bool {
	_, cidr, err := net.ParseCIDR(route.DestinationCIDR)
	if err != nil {
		klog.Errorf("Ignoring route %s, unparsable CIDR: %v", route.Name, err)
		return false
	}
	// Not responsible if this route's CIDR is not within our clusterCIDR
	lastIP := make([]byte, len(cidr.IP))
	for i := range lastIP {
		lastIP[i] = cidr.IP[i] | ^cidr.Mask[i]
	}
	if !rc.clusterCIDR.Contains(cidr.IP) || !rc.clusterCIDR.Contains(lastIP) {
		return false
	}
	return true
}
<commit_msg>route controller: respect rate limit for delete<commit_after>/*
Copyright 2015 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 route

import (
	"context"
	"fmt"
	"net"
	"sync"
	"time"

	"k8s.io/klog"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/types"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	coreinformers "k8s.io/client-go/informers/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	corelisters "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	clientretry "k8s.io/client-go/util/retry"
	cloudprovider "k8s.io/cloud-provider"
	"k8s.io/kubernetes/pkg/controller"
	nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
	"k8s.io/kubernetes/pkg/util/metrics"
	utilnode "k8s.io/kubernetes/pkg/util/node"
)

const (
	// Maximal number of concurrent CreateRoute API calls.
	// TODO: This should be per-provider.
	maxConcurrentRouteCreations int = 200
)

var updateNetworkConditionBackoff = wait.Backoff{
	Steps:    5, // Maximum number of retries.
	Duration: 100 * time.Millisecond,
	Jitter:   1.0,
}

type RouteController struct {
	routes           cloudprovider.Routes
	kubeClient       clientset.Interface
	clusterName      string
	clusterCIDR      *net.IPNet
	nodeLister       corelisters.NodeLister
	nodeListerSynced cache.InformerSynced
	broadcaster      record.EventBroadcaster
	recorder         record.EventRecorder
}

func New(routes cloudprovider.Routes, kubeClient clientset.Interface, nodeInformer coreinformers.NodeInformer, clusterName string, clusterCIDR *net.IPNet) *RouteController {
	if kubeClient != nil && kubeClient.CoreV1().RESTClient().GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("route_controller", kubeClient.CoreV1().RESTClient().GetRateLimiter())
	}

	if clusterCIDR == nil {
		klog.Fatal("RouteController: Must specify clusterCIDR.")
	}

	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(klog.Infof)
	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "route_controller"})

	rc := &RouteController{
		routes:           routes,
		kubeClient:       kubeClient,
		clusterName:      clusterName,
		clusterCIDR:      clusterCIDR,
		nodeLister:       nodeInformer.Lister(),
		nodeListerSynced: nodeInformer.Informer().HasSynced,
		broadcaster:      eventBroadcaster,
		recorder:         recorder,
	}

	return rc
}

func (rc *RouteController) Run(stopCh <-chan struct{}, syncPeriod time.Duration) {
	defer utilruntime.HandleCrash()

	klog.Info("Starting route controller")
	defer klog.Info("Shutting down route controller")

	if !controller.WaitForCacheSync("route", stopCh, rc.nodeListerSynced) {
		return
	}

	if rc.broadcaster != nil {
		rc.broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: rc.kubeClient.CoreV1().Events("")})
	}

	// TODO: If we do just the full Resync every 5 minutes (default value)
	// that means that we may wait up to 5 minutes before even starting
	// creating a route for it. This is bad.
	// We should have a watch on node and if we observe a new node (with CIDR?)
	// trigger reconciliation for that node.
	go wait.NonSlidingUntil(func() {
		if err := rc.reconcileNodeRoutes(); err != nil {
			klog.Errorf("Couldn't reconcile node routes: %v", err)
		}
	}, syncPeriod, stopCh)

	<-stopCh
}

func (rc *RouteController) reconcileNodeRoutes() error {
	routeList, err := rc.routes.ListRoutes(context.TODO(), rc.clusterName)
	if err != nil {
		return fmt.Errorf("error listing routes: %v", err)
	}
	nodes, err := rc.nodeLister.List(labels.Everything())
	if err != nil {
		return fmt.Errorf("error listing nodes: %v", err)
	}
	return rc.reconcile(nodes, routeList)
}

func (rc *RouteController) reconcile(nodes []*v1.Node, routes []*cloudprovider.Route) error {
	// nodeCIDRs maps nodeName->nodeCIDR
	nodeCIDRs := make(map[types.NodeName]string)
	// routeMap maps routeTargetNode->route
	routeMap := make(map[types.NodeName]*cloudprovider.Route)
	for _, route := range routes {
		if route.TargetNode != "" {
			routeMap[route.TargetNode] = route
		}
	}

	wg := sync.WaitGroup{}
	rateLimiter := make(chan struct{}, maxConcurrentRouteCreations)

	for _, node := range nodes {
		// Skip if the node hasn't been assigned a CIDR yet.
		if node.Spec.PodCIDR == "" {
			continue
		}
		nodeName := types.NodeName(node.Name)
		// Check if we have a route for this node w/ the correct CIDR.
		r := routeMap[nodeName]
		if r == nil || r.DestinationCIDR != node.Spec.PodCIDR {
			// If not, create the route.
			route := &cloudprovider.Route{
				TargetNode:      nodeName,
				DestinationCIDR: node.Spec.PodCIDR,
			}
			nameHint := string(node.UID)
			wg.Add(1)
			go func(nodeName types.NodeName, nameHint string, route *cloudprovider.Route) {
				defer wg.Done()
				err := clientretry.RetryOnConflict(updateNetworkConditionBackoff, func() error {
					startTime := time.Now()
					// Ensure that we don't have more than maxConcurrentRouteCreations
					// CreateRoute calls in flight.
					rateLimiter <- struct{}{}
					klog.Infof("Creating route for node %s %s with hint %s, throttled %v", nodeName, route.DestinationCIDR, nameHint, time.Since(startTime))
					err := rc.routes.CreateRoute(context.TODO(), rc.clusterName, nameHint, route)
					<-rateLimiter

					rc.updateNetworkingCondition(nodeName, err == nil)
					if err != nil {
						msg := fmt.Sprintf("Could not create route %s %s for node %s after %v: %v", nameHint, route.DestinationCIDR, nodeName, time.Since(startTime), err)
						if rc.recorder != nil {
							rc.recorder.Eventf(
								&v1.ObjectReference{
									Kind:      "Node",
									Name:      string(nodeName),
									UID:       types.UID(nodeName),
									Namespace: "",
								}, v1.EventTypeWarning, "FailedToCreateRoute", msg)
						}
						klog.V(4).Infof(msg)
						return err
					}
					klog.Infof("Created route for node %s %s with hint %s after %v", nodeName, route.DestinationCIDR, nameHint, time.Since(startTime))
					return nil
				})
				if err != nil {
					klog.Errorf("Could not create route %s %s for node %s: %v", nameHint, route.DestinationCIDR, nodeName, err)
				}
			}(nodeName, nameHint, route)
		} else {
			// Update condition only if it doesn't reflect the current state.
			_, condition := nodeutil.GetNodeCondition(&node.Status, v1.NodeNetworkUnavailable)
			if condition == nil || condition.Status != v1.ConditionFalse {
				rc.updateNetworkingCondition(types.NodeName(node.Name), true)
			}
		}
		nodeCIDRs[nodeName] = node.Spec.PodCIDR
	}
	for _, route := range routes {
		if rc.isResponsibleForRoute(route) {
			// Check if this route is a blackhole, or applies to a node we know about & has an incorrect CIDR.
			if route.Blackhole || (nodeCIDRs[route.TargetNode] != route.DestinationCIDR) {
				wg.Add(1)
				// Delete the route.
				go func(route *cloudprovider.Route, startTime time.Time) {
					defer wg.Done()
					rateLimiter <- struct{}{}
					klog.Infof("Deleting route %s %s", route.Name, route.DestinationCIDR)
					if err := rc.routes.DeleteRoute(context.TODO(), rc.clusterName, route); err != nil {
						klog.Errorf("Could not delete route %s %s after %v: %v", route.Name, route.DestinationCIDR, time.Since(startTime), err)
					} else {
						klog.Infof("Deleted route %s %s after %v", route.Name, route.DestinationCIDR, time.Since(startTime))
					}
					<-rateLimiter
				}(route, time.Now())
			}
		}
	}
	wg.Wait()
	return nil
}

func (rc *RouteController) updateNetworkingCondition(nodeName types.NodeName, routeCreated bool) error {
	err := clientretry.RetryOnConflict(updateNetworkConditionBackoff, func() error {
		var err error
		// Patch could also fail, even though the chance is very slim. So we still do
		// patch in the retry loop.
		currentTime := metav1.Now()
		if routeCreated {
			err = utilnode.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
				Type:               v1.NodeNetworkUnavailable,
				Status:             v1.ConditionFalse,
				Reason:             "RouteCreated",
				Message:            "RouteController created a route",
				LastTransitionTime: currentTime,
			})
		} else {
			err = utilnode.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
				Type:               v1.NodeNetworkUnavailable,
				Status:             v1.ConditionTrue,
				Reason:             "NoRouteCreated",
				Message:            "RouteController failed to create a route",
				LastTransitionTime: currentTime,
			})
		}
		if err != nil {
			klog.V(4).Infof("Error updating node %s, retrying: %v", nodeName, err)
		}
		return err
	})

	if err != nil {
		klog.Errorf("Error updating node %s: %v", nodeName, err)
	}

	return err
}

func (rc *RouteController) isResponsibleForRoute(route *cloudprovider.Route) bool {
	_, cidr, err := net.ParseCIDR(route.DestinationCIDR)
	if err != nil {
		klog.Errorf("Ignoring route %s, unparsable CIDR: %v", route.Name, err)
		return false
	}
	// Not responsible if this route's CIDR is not within our clusterCIDR
	lastIP := make([]byte, len(cidr.IP))
	for i := range lastIP {
		lastIP[i] = cidr.IP[i] | ^cidr.Mask[i]
	}
	if !rc.clusterCIDR.Contains(cidr.IP) || !rc.clusterCIDR.Contains(lastIP) {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package controller

import (
	"testing"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
	"github.com/stretchr/testify/assert"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/pkg/api/v1"
)

type mockEC2Client struct {
	ec2iface.EC2API
	res           *ec2.ModifyInstanceAttributeOutput
	err           error
	CalledCounter int
}

func (c *mockEC2Client) ModifyInstanceAttribute(*ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) {
	c.CalledCounter = c.CalledCounter + 1
	return c.res, c.err
}

func NewMockEC2Client() *mockEC2Client {
	return &mockEC2Client{CalledCounter: 0}
}

func TestDisableSrcDstIfEnabled(t *testing.T) {
	annotations := map[string]string{SrcDstCheckDisabledAnnotation: "true"}
	spec := v1.NodeSpec{ProviderID: "aws:///us-mock-1/i-abcdefgh"}
	node_1 := &v1.Node{Spec: spec}
	node_1.Annotations = annotations
	var tests = []struct {
		node                     *v1.Node
		disableSrcDstCheckCalled bool
	}{
		{&v1.Node{Spec: spec}, true},
		{node_1, false},
	}

	ec2Client := NewMockEC2Client()

	c := &Controller{
		ec2Client: ec2Client,
		client:    fake.NewSimpleClientset(),
	}

	for _, tt := range tests {
		calledCount := ec2Client.CalledCounter
		c.disableSrcDstIfEnabled(tt.node)
		called := (ec2Client.CalledCounter - calledCount) > 0
		assert.Equal(
			t,
			called,
			tt.disableSrcDstCheckCalled,
			"Verify that ModifyInstanceAttribute will get called if node needs srcdstcheck disabled",
		)
		// TODO: validate that node did get updated with the annotation after ModifyInstanceAttribute is called
	}
}

func TestGetInstanceIDFromProviderID(t *testing.T) {

	var tests = []struct {
		providerID         string
		expectedInstanceID string
		expectedError      bool
	}{
		{"aws:///us-west-2a/i-09fc5a0ae524b0333", "i-09fc5a0ae524b0333", false},
		{"aws://us-west-2a/i-a123hd52", "i-a123hd52", false},
		{"gce://us-west-1a/test", "", true},
		{"this_will_fail", "", true},
		{"i-a123hd52", "", true},
	}

	for _, tt := range tests {
		instanceID, err := GetInstanceIDFromProviderID(tt.providerID)
		if !tt.expectedError {
			assert.Equal(
				t,
				tt.expectedInstanceID,
				*instanceID,
				"Check if instance ID is parsed out correctly from provider ID",
			)
		} else {
			assert.NotNil(
				t,
				err,
				err.Error(),
			)
		}
	}

}
<commit_msg>remove underscore from variable name<commit_after>package controller

import (
	"testing"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
	"github.com/stretchr/testify/assert"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/pkg/api/v1"
)

type mockEC2Client struct {
	ec2iface.EC2API
	res           *ec2.ModifyInstanceAttributeOutput
	err           error
	CalledCounter int
}

func (c *mockEC2Client) ModifyInstanceAttribute(*ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) {
	c.CalledCounter = c.CalledCounter + 1
	return c.res, c.err
}

func NewMockEC2Client() *mockEC2Client {
	return &mockEC2Client{CalledCounter: 0}
}

func TestDisableSrcDstIfEnabled(t *testing.T) {
	annotations := map[string]string{SrcDstCheckDisabledAnnotation: "true"}
	spec := v1.NodeSpec{ProviderID: "aws:///us-mock-1/i-abcdefgh"}
	node1 := &v1.Node{Spec: spec}
	node1.Annotations = annotations
	var tests = []struct {
		node                     *v1.Node
		disableSrcDstCheckCalled bool
	}{
		{&v1.Node{Spec: spec}, true},
		{node1, false},
	}

	ec2Client := NewMockEC2Client()

	c := &Controller{
		ec2Client: ec2Client,
		client:    fake.NewSimpleClientset(),
	}

	for _, tt := range tests {
		calledCount := ec2Client.CalledCounter
		c.disableSrcDstIfEnabled(tt.node)
		called := (ec2Client.CalledCounter - calledCount) > 0
		assert.Equal(
			t,
			called,
			tt.disableSrcDstCheckCalled,
			"Verify that ModifyInstanceAttribute will get called if node needs srcdstcheck disabled",
		)
		// TODO: validate that node did get updated with the annotation after ModifyInstanceAttribute is called
	}
}

func TestGetInstanceIDFromProviderID(t *testing.T) {

	var tests = []struct {
		providerID         string
		expectedInstanceID string
		expectedError      bool
	}{
		{"aws:///us-west-2a/i-09fc5a0ae524b0333", "i-09fc5a0ae524b0333", false},
		{"aws://us-west-2a/i-a123hd52", "i-a123hd52", false},
		{"gce://us-west-1a/test", "", true},
		{"this_will_fail", "", true},
		{"i-a123hd52", "", true},
	}

	for _, tt := range tests {
		instanceID, err := GetInstanceIDFromProviderID(tt.providerID)
		if !tt.expectedError {
			assert.Equal(
				t,
				tt.expectedInstanceID,
				*instanceID,
				"Check if instance ID is parsed out correctly from provider ID",
			)
		} else {
			assert.NotNil(
				t,
				err,
				err.Error(),
			)
		}
	}

}
<|endoftext|>


<commit_before>// Package clouddns implements a DNS provider for solving the DNS-01
// challenge using Google Cloud DNS.
package clouddns

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/dns/v1"

	"github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
)

// DNSProvider is an implementation of the DNSProvider interface.
type DNSProvider struct {
	project string
	client  *dns.Service
}

// NewDNSProvider returns a DNSProvider instance configured for Google Cloud
// DNS. Project name must be passed in the environment variable: GCE_PROJECT.
// A Service Account file can be passed in the environment variable:
// GCE_SERVICE_ACCOUNT_FILE
func NewDNSProvider() (*DNSProvider, error) {
	project := os.Getenv("GCE_PROJECT")
	if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok {
		return NewDNSProviderServiceAccount(project, saFile)
	}
	return NewDNSProviderCredentials(project)
}

// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for Google Cloud DNS.
func NewDNSProviderCredentials(project string) (*DNSProvider, error) {
	if project == "" {
		return nil, fmt.Errorf("Google Cloud project name missing")
	}

	client, err := google.DefaultClient(context.Background(), dns.NdevClouddnsReadwriteScope)
	if err != nil {
		return nil, fmt.Errorf("Unable to get Google Cloud client: %v", err)
	}
	svc, err := dns.New(client)
	if err != nil {
		return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err)
	}
	return &DNSProvider{
		project: project,
		client:  svc,
	}, nil
}

// NewDNSProviderServiceAccount uses the supplied service account JSON file to
// return a DNSProvider instance configured for Google Cloud DNS.
func NewDNSProviderServiceAccount(project string, saFile string) (*DNSProvider, error) {
	if project == "" {
		return nil, fmt.Errorf("Google Cloud project name missing")
	}
	if saFile == "" {
		return nil, fmt.Errorf("Google Cloud Service Account file missing")
	}

	dat, err := ioutil.ReadFile(saFile)
	if err != nil {
		return nil, fmt.Errorf("Unable to read Service Account file: %v", err)
	}
	return NewDNSProviderServiceAccountBytes(project, dat)
}

// NewDNSProviderServiceAccountBytes uses the supplied service account JSON
// file data to return a DNSProvider instance configured for Google Cloud DNS.
func NewDNSProviderServiceAccountBytes(project string, saBytes []byte) (*DNSProvider, error) {
	if project == "" {
		return nil, fmt.Errorf("Google Cloud project name missing")
	}
	if len(saBytes) == 0 {
		return nil, fmt.Errorf("Google Cloud Service Account data missing")
	}

	conf, err := google.JWTConfigFromJSON(saBytes, dns.NdevClouddnsReadwriteScope)
	if err != nil {
		return nil, fmt.Errorf("Unable to acquire config: %v", err)
	}
	client := conf.Client(oauth2.NoContext)

	svc, err := dns.New(client)
	if err != nil {
		return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err)
	}
	return &DNSProvider{
		project: project,
		client:  svc,
	}, nil
}

// Present creates a TXT record to fulfil the dns-01 challenge.
func (c *DNSProvider) Present(domain, token, key string) error {
	fqdn, value, ttl := util.DNS01Record(domain, key)

	zone, err := c.getHostedZone(fqdn)
	if err != nil {
		return err
	}

	rec := &dns.ResourceRecordSet{
		Name:    fqdn,
		Rrdatas: []string{value},
		Ttl:     int64(ttl),
		Type:    "TXT",
	}
	change := &dns.Change{
		Additions: []*dns.ResourceRecordSet{rec},
	}

	// Look for existing records.
	list, err := c.client.ResourceRecordSets.List(c.project, zone).Name(fqdn).Type("TXT").Do()
	if err != nil {
		return err
	}
	if len(list.Rrsets) > 0 {
		// Attempt to delete the existing records when adding our new one.
		change.Deletions = list.Rrsets
	}

	chg, err := c.client.Changes.Create(c.project, zone, change).Do()
	if err != nil {
		return err
	}

	// wait for change to be acknowledged
	for chg.Status == "pending" {
		time.Sleep(time.Second)

		chg, err = c.client.Changes.Get(c.project, zone, chg.Id).Do()
		if err != nil {
			return err
		}
	}

	return nil
}

// CleanUp removes the TXT record matching the specified parameters.
func (c *DNSProvider) CleanUp(domain, token, key string) error {
	fqdn, _, _ := util.DNS01Record(domain, key)

	zone, err := c.getHostedZone(domain)
	if err != nil {
		return err
	}

	records, err := c.findTxtRecords(zone, fqdn)
	if err != nil {
		return err
	}

	for _, rec := range records {
		change := &dns.Change{
			Deletions: []*dns.ResourceRecordSet{rec},
		}
		_, err = c.client.Changes.Create(c.project, zone, change).Do()
		if err != nil {
			return err
		}
	}
	return nil
}

// Timeout customizes the timeout values used by the ACME package for checking
// DNS record validity.
func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
	return 180 * time.Second, 5 * time.Second
}

// getHostedZone returns the managed-zone
func (c *DNSProvider) getHostedZone(domain string) (string, error) {
	authZone, err := util.FindZoneByFqdn(util.ToFqdn(domain), util.RecursiveNameservers)
	if err != nil {
		return "", err
	}

	zones, err := c.client.ManagedZones.
		List(c.project).
		DnsName(authZone).
		Do()
	if err != nil {
		return "", fmt.Errorf("GoogleCloud API call failed: %v", err)
	}

	if len(zones.ManagedZones) == 0 {
		return "", fmt.Errorf("No matching GoogleCloud domain found for domain %s", authZone)
	}

	return zones.ManagedZones[0].Name, nil
}

func (c *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) {

	recs, err := c.client.ResourceRecordSets.List(c.project, zone).Do()
	if err != nil {
		return nil, err
	}

	found := []*dns.ResourceRecordSet{}
	for _, r := range recs.Rrsets {
		if r.Type == "TXT" && r.Name == fqdn {
			found = append(found, r)
		}
	}

	return found, nil
}
<commit_msg>clouddns: use fqdn for challenge cleanup<commit_after>// Package clouddns implements a DNS provider for solving the DNS-01
// challenge using Google Cloud DNS.
package clouddns

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/dns/v1"

	"github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
)

// DNSProvider is an implementation of the DNSProvider interface.
type DNSProvider struct {
	project string
	client  *dns.Service
}

// NewDNSProvider returns a DNSProvider instance configured for Google Cloud
// DNS. Project name must be passed in the environment variable: GCE_PROJECT.
// A Service Account file can be passed in the environment variable:
// GCE_SERVICE_ACCOUNT_FILE
func NewDNSProvider() (*DNSProvider, error) {
	project := os.Getenv("GCE_PROJECT")
	if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok {
		return NewDNSProviderServiceAccount(project, saFile)
	}
	return NewDNSProviderCredentials(project)
}

// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for Google Cloud DNS.
func NewDNSProviderCredentials(project string) (*DNSProvider, error) {
	if project == "" {
		return nil, fmt.Errorf("Google Cloud project name missing")
	}

	client, err := google.DefaultClient(context.Background(), dns.NdevClouddnsReadwriteScope)
	if err != nil {
		return nil, fmt.Errorf("Unable to get Google Cloud client: %v", err)
	}
	svc, err := dns.New(client)
	if err != nil {
		return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err)
	}
	return &DNSProvider{
		project: project,
		client:  svc,
	}, nil
}

// NewDNSProviderServiceAccount uses the supplied service account JSON file to
// return a DNSProvider instance configured for Google Cloud DNS.
func NewDNSProviderServiceAccount(project string, saFile string) (*DNSProvider, error) {
	if project == "" {
		return nil, fmt.Errorf("Google Cloud project name missing")
	}
	if saFile == "" {
		return nil, fmt.Errorf("Google Cloud Service Account file missing")
	}

	dat, err := ioutil.ReadFile(saFile)
	if err != nil {
		return nil, fmt.Errorf("Unable to read Service Account file: %v", err)
	}
	return NewDNSProviderServiceAccountBytes(project, dat)
}

// NewDNSProviderServiceAccountBytes uses the supplied service account JSON
// file data to return a DNSProvider instance configured for Google Cloud DNS.
func NewDNSProviderServiceAccountBytes(project string, saBytes []byte) (*DNSProvider, error) {
	if project == "" {
		return nil, fmt.Errorf("Google Cloud project name missing")
	}
	if len(saBytes) == 0 {
		return nil, fmt.Errorf("Google Cloud Service Account data missing")
	}

	conf, err := google.JWTConfigFromJSON(saBytes, dns.NdevClouddnsReadwriteScope)
	if err != nil {
		return nil, fmt.Errorf("Unable to acquire config: %v", err)
	}
	client := conf.Client(oauth2.NoContext)

	svc, err := dns.New(client)
	if err != nil {
		return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err)
	}
	return &DNSProvider{
		project: project,
		client:  svc,
	}, nil
}

// Present creates a TXT record to fulfil the dns-01 challenge.
func (c *DNSProvider) Present(domain, token, key string) error {
	fqdn, value, ttl := util.DNS01Record(domain, key)

	zone, err := c.getHostedZone(fqdn)
	if err != nil {
		return err
	}

	rec := &dns.ResourceRecordSet{
		Name:    fqdn,
		Rrdatas: []string{value},
		Ttl:     int64(ttl),
		Type:    "TXT",
	}
	change := &dns.Change{
		Additions: []*dns.ResourceRecordSet{rec},
	}

	// Look for existing records.
	list, err := c.client.ResourceRecordSets.List(c.project, zone).Name(fqdn).Type("TXT").Do()
	if err != nil {
		return err
	}
	if len(list.Rrsets) > 0 {
		// Attempt to delete the existing records when adding our new one.
		change.Deletions = list.Rrsets
	}

	chg, err := c.client.Changes.Create(c.project, zone, change).Do()
	if err != nil {
		return err
	}

	// wait for change to be acknowledged
	for chg.Status == "pending" {
		time.Sleep(time.Second)

		chg, err = c.client.Changes.Get(c.project, zone, chg.Id).Do()
		if err != nil {
			return err
		}
	}

	return nil
}

// CleanUp removes the TXT record matching the specified parameters.
func (c *DNSProvider) CleanUp(domain, token, key string) error {
	fqdn, _, _ := util.DNS01Record(domain, key)

	zone, err := c.getHostedZone(fqdn)
	if err != nil {
		return err
	}

	records, err := c.findTxtRecords(zone, fqdn)
	if err != nil {
		return err
	}

	for _, rec := range records {
		change := &dns.Change{
			Deletions: []*dns.ResourceRecordSet{rec},
		}
		_, err = c.client.Changes.Create(c.project, zone, change).Do()
		if err != nil {
			return err
		}
	}
	return nil
}

// Timeout customizes the timeout values used by the ACME package for checking
// DNS record validity.
func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
	return 180 * time.Second, 5 * time.Second
}

// getHostedZone returns the managed-zone
func (c *DNSProvider) getHostedZone(domain string) (string, error) {
	authZone, err := util.FindZoneByFqdn(util.ToFqdn(domain), util.RecursiveNameservers)
	if err != nil {
		return "", err
	}

	zones, err := c.client.ManagedZones.
		List(c.project).
		DnsName(authZone).
		Do()
	if err != nil {
		return "", fmt.Errorf("GoogleCloud API call failed: %v", err)
	}

	if len(zones.ManagedZones) == 0 {
		return "", fmt.Errorf("No matching GoogleCloud domain found for domain %s", authZone)
	}

	return zones.ManagedZones[0].Name, nil
}

func (c *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) {

	recs, err := c.client.ResourceRecordSets.List(c.project, zone).Do()
	if err != nil {
		return nil, err
	}

	found := []*dns.ResourceRecordSet{}
	for _, r := range recs.Rrsets {
		if r.Type == "TXT" && r.Name == fqdn {
			found = append(found, r)
		}
	}

	return found, nil
}
<|endoftext|>


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

import (
	"crypto"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"testing"
	"time"

	"github.com/jetstack/cert-manager/pkg/issuer/venafi/client/api"

	"github.com/Venafi/vcert/pkg/certificate"
	"github.com/Venafi/vcert/pkg/endpoint"
	"github.com/Venafi/vcert/pkg/venafi/fake"

	internalfake "github.com/jetstack/cert-manager/pkg/issuer/venafi/client/fake"
	"github.com/jetstack/cert-manager/pkg/util"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

func checkCertificateIssued(t *testing.T, csrPEM []byte, resp []byte) {
	if len(resp) == 0 {
		t.Errorf("expected IssueResponse to be non-nil")
		t.FailNow()
		return
	}

	csr, err := pki.DecodeX509CertificateRequestBytes(csrPEM)
	if err != nil {
		t.Errorf("failed to decode CSR PEM: %s", err)
		return
	}

	crt, err := pki.DecodeX509CertificateBytes(resp)
	if err != nil {
		t.Errorf("unable to decode x509 certificate: %v", err)
		return
	}

	ok, err := pki.PublicKeyMatchesCSR(crt.PublicKey, csr)
	if err != nil {
		t.Errorf("error checking private key: %v", err)
		return
	}
	if !ok {
		t.Errorf("private key does not match certificate")
	}

	// validate the common name is correct
	expectedCN := csr.Subject.CommonName
	if expectedCN != crt.Subject.CommonName {
		t.Errorf("expected common name to be %q but it was %q", expectedCN, crt.Subject.CommonName)
	}

	// validate the dns names are correct
	expectedDNSNames := csr.DNSNames
	if !util.EqualUnsorted(crt.DNSNames, expectedDNSNames) {
		t.Errorf("expected dns names to be %q but it was %q", expectedDNSNames, crt.DNSNames)
	}
}

func checkNoCertificateIssued(t *testing.T, csrPEM []byte, resp []byte) {
	if len(resp) > 0 {
		t.Errorf("expected no response with error but got=%s", resp)
	}
}

func generateCSR(t *testing.T, sk crypto.Signer, commonName string, dnsNames []string) []byte {
	template := x509.CertificateRequest{
		Subject: pkix.Name{
			CommonName: commonName,
		},
		SignatureAlgorithm: x509.SHA256WithRSA,
		DNSNames:           dnsNames,
	}

	csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, sk)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}

	csr := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
	return csr
}

func TestSign(t *testing.T) {
	sk, err := pki.GenerateRSAPrivateKey(2048)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}

	csrPEM := generateCSR(t, sk, "common-name", []string{
		"foo.example.com", "bar.example.com"})

	csrPEMNoDN := generateCSR(t, sk, "", []string{
		"foo.example.com", "bar.example.com"})

	csrNonePEM := generateCSR(t, sk, "", []string{})

	tests := map[string]testSignT{
		"if reading the zone configuration fails then error": {
			csrPEM:             csrPEM,
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
			client: internalfake.Connector{
				ReadZoneConfigurationFunc: func() (*endpoint.ZoneConfiguration, error) {
					return nil, errors.New("zone configuration error")
				},
			}.Default(),
		},
		"if validating the certificate fails then error": {
			csrPEM:             csrPEM,
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
			client: internalfake.Connector{
				ReadZoneConfigurationFunc: func() (*endpoint.ZoneConfiguration, error) {
					return &endpoint.ZoneConfiguration{
						Policy: endpoint.Policy{
							SubjectCNRegexes: []string{"foo"},
						},
					}, nil
				},
			}.Default(),
		},
		"a badly formed CSR should error": {
			csrPEM:             []byte("a badly formed CSR"),
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
		},
		"a CSR wiuth empty DN sshould error": {
			csrPEM:      csrPEMNoDN,
			checkFn:     checkNoCertificateIssued,
			expectedErr: true,
		},
		"if requesting the certificate fails, sign should error": {
			csrPEM: csrPEM,
			client: internalfake.Connector{
				RequestCertificateFunc: func(*certificate.Request) (string, error) {
					return "", errors.New("request error")
				},
			}.Default(),
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
		},
		"if retrieve certificate fails, sign should error": {
			csrPEM: csrPEM,
			client: internalfake.Connector{
				RetrieveCertificateFunc: func(*certificate.Request) (*certificate.PEMCollection, error) {
					return nil, errors.New("request error")
				},
			}.Default(),
			checkFn:     checkNoCertificateIssued,
			expectedErr: true,
		},
		"if no Common Name, DNS Name, or URI SANs in CSR then error": {
			csrPEM:             csrNonePEM,
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
		},
		"obtain a certificate with DNS names specified": {
			csrPEM:      csrPEM,
			checkFn:     checkCertificateIssued,
			expectedErr: false,
		},
		"obtain a certificate with custom fields specified": {
			csrPEM:       csrPEM,
			customFields: []api.CustomField{{Name: "test", Value: "ok"}},
			client: internalfake.Connector{
				RetrieveCertificateFunc: func(r *certificate.Request) (*certificate.PEMCollection, error) {
					// we set 1 field by default
					if len(r.CustomFields) <= 1 {
						return nil, errors.New("custom fields not set")
					}
					foundFields := false
					for _, fieldSet := range r.CustomFields {
						if fieldSet.Name != "test" && fieldSet.Value != "ok" {
							foundFields = true
						}
					}
					if !foundFields {
						return nil, errors.New("custom fields content not correct")
					}
					return internalfake.Connector{}.Default().RetrieveCertificate(r) // hack to return to normal
				},
			}.Default(),
			checkFn:     checkCertificateIssued,
			expectedErr: false,
		},
		"If invalid custom field type found the error": {
			csrPEM:             csrPEM,
			customFields:       []api.CustomField{{Name: "test", Value: "ok", Type: "Bool"}},
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			test.runTest(t)
		})
	}
}

type testSignT struct {
	csrPEM []byte
	client connector

	expectedErr        bool
	expectedRequestErr bool

	customFields []api.CustomField

	checkFn func(*testing.T, []byte, []byte)
}

func (s *testSignT) runTest(t *testing.T) {
	client := s.client
	if client == nil {
		client = fake.NewConnector(true, nil)
	}

	v := &Venafi{
		vcertClient: client,
	}

	pickupID, err := v.RequestCertificate(s.csrPEM, time.Minute, s.customFields)
	if err != nil && !s.expectedRequestErr {
		t.Errorf("expected to not get an error, but got: %v", err)
	}
	if err == nil && s.expectedRequestErr {
		t.Errorf("expected to get an error but did not get one")
	}

	if err != nil {
		// do not call RetreiveCertificate if RequestCertificate failed
		return
	}

	resp, err := v.RetreiveCertificate(pickupID, s.csrPEM, time.Minute, s.customFields)
	if err != nil && !s.expectedErr {
		t.Errorf("expected to not get an error, but got: %v", err)
	}
	if err == nil && s.expectedErr {
		t.Errorf("expected to get an error but did not get one")
	}

	if s.checkFn != nil {
		s.checkFn(t, s.csrPEM, resp)
	}
}
<commit_msg>Fix field name<commit_after>/*
Copyright 2018 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 client

import (
	"crypto"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"testing"
	"time"

	"github.com/jetstack/cert-manager/pkg/issuer/venafi/client/api"

	"github.com/Venafi/vcert/pkg/certificate"
	"github.com/Venafi/vcert/pkg/endpoint"
	"github.com/Venafi/vcert/pkg/venafi/fake"

	internalfake "github.com/jetstack/cert-manager/pkg/issuer/venafi/client/fake"
	"github.com/jetstack/cert-manager/pkg/util"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

func checkCertificateIssued(t *testing.T, csrPEM []byte, resp []byte) {
	if len(resp) == 0 {
		t.Errorf("expected IssueResponse to be non-nil")
		t.FailNow()
		return
	}

	csr, err := pki.DecodeX509CertificateRequestBytes(csrPEM)
	if err != nil {
		t.Errorf("failed to decode CSR PEM: %s", err)
		return
	}

	crt, err := pki.DecodeX509CertificateBytes(resp)
	if err != nil {
		t.Errorf("unable to decode x509 certificate: %v", err)
		return
	}

	ok, err := pki.PublicKeyMatchesCSR(crt.PublicKey, csr)
	if err != nil {
		t.Errorf("error checking private key: %v", err)
		return
	}
	if !ok {
		t.Errorf("private key does not match certificate")
	}

	// validate the common name is correct
	expectedCN := csr.Subject.CommonName
	if expectedCN != crt.Subject.CommonName {
		t.Errorf("expected common name to be %q but it was %q", expectedCN, crt.Subject.CommonName)
	}

	// validate the dns names are correct
	expectedDNSNames := csr.DNSNames
	if !util.EqualUnsorted(crt.DNSNames, expectedDNSNames) {
		t.Errorf("expected dns names to be %q but it was %q", expectedDNSNames, crt.DNSNames)
	}
}

func checkNoCertificateIssued(t *testing.T, csrPEM []byte, resp []byte) {
	if len(resp) > 0 {
		t.Errorf("expected no response with error but got=%s", resp)
	}
}

func generateCSR(t *testing.T, sk crypto.Signer, commonName string, dnsNames []string) []byte {
	template := x509.CertificateRequest{
		Subject: pkix.Name{
			CommonName: commonName,
		},
		SignatureAlgorithm: x509.SHA256WithRSA,
		DNSNames:           dnsNames,
	}

	csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, sk)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}

	csr := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
	return csr
}

func TestSign(t *testing.T) {
	sk, err := pki.GenerateRSAPrivateKey(2048)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}

	csrPEM := generateCSR(t, sk, "common-name", []string{
		"foo.example.com", "bar.example.com"})

	csrPEMNoDN := generateCSR(t, sk, "", []string{
		"foo.example.com", "bar.example.com"})

	csrNonePEM := generateCSR(t, sk, "", []string{})

	tests := map[string]testSignT{
		"if reading the zone configuration fails then error": {
			csrPEM:             csrPEM,
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
			client: internalfake.Connector{
				ReadZoneConfigurationFunc: func() (*endpoint.ZoneConfiguration, error) {
					return nil, errors.New("zone configuration error")
				},
			}.Default(),
		},
		"if validating the certificate fails then error": {
			csrPEM:             csrPEM,
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
			client: internalfake.Connector{
				ReadZoneConfigurationFunc: func() (*endpoint.ZoneConfiguration, error) {
					return &endpoint.ZoneConfiguration{
						Policy: endpoint.Policy{
							SubjectCNRegexes: []string{"foo"},
						},
					}, nil
				},
			}.Default(),
		},
		"a badly formed CSR should error": {
			csrPEM:             []byte("a badly formed CSR"),
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
		},
		"a CSR with empty DN should error": {
			csrPEM:             csrPEMNoDN,
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
		},
		"if requesting the certificate fails, sign should error": {
			csrPEM: csrPEM,
			client: internalfake.Connector{
				RequestCertificateFunc: func(*certificate.Request) (string, error) {
					return "", errors.New("request error")
				},
			}.Default(),
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
		},
		"if retrieve certificate fails, sign should error": {
			csrPEM: csrPEM,
			client: internalfake.Connector{
				RetrieveCertificateFunc: func(*certificate.Request) (*certificate.PEMCollection, error) {
					return nil, errors.New("request error")
				},
			}.Default(),
			checkFn:     checkNoCertificateIssued,
			expectedErr: true,
		},
		"if no Common Name, DNS Name, or URI SANs in CSR then error": {
			csrPEM:             csrNonePEM,
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
		},
		"obtain a certificate with DNS names specified": {
			csrPEM:      csrPEM,
			checkFn:     checkCertificateIssued,
			expectedErr: false,
		},
		"obtain a certificate with custom fields specified": {
			csrPEM:       csrPEM,
			customFields: []api.CustomField{{Name: "test", Value: "ok"}},
			client: internalfake.Connector{
				RetrieveCertificateFunc: func(r *certificate.Request) (*certificate.PEMCollection, error) {
					// we set 1 field by default
					if len(r.CustomFields) <= 1 {
						return nil, errors.New("custom fields not set")
					}
					foundFields := false
					for _, fieldSet := range r.CustomFields {
						if fieldSet.Name != "test" && fieldSet.Value != "ok" {
							foundFields = true
						}
					}
					if !foundFields {
						return nil, errors.New("custom fields content not correct")
					}
					return internalfake.Connector{}.Default().RetrieveCertificate(r) // hack to return to normal
				},
			}.Default(),
			checkFn:     checkCertificateIssued,
			expectedErr: false,
		},
		"If invalid custom field type found the error": {
			csrPEM:             csrPEM,
			customFields:       []api.CustomField{{Name: "test", Value: "ok", Type: "Bool"}},
			checkFn:            checkNoCertificateIssued,
			expectedRequestErr: true,
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			test.runTest(t)
		})
	}
}

type testSignT struct {
	csrPEM []byte
	client connector

	expectedErr        bool
	expectedRequestErr bool

	customFields []api.CustomField

	checkFn func(*testing.T, []byte, []byte)
}

func (s *testSignT) runTest(t *testing.T) {
	client := s.client
	if client == nil {
		client = fake.NewConnector(true, nil)
	}

	v := &Venafi{
		vcertClient: client,
	}

	pickupID, err := v.RequestCertificate(s.csrPEM, time.Minute, s.customFields)
	if err != nil && !s.expectedRequestErr {
		t.Errorf("expected to not get an error, but got: %v", err)
	}
	if err == nil && s.expectedRequestErr {
		t.Errorf("expected to get an error but did not get one")
	}

	if err != nil {
		// do not call RetreiveCertificate if RequestCertificate failed
		return
	}

	resp, err := v.RetreiveCertificate(pickupID, s.csrPEM, time.Minute, s.customFields)
	if err != nil && !s.expectedErr {
		t.Errorf("expected to not get an error, but got: %v", err)
	}
	if err == nil && s.expectedErr {
		t.Errorf("expected to get an error but did not get one")
	}

	if s.checkFn != nil {
		s.checkFn(t, s.csrPEM, resp)
	}
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"

  "github.com/toqueteos/webbrowser"
  "github.com/alecthomas/kingpin"
)

var (
  keyword = kingpin.Arg("keyword", "Keyword of service").Required().String()
)

func main() {
  kingpin.Version("0.0.1")
  kingpin.Parse()

  url := fmt.Sprint("http://kaifa.li/services/", *keyword)
  webbrowser.Open(url)
}
<commit_msg>Bump to v0.0.2.<commit_after>package main

import (
  "fmt"

  "github.com/toqueteos/webbrowser"
  "github.com/alecthomas/kingpin"
)

var (
  keyword = kingpin.Arg("keyword", "Keyword of service").Required().String()
)

func main() {
  kingpin.Version("0.0.2")
  kingpin.Parse()

  url := fmt.Sprint("http://kaifa.li/services/", *keyword)
  webbrowser.Open(url)
}
<|endoftext|>


<commit_before>package dashboards

import (
	"context"
	"strings"

	"github.com/grafana/grafana/pkg/models"
	ac "github.com/grafana/grafana/pkg/services/accesscontrol"
)

const (
	ScopeFoldersRoot   = "folders"
	ScopeFoldersPrefix = "folders:uid:"

	ActionFoldersCreate           = "folders:create"
	ActionFoldersRead             = "folders:read"
	ActionFoldersWrite            = "folders:write"
	ActionFoldersDelete           = "folders:delete"
	ActionFoldersPermissionsRead  = "folders.permissions:read"
	ActionFoldersPermissionsWrite = "folders.permissions:write"

	ScopeDashboardsRoot   = "dashboards"
	ScopeDashboardsPrefix = "dashboards:uid:"

	ActionDashboardsCreate           = "dashboards:create"
	ActionDashboardsRead             = "dashboards:read"
	ActionDashboardsWrite            = "dashboards:write"
	ActionDashboardsDelete           = "dashboards:delete"
	ActionDashboardsPermissionsRead  = "dashboards.permissions:read"
	ActionDashboardsPermissionsWrite = "dashboards.permissions:write"
)

var (
	ScopeFoldersProvider    = ac.NewScopeProvider(ScopeFoldersRoot)
	ScopeFoldersAll         = ScopeFoldersProvider.GetResourceAllScope()
	ScopeDashboardsProvider = ac.NewScopeProvider(ScopeDashboardsRoot)
	ScopeDashboardsAll      = ScopeDashboardsProvider.GetResourceAllScope()
)

// NewFolderNameScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:name:" into an uid based scope.
func NewFolderNameScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
	prefix := ScopeFoldersProvider.GetResourceScopeName("")
	return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
		if !strings.HasPrefix(scope, prefix) {
			return nil, ac.ErrInvalidScope
		}
		nsName := scope[len(prefix):]
		if len(nsName) == 0 {
			return nil, ac.ErrInvalidScope
		}
		folder, err := db.GetFolderByTitle(ctx, orgID, nsName)
		if err != nil {
			return nil, err
		}
		return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.Uid)}, nil
	})
}

// NewFolderIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:id:" into an uid based scope.
func NewFolderIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
	prefix := ScopeFoldersProvider.GetResourceScope("")
	return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
		if !strings.HasPrefix(scope, prefix) {
			return nil, ac.ErrInvalidScope
		}

		id, err := ac.ParseScopeID(scope)
		if err != nil {
			return nil, err
		}

		if id == 0 {
			return []string{ScopeFoldersProvider.GetResourceScopeUID(ac.GeneralFolderUID)}, nil
		}

		folder, err := db.GetFolderByID(ctx, orgID, id)
		if err != nil {
			return nil, err
		}

		return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.Uid)}, nil
	})
}

// NewDashboardIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:id:"
// into uid based scopes for both dashboard and folder
func NewDashboardIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
	prefix := ScopeDashboardsProvider.GetResourceScope("")
	return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
		if !strings.HasPrefix(scope, prefix) {
			return nil, ac.ErrInvalidScope
		}

		id, err := ac.ParseScopeID(scope)
		if err != nil {
			return nil, err
		}

		dashboard, err := db.GetDashboard(ctx, &models.GetDashboardQuery{Id: id, OrgId: orgID})
		if err != nil {
			return nil, err
		}

		return resolveDashboardScope(ctx, db, orgID, dashboard)
	})
}

// NewDashboardUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:uid:"
// into uid based scopes for both dashboard and folder
func NewDashboardUIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
	prefix := ScopeDashboardsProvider.GetResourceScopeUID("")
	return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
		if !strings.HasPrefix(scope, prefix) {
			return nil, ac.ErrInvalidScope
		}

		uid, err := ac.ParseScopeUID(scope)
		if err != nil {
			return nil, err
		}

		dashboard, err := db.GetDashboard(ctx, &models.GetDashboardQuery{Uid: uid, OrgId: orgID})
		if err != nil {
			return nil, err
		}

		return resolveDashboardScope(ctx, db, orgID, dashboard)
	})
}

func resolveDashboardScope(ctx context.Context, db Store, orgID int64, dashboard *models.Dashboard) ([]string, error) {
	var folderUID string
	if dashboard.FolderId == 0 {
		folderUID = ac.GeneralFolderUID
	} else {
		folder, err := db.GetFolderByID(ctx, orgID, dashboard.FolderId)
		if err != nil {
			return nil, err
		}
		folderUID = folder.Uid
	}
	return []string{
		ScopeDashboardsProvider.GetResourceScopeUID(dashboard.Uid),
		ScopeFoldersProvider.GetResourceScopeUID(folderUID),
	}, nil
}
<commit_msg>RBAC: Handle case when folder id is negative (#53438)<commit_after>package dashboards

import (
	"context"
	"strings"

	"github.com/grafana/grafana/pkg/models"
	ac "github.com/grafana/grafana/pkg/services/accesscontrol"
)

const (
	ScopeFoldersRoot   = "folders"
	ScopeFoldersPrefix = "folders:uid:"

	ActionFoldersCreate           = "folders:create"
	ActionFoldersRead             = "folders:read"
	ActionFoldersWrite            = "folders:write"
	ActionFoldersDelete           = "folders:delete"
	ActionFoldersPermissionsRead  = "folders.permissions:read"
	ActionFoldersPermissionsWrite = "folders.permissions:write"

	ScopeDashboardsRoot   = "dashboards"
	ScopeDashboardsPrefix = "dashboards:uid:"

	ActionDashboardsCreate           = "dashboards:create"
	ActionDashboardsRead             = "dashboards:read"
	ActionDashboardsWrite            = "dashboards:write"
	ActionDashboardsDelete           = "dashboards:delete"
	ActionDashboardsPermissionsRead  = "dashboards.permissions:read"
	ActionDashboardsPermissionsWrite = "dashboards.permissions:write"
)

var (
	ScopeFoldersProvider    = ac.NewScopeProvider(ScopeFoldersRoot)
	ScopeFoldersAll         = ScopeFoldersProvider.GetResourceAllScope()
	ScopeDashboardsProvider = ac.NewScopeProvider(ScopeDashboardsRoot)
	ScopeDashboardsAll      = ScopeDashboardsProvider.GetResourceAllScope()
)

// NewFolderNameScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:name:" into an uid based scope.
func NewFolderNameScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
	prefix := ScopeFoldersProvider.GetResourceScopeName("")
	return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
		if !strings.HasPrefix(scope, prefix) {
			return nil, ac.ErrInvalidScope
		}
		nsName := scope[len(prefix):]
		if len(nsName) == 0 {
			return nil, ac.ErrInvalidScope
		}
		folder, err := db.GetFolderByTitle(ctx, orgID, nsName)
		if err != nil {
			return nil, err
		}
		return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.Uid)}, nil
	})
}

// NewFolderIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:id:" into an uid based scope.
func NewFolderIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
	prefix := ScopeFoldersProvider.GetResourceScope("")
	return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
		if !strings.HasPrefix(scope, prefix) {
			return nil, ac.ErrInvalidScope
		}

		id, err := ac.ParseScopeID(scope)
		if err != nil {
			return nil, err
		}

		if id == 0 {
			return []string{ScopeFoldersProvider.GetResourceScopeUID(ac.GeneralFolderUID)}, nil
		}

		folder, err := db.GetFolderByID(ctx, orgID, id)
		if err != nil {
			return nil, err
		}

		return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.Uid)}, nil
	})
}

// NewDashboardIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:id:"
// into uid based scopes for both dashboard and folder
func NewDashboardIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
	prefix := ScopeDashboardsProvider.GetResourceScope("")
	return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
		if !strings.HasPrefix(scope, prefix) {
			return nil, ac.ErrInvalidScope
		}

		id, err := ac.ParseScopeID(scope)
		if err != nil {
			return nil, err
		}

		dashboard, err := db.GetDashboard(ctx, &models.GetDashboardQuery{Id: id, OrgId: orgID})
		if err != nil {
			return nil, err
		}

		return resolveDashboardScope(ctx, db, orgID, dashboard)
	})
}

// NewDashboardUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:uid:"
// into uid based scopes for both dashboard and folder
func NewDashboardUIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) {
	prefix := ScopeDashboardsProvider.GetResourceScopeUID("")
	return prefix, ac.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, scope string) ([]string, error) {
		if !strings.HasPrefix(scope, prefix) {
			return nil, ac.ErrInvalidScope
		}

		uid, err := ac.ParseScopeUID(scope)
		if err != nil {
			return nil, err
		}

		dashboard, err := db.GetDashboard(ctx, &models.GetDashboardQuery{Uid: uid, OrgId: orgID})
		if err != nil {
			return nil, err
		}

		return resolveDashboardScope(ctx, db, orgID, dashboard)
	})
}

func resolveDashboardScope(ctx context.Context, db Store, orgID int64, dashboard *models.Dashboard) ([]string, error) {
	var folderUID string
	if dashboard.FolderId < 0 {
		return []string{ScopeDashboardsProvider.GetResourceScopeUID(dashboard.Uid)}, nil
	}

	if dashboard.FolderId == 0 {
		folderUID = ac.GeneralFolderUID
	} else {
		folder, err := db.GetFolderByID(ctx, orgID, dashboard.FolderId)
		if err != nil {
			return nil, err
		}
		folderUID = folder.Uid
	}

	return []string{
		ScopeDashboardsProvider.GetResourceScopeUID(dashboard.Uid),
		ScopeFoldersProvider.GetResourceScopeUID(folderUID),
	}, nil
}
<|endoftext|>