<commit_before>//Package embed implements all file embedding logic for github.com/mjibson/esc
package embed

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/template"
)

//Config contains all information needed to run esc
type Config struct {
	//Output file, else stdout
	OutputFile string
	//Package declaration for generated file
	Package string
	//Prefix to strip from filenames
	Prefix string
	//Regexp for files we should ignore (for example \\\\.DS_Store)
	Ignore string
	//Regexp for files to include. If provided, only files that match will be included
	Include string
	//Unix timestamp to override as modification time for all files
	ModTime string
	//If true, do not export autogenerated functions
	Private bool

	//List of files or directories to embed
	Files []string
}

var modTime *int64

type headerTemplateParams struct {
	PackageName    string
	FunctionPrefix string
}

type _escFile struct {
	data     []byte
	local    string
	fileinfo os.FileInfo
}

func Run(conf *Config) {
	var err error
	if conf.ModTime != "" {
		i, err := strconv.ParseInt(conf.ModTime, 10, 64)
		if err != nil {
			log.Fatalf("modtime must be an integer: %v", err)
		}
		modTime = &i
	}
	var fnames, dirnames []string
	content := make(map[string]_escFile)
	prefix := filepath.ToSlash(conf.Prefix)
	var ignoreRegexp *regexp.Regexp
	if conf.Ignore != "" {
		ignoreRegexp, err = regexp.Compile(conf.Ignore)
		if err != nil {
			log.Fatal(err)
		}
	}
	var includeRegexp *regexp.Regexp
	if conf.Include != "" {
		includeRegexp, err = regexp.Compile(conf.Include)
		if err != nil {
			log.Fatal(err)
		}
	}
	for _, base := range conf.Files {
		files := []string{base}
		for len(files) > 0 {
			fname := files[0]
			files = files[1:]
			if ignoreRegexp != nil && ignoreRegexp.MatchString(fname) {
				continue
			}
			f, err := os.Open(fname)
			if err != nil {
				log.Fatal(err)
			}
			fi, err := f.Stat()
			if err != nil {
				log.Fatal(err)
			}
			if fi.IsDir() {
				fis, err := f.Readdir(0)
				if err != nil {
					log.Fatal(err)
				}
				for _, fi := range fis {
					files = append(files, filepath.Join(fname, fi.Name()))
				}
			} else if includeRegexp == nil || includeRegexp.MatchString(fname) {
				b, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal(err)
				}
				fpath := filepath.ToSlash(fname)
				n := strings.TrimPrefix(fpath, prefix)
				n = path.Join("/", n)
				content[n] = _escFile{data: b, local: fpath, fileinfo: fi}
				fnames = append(fnames, n)
			}
			f.Close()
		}
	}
	sort.Strings(fnames)
	w := os.Stdout
	if conf.OutputFile != "" {
		if w, err = os.Create(conf.OutputFile); err != nil {
			log.Fatal(err)
		}
		defer w.Close()
	}
	headerText, err := header(conf.Package, !(conf.Private))
	if nil != err {
		log.Fatalf("failed to expand autogenerated code: %s", err)
	}
	if _, err := w.Write(headerText); err != nil {
		log.Fatalf("failed to write output: %s", err)
	}
	dirs := map[string]bool{"/": true}
	for _, fname := range fnames {
		f := content[fname]
		for b := path.Dir(fname); b != "/"; b = path.Dir(b) {
			dirs[b] = true
		}
		var buf bytes.Buffer
		gw := gzip.NewWriter(&buf)
		if _, err := gw.Write(f.data); err != nil {
			log.Fatal(err)
		}
		if err := gw.Close(); err != nil {
			log.Fatal(err)
		}
		t := f.fileinfo.ModTime().Unix()
		if modTime != nil {
			t = *modTime
		}
		fmt.Fprintf(w, `
	%q: {
		local:   %q,
		size:    %v,
		modtime: %v,
		compressed: %s,
	},%s`, fname, f.local, len(f.data), t, segment(&buf), "\n")
	}
	for d := range dirs {
		dirnames = append(dirnames, d)
	}
	sort.Strings(dirnames)
	for _, dir := range dirnames {
		local := path.Join(prefix, dir)
		if len(local) == 0 {
			local = "."
		}
		if local[0] == '/' {
			// Read dirs relative to the go proc's cwd vs system's
			// fs root.
			local = local[1:]
		}
		fmt.Fprintf(w, `
	%q: {
		isDir: true,
		local: %q,
	},%s`, dir, local, "\n")
	}
	fmt.Fprint(w, footer)
}

func segment(s *bytes.Buffer) string {
	var b bytes.Buffer
	b64 := base64.NewEncoder(base64.StdEncoding, &b)
	b64.Write(s.Bytes())
	b64.Close()
	res := "`\n"
	chunk := make([]byte, 80)
	for n, _ := b.Read(chunk); n > 0; n, _ = b.Read(chunk) {
		res += string(chunk[0:n]) + "\n"
	}
	return res + "`"
}

func header(packageName string, enableExports bool) ([]byte, error) {
	functionPrefix := ""
	if !enableExports {
		functionPrefix = "_esc"
	}
	headerParams := headerTemplateParams{
		PackageName:    packageName,
		FunctionPrefix: functionPrefix,
	}
	tmpl, err := template.New("").Parse(headerTemplate)
	if nil != err {
		return nil, err
	}
	var b bytes.Buffer
	err = tmpl.Execute(&b, headerParams)
	if nil != err {
		return nil, err
	}
	return b.Bytes(), nil
}

const (
	headerTemplate = `// This file is automatically generated using github.com/mjibson/esc.
package {{.PackageName}}

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"sync"
	"time"
)

type _escLocalFS struct{}

var _escLocal _escLocalFS

type _escStaticFS struct{}

var _escStatic _escStaticFS

type _escDirectory struct {
	fs   http.FileSystem
	name string
}

type _escFile struct {
	compressed string
	size       int64
	modtime    int64
	local      string
	isDir      bool

	once sync.Once
	data []byte
	name string
}

func (_escLocalFS) Open(name string) (http.File, error) {
	f, present := _escData[path.Clean(name)]
	if !present {
		return nil, os.ErrNotExist
	}
	return os.Open(f.local)
}

func (_escStaticFS) prepare(name string) (*_escFile, error) {
	f, present := _escData[path.Clean(name)]
	if !present {
		return nil, os.ErrNotExist
	}
	var err error
	f.once.Do(func() {
		f.name = path.Base(name)
		if f.size == 0 {
			return
		}
		var gr *gzip.Reader
		b64 := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(f.compressed))
		gr, err = gzip.NewReader(b64)
		if err != nil {
			return
		}
		f.data, err = ioutil.ReadAll(gr)
	})
	if err != nil {
		return nil, err
	}
	return f, nil
}

func (fs _escStaticFS) Open(name string) (http.File, error) {
	f, err := fs.prepare(name)
	if err != nil {
		return nil, err
	}
	return f.File()
}

func (dir _escDirectory) Open(name string) (http.File, error) {
	return dir.fs.Open(dir.name + name)
}

func (f *_escFile) File() (http.File, error) {
	type httpFile struct {
		*bytes.Reader
		*_escFile
	}
	return &httpFile{
		Reader:   bytes.NewReader(f.data),
		_escFile: f,
	}, nil
}

func (f *_escFile) Close() error {
	return nil
}

func (f *_escFile) Readdir(count int) ([]os.FileInfo, error) {
	return nil, nil
}

func (f *_escFile) Stat() (os.FileInfo, error) {
	return f, nil
}

func (f *_escFile) Name() string {
	return f.name
}

func (f *_escFile) Size() int64 {
	return f.size
}

func (f *_escFile) Mode() os.FileMode {
	return 0
}

func (f *_escFile) ModTime() time.Time {
	return time.Unix(f.modtime, 0)
}

func (f *_escFile) IsDir() bool {
	return f.isDir
}

func (f *_escFile) Sys() interface{} {
	return f
}

// {{.FunctionPrefix}}FS returns a http.Filesystem for the embedded assets. If useLocal is true,
// the filesystem's contents are instead used.
func {{.FunctionPrefix}}FS(useLocal bool) http.FileSystem {
	if useLocal {
		return _escLocal
	}
	return _escStatic
}

// {{.FunctionPrefix}}Dir returns a http.Filesystem for the embedded assets on a given prefix dir.
// If useLocal is true, the filesystem's contents are instead used.
func {{.FunctionPrefix}}Dir(useLocal bool, name string) http.FileSystem {
	if useLocal {
		return _escDirectory{fs: _escLocal, name: name}
	}
	return _escDirectory{fs: _escStatic, name: name}
}

// {{.FunctionPrefix}}FSByte returns the named file from the embedded assets. If useLocal is
// true, the filesystem's contents are instead used.
func {{.FunctionPrefix}}FSByte(useLocal bool, name string) ([]byte, error) {
	if useLocal {
		f, err := _escLocal.Open(name)
		if err != nil {
			return nil, err
		}
		b, err := ioutil.ReadAll(f)
		f.Close()
		return b, err
	}
	f, err := _escStatic.prepare(name)
	if err != nil {
		return nil, err
	}
	return f.data, nil
}

// {{.FunctionPrefix}}FSMustByte is the same as {{.FunctionPrefix}}FSByte, but panics if name is not present.
func {{.FunctionPrefix}}FSMustByte(useLocal bool, name string) []byte {
	b, err := {{.FunctionPrefix}}FSByte(useLocal, name)
	if err != nil {
		panic(err)
	}
	return b
}

// {{.FunctionPrefix}}FSString is the string version of {{.FunctionPrefix}}FSByte.
func {{.FunctionPrefix}}FSString(useLocal bool, name string) (string, error) {
	b, err := {{.FunctionPrefix}}FSByte(useLocal, name)
	return string(b), err
}

// {{.FunctionPrefix}}FSMustString is the string version of {{.FunctionPrefix}}FSMustByte.
func {{.FunctionPrefix}}FSMustString(useLocal bool, name string) string {
	return string({{.FunctionPrefix}}FSMustByte(useLocal, name))
}

var _escData = map[string]*_escFile{
`
	footer = `}
`
)
<commit_msg>small changes to fix gometalinter warnings (#38)<commit_after>//Package embed implements all file embedding logic for github.com/mjibson/esc
package embed

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/template"
)

//Config contains all information needed to run esc
type Config struct {
	//Output file, else stdout
	OutputFile string
	//Package declaration for generated file
	Package string
	//Prefix to strip from filenames
	Prefix string
	//Regexp for files we should ignore (for example \\\\.DS_Store)
	Ignore string
	//Regexp for files to include. If provided, only files that match will be included
	Include string
	//Unix timestamp to override as modification time for all files
	ModTime string
	//If true, do not export autogenerated functions
	Private bool

	//List of files or directories to embed
	Files []string
}

var modTime *int64

type headerTemplateParams struct {
	PackageName    string
	FunctionPrefix string
}

type _escFile struct {
	data     []byte
	local    string
	fileinfo os.FileInfo
}

func Run(conf *Config) {
	var err error
	if conf.ModTime != "" {
		i, err := strconv.ParseInt(conf.ModTime, 10, 64)
		if err != nil {
			log.Fatalf("modtime must be an integer: %v", err)
		}
		modTime = &i
	}
	var fnames, dirnames []string
	content := make(map[string]_escFile)
	prefix := filepath.ToSlash(conf.Prefix)
	var ignoreRegexp *regexp.Regexp
	if conf.Ignore != "" {
		ignoreRegexp, err = regexp.Compile(conf.Ignore)
		if err != nil {
			log.Fatal(err)
		}
	}
	var includeRegexp *regexp.Regexp
	if conf.Include != "" {
		includeRegexp, err = regexp.Compile(conf.Include)
		if err != nil {
			log.Fatal(err)
		}
	}
	for _, base := range conf.Files {
		files := []string{base}
		for len(files) > 0 {
			fname := files[0]
			files = files[1:]
			if ignoreRegexp != nil && ignoreRegexp.MatchString(fname) {
				continue
			}
			f, err := os.Open(fname)
			if err != nil {
				log.Fatal(err)
			}
			fi, err := f.Stat()
			if err != nil {
				log.Fatal(err)
			}
			if fi.IsDir() {
				fis, err := f.Readdir(0)
				if err != nil {
					log.Fatal(err)
				}
				for _, fi := range fis {
					files = append(files, filepath.Join(fname, fi.Name()))
				}
			} else if includeRegexp == nil || includeRegexp.MatchString(fname) {
				b, err := ioutil.ReadAll(f)
				if err != nil {
					log.Fatal(err)
				}
				fpath := filepath.ToSlash(fname)
				n := strings.TrimPrefix(fpath, prefix)
				n = path.Join("/", n)
				content[n] = _escFile{data: b, local: fpath, fileinfo: fi}
				fnames = append(fnames, n)
			}
			f.Close()
		}
	}
	sort.Strings(fnames)
	w := os.Stdout
	if conf.OutputFile != "" {
		if w, err = os.Create(conf.OutputFile); err != nil {
			log.Fatal(err)
		}
		defer w.Close()
	}
	headerText, err := header(conf.Package, !(conf.Private))
	if nil != err {
		log.Fatalf("failed to expand autogenerated code: %s", err)
	}
	if _, err := w.Write(headerText); err != nil {
		log.Fatalf("failed to write output: %s", err)
	}
	dirs := map[string]bool{"/": true}
	for _, fname := range fnames {
		f := content[fname]
		for b := path.Dir(fname); b != "/"; b = path.Dir(b) {
			dirs[b] = true
		}
		var buf bytes.Buffer
		gw := gzip.NewWriter(&buf)
		if _, err := gw.Write(f.data); err != nil {
			log.Fatal(err)
		}
		if err := gw.Close(); err != nil {
			log.Fatal(err)
		}
		t := f.fileinfo.ModTime().Unix()
		if modTime != nil {
			t = *modTime
		}
		fmt.Fprintf(w, `
	%q: {
		local:   %q,
		size:    %v,
		modtime: %v,
		compressed: %s,
	},%s`, fname, f.local, len(f.data), t, segment(&buf), "\n")
	}
	for d := range dirs {
		dirnames = append(dirnames, d)
	}
	sort.Strings(dirnames)
	for _, dir := range dirnames {
		local := path.Join(prefix, dir)
		if len(local) == 0 {
			local = "."
		}
		if local[0] == '/' {
			// Read dirs relative to the go proc's cwd vs system's
			// fs root.
			local = local[1:]
		}
		fmt.Fprintf(w, `
	%q: {
		isDir: true,
		local: %q,
	},%s`, dir, local, "\n")
	}
	fmt.Fprint(w, footer)
}

func segment(s *bytes.Buffer) string {
	var b bytes.Buffer
	b64 := base64.NewEncoder(base64.StdEncoding, &b)
	b64.Write(s.Bytes())
	b64.Close()
	res := "`\n"
	chunk := make([]byte, 80)
	for n, _ := b.Read(chunk); n > 0; n, _ = b.Read(chunk) {
		res += string(chunk[0:n]) + "\n"
	}
	return res + "`"
}

func header(packageName string, enableExports bool) ([]byte, error) {
	functionPrefix := ""
	if !enableExports {
		functionPrefix = "_esc"
	}
	headerParams := headerTemplateParams{
		PackageName:    packageName,
		FunctionPrefix: functionPrefix,
	}
	tmpl, err := template.New("").Parse(headerTemplate)
	if nil != err {
		return nil, err
	}
	var b bytes.Buffer
	err = tmpl.Execute(&b, headerParams)
	if nil != err {
		return nil, err
	}
	return b.Bytes(), nil
}

const (
	headerTemplate = `// This file is automatically generated using github.com/mjibson/esc.

package {{.PackageName}}

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"sync"
	"time"
)

type _escLocalFS struct{}

var _escLocal _escLocalFS

type _escStaticFS struct{}

var _escStatic _escStaticFS

type _escDirectory struct {
	fs   http.FileSystem
	name string
}

type _escFile struct {
	compressed string
	size       int64
	modtime    int64
	local      string
	isDir      bool

	once sync.Once
	data []byte
	name string
}

func (_escLocalFS) Open(name string) (http.File, error) {
	f, present := _escData[path.Clean(name)]
	if !present {
		return nil, os.ErrNotExist
	}
	return os.Open(f.local)
}

func (_escStaticFS) prepare(name string) (*_escFile, error) {
	f, present := _escData[path.Clean(name)]
	if !present {
		return nil, os.ErrNotExist
	}
	var err error
	f.once.Do(func() {
		f.name = path.Base(name)
		if f.size == 0 {
			return
		}
		var gr *gzip.Reader
		b64 := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(f.compressed))
		gr, err = gzip.NewReader(b64)
		if err != nil {
			return
		}
		f.data, err = ioutil.ReadAll(gr)
	})
	if err != nil {
		return nil, err
	}
	return f, nil
}

func (fs _escStaticFS) Open(name string) (http.File, error) {
	f, err := fs.prepare(name)
	if err != nil {
		return nil, err
	}
	return f.File()
}

func (dir _escDirectory) Open(name string) (http.File, error) {
	return dir.fs.Open(dir.name + name)
}

func (f *_escFile) File() (http.File, error) {
	type httpFile struct {
		*bytes.Reader
		*_escFile
	}
	return &httpFile{
		Reader:   bytes.NewReader(f.data),
		_escFile: f,
	}, nil
}

func (f *_escFile) Close() error {
	return nil
}

func (f *_escFile) Readdir(count int) ([]os.FileInfo, error) {
	return nil, nil
}

func (f *_escFile) Stat() (os.FileInfo, error) {
	return f, nil
}

func (f *_escFile) Name() string {
	return f.name
}

func (f *_escFile) Size() int64 {
	return f.size
}

func (f *_escFile) Mode() os.FileMode {
	return 0
}

func (f *_escFile) ModTime() time.Time {
	return time.Unix(f.modtime, 0)
}

func (f *_escFile) IsDir() bool {
	return f.isDir
}

func (f *_escFile) Sys() interface{} {
	return f
}

// {{.FunctionPrefix}}FS returns a http.Filesystem for the embedded assets. If useLocal is true,
// the filesystem's contents are instead used.
func {{.FunctionPrefix}}FS(useLocal bool) http.FileSystem {
	if useLocal {
		return _escLocal
	}
	return _escStatic
}

// {{.FunctionPrefix}}Dir returns a http.Filesystem for the embedded assets on a given prefix dir.
// If useLocal is true, the filesystem's contents are instead used.
func {{.FunctionPrefix}}Dir(useLocal bool, name string) http.FileSystem {
	if useLocal {
		return _escDirectory{fs: _escLocal, name: name}
	}
	return _escDirectory{fs: _escStatic, name: name}
}

// {{.FunctionPrefix}}FSByte returns the named file from the embedded assets. If useLocal is
// true, the filesystem's contents are instead used.
func {{.FunctionPrefix}}FSByte(useLocal bool, name string) ([]byte, error) {
	if useLocal {
		f, err := _escLocal.Open(name)
		if err != nil {
			return nil, err
		}
		b, err := ioutil.ReadAll(f)
		_ = f.Close()
		return b, err
	}
	f, err := _escStatic.prepare(name)
	if err != nil {
		return nil, err
	}
	return f.data, nil
}

// {{.FunctionPrefix}}FSMustByte is the same as {{.FunctionPrefix}}FSByte, but panics if name is not present.
func {{.FunctionPrefix}}FSMustByte(useLocal bool, name string) []byte {
	b, err := {{.FunctionPrefix}}FSByte(useLocal, name)
	if err != nil {
		panic(err)
	}
	return b
}

// {{.FunctionPrefix}}FSString is the string version of {{.FunctionPrefix}}FSByte.
func {{.FunctionPrefix}}FSString(useLocal bool, name string) (string, error) {
	b, err := {{.FunctionPrefix}}FSByte(useLocal, name)
	return string(b), err
}

// {{.FunctionPrefix}}FSMustString is the string version of {{.FunctionPrefix}}FSMustByte.
func {{.FunctionPrefix}}FSMustString(useLocal bool, name string) string {
	return string({{.FunctionPrefix}}FSMustByte(useLocal, name))
}

var _escData = map[string]*_escFile{
`
	footer = `}
`
)
<|endoftext|>


<commit_before>package encrypted

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"

	"github.com/tinzenite/shared"
)

type chaninterface struct {
	// reference back to encrypted
	enc *Encrypted
}

func createChanInterface(enc *Encrypted) *chaninterface {
	return &chaninterface{
		enc: enc}
}

// ----------------------- Callbacks ------------------------------

/*
OnFriendRequest is called when a friend request is received. Due to the nature
of the encrypted peer, it will NEVER accept friend requests.
*/
func (c *chaninterface) OnFriendRequest(address, message string) {
	// for now only accept connection from myself for testing
	if address[:8] == "ed284a9f" {
		// TODO remove once done debugging / "dev"-ing
		log.Println("OnFriendRequest: Accepting connection from root.")
		c.enc.channel.AcceptConnection(address)
		return
	}
	// TODO usually encrypted should NEVER accept a friend request
	log.Println("OnFriendRequest: Connection request from", address[:8]+", ignoring!")
}

func (c *chaninterface) OnMessage(address, message string) {
	// check if lock message, or request, or send message
	v := &shared.Message{}
	err := json.Unmarshal([]byte(message), v)
	if err == nil {
		// special case for lock messages (can be received if not locked)
		if v.Type == shared.MsgLock {
			msg := &shared.LockMessage{}
			err := json.Unmarshal([]byte(message), msg)
			if err != nil {
				log.Println("OnMessage: failed to parse JSON!", err)
				return
			}
			c.handleLockMessage(address, msg)
			return
		}
		// for all others ensure that we are locked correctly
		if !c.enc.checkLock(address) {
			// if not warn and ignore message
			log.Println("OnMessage: not locked to given address!", address[:8])
			// TODO send notify that they are unlocked back?
			return
		}
		// if correctly locked handle message according to type
		switch msgType := v.Type; msgType {
		case shared.MsgRequest:
			msg := &shared.RequestMessage{}
			err := json.Unmarshal([]byte(message), msg)
			if err != nil {
				log.Println("OnMessage: failed to parse JSON!", err)
				return
			}
			c.handleRequestMessage(address, msg)
		case shared.MsgPush:
			msg := &shared.PushMessage{}
			err := json.Unmarshal([]byte(message), msg)
			if err != nil {
				log.Println("OnMessage: failed to parse JSON!", err)
				return
			}
			c.handlePushMessage(address, msg)
		default:
			log.Println("OnMessage: WARNING: Unknown object received:", msgType.String())
		}
		// in any case return as we are done handling them
		return
	}
	// if unmarshal didn't work check for plain commands:
	// TODO these are temporary until it works, remove them later
	switch message {
	case "push":
		log.Println("Sending example push message.")
		pm := shared.CreatePushMessage("ID_HERE", "NAME_HERE", shared.OtObject)
		c.enc.channel.Send(address, pm.JSON())
	case "lock":
		log.Println("Sending example lock message.")
		lm := shared.CreateLockMessage(shared.LoRequest)
		c.enc.channel.Send(address, lm.JSON())
	case "unlock":
		log.Println("Sending example unlock message.")
		lm := shared.CreateLockMessage(shared.LoRelease)
		c.enc.channel.Send(address, lm.JSON())
	case "request":
		log.Println("Sending example request message.")
		rm := shared.CreateRequestMessage(shared.OtObject, "ID_HERE")
		c.enc.channel.Send(address, rm.JSON())
	default:
		log.Println("Received:", message)
		c.enc.channel.Send(address, "Received non JSON message.")
	}
}

/*
OnAllowFile is called when a file is to be received. Name should be the
file identification!
*/
func (c *chaninterface) OnAllowFile(address, name string) (bool, string) {
	if !c.enc.checkLock(address) {
		log.Println("OnAllowFile: not locked to given address, refusing!")
		return false, ""
	}
	//check against allowed files and allow if ok
	key := c.buildKey(address, name)
	_, exists := c.enc.allowedTransfers[key]
	if !exists {
		log.Println("OnAllowFile: refusing file transfer due to no allowance!")
		return false, ""
	}
	//write to RECEIVINGDIR
	return true, c.enc.RootPath + "/" + shared.RECEIVINGDIR + "/" + key
}

/*
OnFileReceived is called when a file has been successfully received.
*/
func (c *chaninterface) OnFileReceived(address, path, name string) {
	// TODO fix this: NOTE: no lock check so that locks don't have to stay on for long file transfers
	// no matter what, remove temp file
	defer func() {
		err := os.Remove(path)
		if err != nil {
			log.Println("OnFileReceived: failed to remove temp file:", err)
		}
	}()
	// fetch push message for file
	pm, exists := c.enc.allowedTransfers[name]
	if !exists {
		log.Println("OnFileReceived: no associated push message found!")
		return
	}
	// read data
	data, err := ioutil.ReadFile(path)
	if err != nil {
		log.Println("OnFileReceived: failed to read file:", err)
		return
	}
	// depending on the object type write the file to different locations:
	switch pm.ObjType {
	case shared.OtModel:
		// model is not written to storage but to disk directly
		path := c.enc.RootPath + "/" + shared.IDMODEL
		err = ioutil.WriteFile(path, data, shared.FILEPERMISSIONMODE)
	case shared.OtPeer:
		// peers are written to disk too, but in correct dir with pm.Name
		path := c.enc.RootPath + "/" + shared.ORGDIR + "/" + shared.PEERSDIR + "/" + pm.Name
		err = ioutil.WriteFile(path, data, shared.FILEPERMISSIONMODE)
	case shared.OtObject:
		// write to storage
		err = c.enc.storage.Store(pm.Identification, data)
	default:
		log.Println("OnFileReceived: unknown ObjType for received file!", pm.ObjType)
		return
	}
	// this means something failed
	if err != nil {
		log.Println("OnFileReceived: writing file failed:", err)
		return
	}
}

/*
OnFileCanceled is called when a file has failed to be successfully received.
*/
func (c *chaninterface) OnFileCanceled(address, path string) {
	// note: no lock check so that locks don't have to stay on for long file transfers
	log.Println("OnFileCanceled:", path)
	// remove temp file if exists
	err := os.Remove(path)
	if err != nil {
		log.Println("OnFileCanceled: failed to remove temp file:", err)
		return
	}
}

/*
OnConnected is called when another peer comes online.
*/
func (c *chaninterface) OnConnected(address string) {
	// only notify log, nothing else to do for us here
	log.Println("OnConnected:", address[:8])
}
<commit_msg>auth special case<commit_after>package encrypted

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"

	"github.com/tinzenite/shared"
)

type chaninterface struct {
	// reference back to encrypted
	enc *Encrypted
}

func createChanInterface(enc *Encrypted) *chaninterface {
	return &chaninterface{
		enc: enc}
}

// ----------------------- Callbacks ------------------------------

/*
OnFriendRequest is called when a friend request is received. Due to the nature
of the encrypted peer, it will NEVER accept friend requests.
*/
func (c *chaninterface) OnFriendRequest(address, message string) {
	// for now only accept connection from myself for testing
	if address[:8] == "ed284a9f" {
		// TODO remove once done debugging / "dev"-ing
		log.Println("OnFriendRequest: Accepting connection from root.")
		c.enc.channel.AcceptConnection(address)
		return
	}
	// TODO usually encrypted should NEVER accept a friend request
	log.Println("OnFriendRequest: Connection request from", address[:8]+", ignoring!")
}

func (c *chaninterface) OnMessage(address, message string) {
	// check if lock message, or request, or send message
	v := &shared.Message{}
	err := json.Unmarshal([]byte(message), v)
	if err == nil {
		// special case for lock messages (can be received if not locked)
		if v.Type == shared.MsgLock {
			msg := &shared.LockMessage{}
			err := json.Unmarshal([]byte(message), msg)
			if err != nil {
				log.Println("OnMessage: failed to parse JSON!", err)
				return
			}
			c.handleLockMessage(address, msg)
			return
		}
		// for all others ensure that we are locked correctly
		if !c.enc.checkLock(address) {
			// if not warn and ignore message
			log.Println("OnMessage: not locked to given address!", address[:8])
			// TODO send notify that they are unlocked back?
			return
		}
		// if correctly locked handle message according to type
		switch msgType := v.Type; msgType {
		case shared.MsgRequest:
			msg := &shared.RequestMessage{}
			err := json.Unmarshal([]byte(message), msg)
			if err != nil {
				log.Println("OnMessage: failed to parse JSON!", err)
				return
			}
			c.handleRequestMessage(address, msg)
		case shared.MsgPush:
			msg := &shared.PushMessage{}
			err := json.Unmarshal([]byte(message), msg)
			if err != nil {
				log.Println("OnMessage: failed to parse JSON!", err)
				return
			}
			if msg == nil {
				log.Println("WHY?")
			}
			c.handlePushMessage(address, msg)
		default:
			log.Println("OnMessage: WARNING: Unknown object received:", msgType.String())
		}
		// in any case return as we are done handling them
		return
	}
	// if unmarshal didn't work check for plain commands:
	// TODO these are temporary until it works, remove them later
	switch message {
	case "push":
		log.Println("Sending example push message.")
		pm := shared.CreatePushMessage("ID_HERE", "NAME_HERE", shared.OtObject)
		c.enc.channel.Send(address, pm.JSON())
	case "lock":
		log.Println("Sending example lock message.")
		lm := shared.CreateLockMessage(shared.LoRequest)
		c.enc.channel.Send(address, lm.JSON())
	case "unlock":
		log.Println("Sending example unlock message.")
		lm := shared.CreateLockMessage(shared.LoRelease)
		c.enc.channel.Send(address, lm.JSON())
	case "request":
		log.Println("Sending example request message.")
		rm := shared.CreateRequestMessage(shared.OtObject, "ID_HERE")
		c.enc.channel.Send(address, rm.JSON())
	default:
		log.Println("Received:", message)
		c.enc.channel.Send(address, "Received non JSON message.")
	}
}

/*
OnAllowFile is called when a file is to be received. Name should be the
file identification!
*/
func (c *chaninterface) OnAllowFile(address, name string) (bool, string) {
	if !c.enc.checkLock(address) {
		log.Println("OnAllowFile: not locked to given address, refusing!")
		return false, ""
	}
	//check against allowed files and allow if ok
	key := c.buildKey(address, name)
	_, exists := c.enc.allowedTransfers[key]
	if !exists {
		log.Println("OnAllowFile: refusing file transfer due to no allowance!")
		log.Println("DEBUG:", address[:8], name, key)
		return false, ""
	}
	//write to RECEIVINGDIR
	return true, c.enc.RootPath + "/" + shared.RECEIVINGDIR + "/" + key
}

/*
OnFileReceived is called when a file has been successfully received.
*/
func (c *chaninterface) OnFileReceived(address, path, name string) {
	// TODO fix this: NOTE: no lock check so that locks don't have to stay on for long file transfers
	// no matter what, remove temp file
	defer func() {
		err := os.Remove(path)
		if err != nil {
			log.Println("OnFileReceived: failed to remove temp file:", err)
		}
	}()
	// fetch push message for file
	pm, exists := c.enc.allowedTransfers[name]
	if !exists {
		log.Println("OnFileReceived: no associated push message found!")
		return
	}
	// read data
	data, err := ioutil.ReadFile(path)
	if err != nil {
		log.Println("OnFileReceived: failed to read file:", err)
		return
	}
	// depending on the object type write the file to different locations:
	switch pm.ObjType {
	case shared.OtModel:
		// model is not written to storage but to disk directly
		path := c.enc.RootPath + "/" + shared.IDMODEL
		err = ioutil.WriteFile(path, data, shared.FILEPERMISSIONMODE)
	case shared.OtPeer:
		// peers are written to disk too, but in correct dir with pm.Name
		path := c.enc.RootPath + "/" + shared.ORGDIR + "/" + shared.PEERSDIR + "/" + pm.Name
		err = ioutil.WriteFile(path, data, shared.FILEPERMISSIONMODE)
	case shared.OtAuth:
		// auth is also special case
		path := c.enc.RootPath + "/" + shared.ORGDIR + "/" + shared.AUTHJSON
		err = ioutil.WriteFile(path, data, shared.FILEPERMISSIONMODE)
	case shared.OtObject:
		// write to storage
		err = c.enc.storage.Store(pm.Identification, data)
	default:
		log.Println("OnFileReceived: unknown ObjType for received file!", pm.ObjType)
		return
	}
	// this means something failed
	if err != nil {
		log.Println("OnFileReceived: writing file failed:", err)
		return
	}
}

/*
OnFileCanceled is called when a file has failed to be successfully received.
*/
func (c *chaninterface) OnFileCanceled(address, path string) {
	// note: no lock check so that locks don't have to stay on for long file transfers
	log.Println("OnFileCanceled:", path)
	// remove temp file if exists
	err := os.Remove(path)
	if err != nil {
		log.Println("OnFileCanceled: failed to remove temp file:", err)
		return
	}
}

/*
OnConnected is called when another peer comes online.
*/
func (c *chaninterface) OnConnected(address string) {
	// only notify log, nothing else to do for us here
	log.Println("OnConnected:", address[:8])
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
)

func main() {
	messages := make(chan string)

	go func() {
		messages <- "ping"
	}()

	msg := <-messages
	fmt.Println(msg)

	nextExample()
}

func nextExample() {
	log.Print("nextExample entry...")
	pings := make(chan string, 1)
	pongs := make(chan string, 1)
	ping(pings, "passed message")
	pong(pings, pongs)
	fmt.Println(<-pongs)
}

func ping(pings chan <- string, msg string) {
	pings <- msg
}

func pong(pings <- chan string, pongs chan <- string) {
	msg := <- pings
	pongs <- msg
}
<commit_msg>Add channel pipeline example<commit_after>package main

import (
	"fmt"
	"log"
)

func main() {
	//1
	messages := make(chan string)

	go func() {
		messages <- "ping"
	}()

	msg := <-messages
	fmt.Println(msg)

	//2
	nextExample()

	//3
	c := wibblePostfixer("test", "test2", "test3")
	out := blahBlahPrefixer(c)

	for o := range out {
		fmt.Println(o)
	}

}

func nextExample() {
	log.Print("nextExample entry...")
	pings := make(chan string, 1)
	pongs := make(chan string, 1)
	ping(pings, "passed message")
	pong(pings, pongs)
	fmt.Println(<-pongs)
}

func ping(pings chan <- string, msg string) {
	pings <- msg
}

func pong(pings <- chan string, pongs chan <- string) {
	msg := <-pings
	pongs <- msg
}

func wibblePostfixer(strings ...string) <- chan string {
	out := make(chan string)
	go func() {
		for _, n := range strings {
			out <- n + " wibble"
		}
		close(out)
	}()
	return out
}

func blahBlahPrefixer(in <- chan string) <- chan string {
	out := make(chan string)
	go func() {
		for n := range in {
			out <- "blah blah " + n
		}
		close(out)
	}()
	return out
}
<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package util

import (
	"fmt"

	"github.com/apex/log"
	"github.com/spf13/viper"
)

func PrintConfig(ctx log.Interface, debug bool) {
	prt := ctx.Infof
	if debug {
		prt = ctx.Debugf
	}

	prt("Using config:")
	fmt.Println()
	printKV("config file", viper.ConfigFileUsed())
	printKV("data dir", viper.GetString("data"))
	fmt.Println()

	for key, val := range viper.AllSettings() {
		switch key {
		case "builddate":
			fallthrough
		case "gitcommit":
			fallthrough
		case "gitbranch":
			fallthrough
		case "version":
			continue
		default:
			printKV(key, val)
		}
	}
	fmt.Println()
}

func printKV(key, val interface{}) {
	fmt.Printf("%20s: %s\n", key, val)
}
<commit_msg>Use %v instead of %s in print_config<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package util

import (
	"fmt"

	"github.com/apex/log"
	"github.com/spf13/viper"
)

func PrintConfig(ctx log.Interface, debug bool) {
	prt := ctx.Infof
	if debug {
		prt = ctx.Debugf
	}

	prt("Using config:")
	fmt.Println()
	printKV("config file", viper.ConfigFileUsed())
	printKV("data dir", viper.GetString("data"))
	fmt.Println()

	for key, val := range viper.AllSettings() {
		switch key {
		case "builddate":
			fallthrough
		case "gitcommit":
			fallthrough
		case "gitbranch":
			fallthrough
		case "version":
			continue
		default:
			printKV(key, val)
		}
	}
	fmt.Println()
}

func printKV(key, val interface{}) {
	fmt.Printf("%20s: %v\n", key, val)
}
<|endoftext|>


<commit_before>package deorbit

import (
	"time"

	"github.com/go-kit/kit/log"
	core_v1 "k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/record"

	v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/controller/config"
	"github.com/sapcc/kubernikus/pkg/controller/metrics"
)

type SelfDestructReason string

const (
	APIUnavailable SelfDestructReason = "APIUnavailable"
	DeorbitHanging SelfDestructReason = "DeorbitHanging"

	// If the customer's apiserver is unreachable for this duration, we assume it is
	// already decommissioned, permanently damaged or was never created successfully
	// in the first place.
	APIUnavailableTimeout = 2 * time.Minute

	// After this duration, the deorbiter will utimately give up retrying. This will
	// potentially leave debris in the customer's project in the form of volumes,
	// load balancers or floating IPs.
	DeorbitHangingTimeout = 24 * time.Hour

	// As of this writing services are not yet migrated to make use of
	// finalizers. In effect there's no feedback loop and a deleted service
	// disappears immediately - even though the cloud provider isn't finished yet
	// with the decomissioning of existing load balancers. We guess that it takes
	// around this duration until the clean-up was successful.
	ServiceDeletionGracePeriod = 2 * time.Minute

	// While waiting for deletion use this interval for rechecks
	PollInterval = 15 * time.Second
)

type Deorbiter interface {
	DeletePersistentVolumeClaims() ([]core_v1.PersistentVolumeClaim, error)
	DeleteServices() ([]core_v1.Service, error)
	WaitForPersistentVolumeCleanup() error
	WaitForServiceCleanup() error
	SelfDestruct(SelfDestructReason) error
	IsAPIUnavailableTimeout() bool
	IsDeorbitHangingTimeout() bool
}

type ConcreteDeorbiter struct {
	Kluster *v1.Kluster
	Stop    <-chan struct{}
	Client  kubernetes.Interface
	Logger  log.Logger
}

func NewDeorbiter(kluster *v1.Kluster, stopCh <-chan struct{}, clients config.Clients, recorder record.EventRecorder, logger log.Logger) (Deorbiter, error) {
	client, err := clients.Satellites.ClientFor(kluster)
	if err != nil {
		return nil, err
	}

	var deorbiter Deorbiter
	deorbiter = &ConcreteDeorbiter{kluster, stopCh, client, logger}
	deorbiter = &LoggingDeorbiter{deorbiter, logger}
	deorbiter = &EventingDeorbiter{deorbiter, kluster, recorder}
	deorbiter = &InstrumentingDeorbiter{
		deorbiter,
		metrics.DeorbitOperationsLatency,
		metrics.DeorbitOperationsTotal,
		metrics.DeorbitSuccessfulOperationsTotal,
		metrics.DeorbitFailedOperationsTotal,
	}

	return deorbiter, nil
}

func (d *ConcreteDeorbiter) DeletePersistentVolumeClaims() (deleted []core_v1.PersistentVolumeClaim, err error) {
	deleted = []core_v1.PersistentVolumeClaim{}

	pvcs, err := d.Client.Core().PersistentVolumeClaims(meta_v1.NamespaceAll).List(meta_v1.ListOptions{})
	if err != nil {
		return deleted, err
	}

	for _, pvc := range pvcs.Items {
		if pvc.Status.Phase != core_v1.ClaimBound || pvc.Spec.VolumeName == "" {
			continue
		}

		pv, err := d.Client.Core().PersistentVolumes().Get(pvc.Spec.VolumeName, meta_v1.GetOptions{})
		if err != nil {
			return deleted, err
		}

		if pv.Spec.Cinder == nil && pv.Spec.CSI.Driver != "cinder.csi.openstack.org" {
			continue
		}
		deleted = append(deleted, pvc)

		err = d.Client.Core().PersistentVolumeClaims(pvc.Namespace).Delete(pvc.Name, &meta_v1.DeleteOptions{})
		if err != nil {
			return deleted, err
		}
	}

	return deleted, err
}

func (d *ConcreteDeorbiter) DeleteServices() (deleted []core_v1.Service, err error) {
	deleted = []core_v1.Service{}

	services, err := d.Client.Core().Services(meta_v1.NamespaceAll).List(meta_v1.ListOptions{})
	if err != nil {
		return deleted, err
	}

	for _, service := range services.Items {
		if service.Spec.Type != "LoadBalancer" {
			continue
		}
		deleted = append(deleted, service)

		err = d.Client.Core().Services(service.Namespace).Delete(service.Name, &meta_v1.DeleteOptions{})
		if err != nil {
			return deleted, err
		}
	}

	return deleted, err
}

func (d *ConcreteDeorbiter) WaitForPersistentVolumeCleanup() (err error) {
	done, err := d.isPersistentVolumesCleanupFinished()
	if err != nil {
		return err
	}

	if done {
		return nil
	}

	return wait.PollUntil(PollInterval, d.isPersistentVolumesCleanupFinished, d.Stop)
}

func (d *ConcreteDeorbiter) WaitForServiceCleanup() (err error) {
	done, err := d.isServiceCleanupFinished()
	if err != nil {
		return err
	}

	if done {
		return nil
	}

	return wait.PollUntil(PollInterval, d.isServiceCleanupFinished, d.Stop)
}

func (d *ConcreteDeorbiter) SelfDestruct(reason SelfDestructReason) (err error) {
	// Self-Destruct ironically does nothing
	return nil
}

func (d *ConcreteDeorbiter) IsAPIUnavailableTimeout() bool {
	return d.Kluster.ObjectMeta.DeletionTimestamp.Add(APIUnavailableTimeout).Before(time.Now())
}

func (d *ConcreteDeorbiter) IsDeorbitHangingTimeout() bool {
	return d.Kluster.ObjectMeta.DeletionTimestamp.Add(DeorbitHangingTimeout).Before(time.Now())
}

func (d *ConcreteDeorbiter) isPersistentVolumesCleanupFinished() (bool, error) {
	pvs, err := d.Client.Core().PersistentVolumes().List(meta_v1.ListOptions{})
	if err != nil {
		return false, err
	}

	for _, pv := range pvs.Items {
		//ignore failed PVs
		if pv.Status.Phase == core_v1.VolumeFailed {
			continue
		}
		if pv.Spec.PersistentVolumeSource.Cinder != nil || pv.Spec.PersistentVolumeSource.CSI.Driver == "cinder.csi.openstack.org" {
			return false, nil
		}
	}

	return true, nil
}

func (d *ConcreteDeorbiter) isServiceCleanupFinished() (bool, error) {
	return d.Kluster.ObjectMeta.DeletionTimestamp.Add(ServiceDeletionGracePeriod).Before(time.Now()), nil
}
<commit_msg>Fix pv cleanup test<commit_after>package deorbit

import (
	"time"

	"github.com/go-kit/kit/log"
	core_v1 "k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/record"

	v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/controller/config"
	"github.com/sapcc/kubernikus/pkg/controller/metrics"
)

type SelfDestructReason string

const (
	APIUnavailable SelfDestructReason = "APIUnavailable"
	DeorbitHanging SelfDestructReason = "DeorbitHanging"

	// If the customer's apiserver is unreachable for this duration, we assume it is
	// already decommissioned, permanently damaged or was never created successfully
	// in the first place.
	APIUnavailableTimeout = 2 * time.Minute

	// After this duration, the deorbiter will utimately give up retrying. This will
	// potentially leave debris in the customer's project in the form of volumes,
	// load balancers or floating IPs.
	DeorbitHangingTimeout = 24 * time.Hour

	// As of this writing services are not yet migrated to make use of
	// finalizers. In effect there's no feedback loop and a deleted service
	// disappears immediately - even though the cloud provider isn't finished yet
	// with the decomissioning of existing load balancers. We guess that it takes
	// around this duration until the clean-up was successful.
	ServiceDeletionGracePeriod = 2 * time.Minute

	// While waiting for deletion use this interval for rechecks
	PollInterval = 15 * time.Second
)

type Deorbiter interface {
	DeletePersistentVolumeClaims() ([]core_v1.PersistentVolumeClaim, error)
	DeleteServices() ([]core_v1.Service, error)
	WaitForPersistentVolumeCleanup() error
	WaitForServiceCleanup() error
	SelfDestruct(SelfDestructReason) error
	IsAPIUnavailableTimeout() bool
	IsDeorbitHangingTimeout() bool
}

type ConcreteDeorbiter struct {
	Kluster *v1.Kluster
	Stop    <-chan struct{}
	Client  kubernetes.Interface
	Logger  log.Logger
}

func NewDeorbiter(kluster *v1.Kluster, stopCh <-chan struct{}, clients config.Clients, recorder record.EventRecorder, logger log.Logger) (Deorbiter, error) {
	client, err := clients.Satellites.ClientFor(kluster)
	if err != nil {
		return nil, err
	}

	var deorbiter Deorbiter
	deorbiter = &ConcreteDeorbiter{kluster, stopCh, client, logger}
	deorbiter = &LoggingDeorbiter{deorbiter, logger}
	deorbiter = &EventingDeorbiter{deorbiter, kluster, recorder}
	deorbiter = &InstrumentingDeorbiter{
		deorbiter,
		metrics.DeorbitOperationsLatency,
		metrics.DeorbitOperationsTotal,
		metrics.DeorbitSuccessfulOperationsTotal,
		metrics.DeorbitFailedOperationsTotal,
	}

	return deorbiter, nil
}

func (d *ConcreteDeorbiter) DeletePersistentVolumeClaims() (deleted []core_v1.PersistentVolumeClaim, err error) {
	deleted = []core_v1.PersistentVolumeClaim{}

	pvcs, err := d.Client.Core().PersistentVolumeClaims(meta_v1.NamespaceAll).List(meta_v1.ListOptions{})
	if err != nil {
		return deleted, err
	}

	for _, pvc := range pvcs.Items {
		if pvc.Status.Phase != core_v1.ClaimBound || pvc.Spec.VolumeName == "" {
			continue
		}

		pv, err := d.Client.Core().PersistentVolumes().Get(pvc.Spec.VolumeName, meta_v1.GetOptions{})
		if err != nil {
			return deleted, err
		}

		if pv.Spec.Cinder == nil && pv.Spec.CSI.Driver != "cinder.csi.openstack.org" {
			continue
		}
		deleted = append(deleted, pvc)

		err = d.Client.Core().PersistentVolumeClaims(pvc.Namespace).Delete(pvc.Name, &meta_v1.DeleteOptions{})
		if err != nil {
			return deleted, err
		}
	}

	return deleted, err
}

func (d *ConcreteDeorbiter) DeleteServices() (deleted []core_v1.Service, err error) {
	deleted = []core_v1.Service{}

	services, err := d.Client.Core().Services(meta_v1.NamespaceAll).List(meta_v1.ListOptions{})
	if err != nil {
		return deleted, err
	}

	for _, service := range services.Items {
		if service.Spec.Type != "LoadBalancer" {
			continue
		}
		deleted = append(deleted, service)

		err = d.Client.Core().Services(service.Namespace).Delete(service.Name, &meta_v1.DeleteOptions{})
		if err != nil {
			return deleted, err
		}
	}

	return deleted, err
}

func (d *ConcreteDeorbiter) WaitForPersistentVolumeCleanup() (err error) {
	done, err := d.isPersistentVolumesCleanupFinished()
	if err != nil {
		return err
	}

	if done {
		return nil
	}

	return wait.PollUntil(PollInterval, d.isPersistentVolumesCleanupFinished, d.Stop)
}

func (d *ConcreteDeorbiter) WaitForServiceCleanup() (err error) {
	done, err := d.isServiceCleanupFinished()
	if err != nil {
		return err
	}

	if done {
		return nil
	}

	return wait.PollUntil(PollInterval, d.isServiceCleanupFinished, d.Stop)
}

func (d *ConcreteDeorbiter) SelfDestruct(reason SelfDestructReason) (err error) {
	// Self-Destruct ironically does nothing
	return nil
}

func (d *ConcreteDeorbiter) IsAPIUnavailableTimeout() bool {
	return d.Kluster.ObjectMeta.DeletionTimestamp.Add(APIUnavailableTimeout).Before(time.Now())
}

func (d *ConcreteDeorbiter) IsDeorbitHangingTimeout() bool {
	return d.Kluster.ObjectMeta.DeletionTimestamp.Add(DeorbitHangingTimeout).Before(time.Now())
}

func (d *ConcreteDeorbiter) isPersistentVolumesCleanupFinished() (bool, error) {
	pvs, err := d.Client.Core().PersistentVolumes().List(meta_v1.ListOptions{})
	if err != nil {
		return false, err
	}

	for _, pv := range pvs.Items {
		//ignore failed PVs
		if pv.Status.Phase == core_v1.VolumeFailed {
			continue
		}
		if pv.Spec.PersistentVolumeSource.Cinder != nil || (pv.Spec.PersistentVolumeSource.CSI != nil && pv.Spec.PersistentVolumeSource.CSI.Driver == "cinder.csi.openstack.org") {
			return false, nil
		}
	}

	return true, nil
}

func (d *ConcreteDeorbiter) isServiceCleanupFinished() (bool, error) {
	return d.Kluster.ObjectMeta.DeletionTimestamp.Add(ServiceDeletionGracePeriod).Before(time.Now()), nil
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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 middleware

import (
	"net/http"

	"github.com/google/exposure-notifications-server/pkg/logging"
	"github.com/google/exposure-notifications-verification-server/pkg/controller"
	"go.uber.org/zap"

	"github.com/gorilla/mux"
)

// PopulateLogger populates the logger onto the context.
func PopulateLogger(originalLogger *zap.SugaredLogger) mux.MiddlewareFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx := r.Context()

			logger := originalLogger

			// If there's a request ID, set that on the logger.
			if id := controller.RequestIDFromContext(ctx); id != "" {
				logger = logger.With("request_id", id)
			}

			ctx = logging.WithLogger(ctx, logger)
			r = r.Clone(ctx)

			next.ServeHTTP(w, r)
		})
	}
}
<commit_msg>Extract trace context if present and add to logger (#1422)<commit_after>// Copyright 2020 Google LLC
//
// 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 middleware

import (
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/google/exposure-notifications-server/pkg/logging"
	"github.com/google/exposure-notifications-verification-server/pkg/controller"
	"go.uber.org/zap"

	"github.com/gorilla/mux"
)

const (
	// googleCloudTraceHeader is the header with trace data.
	googleCloudTraceHeader = "X-Cloud-Trace-Context"

	// googleCloudTraceKey is the key in the structured log where trace information
	// is expected to be present.
	googleCloudTraceKey = "logging.googleapis.com/trace"
)

// googleCloudProjectID is the project id, populated by Terraform during service
// deployment.
var googleCloudProjectID = os.Getenv("PROJECT_ID")

// PopulateLogger populates the logger onto the context.
func PopulateLogger(originalLogger *zap.SugaredLogger) mux.MiddlewareFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx := r.Context()

			logger := originalLogger

			// If there's a request ID, set that on the logger.
			if id := controller.RequestIDFromContext(ctx); id != "" {
				logger = logger.With("request_id", id)
			}

			// On Google Cloud, extract the trace context and add it to the logger.
			if v := r.Header.Get(googleCloudTraceHeader); v != "" && googleCloudProjectID != "" {
				parts := strings.Split(v, "/")
				if len(parts) > 0 && len(parts[0]) > 0 {
					val := fmt.Sprintf("projects/%s/traces/%s", googleCloudProjectID, parts[0])
					logger = logger.With(googleCloudTraceKey, val)
				}
			}

			ctx = logging.WithLogger(ctx, logger)
			r = r.Clone(ctx)

			next.ServeHTTP(w, r)
		})
	}
}
<|endoftext|>


<commit_before>/*
 Copyright 2020 The Knative Authors
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

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

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

*/

package generator

import (
	"kourier/pkg/envoy"
	"kourier/pkg/knative"
	"time"

	endpoint "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"

	"go.uber.org/zap"
	kubev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	kubeclient "k8s.io/client-go/kubernetes"
	corev1listers "k8s.io/client-go/listers/core/v1"
	"knative.dev/pkg/tracker"
	"knative.dev/serving/pkg/apis/networking/v1alpha1"

	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
	route "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
)

type IngressTranslator struct {
	kubeclient      kubeclient.Interface
	endpointsLister corev1listers.EndpointsLister
	localDomainName string
	tracker         tracker.Interface
	logger          *zap.SugaredLogger
}

type translatedIngress struct {
	ingressName          string
	ingressNamespace     string
	sniMatches           []*envoy.SNIMatch
	routes               []*route.Route
	clusters             []*v2.Cluster
	externalVirtualHosts []*route.VirtualHost
	internalVirtualHosts []*route.VirtualHost
}

func NewIngressTranslator(kubeclient kubeclient.Interface, endpointsLister corev1listers.EndpointsLister, localDomainName string, tracker tracker.Interface,
	logger *zap.SugaredLogger) IngressTranslator {
	return IngressTranslator{
		kubeclient:      kubeclient,
		endpointsLister: endpointsLister,
		localDomainName: localDomainName,
		tracker:         tracker,
		logger:          logger,
	}
}

func (translator *IngressTranslator) translateIngress(ingress *v1alpha1.Ingress, extAuthzEnabled bool) (*translatedIngress, error) {
	res := &translatedIngress{
		ingressName:      ingress.Name,
		ingressNamespace: ingress.Namespace,
	}

	for _, ingressTLS := range ingress.Spec.TLS {
		sniMatch, err := sniMatchFromIngressTLS(ingressTLS, translator.kubeclient)

		if err != nil {
			translator.logger.Errorf("%s", err)

			// We need to propagate this error to the reconciler so the current
			// event can be retried. This error might be caused because the
			// secrets referenced in the TLS section of the spec do not exist
			// yet. That's expected when auto TLS is configured.
			// See the "TestPerKsvcCert_localCA" test in Knative Serving. It's a
			// test that fails if this error is not propagated:
			// https://github.com/knative/serving/blob/571e4db2392839082c559870ea8d4b72ef61e59d/test/e2e/autotls/auto_tls_test.go#L68
			return nil, err
		}
		res.sniMatches = append(res.sniMatches, sniMatch)
	}

	for _, rule := range ingress.Spec.Rules {

		var ruleRoute []*route.Route

		for _, httpPath := range rule.HTTP.Paths {

			path := "/"
			if httpPath.Path != "" {
				path = httpPath.Path
			}

			var wrs []*route.WeightedCluster_ClusterWeight

			for _, split := range httpPath.Splits {
				headersSplit := split.AppendHeaders

				ref := tracker.Reference{
					Kind:       "Endpoints",
					APIVersion: "v1",
					Namespace:  ingress.Namespace,
					Name:       split.ServiceName,
				}

				err := translator.tracker.TrackReference(ref, ingress)
				if err != nil {
					translator.logger.Errorf("%s", err)
					break
				}

				endpoints, err := translator.endpointsLister.Endpoints(split.ServiceNamespace).Get(split.ServiceName)
				if apierrors.IsNotFound(err) {
					translator.logger.Infof("Endpoints '%s/%s' not yet created", split.ServiceNamespace, split.ServiceName)
					break
				} else if err != nil {
					translator.logger.Errorf("Failed to fetch endpoints '%s/%s': %v", split.ServiceNamespace, split.ServiceName, err)
					break
				}

				service, err := translator.kubeclient.CoreV1().Services(split.ServiceNamespace).Get(split.ServiceName, metav1.GetOptions{})
				if apierrors.IsNotFound(err) {
					translator.logger.Infof("Service '%s/%s' not yet created", split.ServiceNamespace, split.ServiceName)
					break
				} else if err != nil {
					translator.logger.Errorf("Failed to fetch service '%s/%s': %v", split.ServiceNamespace, split.ServiceName, err)
					break
				}

				var targetPort int32
				http2 := false
				for _, port := range service.Spec.Ports {
					if port.Port == split.ServicePort.IntVal || port.Name == split.ServicePort.StrVal {
						targetPort = port.TargetPort.IntVal
						http2 = port.Name == "http2" || port.Name == "h2c"
					}
				}

				publicLbEndpoints := lbEndpointsForKubeEndpoints(endpoints, targetPort)

				connectTimeout := 5 * time.Second
				cluster := envoy.NewCluster(split.ServiceName+path, connectTimeout, publicLbEndpoints, http2, v2.Cluster_STATIC)

				res.clusters = append(res.clusters, cluster)

				weightedCluster := envoy.NewWeightedCluster(split.ServiceName+path, uint32(split.Percent), headersSplit)

				wrs = append(wrs, weightedCluster)
			}

			if len(wrs) != 0 {
				r := createRouteForRevision(ingress.Name, ingress.Namespace, httpPath, wrs)
				ruleRoute = append(ruleRoute, r)
				res.routes = append(res.routes, r)
			}

		}

		if len(ruleRoute) == 0 {
			// Return nothing if there are not routes to generate.
			return nil, nil
		}

		externalDomains := knative.ExternalDomains(rule, translator.localDomainName)

		// External should also be accessible internally
		internalDomains := append(knative.InternalDomains(rule, translator.localDomainName), externalDomains...)

		var virtualHost, internalVirtualHost route.VirtualHost
		if extAuthzEnabled {

			visibility := ingress.Spec.Visibility
			if visibility == "" { // Needed because visibility is optional
				visibility = v1alpha1.IngressVisibilityClusterLocal
			}

			ContextExtensions := map[string]string{
				"client":     "kourier",
				"visibility": string(visibility),
			}

			ContextExtensions = mergeMapString(ContextExtensions, ingress.GetLabels())

			virtualHost = envoy.NewVirtualHostWithExtAuthz(ingress.Name, ContextExtensions, externalDomains, ruleRoute)
			internalVirtualHost = envoy.NewVirtualHostWithExtAuthz(ingress.Name, ContextExtensions, internalDomains,
				ruleRoute)
		} else {
			virtualHost = envoy.NewVirtualHost(ingress.GetName(), externalDomains, ruleRoute)
			internalVirtualHost = envoy.NewVirtualHost(ingress.GetName(), internalDomains, ruleRoute)
		}

		if knative.RuleIsExternal(rule, ingress.Spec.Visibility) {
			res.externalVirtualHosts = append(res.externalVirtualHosts, &virtualHost)
		}

		res.internalVirtualHosts = append(res.internalVirtualHosts, &internalVirtualHost)
	}

	return res, nil
}

func lbEndpointsForKubeEndpoints(kubeEndpoints *kubev1.Endpoints, targetPort int32) (publicLbEndpoints []*endpoint.LbEndpoint) {
	for _, subset := range kubeEndpoints.Subsets {
		for _, address := range subset.Addresses {
			lbEndpoint := envoy.NewLBEndpoint(address.IP, uint32(targetPort))
			publicLbEndpoints = append(publicLbEndpoints, lbEndpoint)
		}
	}

	return publicLbEndpoints
}

func createRouteForRevision(ingressName string, ingressNamespace string, httpPath v1alpha1.HTTPIngressPath, wrs []*route.WeightedCluster_ClusterWeight) *route.Route {
	routeName := ingressName + "_" + ingressNamespace + "_" + httpPath.Path

	path := "/"
	if httpPath.Path != "" {
		path = httpPath.Path
	}

	var routeTimeout time.Duration
	if httpPath.Timeout != nil {
		routeTimeout = httpPath.Timeout.Duration
	}

	attempts := 0
	var perTryTimeout time.Duration
	if httpPath.Retries != nil {
		attempts = httpPath.Retries.Attempts

		if httpPath.Retries.PerTryTimeout != nil {
			perTryTimeout = httpPath.Retries.PerTryTimeout.Duration
		}
	}

	return envoy.NewRoute(
		routeName, path, wrs, routeTimeout, uint32(attempts), perTryTimeout, httpPath.AppendHeaders,
	)
}

func sniMatchFromIngressTLS(ingressTLS v1alpha1.IngressTLS, kubeClient kubeclient.Interface) (*envoy.SNIMatch, error) {
	certChain, privateKey, err := sslCreds(
		kubeClient, ingressTLS.SecretNamespace, ingressTLS.SecretName,
	)

	if err != nil {
		return nil, err
	}

	sniMatch := envoy.NewSNIMatch(ingressTLS.Hosts, certChain, privateKey)
	return &sniMatch, nil
}

func mergeMapString(a, b map[string]string) map[string]string {
	merged := make(map[string]string)
	for k, v := range a {
		merged[k] = v
	}
	for k, v := range b {
		merged[k] = v
	}
	return merged
}
<commit_msg>Make fatal errors more explicit.<commit_after>/*
 Copyright 2020 The Knative Authors
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

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

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

*/

package generator

import (
	"fmt"
	"kourier/pkg/envoy"
	"kourier/pkg/knative"
	"time"

	endpoint "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"

	"go.uber.org/zap"
	kubev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	kubeclient "k8s.io/client-go/kubernetes"
	corev1listers "k8s.io/client-go/listers/core/v1"
	"knative.dev/pkg/tracker"
	"knative.dev/serving/pkg/apis/networking/v1alpha1"

	v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
	route "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
)

type IngressTranslator struct {
	kubeclient      kubeclient.Interface
	endpointsLister corev1listers.EndpointsLister
	localDomainName string
	tracker         tracker.Interface
	logger          *zap.SugaredLogger
}

type translatedIngress struct {
	ingressName          string
	ingressNamespace     string
	sniMatches           []*envoy.SNIMatch
	routes               []*route.Route
	clusters             []*v2.Cluster
	externalVirtualHosts []*route.VirtualHost
	internalVirtualHosts []*route.VirtualHost
}

func NewIngressTranslator(kubeclient kubeclient.Interface, endpointsLister corev1listers.EndpointsLister, localDomainName string, tracker tracker.Interface,
	logger *zap.SugaredLogger) IngressTranslator {
	return IngressTranslator{
		kubeclient:      kubeclient,
		endpointsLister: endpointsLister,
		localDomainName: localDomainName,
		tracker:         tracker,
		logger:          logger,
	}
}

func (translator *IngressTranslator) translateIngress(ingress *v1alpha1.Ingress, extAuthzEnabled bool) (*translatedIngress, error) {
	res := &translatedIngress{
		ingressName:      ingress.Name,
		ingressNamespace: ingress.Namespace,
	}

	for _, ingressTLS := range ingress.Spec.TLS {
		sniMatch, err := sniMatchFromIngressTLS(ingressTLS, translator.kubeclient)

		if err != nil {
			translator.logger.Errorf("%s", err)

			// We need to propagate this error to the reconciler so the current
			// event can be retried. This error might be caused because the
			// secrets referenced in the TLS section of the spec do not exist
			// yet. That's expected when auto TLS is configured.
			// See the "TestPerKsvcCert_localCA" test in Knative Serving. It's a
			// test that fails if this error is not propagated:
			// https://github.com/knative/serving/blob/571e4db2392839082c559870ea8d4b72ef61e59d/test/e2e/autotls/auto_tls_test.go#L68
			return nil, err
		}
		res.sniMatches = append(res.sniMatches, sniMatch)
	}

	for _, rule := range ingress.Spec.Rules {

		var ruleRoute []*route.Route

		for _, httpPath := range rule.HTTP.Paths {

			path := "/"
			if httpPath.Path != "" {
				path = httpPath.Path
			}

			var wrs []*route.WeightedCluster_ClusterWeight

			for _, split := range httpPath.Splits {
				headersSplit := split.AppendHeaders

				ref := tracker.Reference{
					Kind:       "Endpoints",
					APIVersion: "v1",
					Namespace:  ingress.Namespace,
					Name:       split.ServiceName,
				}

				err := translator.tracker.TrackReference(ref, ingress)
				if err != nil {
					return nil, fmt.Errorf("could not track reference: %w", err)
				}

				endpoints, err := translator.endpointsLister.Endpoints(split.ServiceNamespace).Get(split.ServiceName)
				if apierrors.IsNotFound(err) {
					translator.logger.Warnf("Endpoints '%s/%s' not yet created", split.ServiceNamespace, split.ServiceName)
					break
				} else if err != nil {
					return nil, fmt.Errorf("failed to fetch endpoints '%s/%s': %w", split.ServiceNamespace, split.ServiceName, err)
				}

				service, err := translator.kubeclient.CoreV1().Services(split.ServiceNamespace).Get(split.ServiceName, metav1.GetOptions{})
				if apierrors.IsNotFound(err) {
					translator.logger.Warnf("Service '%s/%s' not yet created", split.ServiceNamespace, split.ServiceName)
					break
				} else if err != nil {
					return nil, fmt.Errorf("failed to fetch service '%s/%s': %w", split.ServiceNamespace, split.ServiceName, err)
				}

				var targetPort int32
				http2 := false
				for _, port := range service.Spec.Ports {
					if port.Port == split.ServicePort.IntVal || port.Name == split.ServicePort.StrVal {
						targetPort = port.TargetPort.IntVal
						http2 = port.Name == "http2" || port.Name == "h2c"
					}
				}

				publicLbEndpoints := lbEndpointsForKubeEndpoints(endpoints, targetPort)

				connectTimeout := 5 * time.Second
				cluster := envoy.NewCluster(split.ServiceName+path, connectTimeout, publicLbEndpoints, http2, v2.Cluster_STATIC)

				res.clusters = append(res.clusters, cluster)

				weightedCluster := envoy.NewWeightedCluster(split.ServiceName+path, uint32(split.Percent), headersSplit)

				wrs = append(wrs, weightedCluster)
			}

			if len(wrs) != 0 {
				r := createRouteForRevision(ingress.Name, ingress.Namespace, httpPath, wrs)
				ruleRoute = append(ruleRoute, r)
				res.routes = append(res.routes, r)
			}

		}

		if len(ruleRoute) == 0 {
			// Return nothing if there are not routes to generate.
			return nil, nil
		}

		externalDomains := knative.ExternalDomains(rule, translator.localDomainName)

		// External should also be accessible internally
		internalDomains := append(knative.InternalDomains(rule, translator.localDomainName), externalDomains...)

		var virtualHost, internalVirtualHost route.VirtualHost
		if extAuthzEnabled {

			visibility := ingress.Spec.Visibility
			if visibility == "" { // Needed because visibility is optional
				visibility = v1alpha1.IngressVisibilityClusterLocal
			}

			ContextExtensions := map[string]string{
				"client":     "kourier",
				"visibility": string(visibility),
			}

			ContextExtensions = mergeMapString(ContextExtensions, ingress.GetLabels())

			virtualHost = envoy.NewVirtualHostWithExtAuthz(ingress.Name, ContextExtensions, externalDomains, ruleRoute)
			internalVirtualHost = envoy.NewVirtualHostWithExtAuthz(ingress.Name, ContextExtensions, internalDomains,
				ruleRoute)
		} else {
			virtualHost = envoy.NewVirtualHost(ingress.GetName(), externalDomains, ruleRoute)
			internalVirtualHost = envoy.NewVirtualHost(ingress.GetName(), internalDomains, ruleRoute)
		}

		if knative.RuleIsExternal(rule, ingress.Spec.Visibility) {
			res.externalVirtualHosts = append(res.externalVirtualHosts, &virtualHost)
		}

		res.internalVirtualHosts = append(res.internalVirtualHosts, &internalVirtualHost)
	}

	return res, nil
}

func lbEndpointsForKubeEndpoints(kubeEndpoints *kubev1.Endpoints, targetPort int32) (publicLbEndpoints []*endpoint.LbEndpoint) {
	for _, subset := range kubeEndpoints.Subsets {
		for _, address := range subset.Addresses {
			lbEndpoint := envoy.NewLBEndpoint(address.IP, uint32(targetPort))
			publicLbEndpoints = append(publicLbEndpoints, lbEndpoint)
		}
	}

	return publicLbEndpoints
}

func createRouteForRevision(ingressName string, ingressNamespace string, httpPath v1alpha1.HTTPIngressPath, wrs []*route.WeightedCluster_ClusterWeight) *route.Route {
	routeName := ingressName + "_" + ingressNamespace + "_" + httpPath.Path

	path := "/"
	if httpPath.Path != "" {
		path = httpPath.Path
	}

	var routeTimeout time.Duration
	if httpPath.Timeout != nil {
		routeTimeout = httpPath.Timeout.Duration
	}

	attempts := 0
	var perTryTimeout time.Duration
	if httpPath.Retries != nil {
		attempts = httpPath.Retries.Attempts

		if httpPath.Retries.PerTryTimeout != nil {
			perTryTimeout = httpPath.Retries.PerTryTimeout.Duration
		}
	}

	return envoy.NewRoute(
		routeName, path, wrs, routeTimeout, uint32(attempts), perTryTimeout, httpPath.AppendHeaders,
	)
}

func sniMatchFromIngressTLS(ingressTLS v1alpha1.IngressTLS, kubeClient kubeclient.Interface) (*envoy.SNIMatch, error) {
	certChain, privateKey, err := sslCreds(
		kubeClient, ingressTLS.SecretNamespace, ingressTLS.SecretName,
	)

	if err != nil {
		return nil, err
	}

	sniMatch := envoy.NewSNIMatch(ingressTLS.Hosts, certChain, privateKey)
	return &sniMatch, nil
}

func mergeMapString(a, b map[string]string) map[string]string {
	merged := make(map[string]string)
	for k, v := range a {
		merged[k] = v
	}
	for k, v := range b {
		merged[k] = v
	}
	return merged
}
<|endoftext|>


<commit_before>/*
Copyright 2019 Google LLC

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 pubsub

import (
	"context"
	"errors"
	"time"

	"go.uber.org/zap"
	v1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"knative.dev/pkg/kmeta"

	ops "github.com/google/knative-gcp/pkg/operations"
	operations "github.com/google/knative-gcp/pkg/operations/pubsub"
	"github.com/google/knative-gcp/pkg/reconciler"
)

const (
	// ReconcilerName is the name of the reconciler
	ReconcilerName = "PubSub"
)

// Reconciler implements controller.Reconciler for Channel resources.
type PubSubBase struct {
	*reconciler.Base

	TopicOpsImage        string
	SubscriptionOpsImage string
}

func (c *PubSubBase) EnsureSubscriptionExists(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic, subscription string) (ops.OpsJobStatus, error) {
	return c.ensureSubscriptionJob(ctx, operations.SubArgs{
		Image:          c.SubscriptionOpsImage,
		Action:         ops.ActionExists,
		ProjectID:      project,
		TopicID:        topic,
		SubscriptionID: subscription,
		Secret:         secret,
		Owner:          owner,
	})
}

func (c *PubSubBase) EnsureSubscriptionCreated(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic, subscription string, ackDeadline time.Duration, retainAcked bool, retainDuration time.Duration) (ops.OpsJobStatus, error) {
	return c.ensureSubscriptionJob(ctx, operations.SubArgs{
		Image:               c.SubscriptionOpsImage,
		Action:              ops.ActionCreate,
		ProjectID:           project,
		TopicID:             topic,
		SubscriptionID:      subscription,
		AckDeadline:         ackDeadline,
		RetainAckedMessages: retainAcked,
		RetentionDuration:   retainDuration,
		Secret:              secret,
		Owner:               owner,
	})
}

func (c *PubSubBase) EnsureSubscriptionDeleted(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic, subscription string) (ops.OpsJobStatus, error) {
	return c.ensureSubscriptionJob(ctx, operations.SubArgs{
		Image:          c.SubscriptionOpsImage,
		Action:         ops.ActionDelete,
		ProjectID:      project,
		TopicID:        topic,
		SubscriptionID: subscription,
		Secret:         secret,
		Owner:          owner,
	})
}

func (c *PubSubBase) EnsureTopicExists(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic string) (ops.OpsJobStatus, error) {
	return c.ensureTopicJob(ctx, operations.TopicArgs{
		Image:     c.TopicOpsImage,
		Action:    ops.ActionExists,
		ProjectID: project,
		TopicID:   topic,
		Secret:    secret,
		Owner:     owner,
	})
}

func (c *PubSubBase) EnsureTopicCreated(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic string) (ops.OpsJobStatus, error) {
	return c.ensureTopicJob(ctx, operations.TopicArgs{
		Image:     c.TopicOpsImage,
		Action:    ops.ActionCreate,
		ProjectID: project,
		TopicID:   topic,
		Secret:    secret,
		Owner:     owner,
	})
}

func (c *PubSubBase) EnsureTopicDeleted(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic string) (ops.OpsJobStatus, error) {
	return c.ensureTopicJob(ctx, operations.TopicArgs{
		Image:     c.TopicOpsImage,
		Action:    ops.ActionDelete,
		ProjectID: project,
		TopicID:   topic,
		Secret:    secret,
		Owner:     owner,
	})
}

func (c *PubSubBase) ensureTopicJob(ctx context.Context, args operations.TopicArgs) (ops.OpsJobStatus, error) {
	job, err := c.getJob(ctx, args.Owner.GetObjectMeta(), labels.SelectorFromSet(operations.TopicJobLabels(args.Owner, args.Action)))
	// If the resource doesn't exist, we'll create it
	if apierrs.IsNotFound(err) {
		c.Logger.Debugw("Job not found, creating.")

		job = operations.NewTopicOps(args)

		job, err := c.KubeClientSet.BatchV1().Jobs(args.Owner.GetObjectMeta().GetNamespace()).Create(job)
		if err != nil || job == nil {
			c.Logger.Debugw("Failed to create Job.", zap.Error(err))
			return ops.OpsJobCreateFailed, nil
		}

		c.Logger.Debugw("Created Job.")
		return ops.OpsJobCreated, nil
	} else if err != nil {
		c.Logger.Debugw("Failed to get Job.", zap.Error(err))
		return ops.OpsJobGetFailed, err
	}

	if ops.IsJobComplete(job) {
		c.Logger.Debugw("Job is complete.")
		if ops.IsJobSucceeded(job) {
			return ops.OpsJobCompleteSuccessful, nil
		} else if ops.IsJobFailed(job) {
			return ops.OpsJobCompleteFailed, errors.New(ops.JobFailedMessage(job))
		}
	}
	c.Logger.Debug("Job still active.", zap.Any("job", job))
	return ops.OpsJobOngoing, nil
}

func (c *PubSubBase) ensureSubscriptionJob(ctx context.Context, args operations.SubArgs) (ops.OpsJobStatus, error) {
	job, err := c.getJob(ctx, args.Owner.GetObjectMeta(), labels.SelectorFromSet(operations.SubscriptionJobLabels(args.Owner, args.Action)))
	// If the resource doesn't exist, we'll create it
	if apierrs.IsNotFound(err) {
		c.Logger.Debugw("Job not found, creating.")

		args.Image = c.SubscriptionOpsImage

		job = operations.NewSubscriptionOps(args)

		job, err := c.KubeClientSet.BatchV1().Jobs(args.Owner.GetObjectMeta().GetNamespace()).Create(job)
		if err != nil || job == nil {
			c.Logger.Debugw("Failed to create Job.", zap.Error(err))
			return ops.OpsJobCreateFailed, nil
		}

		c.Logger.Debugw("Created Job.")
		return ops.OpsJobCreated, nil
	} else if err != nil {
		c.Logger.Debugw("Failed to get Job.", zap.Error(err))
		return ops.OpsJobGetFailed, err
	}

	if ops.IsJobComplete(job) {
		c.Logger.Debugw("Job is complete.")
		if ops.IsJobSucceeded(job) {
			return ops.OpsJobCompleteSuccessful, nil
		} else if ops.IsJobFailed(job) {
			return ops.OpsJobCompleteFailed, errors.New(ops.JobFailedMessage(job))
		}
	}
	c.Logger.Debug("Job still active.", zap.Any("job", job))
	return ops.OpsJobOngoing, nil
}

func (r *PubSubBase) getJob(ctx context.Context, owner metav1.Object, ls labels.Selector) (*v1.Job, error) {
	list, err := r.KubeClientSet.BatchV1().Jobs(owner.GetNamespace()).List(metav1.ListOptions{
		LabelSelector: ls.String(),
	})
	if err != nil {
		return nil, err
	}

	for _, i := range list.Items {
		if metav1.IsControlledBy(&i, owner) {
			return &i, nil
		}
	}

	return nil, apierrs.NewNotFound(schema.GroupResource{}, "")
}
<commit_msg>better errors, return err instead of nil on failures<commit_after>/*
Copyright 2019 Google LLC

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 pubsub

import (
	"context"
	"errors"
	"time"

	"go.uber.org/zap"
	v1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"knative.dev/pkg/kmeta"

	ops "github.com/google/knative-gcp/pkg/operations"
	operations "github.com/google/knative-gcp/pkg/operations/pubsub"
	"github.com/google/knative-gcp/pkg/reconciler"
)

const (
	// ReconcilerName is the name of the reconciler
	ReconcilerName = "PubSub"
)

// Reconciler implements controller.Reconciler for Channel resources.
type PubSubBase struct {
	*reconciler.Base

	TopicOpsImage        string
	SubscriptionOpsImage string
}

func (c *PubSubBase) EnsureSubscriptionExists(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic, subscription string) (ops.OpsJobStatus, error) {
	return c.ensureSubscriptionJob(ctx, operations.SubArgs{
		Image:          c.SubscriptionOpsImage,
		Action:         ops.ActionExists,
		ProjectID:      project,
		TopicID:        topic,
		SubscriptionID: subscription,
		Secret:         secret,
		Owner:          owner,
	})
}

func (c *PubSubBase) EnsureSubscriptionCreated(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic, subscription string, ackDeadline time.Duration, retainAcked bool, retainDuration time.Duration) (ops.OpsJobStatus, error) {
	return c.ensureSubscriptionJob(ctx, operations.SubArgs{
		Image:               c.SubscriptionOpsImage,
		Action:              ops.ActionCreate,
		ProjectID:           project,
		TopicID:             topic,
		SubscriptionID:      subscription,
		AckDeadline:         ackDeadline,
		RetainAckedMessages: retainAcked,
		RetentionDuration:   retainDuration,
		Secret:              secret,
		Owner:               owner,
	})
}

func (c *PubSubBase) EnsureSubscriptionDeleted(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic, subscription string) (ops.OpsJobStatus, error) {
	return c.ensureSubscriptionJob(ctx, operations.SubArgs{
		Image:          c.SubscriptionOpsImage,
		Action:         ops.ActionDelete,
		ProjectID:      project,
		TopicID:        topic,
		SubscriptionID: subscription,
		Secret:         secret,
		Owner:          owner,
	})
}

func (c *PubSubBase) EnsureTopicExists(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic string) (ops.OpsJobStatus, error) {
	return c.ensureTopicJob(ctx, operations.TopicArgs{
		Image:     c.TopicOpsImage,
		Action:    ops.ActionExists,
		ProjectID: project,
		TopicID:   topic,
		Secret:    secret,
		Owner:     owner,
	})
}

func (c *PubSubBase) EnsureTopicCreated(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic string) (ops.OpsJobStatus, error) {
	return c.ensureTopicJob(ctx, operations.TopicArgs{
		Image:     c.TopicOpsImage,
		Action:    ops.ActionCreate,
		ProjectID: project,
		TopicID:   topic,
		Secret:    secret,
		Owner:     owner,
	})
}

func (c *PubSubBase) EnsureTopicDeleted(ctx context.Context, owner kmeta.OwnerRefable, secret corev1.SecretKeySelector, project, topic string) (ops.OpsJobStatus, error) {
	return c.ensureTopicJob(ctx, operations.TopicArgs{
		Image:     c.TopicOpsImage,
		Action:    ops.ActionDelete,
		ProjectID: project,
		TopicID:   topic,
		Secret:    secret,
		Owner:     owner,
	})
}

func (c *PubSubBase) ensureTopicJob(ctx context.Context, args operations.TopicArgs) (ops.OpsJobStatus, error) {
	job, err := c.getJob(ctx, args.Owner.GetObjectMeta(), labels.SelectorFromSet(operations.TopicJobLabels(args.Owner, args.Action)))
	// If the resource doesn't exist, we'll create it
	if apierrs.IsNotFound(err) {
		c.Logger.Debugw("Job not found, creating.")

		job = operations.NewTopicOps(args)

		job, err := c.KubeClientSet.BatchV1().Jobs(args.Owner.GetObjectMeta().GetNamespace()).Create(job)
		if err != nil || job == nil {
			c.Logger.Errorw("Failed to create Job.", zap.Error(err))
			return ops.OpsJobCreateFailed, err
		}

		c.Logger.Debugw("Created Job.")
		return ops.OpsJobCreated, nil
	} else if err != nil {
		c.Logger.Errorw("Failed to get Job.", zap.Error(err))
		return ops.OpsJobGetFailed, err
	}

	if ops.IsJobComplete(job) {
		c.Logger.Debugw("Job is complete.")
		if ops.IsJobSucceeded(job) {
			return ops.OpsJobCompleteSuccessful, nil
		} else if ops.IsJobFailed(job) {
			return ops.OpsJobCompleteFailed, errors.New(ops.JobFailedMessage(job))
		}
	}
	c.Logger.Debug("Job still active.", zap.Any("job", job))
	return ops.OpsJobOngoing, nil
}

func (c *PubSubBase) ensureSubscriptionJob(ctx context.Context, args operations.SubArgs) (ops.OpsJobStatus, error) {
	job, err := c.getJob(ctx, args.Owner.GetObjectMeta(), labels.SelectorFromSet(operations.SubscriptionJobLabels(args.Owner, args.Action)))
	// If the resource doesn't exist, we'll create it
	if apierrs.IsNotFound(err) {
		c.Logger.Debugw("Job not found, creating.")

		args.Image = c.SubscriptionOpsImage

		job = operations.NewSubscriptionOps(args)

		job, err := c.KubeClientSet.BatchV1().Jobs(args.Owner.GetObjectMeta().GetNamespace()).Create(job)
		if err != nil || job == nil {
			c.Logger.Errorw("Failed to create Job.", zap.Error(err))
			return ops.OpsJobCreateFailed, err
		}

		c.Logger.Debugw("Created Job.")
		return ops.OpsJobCreated, nil
	} else if err != nil {
		c.Logger.Errorw("Failed to get Job.", zap.Error(err))
		return ops.OpsJobGetFailed, err
	}

	if ops.IsJobComplete(job) {
		c.Logger.Debugw("Job is complete.")
		if ops.IsJobSucceeded(job) {
			return ops.OpsJobCompleteSuccessful, nil
		} else if ops.IsJobFailed(job) {
			return ops.OpsJobCompleteFailed, errors.New(ops.JobFailedMessage(job))
		}
	}
	c.Logger.Debug("Job still active.", zap.Any("job", job))
	return ops.OpsJobOngoing, nil
}

func (r *PubSubBase) getJob(ctx context.Context, owner metav1.Object, ls labels.Selector) (*v1.Job, error) {
	list, err := r.KubeClientSet.BatchV1().Jobs(owner.GetNamespace()).List(metav1.ListOptions{
		LabelSelector: ls.String(),
	})
	if err != nil {
		return nil, err
	}

	for _, i := range list.Items {
		if metav1.IsControlledBy(&i, owner) {
			return &i, nil
		}
	}

	return nil, apierrs.NewNotFound(schema.GroupResource{}, "")
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative Authors

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

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

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

/*
This package contains code that translate our TrafficTarget to an
intermediate format that has less semantic.  In particular it deals
with flattening the TrafficTarget to the revision level -- it also
does the grouping of traffic target into their traffic groups.
*/

package traffic
<commit_msg>Update the docs for the traffic package. (#10757)<commit_after>/*
Copyright 2018 The Knative Authors

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

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

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

/*
This package contains the following:
- The types and functions that translate our TrafficTarget to an
	intermediate format that is used to construct Kingress.
	In particular it deals with flattening the TrafficTarget to the
	revision level -- it also does the grouping of traffic target
	into their traffic groups.
- The types and functions that deal with latest revision rollout.
	The types encapsulate the rollout state and its serialization.
	The Rollout type is a state machine that migrates the traffic
	from source revisions to the latest target revision.
*/

package traffic
<|endoftext|>


<commit_before>package payment_test

import (
	"database/sql"
	"github.com/fritzpay/paymentd/pkg/paymentd/payment"
	"github.com/fritzpay/paymentd/pkg/paymentd/payment_method"
	"github.com/fritzpay/paymentd/pkg/paymentd/project"
	"github.com/fritzpay/paymentd/pkg/paymentd/provider"
	"github.com/fritzpay/paymentd/pkg/service"
	paymentService "github.com/fritzpay/paymentd/pkg/service/payment"
	"github.com/fritzpay/paymentd/pkg/testutil"
	. "github.com/smartystreets/goconvey/convey"
	"gopkg.in/inconshreveable/log15.v2"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func WithService(ctx *service.Context, f func(s *paymentService.Service)) func() {
	return func() {
		s, err := paymentService.NewService(ctx)
		So(err, ShouldBeNil)

		f(s)
	}
}

func WithPayment(tx *sql.Tx, f func(p *payment.Payment)) func() {
	return func() {
		pr := &project.Project{
			ID:          1,
			PrincipalID: 1,
		}
		method := &payment_method.Method{
			ProjectID: 1,
			Provider: provider.Provider{
				ID:   1,
				Name: "test",
			},
			MethodKey: "test",
			Created:   time.Now(),
			CreatedBy: "test",
			Status:    payment_method.PaymentMethodStatusActive,
		}
		err := payment_method.InsertPaymentMethodTx(tx, method)
		So(err, ShouldBeNil)
		So(method.Active(), ShouldBeTrue)
		So(method.ID, ShouldNotEqual, 0)

		err = payment_method.InsertPaymentMethodStatusTx(tx, method)
		So(err, ShouldBeNil)

		p := &payment.Payment{
			Created:  time.Now(),
			Ident:    "test001",
			Amount:   1234,
			Subunits: 2,
			Currency: "EUR",
		}
		err = p.SetProject(pr)
		So(err, ShouldBeNil)
		p.Config.SetCountry("DE")
		p.Config.SetLocale("en-US")
		p.Config.SetPaymentMethodID(method.ID)

		err = payment.InsertPaymentTx(tx, p)
		So(err, ShouldBeNil)
		err = payment.InsertPaymentConfigTx(tx, p)
		So(err, ShouldBeNil)

		p, err = payment.PaymentByIDTx(tx, p.PaymentID())
		So(err, ShouldBeNil)

		f(p)
	}
}

func TestPaymentNotification(t *testing.T) {
	Convey("Given a payment db connection", t, testutil.WithPaymentDB(t, func(db *sql.DB) {
		Convey("Given a principal db connection", testutil.WithPrincipalDB(t, func(principalDB *sql.DB) {
			Convey("Given a transaction", func() {
				tx, err := db.Begin()
				So(err, ShouldBeNil)
				Reset(func() {
					err = tx.Rollback()
					So(err, ShouldBeNil)
				})

				Convey("Given a service context", testutil.WithContext(func(ctx *service.Context, logs <-chan *log15.Record) {
					ctx.SetPaymentDB(db, nil)
					ctx.SetPrincipalDB(principalDB, nil)

					Convey("Given a payment service", WithService(ctx, func(s *paymentService.Service) {

						Convey("Given a payment", WithPayment(tx, func(p *payment.Payment) {

							Convey("Given a test HTTP server", func() {
								testSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

								}))

								Reset(func() {
									testSrv.Close()
								})

								So(paymentService.CanCallback(&p.Config), ShouldBeTrue)

								Convey("When the payment has no transaction", func() {
									paymentTx, err := s.PaymentTransaction(tx, p)
									So(err, ShouldEqual, payment.ErrPaymentTransactionNotFound)
									So(paymentTx.Status.Valid(), ShouldBeFalse)

									Convey("When creating a transaction", func() {
										So(s.IsProcessablePayment(p), ShouldBeTrue)
										paymentTx = p.NewTransaction(payment.PaymentStatusOpen)
										err = s.SetPaymentTransaction(tx, paymentTx)

										Convey("It should succeed", func() {
											So(err, ShouldBeNil)
										})

										Convey("A notification should be sent", nil)

										Convey("The notification should contain the transaction", nil)

									})
								})

							})
						}))
					}))
				}))
			})
		}))
	}))
}
<commit_msg>failing notification test: when notifying inside transaction, it would not contain the actual state<commit_after>package payment_test

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"github.com/fritzpay/paymentd/pkg/paymentd/payment"
	"github.com/fritzpay/paymentd/pkg/paymentd/payment_method"
	"github.com/fritzpay/paymentd/pkg/paymentd/project"
	"github.com/fritzpay/paymentd/pkg/service"
	paymentService "github.com/fritzpay/paymentd/pkg/service/payment"
	"github.com/fritzpay/paymentd/pkg/service/payment/notification/v2"
	"github.com/fritzpay/paymentd/pkg/testutil"
	. "github.com/smartystreets/goconvey/convey"
	"gopkg.in/inconshreveable/log15.v2"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func WithService(ctx *service.Context, f func(s *paymentService.Service)) func() {
	return func() {
		s, err := paymentService.NewService(ctx)
		So(err, ShouldBeNil)

		f(s)
	}
}

func WithPayment(tx *sql.Tx, f func(p *payment.Payment)) func() {
	return func() {
		pr := &project.Project{
			ID:          1,
			PrincipalID: 1,
		}
		method, err := payment_method.PaymentMethodByIDTx(tx, 1)
		So(err, ShouldBeNil)
		So(method.Active(), ShouldBeTrue)
		So(method.ID, ShouldNotEqual, 0)

		err = payment_method.InsertPaymentMethodStatusTx(tx, method)
		So(err, ShouldBeNil)

		p := &payment.Payment{
			Created:  time.Now(),
			Ident:    "test001",
			Amount:   1234,
			Subunits: 2,
			Currency: "EUR",
		}
		err = p.SetProject(pr)
		So(err, ShouldBeNil)
		p.Config.SetCountry("DE")
		p.Config.SetLocale("en-US")
		p.Config.SetPaymentMethodID(method.ID)

		err = payment.InsertPaymentTx(tx, p)
		So(err, ShouldBeNil)
		err = payment.InsertPaymentConfigTx(tx, p)
		So(err, ShouldBeNil)

		p, err = payment.PaymentByIDTx(tx, p.PaymentID())
		So(err, ShouldBeNil)

		f(p)
	}
}

func TestPaymentNotification(t *testing.T) {
	Convey("Given a payment db connection", t, testutil.WithPaymentDB(t, func(db *sql.DB) {
		Convey("Given a principal db connection", testutil.WithPrincipalDB(t, func(principalDB *sql.DB) {
			Convey("Given a transaction", func() {
				tx, err := db.Begin()
				So(err, ShouldBeNil)
				Reset(func() {
					err = tx.Rollback()
					So(err, ShouldBeNil)
				})

				Convey("Given a service context", testutil.WithContext(func(ctx *service.Context, logs <-chan *log15.Record) {
					ctx.SetPaymentDB(db, nil)
					ctx.SetPrincipalDB(principalDB, nil)

					Convey("Given a payment service", WithService(ctx, func(s *paymentService.Service) {

						Convey("Given a payment", WithPayment(tx, func(p *payment.Payment) {

							Convey("Given a test HTTP server", func() {
								srvOk := make(chan struct{})
								var req *http.Request
								var body []byte
								testSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
									req = r
									body, err = ioutil.ReadAll(r.Body)
									So(err, ShouldBeNil)
									close(srvOk)
								}))

								Reset(func() {
									testSrv.Close()
								})

								Convey("Given a test callback configuration", func() {
									testPk, err := project.ProjectKeyByKeyDB(principalDB, "testkey")
									So(err, ShouldBeNil)
									So(testPk.IsValid(), ShouldBeTrue)

									p.Config.SetCallbackURL(testSrv.URL)
									p.Config.SetCallbackAPIVersion("2")
									p.Config.SetCallbackProjectKey(testPk.Key)

									So(paymentService.CanCallback(&p.Config), ShouldBeTrue)

									Convey("When the payment has no transaction", func() {
										paymentTx, err := s.PaymentTransaction(tx, p)
										So(err, ShouldEqual, payment.ErrPaymentTransactionNotFound)
										So(paymentTx.Status.Valid(), ShouldBeFalse)

										Convey("When creating a transaction", func() {
											So(s.IsProcessablePayment(p), ShouldBeTrue)
											paymentTx = p.NewTransaction(payment.PaymentStatusOpen)
											err = s.SetPaymentTransaction(tx, paymentTx)

											Convey("It should succeed", func() {
												So(err, ShouldBeNil)
											})

											Convey("A notification should be sent", func() {
												select {
												case <-srvOk:
													So(req, ShouldNotBeNil)
												case <-time.After(time.Second):
													t.Errorf("request timeout on %s", testSrv.URL)
													close(srvOk)
												drain:
													for {
														select {
														case msg := <-logs:
															t.Logf("%v", msg)
														default:
															break drain
														}
													}
												}

												Convey("The notification should contain the transaction", func() {
													not := &notification.Notification{}
													dec := json.NewDecoder(bytes.NewBuffer(body))
													err := dec.Decode(not)
													So(err, ShouldBeNil)

													So(not.TransactionTimestamp, ShouldNotEqual, 0)
													So(not.Status, ShouldEqual, payment.PaymentStatusOpen)
												})
											})
										})
									})
								})
							})
						}))
					}))
				}))
			})
		}))
	}))
}
<|endoftext|>


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

// +build windows

// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
// Putting a terminal into raw mode is the most common requirement:
//
// 	oldState, err := terminal.MakeRaw(0)
// 	if err != nil {
// 	        panic(err)
// 	}
// 	defer terminal.Restore(0, oldState)
package terminal

import (
	"os"

	"golang.org/x/sys/windows"
)

type State struct {
	mode uint32
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
	var st uint32
	err := windows.GetConsoleMode(windows.Handle(fd), &st)
	return err == nil
}

// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}

// GetSize returns the dimensions of the given terminal.
func GetSize(fd int) (width, height int, err error) {
	var info windows.ConsoleScreenBufferInfo
	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
		return 0, 0, err
	}
	return int(info.Size.X), int(info.Size.Y), nil
}

// ReadPassword reads a line of input from a terminal without local echo.  This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	old := st

	st &^= (windows.ENABLE_ECHO_INPUT)
	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
		return nil, err
	}

	defer func() {
		windows.SetConsoleMode(windows.Handle(fd), old)
	}()

	return readPasswordLine(os.NewFile(uintptr(fd), "stdin"))
}
<commit_msg>ssh/terminal: use duplicate handle in ReadPassword<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
// Putting a terminal into raw mode is the most common requirement:
//
// 	oldState, err := terminal.MakeRaw(0)
// 	if err != nil {
// 	        panic(err)
// 	}
// 	defer terminal.Restore(0, oldState)
package terminal

import (
	"os"

	"golang.org/x/sys/windows"
)

type State struct {
	mode uint32
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
	var st uint32
	err := windows.GetConsoleMode(windows.Handle(fd), &st)
	return err == nil
}

// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}

// GetSize returns the dimensions of the given terminal.
func GetSize(fd int) (width, height int, err error) {
	var info windows.ConsoleScreenBufferInfo
	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
		return 0, 0, err
	}
	return int(info.Size.X), int(info.Size.Y), nil
}

// ReadPassword reads a line of input from a terminal without local echo.  This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	old := st

	st &^= (windows.ENABLE_ECHO_INPUT)
	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
		return nil, err
	}

	defer func() {
		windows.SetConsoleMode(windows.Handle(fd), old)
	}()

	var h windows.Handle
	p, _ := windows.GetCurrentProcess()
	if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
		return nil, err
	}

	f := os.NewFile(uintptr(h), "stdin")
	defer f.Close()
	return readPasswordLine(f)
}
<|endoftext|>


<commit_before>package check

import (
	"bytes"
	"os/exec"
)

// License is the check for the existance of a license file
type License struct {
	Dir       string
	Filenames []string
}

// Name returns the name of the display name of the command
func (g License) Name() string {
	return "license"
}

// Weight returns the weight this check has in the overall average
func (g License) Weight() float64 {
	return .10
}

// Percentage returns 0 if no LICENSE, 1 if LICENSE
func (g License) Percentage() (float64, []FileSummary, error) {
	cmd := exec.Command("find", g.Dir, "-maxdepth", "1", "-type", "f", "-name", "LICENSE*")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return 0.0, []FileSummary{}, err
	}
	if out.String() == "" {
		return 0.0, []FileSummary{{"", "http://choosealicense.com/", []Error{}}}, nil
	}

	return 1.0, []FileSummary{}, nil
}

// Description returns the description of License
func (g License) Description() string {
	return "Checks whether your project has a LICENSE file."
}
<commit_msg>fix typo<commit_after>package check

import (
	"bytes"
	"os/exec"
)

// License is the check for the existence of a license file
type License struct {
	Dir       string
	Filenames []string
}

// Name returns the name of the display name of the command
func (g License) Name() string {
	return "license"
}

// Weight returns the weight this check has in the overall average
func (g License) Weight() float64 {
	return .10
}

// Percentage returns 0 if no LICENSE, 1 if LICENSE
func (g License) Percentage() (float64, []FileSummary, error) {
	cmd := exec.Command("find", g.Dir, "-maxdepth", "1", "-type", "f", "-name", "LICENSE*")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return 0.0, []FileSummary{}, err
	}
	if out.String() == "" {
		return 0.0, []FileSummary{{"", "http://choosealicense.com/", []Error{}}}, nil
	}

	return 1.0, []FileSummary{}, nil
}

// Description returns the description of License
func (g License) Description() string {
	return "Checks whether your project has a LICENSE file."
}
<|endoftext|>