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

package redux

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/gyepisam/fileutils"
)

// Redo finds and executes the .do file for the given target.
func (target *File) Redo() error {

	doFilesNotFound, err := target.findDoFile()
	if err != nil {
		return err
	}

	cachedMeta, recordFound, err := target.GetMetadata()
	if err != nil {
		return err
	}

	targetMeta, err := target.NewMetadata()
	if err != nil {
		return err
	}
	targetExists := targetMeta != nil

	if targetExists {
		if recordFound {
			if target.HasDoFile() {
				return target.redoTarget(doFilesNotFound, targetMeta)
			} else if cachedMeta.HasDoFile() {
				return target.Errorf("Missing .do file")
			} else if !targetMeta.Equal(&cachedMeta) {
				return target.redoStatic(IFCHANGE, targetMeta)
			}
		} else {
			if target.HasDoFile() {
				return target.redoTarget(doFilesNotFound, targetMeta)
			} else {
				return target.redoStatic(IFCREATE, targetMeta)
			}
		}
	} else {
		if recordFound {
			// target existed at one point but was deleted...
			if target.HasDoFile() {
				return target.redoTarget(doFilesNotFound, targetMeta)
			} else if cachedMeta.HasDoFile() {
				return target.Errorf("Missing .do file")
			} else {
				// target is a deleted source file. Clean up and fail.
				if err = target.NotifyDependents(IFCHANGE); err != nil {
					return err
				} else if err = target.DeleteMetadata(); err != nil {
					return err
				}
				return fmt.Errorf("Source file %s does not exist", target.Target)
			}
		} else {
			if target.HasDoFile() {
				return target.redoTarget(doFilesNotFound, targetMeta)
			} else {
				return target.Errorf(".do file not found")
			}
		}
	}

	return nil
}

// redoTarget records a target's .do file dependencies, runs the target's do file and notifies dependents.
func (f *File) redoTarget(doFilesNotFound []string, oldMeta *Metadata) error {

	// can't build without a database...
	if f.HasNullDb() {
		return f.ErrUninitialized()
	}

	// Prerequisites will be recreated...
	// Ideally, this could be done within a transaction to allow for rollback
	// in the event of failure.
	if err := f.DeleteAutoPrerequisites(); err != nil {
		return err
	}

	for _, path := range doFilesNotFound {
		relpath := f.Rel(path)
		err := f.PutPrerequisite(AUTO_IFCREATE, MakeHash(relpath), Prerequisite{Path: relpath})
		if err != nil {
			return err
		}
	}

	doFile, err := NewFile(f.RootDir, f.DoFile)
	if err != nil {
		return err
	}

	// metadata needs to be stored twice and is relatively expensive to acquire.
	doMeta, err := doFile.NewMetadata()

	if err != nil {
		return err
	} else if doMeta == nil {
		return doFile.ErrNotFound("redoTarget: doFile.NewMetadata")
	} else if err := doFile.PutMetadata(doMeta); err != nil {
		return err
	}

	relpath := f.Rel(f.DoFile)
	if err := f.PutPrerequisite(AUTO_IFCHANGE, MakeHash(relpath), Prerequisite{relpath, doMeta}); err != nil {
		return err
	}

	if err := f.RunDoFile(); err != nil {
		return err
	}

	// A task script does not produce output and has no dependencies...
	if f.IsTask() {
		return nil
	}

	newMeta, err := f.NewMetadata()
	if err != nil {
		return err
	} else if newMeta == nil {
		return f.ErrNotFound("redoTarget: f.NewMetadata")
	}

	if err := f.PutMetadata(newMeta); err != nil {
		return err
	}

	if err := f.DeleteMustRebuild(); err != nil {
		return err
	}

	// Notify dependents if a content change has occurred.
	return f.GenerateNotifications(oldMeta, newMeta)
}

// redoStatic tracks changes and dependencies for static files, which are edited manually and do not have a do script.
func (f *File) redoStatic(event Event, oldMeta *Metadata) error {

	// A file that exists outside this (or any) redo project directory
	// and has no database in which to store metadata or dependencies is assigned a NullDb.
	// Such a file is still useful it can serve as a prerequisite for files inside a redo project directory.
	// However, it cannot store metadata or notify dependents of changes.
	if f.HasNullDb() {
		return nil
	}

	newMeta, err := f.NewMetadata()
	if err != nil {
		return err
	} else if newMeta == nil {
		return f.ErrNotFound("redoStatic")
	}

	if err := f.PutMetadata(newMeta); err != nil {
		return err
	}

	return f.GenerateNotifications(oldMeta, newMeta)
}

/* FindDoFile searches for the most specific .do file for the target and, if found, stores its path in f.DoFile.
It returns an array of paths to more specific .do files, if any, that were not found.
Target with extension searches for: target.ext.do, default.ext.do, default.do
 Target without extension searches for: target.do, default.do
*/
func (f *File) findDoFile() (missing []string, err error) {

	var candidates []string

	candidates = append(candidates, f.Name+".do")
	if len(f.Ext) > 0 {
		candidates = append(candidates, "default"+f.Ext+".do")
	}
	candidates = append(candidates, "default.do")

TOP:
	for dir := f.Dir; ; /* no test */ dir = filepath.Dir(dir) {
		for _, candidate := range candidates {
			path := filepath.Join(dir, candidate)
			var exists bool // avoid rescoping err
			exists, err = fileutils.FileExists(path)
			if err != nil {
				break TOP
			} else if exists {
				f.DoFile = path
				break TOP
			} else {
				missing = append(missing, path)
			}
		}
		if dir == f.RootDir {
			break TOP
		}
	}

	return
}

// RunDoFile executes the do file script, records the metadata for the resulting output, then
// saves the resulting output to the target file, if applicable.
func (target *File) RunDoFile() (err error) {
	/*
	  A well behaved .do file writes to stdout or to the $3 file, but not both.

	  In order to catch misbehaviour, the .do script is run with stdout going to
	  a different temp file from $3.

	  In the ideal case where one file has content and the other is empty,
	  the former is returned while the latter is deleted.
	  If both are non-empty, both are deleted and an error reported.
	  If both are empty, the first one is returned and the second deleted.
	*/

	var outputs [2]*os.File

	// If the do file is a task, the first output goes to stdout
	// and the second to a file that will be subsequently deleted.
	for i := 0; i < len(outputs); i++ {
		if i == 0 && target.IsTask() {
			outputs[i] = os.Stdout
		} else {
			outputs[i], err = ioutil.TempFile(target.Dir, target.Basename+"-redo-tmp-")
			if err != nil {
				return
			}
			// TODO: add an option to control temp file deletion on failure for debugging purposes?
			defer func(path string) {
				if err != nil {
					os.Remove(path)
				}
			}(outputs[i].Name())
		}
	}

	redoDepth := os.Getenv("REDO_DEPTH")

	if Verbose() {
		prefix := redoDepth
		if redoParent := os.Getenv(REDO_PARENT_ENV_NAME); redoParent != "" {
			prefix += target.Rel(redoParent) + " => "
		}
		target.Log("%s%s (%s)\n", prefix, target.Rel(target.Fullpath()), target.Rel(target.DoFile))
	}

	args := []string{"-e"}

	if ShellArgs != "" {
		args = append(args, ShellArgs)
	}

	args = append(args, target.DoFile, target.Path, target.Basename, outputs[1].Name())

	const shell = "/bin/sh"
	cmd := exec.Command(shell, args...)
	cmd.Dir = filepath.Dir(target.DoFile) //TODO -- run in target directory instead?
	cmd.Stdout = outputs[0]
	cmd.Stderr = os.Stderr

	// Add environment variables, replacing existing entries if necessary.
	cmdEnv := os.Environ()
	env := map[string]string{REDO_PARENT_ENV_NAME: target.Fullpath(), "REDO_DEPTH": redoDepth + " "}

	// Update environment values, if they exist and append when they dont.
TOP:
	for key, value := range env {
		prefix := key + "="
		for i, entry := range cmdEnv {
			if strings.HasPrefix(entry, prefix) {
				cmdEnv[i] = prefix + value
				continue TOP
			}
		}
		cmdEnv = append(cmdEnv, prefix+value)
	}

	cmd.Env = cmdEnv

	if err := cmd.Run(); err != nil {
		if Verbose() {
			return target.Errorf("%s %s: %s", shell, strings.Join(args, " "), err)
		}
		return target.Errorf("%s", err)
	}

	if target.IsTask() {
		// Task files should not write to the temp file.
		f := outputs[1]

		defer func(f *os.File) {
			f.Close()
			os.Remove(f.Name())
		}(f)

		if finfo, err := f.Stat(); err != nil {
			return err
		} else if finfo.Size() > 0 {
			return target.Errorf("Task do file %s unexpectedly wrote to $3", target.DoFile)
		}

		return nil
	}

	writtenTo := 0 // number of files written to
	idx := 0       // index of correct output, with appropriate default.

	for i, f := range outputs {
		// f.Stat() doesn't work for the file on $3 since it was written to by a different process.
		// Rather than using f.Stat() on one and os.Stat() on the other, use the latter on both.
		if finfo, err := os.Stat(f.Name()); err != nil {
			return err
		} else if finfo.Size() > 0 {
			writtenTo++
			idx = i
		}
	}

	// It is an error to write to both files.
	// Select neither so both will be deleted.
	if writtenTo == len(outputs) {
		idx = -1
	}

	for i, f := range outputs {
		f.Close()
		if i != idx {
			os.Remove(f.Name()) // ignored file.
		}
	}

	// and finally, the reckoning
	if writtenTo < len(outputs) {
		return os.Rename(outputs[idx].Name(), target.Fullpath())
	}

	return target.Errorf(".do file %s wrote to stdout and to file $3", target.DoFile)
}

// RedoIfChange runs redo on the target if it is out of date or its current state
// disagrees with its dependent's version of its state.
func (target *File) RedoIfChange(dependent *File) error {

	recordRelation := func(m *Metadata) error {
		return RecordRelation(dependent, target, IFCHANGE, m)
	}

	targetMeta, err := target.NewMetadata()
	if err != nil {
		return err
	} else if targetMeta == nil {
		goto REDO
	}

	if isCurrent, err := target.IsCurrent(); err != nil {
		return err
	} else if !isCurrent {
		goto REDO
	} else {

		// dependent's version of the target's state.
		prereq, found, err := dependent.GetPrerequisite(IFCHANGE, target.PathHash)
		if err != nil {
			return err
		} else if !found {
			// There is no record of the dependency so this is the first time through.
			// Since the target is up to date, use its metadata for the dependency.
			return recordRelation(targetMeta)
		}

		if prereq.Equal(targetMeta) {
			// target is up to date and its current state agrees with dependent's version.
			// Nothing to do here.
			return nil
		}
	}

REDO:
	if err := target.Redo(); err != nil {
		return err
	}

	targetMeta, err = target.NewMetadata()
	if err != nil {
		return err
	}
	if targetMeta == nil {
		return fmt.Errorf("Cannot find recently created target: %s", target.Target)
	}

	return recordRelation(targetMeta)
}

/* RedoIfCreate records a dependency record on a file that does not yet exist */
func (target *File) RedoIfCreate(dependent *File) error {
	if exists, err := target.Exists(); err != nil {
		return err
	} else if exists {
		return fmt.Errorf("%s. File exists", dependent.Target)
	}

	//In case it existed before
	target.DeleteMetadata()

	return RecordRelation(dependent, target, IFCREATE, nil)
}
<commit_msg>Refactoring - moved do file operations to new file<commit_after>// Copyright 2014 Gyepi Sam. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package redux

import (
	"fmt"
	"path/filepath"

	"github.com/gyepisam/fileutils"
)

// Redo finds and executes the .do file for the given target.
func (target *File) Redo() error {

	doFilesNotFound, err := target.findDoFile()
	if err != nil {
		return err
	}

	cachedMeta, recordFound, err := target.GetMetadata()
	if err != nil {
		return err
	}

	targetMeta, err := target.NewMetadata()
	if err != nil {
		return err
	}
	targetExists := targetMeta != nil

	if targetExists {
		if recordFound {
			if target.HasDoFile() {
				return target.redoTarget(doFilesNotFound, targetMeta)
			} else if cachedMeta.HasDoFile() {
				return target.Errorf("Missing .do file")
			} else if !targetMeta.Equal(&cachedMeta) {
				return target.redoStatic(IFCHANGE, targetMeta)
			}
		} else {
			if target.HasDoFile() {
				return target.redoTarget(doFilesNotFound, targetMeta)
			} else {
				return target.redoStatic(IFCREATE, targetMeta)
			}
		}
	} else {
		if recordFound {
			// target existed at one point but was deleted...
			if target.HasDoFile() {
				return target.redoTarget(doFilesNotFound, targetMeta)
			} else if cachedMeta.HasDoFile() {
				return target.Errorf("Missing .do file")
			} else {
				// target is a deleted source file. Clean up and fail.
				if err = target.NotifyDependents(IFCHANGE); err != nil {
					return err
				} else if err = target.DeleteMetadata(); err != nil {
					return err
				}
				return fmt.Errorf("Source file %s does not exist", target.Target)
			}
		} else {
			if target.HasDoFile() {
				return target.redoTarget(doFilesNotFound, targetMeta)
			} else {
				return target.Errorf(".do file not found")
			}
		}
	}

	return nil
}

// redoTarget records a target's .do file dependencies, runs the target's do file and notifies dependents.
func (f *File) redoTarget(doFilesNotFound []string, oldMeta *Metadata) error {

	// can't build without a database...
	if f.HasNullDb() {
		return f.ErrUninitialized()
	}

	// Prerequisites will be recreated...
	// Ideally, this could be done within a transaction to allow for rollback
	// in the event of failure.
	if err := f.DeleteAutoPrerequisites(); err != nil {
		return err
	}

	for _, path := range doFilesNotFound {
		relpath := f.Rel(path)
		err := f.PutPrerequisite(AUTO_IFCREATE, MakeHash(relpath), Prerequisite{Path: relpath})
		if err != nil {
			return err
		}
	}

	doFile, err := NewFile(f.RootDir, f.DoFile)
	if err != nil {
		return err
	}

	// metadata needs to be stored twice and is relatively expensive to acquire.
	doMeta, err := doFile.NewMetadata()

	if err != nil {
		return err
	} else if doMeta == nil {
		return doFile.ErrNotFound("redoTarget: doFile.NewMetadata")
	} else if err := doFile.PutMetadata(doMeta); err != nil {
		return err
	}

	relpath := f.Rel(f.DoFile)
	if err := f.PutPrerequisite(AUTO_IFCHANGE, MakeHash(relpath), Prerequisite{relpath, doMeta}); err != nil {
		return err
	}

	if err := f.RunDoFile(); err != nil {
		return err
	}

	// A task script does not produce output and has no dependencies...
	if f.IsTask() {
		return nil
	}

	newMeta, err := f.NewMetadata()
	if err != nil {
		return err
	} else if newMeta == nil {
		return f.ErrNotFound("redoTarget: f.NewMetadata")
	}

	if err := f.PutMetadata(newMeta); err != nil {
		return err
	}

	if err := f.DeleteMustRebuild(); err != nil {
		return err
	}

	// Notify dependents if a content change has occurred.
	return f.GenerateNotifications(oldMeta, newMeta)
}

// redoStatic tracks changes and dependencies for static files, which are edited manually and do not have a do script.
func (f *File) redoStatic(event Event, oldMeta *Metadata) error {

	// A file that exists outside this (or any) redo project directory
	// and has no database in which to store metadata or dependencies is assigned a NullDb.
	// Such a file is still useful it can serve as a prerequisite for files inside a redo project directory.
	// However, it cannot store metadata or notify dependents of changes.
	if f.HasNullDb() {
		return nil
	}

	newMeta, err := f.NewMetadata()
	if err != nil {
		return err
	} else if newMeta == nil {
		return f.ErrNotFound("redoStatic")
	}

	if err := f.PutMetadata(newMeta); err != nil {
		return err
	}

	return f.GenerateNotifications(oldMeta, newMeta)
}

/* FindDoFile searches for the most specific .do file for the target and, if found, stores its path in f.DoFile.
It returns an array of paths to more specific .do files, if any, that were not found.
Target with extension searches for: target.ext.do, default.ext.do, default.do
 Target without extension searches for: target.do, default.do
*/
func (f *File) findDoFile() (missing []string, err error) {

	var candidates []string

	candidates = append(candidates, f.Name+".do")
	if len(f.Ext) > 0 {
		candidates = append(candidates, "default"+f.Ext+".do")
	}
	candidates = append(candidates, "default.do")

TOP:
	for dir := f.Dir; ; /* no test */ dir = filepath.Dir(dir) {
		for _, candidate := range candidates {
			path := filepath.Join(dir, candidate)
			var exists bool // avoid rescoping err
			exists, err = fileutils.FileExists(path)
			if err != nil {
				break TOP
			} else if exists {
				f.DoFile = path
				break TOP
			} else {
				missing = append(missing, path)
			}
		}
		if dir == f.RootDir {
			break TOP
		}
	}

	return
}

// RedoIfChange runs redo on the target if it is out of date or its current state
// disagrees with its dependent's version of its state.
func (target *File) RedoIfChange(dependent *File) error {

	recordRelation := func(m *Metadata) error {
		return RecordRelation(dependent, target, IFCHANGE, m)
	}

	targetMeta, err := target.NewMetadata()
	if err != nil {
		return err
	} else if targetMeta == nil {
		goto REDO
	}

	if isCurrent, err := target.IsCurrent(); err != nil {
		return err
	} else if !isCurrent {
		goto REDO
	} else {

		// dependent's version of the target's state.
		prereq, found, err := dependent.GetPrerequisite(IFCHANGE, target.PathHash)
		if err != nil {
			return err
		} else if !found {
			// There is no record of the dependency so this is the first time through.
			// Since the target is up to date, use its metadata for the dependency.
			return recordRelation(targetMeta)
		}

		if prereq.Equal(targetMeta) {
			// target is up to date and its current state agrees with dependent's version.
			// Nothing to do here.
			return nil
		}
	}

REDO:
	if err := target.Redo(); err != nil {
		return err
	}

	targetMeta, err = target.NewMetadata()
	if err != nil {
		return err
	}
	if targetMeta == nil {
		return fmt.Errorf("Cannot find recently created target: %s", target.Target)
	}

	return recordRelation(targetMeta)
}

/* RedoIfCreate records a dependency record on a file that does not yet exist */
func (target *File) RedoIfCreate(dependent *File) error {
	if exists, err := target.Exists(); err != nil {
		return err
	} else if exists {
		return fmt.Errorf("%s. File exists", dependent.Target)
	}

	//In case it existed before
	target.DeleteMetadata()

	return RecordRelation(dependent, target, IFCREATE, nil)
}
<|endoftext|>


<commit_before>/*
 * Minio Client (C) 2014, 2015 Minio, Inc.
 *
 * 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 main

import (
	"io"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/cheggaaa/pb"
	"github.com/minio/mc/pkg/console"
)

type pbBarCmd int

const (
	pbBarCmdExtend pbBarCmd = iota
	pbBarCmdProgress
	pbBarCmdFinish
	pbBarCmdPutError
	pbBarCmdGetError
	pbBarCmdSetCaption
)

type proxyReader struct {
	io.Reader
	bar *barSend
}

func (r *proxyReader) Read(p []byte) (n int, err error) {
	n, err = r.Reader.Read(p)
	r.bar.progress(int64(n))
	return
}

type barMsg struct {
	Cmd pbBarCmd
	Arg interface{}
}

type barSend struct {
	cmdCh    chan<- barMsg
	finishCh <-chan bool
}

func (b barSend) Extend(total int64) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdExtend, Arg: total}
}

func (b barSend) progress(progress int64) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdProgress, Arg: progress}
}

func (b barSend) ErrorPut(size int64) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdPutError, Arg: size}
}

func (b barSend) ErrorGet(size int64) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdGetError, Arg: size}
}

func (b *barSend) NewProxyReader(r io.Reader) *proxyReader {
	return &proxyReader{r, b}
}

type caption struct {
	message   string
	separator rune
}

func (b *barSend) SetCaption(c caption) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdSetCaption, Arg: c}
}

func (b barSend) Finish() {
	defer close(b.cmdCh)
	b.cmdCh <- barMsg{Cmd: pbBarCmdFinish}
	<-b.finishCh
}

func trimBarCaption(c caption, width int) string {
	if len(c.message) > width {
		// Trim caption to fit within the screen
		trimSize := len(c.message) - width + 3 + 1
		if trimSize < len(c.message) {
			c.message = "..." + c.message[trimSize:]
			// Further trim partial names.
			partialTrimSize := strings.IndexByte(c.message, byte(c.separator))
			if partialTrimSize > 0 {
				c.message = c.message[partialTrimSize:]
			}
		}
	}
	return c.message
}

// newCpBar - instantiate a pbBar.
func newCpBar() barSend {
	cmdCh := make(chan barMsg)
	finishCh := make(chan bool)
	go func(cmdCh <-chan barMsg, finishCh chan<- bool) {
		var started bool
		var barCaption string
		var totalBytesRead int64 // total amounts of bytes read
		bar := pb.New64(0)
		bar.SetUnits(pb.U_BYTES)
		bar.SetRefreshRate(time.Millisecond * 10)
		bar.NotPrint = true
		bar.ShowSpeed = true
		firstTime := true
		barLock := &sync.Mutex{}
		bar.Callback = func(s string) {
			barLock.Lock()
			switch runtime.GOOS {
			case "windows":
				console.Print("\r" + strings.Repeat(" ", (bar.GetWidth()-1)) + "\r")
				console.Bar(barCaption + "\n")
				console.Bar("\r" + s)
			default:
				cursorUP := "\x1b[A"
				cursorDown := "\x1b[B"
				eraseCurrentLine := "\x1b[2K\r"
				if !firstTime {
					console.Print(cursorUP)
					console.Print(eraseCurrentLine)
				}
				console.Bar(barCaption)
				console.Print(cursorDown)
				console.Print(eraseCurrentLine)
				console.Bar(s)
			}
			firstTime = false
			barLock.Unlock()
		}
		// Feels like wget
		bar.Format("[=> ]")
		for msg := range cmdCh {
			switch msg.Cmd {
			case pbBarCmdSetCaption:
				barCaption = trimBarCaption(msg.Arg.(caption), bar.GetWidth())
			case pbBarCmdExtend:
				atomic.AddInt64(&bar.Total, msg.Arg.(int64))
			case pbBarCmdProgress:
				if bar.Total > 0 && !started {
					started = true
					bar.Start()
				}
				if msg.Arg.(int64) > 0 {
					totalBytesRead += msg.Arg.(int64)
					bar.Add64(msg.Arg.(int64))
				}
			case pbBarCmdPutError:
				if totalBytesRead > msg.Arg.(int64) {
					bar.Set64(totalBytesRead - msg.Arg.(int64))
				}
			case pbBarCmdGetError:
				if msg.Arg.(int64) > 0 {
					bar.Add64(msg.Arg.(int64))
				}
			case pbBarCmdFinish:
				if started {
					bar.Finish()
				}
				finishCh <- true
				return
			}
		}
	}(cmdCh, finishCh)
	return barSend{cmdCh, finishCh}
}
<commit_msg>Move to a more portable bar.Prefix approach from progress bar<commit_after>/*
 * Minio Client (C) 2014, 2015 Minio, Inc.
 *
 * 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 main

import (
	"io"
	"strings"
	"sync/atomic"
	"time"

	"github.com/cheggaaa/pb"
	"github.com/minio/mc/pkg/console"
)

type pbBarCmd int

const (
	pbBarCmdExtend pbBarCmd = iota
	pbBarCmdProgress
	pbBarCmdFinish
	pbBarCmdPutError
	pbBarCmdGetError
	pbBarCmdSetCaption
)

type proxyReader struct {
	io.Reader
	bar *barSend
}

func (r *proxyReader) Read(p []byte) (n int, err error) {
	n, err = r.Reader.Read(p)
	r.bar.progress(int64(n))
	return
}

type barMsg struct {
	Cmd pbBarCmd
	Arg interface{}
}

type barSend struct {
	cmdCh    chan<- barMsg
	finishCh <-chan bool
}

func (b barSend) Extend(total int64) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdExtend, Arg: total}
}

func (b barSend) progress(progress int64) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdProgress, Arg: progress}
}

func (b barSend) ErrorPut(size int64) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdPutError, Arg: size}
}

func (b barSend) ErrorGet(size int64) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdGetError, Arg: size}
}

func (b *barSend) NewProxyReader(r io.Reader) *proxyReader {
	return &proxyReader{r, b}
}

type caption struct {
	message   string
	separator rune
}

func (b *barSend) SetCaption(c caption) {
	b.cmdCh <- barMsg{Cmd: pbBarCmdSetCaption, Arg: c}
}

func (b barSend) Finish() {
	defer close(b.cmdCh)
	b.cmdCh <- barMsg{Cmd: pbBarCmdFinish}
	<-b.finishCh
	console.Println()
}

func fixateBarCaption(c caption, width int) string {
	if len(c.message) > width {
		// Trim caption to fit within the screen
		trimSize := len(c.message) - width + 3 + 1
		if trimSize < len(c.message) {
			c.message = "..." + c.message[trimSize:]
			// Further trim partial names.
			partialTrimSize := strings.IndexByte(c.message, byte(c.separator))
			if partialTrimSize > 0 {
				c.message = c.message[partialTrimSize:]
			}
		}
	}
	return c.message
}

// newCpBar - instantiate a pbBar.
func newCpBar() barSend {
	cmdCh := make(chan barMsg)
	finishCh := make(chan bool)
	go func(cmdCh <-chan barMsg, finishCh chan<- bool) {
		var started bool
		var totalBytesRead int64 // total amounts of bytes read
		bar := pb.New64(0)
		bar.SetUnits(pb.U_BYTES)
		bar.SetRefreshRate(time.Millisecond * 10)
		bar.NotPrint = true
		bar.ShowSpeed = true
		bar.Callback = func(s string) {
			console.Bar(s + "\r")
		}
		// Feels like wget
		bar.Format("[=> ]")
		for msg := range cmdCh {
			switch msg.Cmd {
			case pbBarCmdSetCaption:
				bar.Prefix(fixateBarCaption(msg.Arg.(caption), 15))
			case pbBarCmdExtend:
				atomic.AddInt64(&bar.Total, msg.Arg.(int64))
			case pbBarCmdProgress:
				if bar.Total > 0 && !started {
					started = true
					bar.Start()
				}
				if msg.Arg.(int64) > 0 {
					totalBytesRead += msg.Arg.(int64)
					bar.Add64(msg.Arg.(int64))
				}
			case pbBarCmdPutError:
				if totalBytesRead > msg.Arg.(int64) {
					bar.Set64(totalBytesRead - msg.Arg.(int64))
				}
			case pbBarCmdGetError:
				if msg.Arg.(int64) > 0 {
					bar.Add64(msg.Arg.(int64))
				}
			case pbBarCmdFinish:
				if started {
					bar.Finish()
				}
				finishCh <- true
				return
			}
		}
	}(cmdCh, finishCh)
	return barSend{cmdCh, finishCh}
}
<|endoftext|>


<commit_before>package s3signer

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"
)

var subresourcesS3 = []string{
	"acl",
	"lifecycle",
	"location",
	"logging",
	"notification",
	"partNumber",
	"policy",
	"requestPayment",
	"torrent",
	"uploadId",
	"uploads",
	"versionId",
	"versioning",
	"versions",
	"website",
}

// Credentials stores AWS credentials used for signing
type Credentials struct {
	AccessKeyID     string
	SecretAccessKey string
}

// SignV2 takes the HTTP request to sign, and the credentials that should be used to sign it
func SignV2(request *http.Request, credentials Credentials) *http.Request {
	request.Header.Set("Authorization", fmt.Sprintf("AWS %s:%s", credentials.AccessKeyID, signString(stringToSign(request), credentials)))
	return request
}

func signString(stringToSign string, keys Credentials) string {
	hash := hmac.New(sha1.New, []byte(keys.SecretAccessKey))
	hash.Write([]byte(stringToSign))
	signature := make([]byte, base64.StdEncoding.EncodedLen(hash.Size()))
	base64.StdEncoding.Encode(signature, hash.Sum(nil))
	return string(signature)
}

func stringToSign(request *http.Request) string {
	var buffer bytes.Buffer
	buffer.WriteString(request.Method + "\n")
	buffer.WriteString(request.Header.Get("Content-MD5") + "\n")
	buffer.WriteString(request.Header.Get("Content-Type") + "\n")
	buffer.WriteString(getDateHeader(request) + "\n")
	buffer.WriteString(canonicalAmzHeaders(request))
	buffer.WriteString(canonicalResource(request))
	return buffer.String()
}

func getDateHeader(request *http.Request) string {
	if header := request.Header.Get("x-amz-date"); header != "" {
		return ""
	} else if header := request.Header.Get("Date"); header != "" {
		return header
	} else {
		return time.Now().UTC().Format(time.RFC1123Z)
	}
}

func canonicalAmzHeaders(request *http.Request) string {
	var headers []string

	for header := range request.Header {
		standardized := strings.ToLower(strings.TrimSpace(header))
		if strings.HasPrefix(standardized, "x-amz") {
			headers = append(headers, standardized)
		}
	}

	sort.Strings(headers)

	//TODO(jstackhouse): Combine headers into header-name:csv
	//  Combine header fields with the same name into one
	//  "header-name:comma-separated-value-list" pair as
	//  prescribed by RFC 2616, section 4.2, without any
	//  whitespace between values. For example, the two
	//  metadata headers 'x-amz-meta-username: fred' and
	//  'x-amz-meta-username: barney' would be combined
	//  into the single header 'x-amz-meta-username: fred,barney'.

	for i, header := range headers {
		val := strings.Join(request.Header[http.CanonicalHeaderKey(header)], ",")
		headers[i] = header + ":" + strings.Replace(val, "\n", " ", -1)
	}

	if len(headers) > 0 {
		return strings.Join(headers, "\n") + "\n"
	}
	return ""
}

func canonicalResource(request *http.Request) string {
	resource := ""

	// If Bucket in Host header, add it to canonical resource
	host := request.Header.Get("Host")
	if host == "" || host == "s3.amazonaws.com" {
		// Bucket Name First Part of Request URI, will be added by path
	} else if strings.HasSuffix(host, ".s3.amazonaws.com") {
		val := strings.Split(host, ".")
		resource += "/" + strings.Join(val[0:len(val)-3], ".")
	} else {
		resource += "/" + strings.ToLower(host)
	}

	resource += request.URL.EscapedPath()

	subresources := []string{}
	for _, subResource := range subresourcesS3 {
		if strings.HasPrefix(request.URL.RawQuery, subResource) {
			subresources = append(subresources, subResource)
		}
	}
	if len(subresources) > 0 {
		resource += "?" + strings.Join(subresources, "&")
	}

	return resource
}
<commit_msg>Remove TODO.<commit_after>package s3signer

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"
)

var subresourcesS3 = []string{
	"acl",
	"lifecycle",
	"location",
	"logging",
	"notification",
	"partNumber",
	"policy",
	"requestPayment",
	"torrent",
	"uploadId",
	"uploads",
	"versionId",
	"versioning",
	"versions",
	"website",
}

// Credentials stores AWS credentials used for signing
type Credentials struct {
	AccessKeyID     string
	SecretAccessKey string
}

// SignV2 takes the HTTP request to sign, and the credentials that should be used to sign it
func SignV2(request *http.Request, credentials Credentials) *http.Request {
	request.Header.Set("Authorization", fmt.Sprintf("AWS %s:%s", credentials.AccessKeyID, signString(stringToSign(request), credentials)))
	return request
}

func signString(stringToSign string, keys Credentials) string {
	hash := hmac.New(sha1.New, []byte(keys.SecretAccessKey))
	hash.Write([]byte(stringToSign))
	signature := make([]byte, base64.StdEncoding.EncodedLen(hash.Size()))
	base64.StdEncoding.Encode(signature, hash.Sum(nil))
	return string(signature)
}

func stringToSign(request *http.Request) string {
	var buffer bytes.Buffer
	buffer.WriteString(request.Method + "\n")
	buffer.WriteString(request.Header.Get("Content-MD5") + "\n")
	buffer.WriteString(request.Header.Get("Content-Type") + "\n")
	buffer.WriteString(getDateHeader(request) + "\n")
	buffer.WriteString(canonicalAmzHeaders(request))
	buffer.WriteString(canonicalResource(request))
	return buffer.String()
}

func getDateHeader(request *http.Request) string {
	if header := request.Header.Get("x-amz-date"); header != "" {
		return ""
	} else if header := request.Header.Get("Date"); header != "" {
		return header
	} else {
		return time.Now().UTC().Format(time.RFC1123Z)
	}
}

func canonicalAmzHeaders(request *http.Request) string {
	var headers []string

	for header := range request.Header {
		standardized := strings.ToLower(strings.TrimSpace(header))
		if strings.HasPrefix(standardized, "x-amz") {
			headers = append(headers, standardized)
		}
	}

	sort.Strings(headers)

	for i, header := range headers {
		val := strings.Join(request.Header[http.CanonicalHeaderKey(header)], ",")
		headers[i] = header + ":" + strings.Replace(val, "\n", " ", -1)
	}

	if len(headers) > 0 {
		return strings.Join(headers, "\n") + "\n"
	}
	return ""
}

func canonicalResource(request *http.Request) string {
	resource := ""

	// If Bucket in Host header, add it to canonical resource
	host := request.Header.Get("Host")
	if host == "" || host == "s3.amazonaws.com" {
		// Bucket Name First Part of Request URI, will be added by path
	} else if strings.HasSuffix(host, ".s3.amazonaws.com") {
		val := strings.Split(host, ".")
		resource += "/" + strings.Join(val[0:len(val)-3], ".")
	} else {
		resource += "/" + strings.ToLower(host)
	}

	resource += request.URL.EscapedPath()

	subresources := []string{}
	for _, subResource := range subresourcesS3 {
		if strings.HasPrefix(request.URL.RawQuery, subResource) {
			subresources = append(subresources, subResource)
		}
	}
	if len(subresources) > 0 {
		resource += "?" + strings.Join(subresources, "&")
	}

	return resource
}
<|endoftext|>


<commit_before>package veneur

import (
	"errors"
	"io"
	"path"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3iface"
)

const S3Bucket = "stripe-veneur"

// TODO(aditya) config-ify this
const DefaultAWSRegion = "us-west-2"
const AwsProfile = "veneur-s3-test"

var svc s3iface.S3API

var S3ClientUninitializedError = errors.New("s3 client has not been initialized")

// credentials will be pull from environment variables
// AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY

func init() {
	sess := session.New(&aws.Config{
		Region: aws.String(DefaultAWSRegion),
	})

	_, err := sess.Config.Credentials.Get()
	if err == nil {
		svc = s3.New(sess)
	}
}

func s3Post(hostname string, data io.ReadSeeker) error {
	if svc == nil {
		return S3ClientUninitializedError
	}
	params := &s3.PutObjectInput{
		Bucket: aws.String(S3Bucket),
		Key:    s3Path(hostname),
		Body:   data,
	}

	_, err := svc.PutObject(params)
	return err
}

func s3Path(hostname string) *string {
	t := time.Now()
	filename := strconv.FormatInt(t.Unix(), 10) + ".json"
	return aws.String(path.Join(t.Format("2006/01/02"), hostname, filename))
}
<commit_msg>Change s3 bucket name<commit_after>package veneur

import (
	"errors"
	"io"
	"path"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3iface"
)

const S3Bucket = "stripe-test-veneur"

// TODO(aditya) config-ify this
const DefaultAWSRegion = "us-west-2"
const AwsProfile = "veneur-s3-test"

var svc s3iface.S3API

var S3ClientUninitializedError = errors.New("s3 client has not been initialized")

// credentials will be pull from environment variables
// AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY

func init() {
	sess := session.New(&aws.Config{
		Region: aws.String(DefaultAWSRegion),
	})

	_, err := sess.Config.Credentials.Get()
	if err == nil {
		svc = s3.New(sess)
	}
}

func s3Post(hostname string, data io.ReadSeeker) error {
	if svc == nil {
		return S3ClientUninitializedError
	}
	params := &s3.PutObjectInput{
		Bucket: aws.String(S3Bucket),
		Key:    s3Path(hostname),
		Body:   data,
	}

	_, err := svc.PutObject(params)
	return err
}

func s3Path(hostname string) *string {
	t := time.Now()
	filename := strconv.FormatInt(t.Unix(), 10) + ".json"
	return aws.String(path.Join(t.Format("2006/01/02"), hostname, filename))
}
<|endoftext|>


<commit_before>package opensearch

import (
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/bgpat/tweet-via-searchbar/middleware"
	"github.com/bgpat/twtr"
)

var (
	baseURL = os.Getenv("BASE_URL")
)

type OpenSearch struct {
	XMLName        xml.Name `xml:"OpenSearchDescription"`
	XMLNS          string   `xml:"xmlns,attr"`
	ShortName      string   `xml:"ShortName"`
	LongName       string   `xml:"LongName"`
	Description    string   `xml:"Description"`
	Image          Image    `xml:"Image"`
	Site           string   `xml:"site"`
	InputEncoding  string   `xml:"InputEncoding"`
	OutputEncoding string   `xml:"OutputEncoding"`
	URL            URL      `xml:"Url"`
}

type Image struct {
	XMLName xml.Name `xml:"Image"`
	Width   int      `xml:"width,attr"`
	Height  int      `xml:"height,attr"`
	Source  string   `xml:",chardata"`
}

type URL struct {
	XMLName  xml.Name `xml:"Url"`
	Type     string   `xml:"type,attr"`
	Method   string   `xml:"method,attr"`
	Template string   `xml:"template,attr"`
	Params   []Param  `xml:"Params"`
}

type Param struct {
	XMLName xml.Name `xml:"Param"`
	Name    string   `xml:"name,attr"`
	Value   string   `xml:"value,attr"`
}

func NewOpenSearch(user *twtr.User, client *middleware.Client) *OpenSearch {
	return &OpenSearch{
		XMLNS:       "http://a9.com/-/spec/opensearch/1.1/",
		ShortName:   "@" + user.ScreenName,
		LongName:    fmt.Sprintf("@%sでツイート", user.ScreenName),
		Description: fmt.Sprintf("検索窓ツイート (@%s)", user.ScreenName),
		Image: Image{
			Width:  16,
			Height: 16,
			Source: user.ProfileImageURLHttps,
		},
		Site:           baseURL + "/",
		InputEncoding:  "UTF-8",
		OutputEncoding: "UTF-8",
		URL: URL{
			Type:     "text/html",
			Method:   "POST",
			Template: baseURL + "/search",
			Params: []Param{
				Param{
					Name:  "q",
					Value: "{searchTerms}",
				},
				Param{
					Name:  "token",
					Value: client.AccessToken.Token,
				},
				Param{
					Name:  "secret",
					Value: client.AccessToken.Secret,
				},
				Param{
					Name:  "redirect",
					Value: client.Config.Redirect,
				},
			},
		},
	}
}

func (o *OpenSearch) ToString() (string, error) {
	buf, err := xml.MarshalIndent(o, "", "    ")
	if err != nil {
		return "", err
	}
	return xml.Header + string(buf), nil
}

func (o *OpenSearch) Render(w http.ResponseWriter) error {
	header := w.Header()
	header["Content-Type"] = []string{"application/opensearchdescription+xml; charset=utf-8"}

	str, err := o.ToString()
	if err != nil {
		return err
	}
	_, err = io.WriteString(w, str)
	return err
}
<commit_msg>Fix error for opensearch<commit_after>package opensearch

import (
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/bgpat/tweet-via-searchbar/middleware"
	"github.com/bgpat/twtr"
)

var (
	baseURL = os.Getenv("BASE_URL")
)

type OpenSearch struct {
	XMLName        xml.Name `xml:"OpenSearchDescription"`
	XMLNS          string   `xml:"xmlns,attr"`
	ShortName      string   `xml:"ShortName"`
	LongName       string   `xml:"LongName"`
	Description    string   `xml:"Description"`
	Image          Image    `xml:"Image"`
	Site           string   `xml:"site"`
	InputEncoding  string   `xml:"InputEncoding"`
	OutputEncoding string   `xml:"OutputEncoding"`
	URL            URL      `xml:"Url"`
}

type Image struct {
	XMLName xml.Name `xml:"Image"`
	Width   int      `xml:"width,attr"`
	Height  int      `xml:"height,attr"`
	Source  string   `xml:",chardata"`
}

type URL struct {
	XMLName  xml.Name `xml:"Url"`
	Type     string   `xml:"type,attr"`
	Method   string   `xml:"method,attr"`
	Template string   `xml:"template,attr"`
	Params   []Param  `xml:"Params"`
}

type Param struct {
	XMLName xml.Name `xml:"Param"`
	Name    string   `xml:"name,attr"`
	Value   string   `xml:"value,attr"`
}

func NewOpenSearch(user *twtr.User, client *middleware.Client) *OpenSearch {
	return &OpenSearch{
		XMLNS:       "http://a9.com/-/spec/opensearch/1.1/",
		ShortName:   "@" + user.ScreenName,
		LongName:    fmt.Sprintf("@%sでツイート", user.ScreenName),
		Description: fmt.Sprintf("検索窓ツイート (@%s)", user.ScreenName),
		Image: Image{
			Width:  16,
			Height: 16,
			Source: user.ProfileImageURLHttps,
		},
		Site:           baseURL + "/",
		InputEncoding:  "UTF-8",
		OutputEncoding: "UTF-8",
		URL: URL{
			Type:     "text/html",
			Method:   "POST",
			Template: baseURL + "/search",
			Params: []Param{
				Param{
					Name:  "q",
					Value: "{searchTerms}",
				},
				Param{
					Name:  "token",
					Value: client.AccessToken.Token,
				},
				Param{
					Name:  "secret",
					Value: client.AccessToken.Secret,
				},
				Param{
					Name:  "redirect",
					Value: client.Config.Redirect,
				},
			},
		},
	}
}

func (o *OpenSearch) ToString() (string, error) {
	buf, err := xml.MarshalIndent(o, "", "    ")
	if err != nil {
		return "", err
	}
	return xml.Header + string(buf), nil
}

func (o *OpenSearch) Render(w http.ResponseWriter) error {
	str, err := o.ToString()
	if err != nil {
		return err
	}
	_, err = io.WriteString(w, str)
	return err
}

func (o *OpenSearch) WriteContentType(w http.ResponseWriter) {
	header := w.Header()
	header["Content-Type"] = []string{"application/opensearchdescription+xml; charset=utf-8"}
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Kubernetes Authors.

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

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

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

package cel

import (
	"fmt"
	"math"
	"strings"
	"time"

	"github.com/google/cel-go/cel"
	"github.com/google/cel-go/checker/decls"
	expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
	"google.golang.org/protobuf/proto"

	apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
	"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library"
	celmodel "k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model"
)

const (
	// ScopedVarName is the variable name assigned to the locally scoped data element of a CEL validation
	// expression.
	ScopedVarName = "self"

	// OldScopedVarName is the variable name assigned to the existing value of the locally scoped data element of a
	// CEL validation expression.
	OldScopedVarName = "oldSelf"

	// PerCallLimit specify the actual cost limit per CEL validation call
	//TODO: pick the number for PerCallLimit
	PerCallLimit = uint64(math.MaxInt64)

	// RuntimeCELCostBudget is the overall cost budget for runtime CEL validation cost per CustomResource
	//TODO: pick the RuntimeCELCostBudget
	RuntimeCELCostBudget = math.MaxInt64
)

// CompilationResult represents the cel compilation result for one rule
type CompilationResult struct {
	Program cel.Program
	Error   *Error

	// If true, the compiled expression contains a reference to the identifier "oldSelf", and its corresponding rule
	// is implicitly a transition rule.
	TransitionRule bool
}

// Compile compiles all the XValidations rules (without recursing into the schema) and returns a slice containing a
// CompilationResult for each ValidationRule, or an error.
// Each CompilationResult may contain:
/// - non-nil Program, nil Error: The program was compiled successfully
//  - nil Program, non-nil Error: Compilation resulted in an error
//  - nil Program, nil Error: The provided rule was empty so compilation was not attempted
// perCallLimit was added for testing purpose only. Callers should always use const PerCallLimit as input.
func Compile(s *schema.Structural, isResourceRoot bool, perCallLimit uint64) ([]CompilationResult, error) {
	if len(s.Extensions.XValidations) == 0 {
		return nil, nil
	}
	celRules := s.Extensions.XValidations

	var propDecls []*expr.Decl
	var root *celmodel.DeclType
	var ok bool
	env, err := cel.NewEnv(
		cel.HomogeneousAggregateLiterals(),
	)
	if err != nil {
		return nil, err
	}
	reg := celmodel.NewRegistry(env)
	scopedTypeName := generateUniqueSelfTypeName()
	rt, err := celmodel.NewRuleTypes(scopedTypeName, s, isResourceRoot, reg)
	if err != nil {
		return nil, err
	}
	if rt == nil {
		return nil, nil
	}
	opts, err := rt.EnvOptions(env.TypeProvider())
	if err != nil {
		return nil, err
	}
	root, ok = rt.FindDeclType(scopedTypeName)
	if !ok {
		rootDecl := celmodel.SchemaDeclType(s, isResourceRoot)
		if rootDecl == nil {
			return nil, fmt.Errorf("rule declared on schema that does not support validation rules type: '%s' x-kubernetes-preserve-unknown-fields: '%t'", s.Type, s.XPreserveUnknownFields)
		}
		root = rootDecl.MaybeAssignTypeName(scopedTypeName)
	}
	propDecls = append(propDecls, decls.NewVar(ScopedVarName, root.ExprType()))
	propDecls = append(propDecls, decls.NewVar(OldScopedVarName, root.ExprType()))
	opts = append(opts, cel.Declarations(propDecls...), cel.HomogeneousAggregateLiterals())
	opts = append(opts, library.ExtensionLibs...)
	env, err = env.Extend(opts...)
	if err != nil {
		return nil, err
	}

	// compResults is the return value which saves a list of compilation results in the same order as x-kubernetes-validations rules.
	compResults := make([]CompilationResult, len(celRules))
	for i, rule := range celRules {
		compResults[i] = compileRule(rule, env, perCallLimit)
	}

	return compResults, nil
}

func compileRule(rule apiextensions.ValidationRule, env *cel.Env, perCallLimit uint64) (compilationResult CompilationResult) {
	if len(strings.TrimSpace(rule.Rule)) == 0 {
		// include a compilation result, but leave both program and error nil per documented return semantics of this
		// function
		return
	}
	ast, issues := env.Compile(rule.Rule)
	if issues != nil {
		compilationResult.Error = &Error{ErrorTypeInvalid, "compilation failed: " + issues.String()}
		return
	}
	if !proto.Equal(ast.ResultType(), decls.Bool) {
		compilationResult.Error = &Error{ErrorTypeInvalid, "cel expression must evaluate to a bool"}
		return
	}

	checkedExpr, err := cel.AstToCheckedExpr(ast)
	if err != nil {
		// should be impossible since env.Compile returned no issues
		compilationResult.Error = &Error{ErrorTypeInternal, "unexpected compilation error: " + err.Error()}
		return
	}
	for _, ref := range checkedExpr.ReferenceMap {
		if ref.Name == OldScopedVarName {
			compilationResult.TransitionRule = true
			break
		}
	}

	// TODO: Ideally we could configure the per expression limit at validation time and set it to the remaining overall budget, but we would either need a way to pass in a limit at evaluation time or move program creation to validation time
	prog, err := env.Program(ast, cel.EvalOptions(cel.OptOptimize, cel.OptTrackCost), cel.CostLimit(perCallLimit))
	if err != nil {
		compilationResult.Error = &Error{ErrorTypeInvalid, "program instantiation failed: " + err.Error()}
		return
	}

	compilationResult.Program = prog
	return
}

// generateUniqueSelfTypeName creates a placeholder type name to use in a CEL programs for cases
// where we do not wish to expose a stable type name to CEL validator rule authors. For this to effectively prevent
// developers from depending on the generated name (i.e. using it in CEL programs), it must be changed each time a
// CRD is created or updated.
func generateUniqueSelfTypeName() string {
	return fmt.Sprintf("selfType%d", time.Now().Nanosecond())
}
<commit_msg>Update cost budget<commit_after>/*
Copyright 2021 The Kubernetes Authors.

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

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

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

package cel

import (
	"fmt"
	"strings"
	"time"

	"github.com/google/cel-go/cel"
	"github.com/google/cel-go/checker/decls"
	expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
	"google.golang.org/protobuf/proto"

	apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
	"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library"
	celmodel "k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model"
)

const (
	// ScopedVarName is the variable name assigned to the locally scoped data element of a CEL validation
	// expression.
	ScopedVarName = "self"

	// OldScopedVarName is the variable name assigned to the existing value of the locally scoped data element of a
	// CEL validation expression.
	OldScopedVarName = "oldSelf"

	// PerCallLimit specify the actual cost limit per CEL validation call
	// current PerCallLimit gives roughly 0.1 second for each expression validation call
	PerCallLimit = 2000000

	// RuntimeCELCostBudget is the overall cost budget for runtime CEL validation cost per CustomResource
	// current RuntimeCELCostBudget gives roughly 1 seconds for CR validation
	RuntimeCELCostBudget = 20000000
)

// CompilationResult represents the cel compilation result for one rule
type CompilationResult struct {
	Program cel.Program
	Error   *Error

	// If true, the compiled expression contains a reference to the identifier "oldSelf", and its corresponding rule
	// is implicitly a transition rule.
	TransitionRule bool
}

// Compile compiles all the XValidations rules (without recursing into the schema) and returns a slice containing a
// CompilationResult for each ValidationRule, or an error.
// Each CompilationResult may contain:
/// - non-nil Program, nil Error: The program was compiled successfully
//  - nil Program, non-nil Error: Compilation resulted in an error
//  - nil Program, nil Error: The provided rule was empty so compilation was not attempted
// perCallLimit was added for testing purpose only. Callers should always use const PerCallLimit as input.
func Compile(s *schema.Structural, isResourceRoot bool, perCallLimit uint64) ([]CompilationResult, error) {
	if len(s.Extensions.XValidations) == 0 {
		return nil, nil
	}
	celRules := s.Extensions.XValidations

	var propDecls []*expr.Decl
	var root *celmodel.DeclType
	var ok bool
	env, err := cel.NewEnv(
		cel.HomogeneousAggregateLiterals(),
	)
	if err != nil {
		return nil, err
	}
	reg := celmodel.NewRegistry(env)
	scopedTypeName := generateUniqueSelfTypeName()
	rt, err := celmodel.NewRuleTypes(scopedTypeName, s, isResourceRoot, reg)
	if err != nil {
		return nil, err
	}
	if rt == nil {
		return nil, nil
	}
	opts, err := rt.EnvOptions(env.TypeProvider())
	if err != nil {
		return nil, err
	}
	root, ok = rt.FindDeclType(scopedTypeName)
	if !ok {
		rootDecl := celmodel.SchemaDeclType(s, isResourceRoot)
		if rootDecl == nil {
			return nil, fmt.Errorf("rule declared on schema that does not support validation rules type: '%s' x-kubernetes-preserve-unknown-fields: '%t'", s.Type, s.XPreserveUnknownFields)
		}
		root = rootDecl.MaybeAssignTypeName(scopedTypeName)
	}
	propDecls = append(propDecls, decls.NewVar(ScopedVarName, root.ExprType()))
	propDecls = append(propDecls, decls.NewVar(OldScopedVarName, root.ExprType()))
	opts = append(opts, cel.Declarations(propDecls...), cel.HomogeneousAggregateLiterals())
	opts = append(opts, library.ExtensionLibs...)
	env, err = env.Extend(opts...)
	if err != nil {
		return nil, err
	}

	// compResults is the return value which saves a list of compilation results in the same order as x-kubernetes-validations rules.
	compResults := make([]CompilationResult, len(celRules))
	for i, rule := range celRules {
		compResults[i] = compileRule(rule, env, perCallLimit)
	}

	return compResults, nil
}

func compileRule(rule apiextensions.ValidationRule, env *cel.Env, perCallLimit uint64) (compilationResult CompilationResult) {
	if len(strings.TrimSpace(rule.Rule)) == 0 {
		// include a compilation result, but leave both program and error nil per documented return semantics of this
		// function
		return
	}
	ast, issues := env.Compile(rule.Rule)
	if issues != nil {
		compilationResult.Error = &Error{ErrorTypeInvalid, "compilation failed: " + issues.String()}
		return
	}
	if !proto.Equal(ast.ResultType(), decls.Bool) {
		compilationResult.Error = &Error{ErrorTypeInvalid, "cel expression must evaluate to a bool"}
		return
	}

	checkedExpr, err := cel.AstToCheckedExpr(ast)
	if err != nil {
		// should be impossible since env.Compile returned no issues
		compilationResult.Error = &Error{ErrorTypeInternal, "unexpected compilation error: " + err.Error()}
		return
	}
	for _, ref := range checkedExpr.ReferenceMap {
		if ref.Name == OldScopedVarName {
			compilationResult.TransitionRule = true
			break
		}
	}

	// TODO: Ideally we could configure the per expression limit at validation time and set it to the remaining overall budget, but we would either need a way to pass in a limit at evaluation time or move program creation to validation time
	prog, err := env.Program(ast, cel.EvalOptions(cel.OptOptimize, cel.OptTrackCost), cel.CostLimit(perCallLimit))
	if err != nil {
		compilationResult.Error = &Error{ErrorTypeInvalid, "program instantiation failed: " + err.Error()}
		return
	}

	compilationResult.Program = prog
	return
}

// generateUniqueSelfTypeName creates a placeholder type name to use in a CEL programs for cases
// where we do not wish to expose a stable type name to CEL validator rule authors. For this to effectively prevent
// developers from depending on the generated name (i.e. using it in CEL programs), it must be changed each time a
// CRD is created or updated.
func generateUniqueSelfTypeName() string {
	return fmt.Sprintf("selfType%d", time.Now().Nanosecond())
}
<|endoftext|>


<commit_before><commit_msg>Testcase: Improve logging<commit_after><|endoftext|>


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

package security

import (
	"crypto/ecdsa"
	"fmt"
	"time"

	"github.com/dgrijalva/jwt-go"
)

// BuildJWT builds a JSON Web Token for the given subject and ttl, and signs it with the given private key
func BuildJWT(subject string, ttl time.Duration, privateKey []byte) (token string, err error) {
	claims := jwt.StandardClaims{
		Issuer:    subject,
		Subject:   subject,
		// IssuedAt:  time.Now().Add(-20 * time.Second).Unix()
		// NotBefore: time.Now().Add(-20 * time.Second).Unix(),
	}
	if ttl > 0 {
		claims.ExpiresAt = time.Now().Add(ttl).Unix()
	}
	tokenBuilder := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
	var key *ecdsa.PrivateKey
	key, err = jwt.ParseECPrivateKeyFromPEM(privateKey)
	if err != nil {
		return
	}
	token, err = tokenBuilder.SignedString(key)
	if err != nil {
		return
	}
	return
}

// ValidateJWT validates a JSON Web Token with the given public key
func ValidateJWT(token string, publicKey []byte) (*jwt.StandardClaims, error) {
	claims := &jwt.StandardClaims{}
	_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
			return nil, fmt.Errorf("Unexpected JWT signing method: %v", token.Header["alg"])
		}
		key, err := jwt.ParseECPublicKeyFromPEM(publicKey)
		if err != nil {
			return nil, err
		}
		return key, nil
	})
	if err != nil {
		return nil, fmt.Errorf("Unable to verify JWT: %s", err.Error())
	}
	return claims, nil
}
<commit_msg>reverted JWT changes, fixed server time instead<commit_after>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package security

import (
	"crypto/ecdsa"
	"fmt"
	"time"

	"github.com/dgrijalva/jwt-go"
)

// BuildJWT builds a JSON Web Token for the given subject and ttl, and signs it with the given private key
func BuildJWT(subject string, ttl time.Duration, privateKey []byte) (token string, err error) {
	claims := jwt.StandardClaims{
		Issuer:    subject,
		Subject:   subject,
		IssuedAt:  time.Now().Add(-20 * time.Second).Unix(),
		NotBefore: time.Now().Add(-20 * time.Second).Unix(),
	}
	if ttl > 0 {
		claims.ExpiresAt = time.Now().Add(ttl).Unix()
	}
	tokenBuilder := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
	var key *ecdsa.PrivateKey
	key, err = jwt.ParseECPrivateKeyFromPEM(privateKey)
	if err != nil {
		return
	}
	token, err = tokenBuilder.SignedString(key)
	if err != nil {
		return
	}
	return
}

// ValidateJWT validates a JSON Web Token with the given public key
func ValidateJWT(token string, publicKey []byte) (*jwt.StandardClaims, error) {
	claims := &jwt.StandardClaims{}
	_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
			return nil, fmt.Errorf("Unexpected JWT signing method: %v", token.Header["alg"])
		}
		key, err := jwt.ParseECPublicKeyFromPEM(publicKey)
		if err != nil {
			return nil, err
		}
		return key, nil
	})
	if err != nil {
		return nil, fmt.Errorf("Unable to verify JWT: %s", err.Error())
	}
	return claims, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/rsa"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strconv"
)

func inputReader(ret chan string) {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		ret <- scanner.Text()
	}
}

func mainLoop() {
	fmt.Println("Welcome to GoCoin")
	fmt.Println()
	flag.Usage()
	printHelp()

	input := make(chan string)

	go inputReader(input)

	fmt.Print("> ")
	for text := range input {
		switch text {
		case "": // do nothing, ignore
		case "cons":
			consWallet()
		case "pay":
			doPay(input)
		case "state":
			printState()
		case "wallet":
			printWallet()
		case "help":
			printHelp()
		case "quit":
			return
		default:
			fmt.Println("Unknown input, try 'help' or 'quit'")
		}

		fmt.Print("> ")
	}
}

func consWallet() {
	var total uint64
	txn := new(Transaction)

	for key, amount := range state.GetWallet() {
		if amount > 0 {
			total += amount
			txn.Inputs = append(txn.Inputs, state.GenTxnInput(key))
		}
	}

	if len(txn.Inputs) == 0 {
		fmt.Println("Wallet empty.")
		return
	}

	key := genKey()
	txn.Outputs = append(txn.Outputs, TxnOutput{key.PublicKey, total})

	state.Sign(txn)

	success := state.AddTxn(txn)
	if success {
		state.AddToWallet(key)
		network.BroadcastTxn(txn)
		fmt.Println("Wallet consolidated.")
	} else {
		fmt.Println("Failed.")
	}
}

func printWallet() {
	fmt.Printf("\n  Amount | Public Key\n")
	var total uint64
	for key, val := range state.GetWallet() {
		fmt.Printf("%8d | %s...\n", val, key.N.String()[0:40])
		total += val
	}
	fmt.Printf("\nTotal Coins: %d\n\n", total)
}

func printState() {
	state.RLock()
	defer state.RUnlock()

	fmt.Printf("\nPrimary Chain (%d Blocks)", len(state.primary.Blocks))
	printBlockChain(state.primary)

	fmt.Printf("\n%d Alternate Chains\n", len(state.alternates))

	fmt.Printf("\n%d Transactions Being Mined (+1 miner's fee)\n", state.beingMined-1)
	for _, txn := range state.pendingTxns[:state.beingMined-1] {
		printTxn(txn)
	}

	fmt.Printf("\n%d Transactions Pending\n", len(state.pendingTxns)+1-state.beingMined)
	for _, txn := range state.pendingTxns[state.beingMined-1:] {
		printTxn(txn)
	}

	fmt.Println()
}

func printBlockChain(chain *BlockChain) {
	if len(chain.Blocks) > 0 {
		fmt.Println()
	}
	for _, block := range chain.Blocks {
		fmt.Printf("\tBlock (%d Txns) - Nonce: %10d; Hash: 0x%x...",
			len(block.Txns), block.Nonce, block.Hash()[0:12])
		if len(block.Txns) > 0 {
			fmt.Println()
		}
		for _, txn := range block.Txns {
			fmt.Printf("\t\t")
			printTxn(txn)
		}
	}
}

func printTxn(txn *Transaction) {
	if txn.IsMiner() {
		fmt.Printf("Txn mined %d coins for %s\n", miningAmount,
			txn.Outputs[0].Key.N.String()[:8])
		return
	}

	switch len(txn.Outputs) {
	case 0:
		fmt.Printf("Txn from %d keys payed %d coins to nobody!?\n",
			len(txn.Inputs), txn.Total())
	case 1:
		fmt.Printf("Txn from %d keys payed %d coins to %s\n",
			len(txn.Inputs), txn.Total(), txn.Outputs[0].Key.N.String()[:8])
	default:
		fmt.Printf("Txn from %d keys payed ", len(txn.Inputs))
		for i := range txn.Outputs[:len(txn.Outputs)-1] {
			fmt.Printf("%d to %s, ", txn.Outputs[i].Amount, txn.Outputs[i].Key.N.String()[:8])
		}
		fmt.Printf("%d to %s\n", txn.Outputs[len(txn.Outputs)-1].Amount, txn.Outputs[len(txn.Outputs)-1].Key.N.String()[:8])
	}
}

func doPay(input chan string) {
	peers := network.PeerAddrList()
	if len(peers) < 1 {
		fmt.Println("No connected peers to pay.")
		return
	}

	interrupt := make(chan os.Signal)
	signal.Notify(interrupt, os.Interrupt)
	defer signal.Stop(interrupt)
	defer close(interrupt)
	defer fmt.Println()

	fmt.Println("Select your payee:")
	for i, peer := range peers {
		fmt.Printf(" %2d -- %s\n", i+1, peer)
	}

	peer := ""
	for len(peer) == 0 {
		fmt.Print(">> ")
		select {
		case text := <-input:
			i, err := strconv.Atoi(text)
			if err != nil || i < 1 || i > len(peers) {
				fmt.Println("Invalid input")
			} else {
				peer = peers[i-1]
			}
		case <-interrupt:
			return
		}
	}

	var total uint64
	for _, val := range state.GetWallet() {
		total += val
	}
	var amount uint64
	fmt.Println("Pay how much? (You have", total, "in your wallet)")
	for amount == 0 {
		fmt.Print(">> ")
		select {
		case text := <-input:
			i, err := strconv.ParseInt(text, 10, 64)
			if err != nil || i < 1 || uint64(i) > total {
				fmt.Println("Invalid input")
			} else {
				amount = uint64(i)
			}
		case <-interrupt:
			return
		}
	}

	expect, err := network.RequestPayableAddress(peer)
	if err != nil {
		fmt.Print(err)
		return
	}

	var key *rsa.PublicKey
	select {
	case key = <-expect:
	case <-interrupt:
		network.CancelPayExpectation(peer)
	}

	txn := new(Transaction)

	total = 0
	for key, val := range state.GetWallet() {
		if val > 0 {
			total += val
			txn.Inputs = append(txn.Inputs, state.GenTxnInput(key))
		}
		if total >= amount {
			break
		}
	}

	txn.Outputs = append(txn.Outputs, TxnOutput{*key, amount})
	var change *rsa.PrivateKey
	if total > amount {
		// calculate change
		change = genKey()
		txn.Outputs = append(txn.Outputs, TxnOutput{change.PublicKey, total - amount})
	}

	err = state.Sign(txn)
	if err != nil {
		fmt.Print(err)
		return
	}

	success := state.AddTxn(txn)
	if success {
		if change != nil {
			state.AddToWallet(change)
		}
		network.BroadcastTxn(txn)
		fmt.Println("Payment sent.")
	} else {
		fmt.Println("Failed, please try again.")
	}
}

func printHelp() {
	fmt.Println()
	fmt.Println("Possible commands are:")
	fmt.Println()
	fmt.Println("  state  - display blockchain and transaction state")
	fmt.Println("  wallet - display wallet")
	fmt.Println()
	fmt.Println("  cons   - consolidate wallet into a single key")
	fmt.Println("  pay    - perform a payment to another peer")
	fmt.Println()
	fmt.Println("  help   - display this help")
	fmt.Println("  quit   - shut down gocoin (your wallet will be lost)")
	fmt.Println()
}
<commit_msg>Give more instructions for input format<commit_after>package main

import (
	"bufio"
	"crypto/rsa"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strconv"
)

func inputReader(ret chan string) {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		ret <- scanner.Text()
	}
}

func mainLoop() {
	fmt.Println("Welcome to GoCoin")
	fmt.Println()
	flag.Usage()
	printHelp()

	input := make(chan string)

	go inputReader(input)

	fmt.Print("> ")
	for text := range input {
		switch text {
		case "": // do nothing, ignore
		case "cons":
			consWallet()
		case "pay":
			doPay(input)
		case "state":
			printState()
		case "wallet":
			printWallet()
		case "help":
			printHelp()
		case "quit":
			return
		default:
			fmt.Println("Unknown input, try 'help' or 'quit'")
		}

		fmt.Print("> ")
	}
}

func consWallet() {
	var total uint64
	txn := new(Transaction)

	for key, amount := range state.GetWallet() {
		if amount > 0 {
			total += amount
			txn.Inputs = append(txn.Inputs, state.GenTxnInput(key))
		}
	}

	if len(txn.Inputs) == 0 {
		fmt.Println("Wallet empty.")
		return
	}

	key := genKey()
	txn.Outputs = append(txn.Outputs, TxnOutput{key.PublicKey, total})

	state.Sign(txn)

	success := state.AddTxn(txn)
	if success {
		state.AddToWallet(key)
		network.BroadcastTxn(txn)
		fmt.Println("Wallet consolidated.")
	} else {
		fmt.Println("Failed.")
	}
}

func printWallet() {
	fmt.Printf("\n  Amount | Public Key\n")
	var total uint64
	for key, val := range state.GetWallet() {
		fmt.Printf("%8d | %s...\n", val, key.N.String()[0:40])
		total += val
	}
	fmt.Printf("\nTotal Coins: %d\n\n", total)
}

func printState() {
	state.RLock()
	defer state.RUnlock()

	fmt.Printf("\nPrimary Chain (%d Blocks)", len(state.primary.Blocks))
	printBlockChain(state.primary)

	fmt.Printf("\n%d Alternate Chains\n", len(state.alternates))

	fmt.Printf("\n%d Transactions Being Mined (+1 miner's fee)\n", state.beingMined-1)
	for _, txn := range state.pendingTxns[:state.beingMined-1] {
		printTxn(txn)
	}

	fmt.Printf("\n%d Transactions Pending\n", len(state.pendingTxns)+1-state.beingMined)
	for _, txn := range state.pendingTxns[state.beingMined-1:] {
		printTxn(txn)
	}

	fmt.Println()
}

func printBlockChain(chain *BlockChain) {
	if len(chain.Blocks) > 0 {
		fmt.Println()
	}
	for _, block := range chain.Blocks {
		fmt.Printf("\tBlock (%d Txns) - Nonce: %10d; Hash: 0x%x...",
			len(block.Txns), block.Nonce, block.Hash()[0:12])
		if len(block.Txns) > 0 {
			fmt.Println()
		}
		for _, txn := range block.Txns {
			fmt.Printf("\t\t")
			printTxn(txn)
		}
	}
}

func printTxn(txn *Transaction) {
	if txn.IsMiner() {
		fmt.Printf("Txn mined %d coins for %s\n", miningAmount,
			txn.Outputs[0].Key.N.String()[:8])
		return
	}

	switch len(txn.Outputs) {
	case 0:
		fmt.Printf("Txn from %d keys payed %d coins to nobody!?\n",
			len(txn.Inputs), txn.Total())
	case 1:
		fmt.Printf("Txn from %d keys payed %d coins to %s\n",
			len(txn.Inputs), txn.Total(), txn.Outputs[0].Key.N.String()[:8])
	default:
		fmt.Printf("Txn from %d keys payed ", len(txn.Inputs))
		for i := range txn.Outputs[:len(txn.Outputs)-1] {
			fmt.Printf("%d to %s, ", txn.Outputs[i].Amount, txn.Outputs[i].Key.N.String()[:8])
		}
		fmt.Printf("%d to %s\n", txn.Outputs[len(txn.Outputs)-1].Amount, txn.Outputs[len(txn.Outputs)-1].Key.N.String()[:8])
	}
}

func doPay(input chan string) {
	peers := network.PeerAddrList()
	if len(peers) < 1 {
		fmt.Println("No connected peers to pay.")
		return
	}

	interrupt := make(chan os.Signal)
	signal.Notify(interrupt, os.Interrupt)
	defer signal.Stop(interrupt)
	defer close(interrupt)
	defer fmt.Println()

	fmt.Println("Select your payee:")
	for i, peer := range peers {
		fmt.Printf(" %2d -- %s\n", i+1, peer)
	}
	fmt.Println("Enter just the peer ID (eg \"1\")")

	peer := ""
	for len(peer) == 0 {
		fmt.Print(">> ")
		select {
		case text := <-input:
			i, err := strconv.Atoi(text)
			if err != nil || i < 1 || i > len(peers) {
				fmt.Println("Invalid input")
			} else {
				peer = peers[i-1]
			}
		case <-interrupt:
			return
		}
	}

	var total uint64
	for _, val := range state.GetWallet() {
		total += val
	}
	var amount uint64
	fmt.Println("Pay how much? (You have", total, "in your wallet)")
	fmt.Println("Enter just the value (eg \"10\")")
	for amount == 0 {
		fmt.Print(">> ")
		select {
		case text := <-input:
			i, err := strconv.ParseInt(text, 10, 64)
			if err != nil || i < 1 || uint64(i) > total {
				fmt.Println("Invalid input")
			} else {
				amount = uint64(i)
			}
		case <-interrupt:
			return
		}
	}

	expect, err := network.RequestPayableAddress(peer)
	if err != nil {
		fmt.Print(err)
		return
	}

	var key *rsa.PublicKey
	select {
	case key = <-expect:
	case <-interrupt:
		network.CancelPayExpectation(peer)
	}

	txn := new(Transaction)

	total = 0
	for key, val := range state.GetWallet() {
		if val > 0 {
			total += val
			txn.Inputs = append(txn.Inputs, state.GenTxnInput(key))
		}
		if total >= amount {
			break
		}
	}

	txn.Outputs = append(txn.Outputs, TxnOutput{*key, amount})
	var change *rsa.PrivateKey
	if total > amount {
		// calculate change
		change = genKey()
		txn.Outputs = append(txn.Outputs, TxnOutput{change.PublicKey, total - amount})
	}

	err = state.Sign(txn)
	if err != nil {
		fmt.Print(err)
		return
	}

	success := state.AddTxn(txn)
	if success {
		if change != nil {
			state.AddToWallet(change)
		}
		network.BroadcastTxn(txn)
		fmt.Println("Payment sent.")
	} else {
		fmt.Println("Failed, please try again.")
	}
}

func printHelp() {
	fmt.Println()
	fmt.Println("Possible commands are:")
	fmt.Println()
	fmt.Println("  state  - display blockchain and transaction state")
	fmt.Println("  wallet - display wallet")
	fmt.Println()
	fmt.Println("  cons   - consolidate wallet into a single key")
	fmt.Println("  pay    - perform a payment to another peer")
	fmt.Println()
	fmt.Println("  help   - display this help")
	fmt.Println("  quit   - shut down gocoin (your wallet will be lost)")
	fmt.Println()
}
<|endoftext|>


<commit_before>package backends

import (
	"errors"
	"reflect"

	"github.com/RichardKnop/machinery/v1/signatures"
	"github.com/RichardKnop/machinery/v1/utils"
)

// AsyncResult represents a task result
type AsyncResult struct {
	Signature *signatures.TaskSignature
	taskState *TaskState
	backend   Backend
}

// ChordAsyncResult represents a result of a chord
type ChordAsyncResult struct {
	groupAsyncResults []*AsyncResult
	chordAsyncResult  *AsyncResult
	backend           Backend
}

// ChainAsyncResult represents a result of a chain of tasks
type ChainAsyncResult struct {
	asyncResults []*AsyncResult
	backend      Backend
}

// NewAsyncResult creates AsyncResult instance
func NewAsyncResult(signature *signatures.TaskSignature, backend Backend) *AsyncResult {
	return &AsyncResult{
		Signature: signature,
		taskState: &TaskState{},
		backend:   backend,
	}
}

// NewChordAsyncResult creates ChordAsyncResult instance
func NewChordAsyncResult(groupTasks []*signatures.TaskSignature, chordCallback *signatures.TaskSignature, backend Backend) *ChordAsyncResult {
	asyncResults := make([]*AsyncResult, len(groupTasks))
	for i, task := range groupTasks {
		asyncResults[i] = NewAsyncResult(task, backend)
	}
	return &ChordAsyncResult{
		groupAsyncResults: asyncResults,
		chordAsyncResult:  NewAsyncResult(chordCallback, backend),
		backend:           backend,
	}
}

// NewChainAsyncResult creates ChainAsyncResult instance
func NewChainAsyncResult(tasks []*signatures.TaskSignature, backend Backend) *ChainAsyncResult {
	asyncResults := make([]*AsyncResult, len(tasks))
	for i, task := range tasks {
		asyncResults[i] = NewAsyncResult(task, backend)
	}
	return &ChainAsyncResult{
		asyncResults: asyncResults,
		backend:      backend,
	}
}

// Get returns task result (synchronous blocking call)
func (asyncResult *AsyncResult) Get() (reflect.Value, error) {
	if asyncResult.backend == nil {
		return reflect.Value{}, errors.New("Result backend not configured")
	}

	for {
		asyncResult.GetState()

		// Purge state if we are using AMQP backend
		_, isAMQPBackend := asyncResult.backend.(*AMQPBackend)
		if isAMQPBackend && asyncResult.taskState.IsCompleted() {
			asyncResult.backend.PurgeState(asyncResult.taskState.TaskUUID)
		}

		if asyncResult.taskState.IsSuccess() {
			return utils.ReflectValue(
				asyncResult.taskState.Result.Type,
				asyncResult.taskState.Result.Value,
			)
		}

		if asyncResult.taskState.IsFailure() {
			return reflect.Value{}, errors.New(asyncResult.taskState.Error)
		}
	}
}

// GetState returns latest task state
func (asyncResult *AsyncResult) GetState() *TaskState {
	if asyncResult.taskState.IsCompleted() {
		return asyncResult.taskState
	}

	taskState, err := asyncResult.backend.GetState(asyncResult.Signature.UUID)
	if err == nil {
		asyncResult.taskState = taskState
	}

	return asyncResult.taskState
}

// Get returns result of a chain of tasks (synchronous blocking call)
func (chainAsyncResult *ChainAsyncResult) Get() (reflect.Value, error) {
	if chainAsyncResult.backend == nil {
		return reflect.Value{}, errors.New("Result backend not configured")
	}

	var (
		result reflect.Value
		err    error
	)

	for _, asyncResult := range chainAsyncResult.asyncResults {
		result, err = asyncResult.Get()
		if err != nil {
			return result, err
		}
	}

	return result, err
}

// Get returns result of a chord (synchronous blocking call)
func (chordAsyncResult *ChordAsyncResult) Get() (reflect.Value, error) {
	if chordAsyncResult.backend == nil {
		return reflect.Value{}, errors.New("Result backend not configured")
	}

	var (
		result reflect.Value
		err    error
	)

	for _, asyncResult := range chordAsyncResult.groupAsyncResults {
		result, err = asyncResult.Get()
		if err != nil {
			return result, err
		}
	}

	return chordAsyncResult.chordAsyncResult.Get()
}
<commit_msg>Avoid hammering and configure timeouts<commit_after>package backends

import (
	"errors"
	"reflect"
	"time"

	"github.com/RichardKnop/machinery/v1/signatures"
	"github.com/RichardKnop/machinery/v1/utils"
)

// AsyncResult represents a task result
type AsyncResult struct {
	Signature *signatures.TaskSignature
	taskState *TaskState
	backend   Backend
}

// ChordAsyncResult represents a result of a chord
type ChordAsyncResult struct {
	groupAsyncResults []*AsyncResult
	chordAsyncResult  *AsyncResult
	backend           Backend
}

// ChainAsyncResult represents a result of a chain of tasks
type ChainAsyncResult struct {
	asyncResults []*AsyncResult
	backend      Backend
}

// NewAsyncResult creates AsyncResult instance
func NewAsyncResult(signature *signatures.TaskSignature, backend Backend) *AsyncResult {
	return &AsyncResult{
		Signature: signature,
		taskState: &TaskState{},
		backend:   backend,
	}
}

// NewChordAsyncResult creates ChordAsyncResult instance
func NewChordAsyncResult(groupTasks []*signatures.TaskSignature, chordCallback *signatures.TaskSignature, backend Backend) *ChordAsyncResult {
	asyncResults := make([]*AsyncResult, len(groupTasks))
	for i, task := range groupTasks {
		asyncResults[i] = NewAsyncResult(task, backend)
	}
	return &ChordAsyncResult{
		groupAsyncResults: asyncResults,
		chordAsyncResult:  NewAsyncResult(chordCallback, backend),
		backend:           backend,
	}
}

// NewChainAsyncResult creates ChainAsyncResult instance
func NewChainAsyncResult(tasks []*signatures.TaskSignature, backend Backend) *ChainAsyncResult {
	asyncResults := make([]*AsyncResult, len(tasks))
	for i, task := range tasks {
		asyncResults[i] = NewAsyncResult(task, backend)
	}
	return &ChainAsyncResult{
		asyncResults: asyncResults,
		backend:      backend,
	}
}

// Get returns task result (synchronous blocking call)
func (asyncResult *AsyncResult) Get() (reflect.Value, error) {
	if asyncResult.backend == nil {
		return reflect.Value{}, errors.New("Result backend not configured")
	}

	for {
		asyncResult.GetState()

		// Purge state if we are using AMQP backend
		_, isAMQPBackend := asyncResult.backend.(*AMQPBackend)
		if isAMQPBackend && asyncResult.taskState.IsCompleted() {
			asyncResult.backend.PurgeState(asyncResult.taskState.TaskUUID)
		}

		if asyncResult.taskState.IsSuccess() {
			return utils.ReflectValue(
				asyncResult.taskState.Result.Type,
				asyncResult.taskState.Result.Value,
			)
		}

		if asyncResult.taskState.IsFailure() {
			return reflect.Value{}, errors.New(asyncResult.taskState.Error)
		}
	}
}

// Get returns task result limited in time(synchronous blocking call)
func (asyncResult *AsyncResult) GetWithTimeout(timeoutD, sleepD time.Duration) (reflect.Value, error) {
       if asyncResult.backend == nil {
               return reflect.Value{}, errors.New("Result backend not configured")
       }

       timeout := time.NewTimer(timeoutD)

	for {
               select {
               case <-timeout.C:
                       return reflect.Value{}, errors.New("Timeout reached")
               default:
                       asyncResult.GetState()

                       // Purge state if we are using AMQP backend
                       _, isAMQPBackend := asyncResult.backend.(*AMQPBackend)
                       if isAMQPBackend && asyncResult.taskState.IsCompleted() {
                               asyncResult.backend.PurgeState(asyncResult.taskState.TaskUUID)
                       }

                       if asyncResult.taskState.IsSuccess() {
                               return utils.ReflectValue(
                                       asyncResult.taskState.Result.Type,
                                       asyncResult.taskState.Result.Value,
                               )
                       }

                       if asyncResult.taskState.IsFailure() {
                               return reflect.Value{}, errors.New(asyncResult.taskState.Error)
                       }
                       time.Sleep(sleepD)
               }
       }
}

// GetState returns latest task state
func (asyncResult *AsyncResult) GetState() *TaskState {
	if asyncResult.taskState.IsCompleted() {
		return asyncResult.taskState
	}

	taskState, err := asyncResult.backend.GetState(asyncResult.Signature.UUID)
	if err == nil {
		asyncResult.taskState = taskState
	}

	return asyncResult.taskState
}

// Get returns result of a chain of tasks (synchronous blocking call)
func (chainAsyncResult *ChainAsyncResult) Get() (reflect.Value, error) {
	if chainAsyncResult.backend == nil {
		return reflect.Value{}, errors.New("Result backend not configured")
	}

	var (
		result reflect.Value
		err    error
	)

	for _, asyncResult := range chainAsyncResult.asyncResults {
		result, err = asyncResult.Get()
		if err != nil {
			return result, err
		}
	}

	return result, err
}

// Get returns result of a chord (synchronous blocking call)
func (chordAsyncResult *ChordAsyncResult) Get() (reflect.Value, error) {
	if chordAsyncResult.backend == nil {
		return reflect.Value{}, errors.New("Result backend not configured")
	}

	var (
		result reflect.Value
		err    error
	)

	for _, asyncResult := range chordAsyncResult.groupAsyncResults {
		result, err = asyncResult.Get()
		if err != nil {
			return result, err
		}
	}

	return chordAsyncResult.chordAsyncResult.Get()
}
<|endoftext|>