<commit_before>// Copyright 2015 CoreOS, 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 iptables

import (
	"bytes"
	"fmt"
	"log"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"syscall"
)

// Adds the output of stderr to exec.ExitError
type Error struct {
	exec.ExitError
	msg string
}

func (e *Error) ExitStatus() int {
	return e.Sys().(syscall.WaitStatus).ExitStatus()
}

func (e *Error) Error() string {
	return fmt.Sprintf("exit status %v: %v", e.ExitStatus(), e.msg)
}

type IPTables struct {
	path     string
	hasCheck bool
	hasWait  bool

	fmu *fileLock
}

func New() (*IPTables, error) {
	path, err := exec.LookPath("iptables")
	if err != nil {
		return nil, err
	}
	checkPresent, waitPresent, err := getIptablesCommandSupport()
	if err != nil {
		log.Printf("Error checking iptables version, assuming version at least 1.4.20: %v", err)
		checkPresent = true
		waitPresent = true
	}
	ipt := IPTables{
		path:     path,
		hasCheck: checkPresent,
		hasWait:  waitPresent,
	}
	if !waitPresent {
		ipt.fmu, err = newXtablesFileLock()
		if err != nil {
			return nil, err
		}
	}
	return &ipt, nil
}

// Exists checks if given rulespec in specified table/chain exists
func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) {
	if !ipt.hasCheck {
		cmd := append([]string{"-A", chain}, rulespec...)
		return existsForOldIpTables(table, strings.Join(cmd, " "))
	}
	cmd := append([]string{"-t", table, "-C", chain}, rulespec...)
	err := ipt.run(cmd...)
	switch {
	case err == nil:
		return true, nil
	case err.(*Error).ExitStatus() == 1:
		return false, nil
	default:
		return false, err
	}
}

// Insert inserts rulespec to specified table/chain (in specified pos)
func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error {
	cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...)
	return ipt.run(cmd...)
}

// Append appends rulespec to specified table/chain
func (ipt *IPTables) Append(table, chain string, rulespec ...string) error {
	cmd := append([]string{"-t", table, "-A", chain}, rulespec...)
	return ipt.run(cmd...)
}

// AppendUnique acts like Append except that it won't add a duplicate
func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error {
	exists, err := ipt.Exists(table, chain, rulespec...)
	if err != nil {
		return err
	}

	if !exists {
		return ipt.Append(table, chain, rulespec...)
	}

	return nil
}

// Delete removes rulespec in specified table/chain
func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error {
	cmd := append([]string{"-t", table, "-D", chain}, rulespec...)
	return ipt.run(cmd...)
}

// List rules in specified table/chain
func (ipt *IPTables) List(table, chain string) ([]string, error) {
	var stdout, stderr bytes.Buffer
	args := []string{ipt.path, "-t", table, "-S", chain}

	if ipt.hasWait {
		args = append(args, "--wait")
	} else {
		ul, err := ipt.fmu.tryLock()
		if err != nil {
			return nil, err
		}
		defer ul.Unlock()
	}

	cmd := exec.Cmd{
		Path:   ipt.path,
		Args:   args,
		Stdout: &stdout,
		Stderr: &stderr,
	}

	if err := cmd.Run(); err != nil {
		return nil, &Error{*(err.(*exec.ExitError)), stderr.String()}
	}

	rules := strings.Split(stdout.String(), "\n")
	if len(rules) > 0 && rules[len(rules)-1] == "" {
		rules = rules[:len(rules)-1]
	}

	return rules, nil
}

func (ipt *IPTables) NewChain(table, chain string) error {
	return ipt.run("-t", table, "-N", chain)
}

// ClearChain flushed (deletes all rules) in the specified table/chain.
// If the chain does not exist, a new one will be created
func (ipt *IPTables) ClearChain(table, chain string) error {
	err := ipt.NewChain(table, chain)

	switch {
	case err == nil:
		return nil
	case err.(*Error).ExitStatus() == 1:
		// chain already exists. Flush (clear) it.
		return ipt.run("-t", table, "-F", chain)
	default:
		return err
	}
}

// DeleteChain deletes the chain in the specified table.
// The chain must be empty
func (ipt *IPTables) DeleteChain(table, chain string) error {
	return ipt.run("-t", table, "-X", chain)
}

func (ipt *IPTables) run(args ...string) error {
	var stderr bytes.Buffer
	if ipt.hasWait {
		args = append([]string{"--wait"}, args...)
	} else {
		ul, err := ipt.fmu.tryLock()
		if err != nil {
			return err
		}
		defer ul.Unlock()
	}

	cmd := exec.Cmd{
		Path:   ipt.path,
		Args:   append([]string{ipt.path}, args...),
		Stderr: &stderr,
	}

	if err := cmd.Run(); err != nil {
		return &Error{*(err.(*exec.ExitError)), stderr.String()}
	}

	return nil
}

// Checks if iptables has the "-C" and "--wait" flag
func getIptablesCommandSupport() (bool, bool, error) {
	vstring, err := getIptablesVersionString()
	if err != nil {
		return false, false, err
	}

	v1, v2, v3, err := extractIptablesVersion(vstring)
	if err != nil {
		return false, false, err
	}

	return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), nil
}

// getIptablesVersion returns the first three components of the iptables version.
// e.g. "iptables v1.3.66" would return (1, 3, 66, nil)
func extractIptablesVersion(str string) (int, int, int, error) {
	versionMatcher := regexp.MustCompile("v([0-9]+)\\.([0-9]+)\\.([0-9]+)")
	result := versionMatcher.FindStringSubmatch(str)
	if result == nil {
		return 0, 0, 0, fmt.Errorf("no iptables version found in string: %s", str)
	}

	v1, err := strconv.Atoi(result[1])
	if err != nil {
		return 0, 0, 0, err
	}

	v2, err := strconv.Atoi(result[2])
	if err != nil {
		return 0, 0, 0, err
	}

	v3, err := strconv.Atoi(result[3])
	if err != nil {
		return 0, 0, 0, err
	}

	return v1, v2, v3, nil
}

// Runs "iptables --version" to get the version string
func getIptablesVersionString() (string, error) {
	cmd := exec.Command("iptables", "--version")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return "", err
	}
	return out.String(), nil
}

// Checks if an iptables version is after 1.4.11, when --check was added
func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool {
	if v1 > 1 {
		return true
	}
	if v1 == 1 && v2 > 4 {
		return true
	}
	if v1 == 1 && v2 == 4 && v3 >= 11 {
		return true
	}
	return false
}

// Checks if an iptables version is after 1.4.20, when --wait was added
func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool {
	if v1 > 1 {
		return true
	}
	if v1 == 1 && v2 > 4 {
		return true
	}
	if v1 == 1 && v2 == 4 && v3 >= 20 {
		return true
	}
	return false
}

// Checks if a rule specification exists for a table
func existsForOldIpTables(table string, ruleSpec string) (bool, error) {
	cmd := exec.Command("iptables", "-t", table, "-S")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return false, err
	}
	rules := out.String()
	return strings.Contains(rules, ruleSpec), nil
}
<commit_msg>*: refactor iptables invocation<commit_after>// Copyright 2015 CoreOS, 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 iptables

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"syscall"
)

// Adds the output of stderr to exec.ExitError
type Error struct {
	exec.ExitError
	msg string
}

func (e *Error) ExitStatus() int {
	return e.Sys().(syscall.WaitStatus).ExitStatus()
}

func (e *Error) Error() string {
	return fmt.Sprintf("exit status %v: %v", e.ExitStatus(), e.msg)
}

type IPTables struct {
	path     string
	hasCheck bool
	hasWait  bool

	fmu *fileLock
}

func New() (*IPTables, error) {
	path, err := exec.LookPath("iptables")
	if err != nil {
		return nil, err
	}
	checkPresent, waitPresent, err := getIptablesCommandSupport()
	if err != nil {
		log.Printf("Error checking iptables version, assuming version at least 1.4.20: %v", err)
		checkPresent = true
		waitPresent = true
	}
	ipt := IPTables{
		path:     path,
		hasCheck: checkPresent,
		hasWait:  waitPresent,
	}
	if !waitPresent {
		ipt.fmu, err = newXtablesFileLock()
		if err != nil {
			return nil, err
		}
	}
	return &ipt, nil
}

// Exists checks if given rulespec in specified table/chain exists
func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) {
	if !ipt.hasCheck {
		return ipt.existsForOldIptables(table, chain, rulespec)

	}
	cmd := append([]string{"-t", table, "-C", chain}, rulespec...)
	err := ipt.run(cmd...)
	switch {
	case err == nil:
		return true, nil
	case err.(*Error).ExitStatus() == 1:
		return false, nil
	default:
		return false, err
	}
}

// Insert inserts rulespec to specified table/chain (in specified pos)
func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error {
	cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...)
	return ipt.run(cmd...)
}

// Append appends rulespec to specified table/chain
func (ipt *IPTables) Append(table, chain string, rulespec ...string) error {
	cmd := append([]string{"-t", table, "-A", chain}, rulespec...)
	return ipt.run(cmd...)
}

// AppendUnique acts like Append except that it won't add a duplicate
func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error {
	exists, err := ipt.Exists(table, chain, rulespec...)
	if err != nil {
		return err
	}

	if !exists {
		return ipt.Append(table, chain, rulespec...)
	}

	return nil
}

// Delete removes rulespec in specified table/chain
func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error {
	cmd := append([]string{"-t", table, "-D", chain}, rulespec...)
	return ipt.run(cmd...)
}

// List rules in specified table/chain
func (ipt *IPTables) List(table, chain string) ([]string, error) {
	args := []string{"-t", table, "-S", chain}
	var stdout bytes.Buffer
	if err := ipt.runWithOutput(args, &stdout); err != nil {
		return nil, err
	}

	rules := strings.Split(stdout.String(), "\n")
	if len(rules) > 0 && rules[len(rules)-1] == "" {
		rules = rules[:len(rules)-1]
	}

	return rules, nil
}

func (ipt *IPTables) NewChain(table, chain string) error {
	return ipt.run("-t", table, "-N", chain)
}

// ClearChain flushed (deletes all rules) in the specified table/chain.
// If the chain does not exist, a new one will be created
func (ipt *IPTables) ClearChain(table, chain string) error {
	err := ipt.NewChain(table, chain)

	switch {
	case err == nil:
		return nil
	case err.(*Error).ExitStatus() == 1:
		// chain already exists. Flush (clear) it.
		return ipt.run("-t", table, "-F", chain)
	default:
		return err
	}
}

// DeleteChain deletes the chain in the specified table.
// The chain must be empty
func (ipt *IPTables) DeleteChain(table, chain string) error {
	return ipt.run("-t", table, "-X", chain)
}

// run runs an iptables command with the given arguments, ignoring
// any stdout output
func (ipt *IPTables) run(args ...string) error {
	return ipt.runWithOutput(args, nil)
}

// runWithOutput runs an iptables command with the given arguments,
// writing any stdout output to the given writer
func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
	args = append([]string{ipt.path}, args...)
	if ipt.hasWait {
		args = append(args, "--wait")
	} else {
		ul, err := ipt.fmu.tryLock()
		if err != nil {
			return err
		}
		defer ul.Unlock()
	}

	var stderr bytes.Buffer
	cmd := exec.Cmd{
		Path:   ipt.path,
		Args:   args,
		Stdout: stdout,
		Stderr: &stderr,
	}

	if err := cmd.Run(); err != nil {
		return &Error{*(err.(*exec.ExitError)), stderr.String()}
	}

	return nil
}

// Checks if iptables has the "-C" and "--wait" flag
func getIptablesCommandSupport() (bool, bool, error) {
	vstring, err := getIptablesVersionString()
	if err != nil {
		return false, false, err
	}

	v1, v2, v3, err := extractIptablesVersion(vstring)
	if err != nil {
		return false, false, err
	}

	return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), nil
}

// getIptablesVersion returns the first three components of the iptables version.
// e.g. "iptables v1.3.66" would return (1, 3, 66, nil)
func extractIptablesVersion(str string) (int, int, int, error) {
	versionMatcher := regexp.MustCompile("v([0-9]+)\\.([0-9]+)\\.([0-9]+)")
	result := versionMatcher.FindStringSubmatch(str)
	if result == nil {
		return 0, 0, 0, fmt.Errorf("no iptables version found in string: %s", str)
	}

	v1, err := strconv.Atoi(result[1])
	if err != nil {
		return 0, 0, 0, err
	}

	v2, err := strconv.Atoi(result[2])
	if err != nil {
		return 0, 0, 0, err
	}

	v3, err := strconv.Atoi(result[3])
	if err != nil {
		return 0, 0, 0, err
	}

	return v1, v2, v3, nil
}

// Runs "iptables --version" to get the version string
func getIptablesVersionString() (string, error) {
	cmd := exec.Command("iptables", "--version")
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return "", err
	}
	return out.String(), nil
}

// Checks if an iptables version is after 1.4.11, when --check was added
func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool {
	if v1 > 1 {
		return true
	}
	if v1 == 1 && v2 > 4 {
		return true
	}
	if v1 == 1 && v2 == 4 && v3 >= 11 {
		return true
	}
	return false
}

// Checks if an iptables version is after 1.4.20, when --wait was added
func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool {
	if v1 > 1 {
		return true
	}
	if v1 == 1 && v2 > 4 {
		return true
	}
	if v1 == 1 && v2 == 4 && v3 >= 20 {
		return true
	}
	return false
}

// Checks if a rule specification exists for a table
func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) {
	rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ")
	args := []string{"-t", table, "-S"}
	var stdout bytes.Buffer
	err := ipt.runWithOutput(args, &stdout)
	if err != nil {
		return false, err
	}
	return strings.Contains(stdout.String(), rs), nil
}
<|endoftext|>


<commit_before>package message

import (
	"strings"
)

// Check if this Message is CTCP message.
func (m *Message) IsCTCP() bool {
	return m.Body[0] == '\001' && m.Body[len(m.Body)-1] == '\001'
}

// Return the nick whose this Message came from.
func (m *Message) FromNick() string {
	offset := strings.Index(m.From, "!")
	if offset == -1 {
		offset = len(m.From)
	}
	return m.From[:offset]
}

// -----------------------------------------------

// CTCP message quoted with \001
func TagCTCP(cmd string) string {
	quoted := "\001" + lowQuote(cmd) + "\001"
	return quoted
}

// Remove CTCP \001 quote,
// return the original string if it has no quote.
func UntagCTCP(cmd string) string {
	if cmd[0] != '\001' || cmd[len(cmd)-1] != '\001' {
		return ctcpDequote(cmd)
	}
	return ctcpDequote(cmd[1 : len(cmd)-1])
}

func ctcpQuote(cmd string) string {
	str := strings.Replace(cmd, `\`, `\\`, -1)
	return strings.Replace(str, "\001", `\a`, -1)
}

func ctcpDequote(cmd string) string {
	str := strings.Replace(lowDequote(cmd), `\a`, "\001", -1)
	return cpyExclude(str, 0134)
}

func lowQuote(cmd string) string {
	str := strings.Replace(ctcpQuote(cmd), "\020", "\020\020", -1)
	str = strings.Replace(str, "\r", "\020r", -1)
	str = strings.Replace(str, "\n", "\020n", -1)
	return strings.Replace(str, "\000", "\0200", -1)
}

func lowDequote(cmd string) string {
	str := strings.Replace(cmd, "\0200", "\000", -1)
	str = strings.Replace(str, "\020n", "\n", -1)
	str = strings.Replace(str, "\020r", "\r", -1)
	return cpyExclude(str, 020)
}

func cpyExclude(str string, chr byte) string {
	var tch = []byte{}
	for i := 0; i < len(str); i++ {
		if str[i] == chr {
			switch {
			case i+1 == len(str):
				continue
			case str[i+1] == chr:
				i++
				tch = append(tch, str[i])
				continue
			default:
				continue
			}
		}
		tch = append(tch, str[i])
	}
	return string(tch)
}
<commit_msg>Refactor message package, publish Quote and Dequote function<commit_after>// message package implements irc message parser, etc.
package message

import (
	"strings"
)

// IsCTCP check if this Message is CTCP message.
func (m *Message) IsCTCP() bool {
	return m.Body[0] == '\001' && m.Body[len(m.Body)-1] == '\001'
}

// FromNick returns the nick whose this Message came from.
func (m *Message) FromNick() string {
	offset := strings.Index(m.From, "!")
	if offset == -1 {
		offset = len(m.From)
	}
	return m.From[:offset]
}

// -----------------------------------------------

// TagCTCP tags message `cmd` with CTCP delimiter and
// escape unsafe characters.
func TagCTCP(cmd string) string {
	quoted := "\001" + Quote(cmd) + "\001"
	return quoted
}

// UntagCTCP removes CTCP delimiter tag and unescape
// quoted characters.
func UntagCTCP(cmd string) string {
	if cmd[0] != '\001' || cmd[len(cmd)-1] != '\001' {
		return Dequote(cmd)
	}
	return Dequote(cmd[1 : len(cmd)-1])
}

// Dequote unescapes quoted characters from cmd.
func Dequote(cmd string) string {
	str := strings.Replace(lowDequote(cmd), `\a`, "\001", -1)
	return cpyExclude(str, 0134)
}

// Quote escapes unsafe characters drom cmd.
func Quote(cmd string) string {
	str := strings.Replace(ctcpQuote(cmd), "\020", "\020\020", -1)
	str = strings.Replace(str, "\r", "\020r", -1)
	str = strings.Replace(str, "\n", "\020n", -1)
	return strings.Replace(str, "\000", "\0200", -1)
}

func ctcpQuote(cmd string) string {
	str := strings.Replace(cmd, `\`, `\\`, -1)
	return strings.Replace(str, "\001", `\a`, -1)
}

func lowDequote(cmd string) string {
	str := strings.Replace(cmd, "\0200", "\000", -1)
	str = strings.Replace(str, "\020n", "\n", -1)
	str = strings.Replace(str, "\020r", "\r", -1)
	return cpyExclude(str, 020)
}

func cpyExclude(str string, chr byte) string {
	var tch = []byte{}
	for i := 0; i < len(str); i++ {
		if str[i] == chr {
			switch {
			case i+1 == len(str):
				continue
			case str[i+1] == chr:
				i++
				tch = append(tch, str[i])
				continue
			default:
				continue
			}
		}
		tch = append(tch, str[i])
	}
	return string(tch)
}
<|endoftext|>


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

import (
	"context"
	"flag"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"time"

	"github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	prowapi "k8s.io/test-infra/prow/apis/prowjobs/v1"
	prowv1 "k8s.io/test-infra/prow/client/clientset/versioned/typed/prowjobs/v1"
	"k8s.io/test-infra/prow/crier/reporters/pubsub"
	"k8s.io/test-infra/prow/flagutil"
	configflagutil "k8s.io/test-infra/prow/flagutil/config"
	"k8s.io/test-infra/prow/interrupts"
	"k8s.io/test-infra/prow/logrusutil"
	"k8s.io/test-infra/prow/metrics"
	"k8s.io/test-infra/prow/pjutil/pprof"
	"k8s.io/test-infra/prow/pubsub/subscriber"
)

var (
	flagOptions *options
)

type options struct {
	client                  flagutil.KubernetesOptions
	github                  flagutil.GitHubOptions
	port                    int
	inRepoConfigCacheSize   int
	inRepoConfigCacheCopies int
	cookiefilePath          string

	config configflagutil.ConfigOptions

	dryRun                 bool
	gracePeriod            time.Duration
	instrumentationOptions flagutil.InstrumentationOptions
}

type kubeClient struct {
	client prowv1.ProwJobInterface
	dryRun bool
}

func (c *kubeClient) Create(ctx context.Context, job *prowapi.ProwJob, o metav1.CreateOptions) (*prowapi.ProwJob, error) {
	if c.dryRun {
		return job, nil
	}
	return c.client.Create(ctx, job, o)
}

func init() {
	flagOptions = &options{config: configflagutil.ConfigOptions{ConfigPath: "/etc/config/config.yaml"}}
	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)

	fs.IntVar(&flagOptions.port, "port", 80, "HTTP Port.")

	fs.BoolVar(&flagOptions.dryRun, "dry-run", true, "Dry run for testing. Uses API tokens but does not mutate.")
	fs.DurationVar(&flagOptions.gracePeriod, "grace-period", 180*time.Second, "On shutdown, try to handle remaining events for the specified duration. ")
	fs.IntVar(&flagOptions.inRepoConfigCacheSize, "in-repo-config-cache-size", 1000, "Cache size for ProwYAMLs read from in-repo configs.")
	fs.IntVar(&flagOptions.inRepoConfigCacheCopies, "in-repo-config-cache-copies", 1, "Copy of caches for ProwYAMLs read from in-repo configs.")
	fs.StringVar(&flagOptions.cookiefilePath, "cookiefile", "", "Path to git http.cookiefile, leave empty for github or anonymous")
	flagOptions.config.AddFlags(fs)
	flagOptions.client.AddFlags(fs)
	flagOptions.github.AddFlags(fs)
	flagOptions.instrumentationOptions.AddFlags(fs)

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

func main() {
	logrusutil.ComponentInit()

	configAgent, err := flagOptions.config.ConfigAgent()
	if err != nil {
		logrus.WithError(err).Fatal("Error starting config agent.")
	}

	prowjobClient, err := flagOptions.client.ProwJobClient(configAgent.Config().ProwJobNamespace, flagOptions.dryRun)
	if err != nil {
		logrus.WithError(err).Fatal("unable to create prow job client")
	}
	kubeClient := &kubeClient{
		client: prowjobClient,
		dryRun: flagOptions.dryRun,
	}

	promMetrics := subscriber.NewMetrics()

	defer interrupts.WaitForGracefulShutdown()

	// Expose prometheus and pprof metrics
	metrics.ExposeMetrics("sub", configAgent.Config().PushGateway, flagOptions.instrumentationOptions.MetricsPort)
	pprof.Instrument(flagOptions.instrumentationOptions)

	// If we are provided credentials for Git hosts, use them. These credentials
	// hold per-host information in them so it's safe to set them globally.
	if flagOptions.cookiefilePath != "" {
		cmd := exec.Command("git", "config", "--global", "http.cookiefile", flagOptions.cookiefilePath)
		if err := cmd.Run(); err != nil {
			logrus.WithError(err).Fatal("unable to set cookiefile")
		}
	}

	cacheGetter := subscriber.InRepoConfigCacheGetter{
		CacheSize:     flagOptions.inRepoConfigCacheSize,
		CacheCopies:   flagOptions.inRepoConfigCacheCopies,
		Agent:         configAgent,
		GitHubOptions: flagOptions.github,
		DryRun:        flagOptions.dryRun,
	}

	s := &subscriber.Subscriber{
		ConfigAgent:             configAgent,
		Metrics:                 promMetrics,
		ProwJobClient:           kubeClient,
		Reporter:                pubsub.NewReporter(configAgent.Config), // reuse crier reporter
		InRepoConfigCacheGetter: &cacheGetter,
	}

	subMux := http.NewServeMux()
	// Return 200 on / for health checks.
	subMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {})

	// Setting up Pull Server
	logrus.Info("Setting up Pull Server")
	pullServer := subscriber.NewPullServer(s)
	interrupts.Run(func(ctx context.Context) {
		if err := pullServer.Run(ctx); err != nil {
			logrus.WithError(err).Fatal("Failed to run Pull Server")
		}
	})

	httpServer := &http.Server{Addr: ":" + strconv.Itoa(flagOptions.port), Handler: subMux}
	interrupts.ListenAndServe(httpServer, flagOptions.gracePeriod)
}
<commit_msg>Modernize sub options handling<commit_after>/*
Copyright 2018 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 main

import (
	"context"
	"errors"
	"flag"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"time"

	"github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/test-infra/pkg/flagutil"
	prowapi "k8s.io/test-infra/prow/apis/prowjobs/v1"
	prowv1 "k8s.io/test-infra/prow/client/clientset/versioned/typed/prowjobs/v1"
	"k8s.io/test-infra/prow/crier/reporters/pubsub"
	prowflagutil "k8s.io/test-infra/prow/flagutil"
	configflagutil "k8s.io/test-infra/prow/flagutil/config"
	"k8s.io/test-infra/prow/interrupts"
	"k8s.io/test-infra/prow/logrusutil"
	"k8s.io/test-infra/prow/metrics"
	"k8s.io/test-infra/prow/pjutil/pprof"
	"k8s.io/test-infra/prow/pubsub/subscriber"
)

type options struct {
	client                  prowflagutil.KubernetesOptions
	github                  prowflagutil.GitHubOptions
	port                    int
	inRepoConfigCacheSize   int
	inRepoConfigCacheCopies int
	cookiefilePath          string

	config configflagutil.ConfigOptions

	dryRun                 bool
	gracePeriod            time.Duration
	instrumentationOptions prowflagutil.InstrumentationOptions
}

func (o *options) validate() error {
	var errs []error
	for _, group := range []flagutil.OptionGroup{&o.client, &o.github, &o.instrumentationOptions, &o.config} {
		if err := group.Validate(o.dryRun); err != nil {
			errs = append(errs, err)
		}
	}

	if o.inRepoConfigCacheCopies < 1 {
		errs = append(errs, errors.New("in-repo-config-cache-copies must be at least 1"))
	}
	return utilerrors.NewAggregate(errs)
}

func gatherOptions(fs *flag.FlagSet, args ...string) options {
	var o options
	fs.IntVar(&o.port, "port", 80, "HTTP Port.")
	fs.BoolVar(&o.dryRun, "dry-run", true, "Dry run for testing. Uses API tokens but does not mutate.")
	fs.DurationVar(&o.gracePeriod, "grace-period", 180*time.Second, "On shutdown, try to handle remaining events for the specified duration. ")
	fs.IntVar(&o.inRepoConfigCacheSize, "in-repo-config-cache-size", 1000, "Cache size for ProwYAMLs read from in-repo configs.")
	fs.IntVar(&o.inRepoConfigCacheCopies, "in-repo-config-cache-copies", 1, "Copy of caches for ProwYAMLs read from in-repo configs.")
	fs.StringVar(&o.cookiefilePath, "cookiefile", "", "Path to git http.cookiefile, leave empty for github or anonymous")
	for _, group := range []flagutil.OptionGroup{&o.client, &o.github, &o.instrumentationOptions, &o.config} {
		group.AddFlags(fs)
	}

	fs.Parse(args)

	return o
}

type kubeClient struct {
	client prowv1.ProwJobInterface
	dryRun bool
}

func (c *kubeClient) Create(ctx context.Context, job *prowapi.ProwJob, o metav1.CreateOptions) (*prowapi.ProwJob, error) {
	if c.dryRun {
		return job, nil
	}
	return c.client.Create(ctx, job, o)
}

func main() {
	logrusutil.ComponentInit()

	o := gatherOptions(flag.NewFlagSet(os.Args[0], flag.ExitOnError), os.Args[1:]...)
	if err := o.validate(); err != nil {
		logrus.WithError(err).Fatal("Invalid options")
	}

	configAgent, err := o.config.ConfigAgent()
	if err != nil {
		logrus.WithError(err).Fatal("Error starting config agent.")
	}

	prowjobClient, err := o.client.ProwJobClient(configAgent.Config().ProwJobNamespace, o.dryRun)
	if err != nil {
		logrus.WithError(err).Fatal("unable to create prow job client")
	}
	kubeClient := &kubeClient{
		client: prowjobClient,
		dryRun: o.dryRun,
	}

	promMetrics := subscriber.NewMetrics()

	defer interrupts.WaitForGracefulShutdown()

	// Expose prometheus and pprof metrics
	metrics.ExposeMetrics("sub", configAgent.Config().PushGateway, o.instrumentationOptions.MetricsPort)
	pprof.Instrument(o.instrumentationOptions)

	// If we are provided credentials for Git hosts, use them. These credentials
	// hold per-host information in them so it's safe to set them globally.
	if o.cookiefilePath != "" {
		cmd := exec.Command("git", "config", "--global", "http.cookiefile", o.cookiefilePath)
		if err := cmd.Run(); err != nil {
			logrus.WithError(err).Fatal("unable to set cookiefile")
		}
	}

	cacheGetter := subscriber.InRepoConfigCacheGetter{
		CacheSize:     o.inRepoConfigCacheSize,
		CacheCopies:   o.inRepoConfigCacheCopies,
		Agent:         configAgent,
		GitHubOptions: o.github,
		DryRun:        o.dryRun,
	}

	s := &subscriber.Subscriber{
		ConfigAgent:             configAgent,
		Metrics:                 promMetrics,
		ProwJobClient:           kubeClient,
		Reporter:                pubsub.NewReporter(configAgent.Config), // reuse crier reporter
		InRepoConfigCacheGetter: &cacheGetter,
	}

	subMux := http.NewServeMux()
	// Return 200 on / for health checks.
	subMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {})

	// Setting up Pull Server
	logrus.Info("Setting up Pull Server")
	pullServer := subscriber.NewPullServer(s)
	interrupts.Run(func(ctx context.Context) {
		if err := pullServer.Run(ctx); err != nil {
			logrus.WithError(err).Fatal("Failed to run Pull Server")
		}
	})

	httpServer := &http.Server{Addr: ":" + strconv.Itoa(o.port), Handler: subMux}
	interrupts.ListenAndServe(httpServer, o.gracePeriod)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package config

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
	"gopkg.in/fsnotify.v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/test-infra/prow/interrupts"
)

// Delta represents the before and after states of a Config change detected by the Agent.
type Delta struct {
	Before, After Config
}

// DeltaChan is a channel to receive config delta events when config changes.
type DeltaChan = chan<- Delta

// Agent watches a path and automatically loads the config stored
// therein.
type Agent struct {
	mut           sync.RWMutex // do not export Lock, etc methods
	c             *Config
	subscriptions []DeltaChan
}

// IsConfigMapMount determines whether the provided directory is a configmap mounted directory
func IsConfigMapMount(path string) (bool, error) {
	files, err := ioutil.ReadDir(path)
	if err != nil {
		return false, fmt.Errorf("Could not read provided directory %s: %v", path, err)
	}
	for _, file := range files {
		if file.Name() == "..data" {
			return true, nil
		}
	}
	return false, nil
}

// GetCMMountWatcher returns a function that watches a configmap mounted directory and runs the provided "eventFunc" every time
// the directory gets updated and the provided "errFunc" every time it encounters an error
func GetCMMountWatcher(eventFunc func() error, errFunc func(error, string), path string) (func(ctx context.Context), error) {
	isCMMount, err := IsConfigMapMount(path)
	if err != nil {
		return nil, err
	} else if !isCMMount {
		return nil, fmt.Errorf("Provided directory %s is not a configmap directory", path)
	}
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	err = w.Add(path)
	if err != nil {
		return nil, err
	}
	logrus.Debugf("Watching %s", path)
	dataPath := filepath.Join(path, "..data")
	return func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				if err := w.Close(); err != nil {
					errFunc(err, fmt.Sprintf("failed to close fsnotify watcher for directory %s", path))
				}
				return
			case event := <-w.Events:
				if event.Name == dataPath && event.Op == fsnotify.Create {
					err := eventFunc()
					if err != nil {
						errFunc(err, fmt.Sprintf("event function for watch directory %s failed", path))
					}
				}
			case err := <-w.Errors:
				errFunc(err, fmt.Sprintf("received fsnotify error for directory %s", path))
			}
		}
	}, nil
}

// GetFileWatcher returns a function that watches the specified file(s), running the "eventFunc" whenever an event for the file(s) occurs
// and the "errFunc" whenever an error is encountered. In this function, the eventFunc has access to the watcher, allowing the eventFunc
// to add new files/directories to be watched as needed.
func GetFileWatcher(eventFunc func(*fsnotify.Watcher) error, errFunc func(error, string), files ...string) (func(ctx context.Context), error) {
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		if err := w.Add(file); err != nil {
			return nil, err
		}
	}
	logrus.Debugf("Watching files: %v", files)
	return func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				if err := w.Close(); err != nil {
					errFunc(err, fmt.Sprintf("failed to close fsnotify watcher for files: %v", files))
				}
				return
			case <-w.Events:
				err := eventFunc(w)
				if err != nil {
					errFunc(err, fmt.Sprintf("event function failed watching files: %v", files))
				}
			case err := <-w.Errors:
				errFunc(err, fmt.Sprintf("received fsnotify error watching files: %v", files))
			}
		}
	}, nil
}

// ListCMsAndDirs returns a 2 sets of strings containing the paths of configmapped directories and standard
// directories respectively starting from the provided path. This can be used to watch a large number of
// files, some of which may be populated via configmaps
func ListCMsAndDirs(path string) (cms sets.String, dirs sets.String, err error) {
	cms = sets.NewString()
	dirs = sets.NewString()
	err = filepath.Walk(path, func(path string, info os.FileInfo, _ error) error {
		// We only need to watch directories as creation, deletion, and writes
		// for files in a directory trigger events for the directory
		if info != nil && info.IsDir() {
			if isCM, err := IsConfigMapMount(path); err != nil {
				return fmt.Errorf("Failed to check is path %s is configmap mounted: %v", path, err)
			} else if isCM {
				cms.Insert(path)
				// configmaps can't have nested directories
				return filepath.SkipDir
			} else {
				dirs.Insert(path)
				return nil
			}
		}
		return nil
	})
	return cms, dirs, err
}

func watchConfigs(ca *Agent, prowConfig, jobConfig string, additionals ...func(*Config) error) error {
	cmEventFunc := func() error {
		c, err := Load(prowConfig, jobConfig, additionals...)
		if err != nil {
			return err
		}
		ca.Set(c)
		return nil
	}
	// We may need to add more directories to be watched
	dirsEventFunc := func(w *fsnotify.Watcher) error {
		c, err := Load(prowConfig, jobConfig, additionals...)
		if err != nil {
			return err
		}
		ca.Set(c)
		// TODO(AlexNPavel): Is there a chance that a ConfigMap mounted directory may appear without making a new pod? If yes, handle that.
		_, dirs, err := ListCMsAndDirs(jobConfig)
		if err != nil {
			return err
		}
		for dir := range dirs {
			// Adding a file or directory that already exists in fsnotify is a no-op, so it is safe to always run Add
			if err := w.Add(dir); err != nil {
				return err
			}
		}
		return nil
	}
	errFunc := func(err error, msg string) {
		logrus.WithField("prowConfig", prowConfig).
			WithField("jobConfig", jobConfig).
			WithError(err).Error(msg)
	}
	cms := sets.NewString()
	dirs := sets.NewString()
	// TODO(AlexNPavel): allow empty jobConfig till fully migrate config to subdirs
	if jobConfig != "" {
		stat, err := os.Stat(jobConfig)
		if err != nil {
			return err
		}
		// TODO(AlexNPavel): allow single file jobConfig till fully migrate config to subdirs
		if stat.IsDir() {
			var err error
			// jobConfig points to directories of configs that may be nested
			cms, dirs, err = ListCMsAndDirs(jobConfig)
			if err != nil {
				return err
			}
		} else {
			// If jobConfig is a single file, we handle it identically to how prowConfig is handled
			if jobIsCMMounted, err := IsConfigMapMount(filepath.Dir(jobConfig)); err != nil {
				return err
			} else if jobIsCMMounted {
				cms.Insert(filepath.Dir(jobConfig))
			} else {
				dirs.Insert(jobConfig)
			}
		}
	}
	// The prow config is always a single file
	if prowIsCMMounted, err := IsConfigMapMount(filepath.Dir(prowConfig)); err != nil {
		return err
	} else if prowIsCMMounted {
		cms.Insert(filepath.Dir(prowConfig))
	} else {
		dirs.Insert(prowConfig)
	}
	for cm := range cms {
		runFunc, err := GetCMMountWatcher(cmEventFunc, errFunc, cm)
		if err != nil {
			return err
		}
		interrupts.Run(runFunc)
	}
	if len(dirs) > 0 {
		runFunc, err := GetFileWatcher(dirsEventFunc, errFunc, dirs.UnsortedList()...)
		if err != nil {
			return err
		}
		interrupts.Run(runFunc)
	}
	return nil
}

// Start will begin watching the config file at the path. If the first load
// fails, Start will return the error and abort. Future load failures will log
// the failure message but continue attempting to load.
func (ca *Agent) Start(prowConfig, jobConfig string, additionals ...func(*Config) error) error {
	c, err := Load(prowConfig, jobConfig, additionals...)
	if err != nil {
		return err
	}
	ca.Set(c)
	watchConfigs(ca, prowConfig, jobConfig, additionals...)
	return nil
}

// Subscribe registers the channel for messages on config reload.
// The caller can expect a copy of the previous and current config
// to be sent down the subscribed channel when a new configuration
// is loaded.
func (ca *Agent) Subscribe(subscription DeltaChan) {
	ca.mut.Lock()
	defer ca.mut.Unlock()
	ca.subscriptions = append(ca.subscriptions, subscription)
}

// Getter returns the current Config in a thread-safe manner.
type Getter func() *Config

// Config returns the latest config. Do not modify the config.
func (ca *Agent) Config() *Config {
	ca.mut.RLock()
	defer ca.mut.RUnlock()
	return ca.c
}

// Set sets the config. Useful for testing.
// Also used by statusreconciler to load last known config
func (ca *Agent) Set(c *Config) {
	ca.mut.Lock()
	defer ca.mut.Unlock()
	var oldConfig Config
	if ca.c != nil {
		oldConfig = *ca.c
	}
	delta := Delta{oldConfig, *c}
	ca.c = c
	for _, subscription := range ca.subscriptions {
		go func(sub DeltaChan) { // wait a minute to send each event
			end := time.NewTimer(time.Minute)
			select {
			case sub <- delta:
			case <-end.C:
			}
			if !end.Stop() { // prevent new events
				<-end.C // drain the pending event
			}
		}(subscription)
	}
}
<commit_msg>config agent: improve godoc<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package config

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
	"gopkg.in/fsnotify.v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/test-infra/prow/interrupts"
)

// Delta represents the before and after states of a Config change detected by the Agent.
type Delta struct {
	Before, After Config
}

// DeltaChan is a channel to receive config delta events when config changes.
type DeltaChan = chan<- Delta

// Agent watches a path and automatically loads the config stored
// therein.
type Agent struct {
	mut           sync.RWMutex // do not export Lock, etc methods
	c             *Config
	subscriptions []DeltaChan
}

// IsConfigMapMount determines whether the provided directory is a configmap mounted directory
func IsConfigMapMount(path string) (bool, error) {
	files, err := ioutil.ReadDir(path)
	if err != nil {
		return false, fmt.Errorf("Could not read provided directory %s: %v", path, err)
	}
	for _, file := range files {
		if file.Name() == "..data" {
			return true, nil
		}
	}
	return false, nil
}

// GetCMMountWatcher returns a function that watches a configmap mounted directory and runs the provided "eventFunc" every time
// the directory gets updated and the provided "errFunc" every time it encounters an error.
// Example of a possible eventFunc:
// func() error {
//		value, err := RunUpdate()
//		if err != nil {
//			return err
//		}
//		globalValue = value
//		return nil
// }
// Example of errFunc:
// func(err error, msg string) {
//		logrus.WithError(err).Error(msg)
// }
func GetCMMountWatcher(eventFunc func() error, errFunc func(error, string), path string) (func(ctx context.Context), error) {
	isCMMount, err := IsConfigMapMount(path)
	if err != nil {
		return nil, err
	} else if !isCMMount {
		return nil, fmt.Errorf("Provided directory %s is not a configmap directory", path)
	}
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	err = w.Add(path)
	if err != nil {
		return nil, err
	}
	logrus.Debugf("Watching %s", path)
	dataPath := filepath.Join(path, "..data")
	return func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				if err := w.Close(); err != nil {
					errFunc(err, fmt.Sprintf("failed to close fsnotify watcher for directory %s", path))
				}
				return
			case event := <-w.Events:
				if event.Name == dataPath && event.Op == fsnotify.Create {
					err := eventFunc()
					if err != nil {
						errFunc(err, fmt.Sprintf("event function for watch directory %s failed", path))
					}
				}
			case err := <-w.Errors:
				errFunc(err, fmt.Sprintf("received fsnotify error for directory %s", path))
			}
		}
	}, nil
}

// GetFileWatcher returns a function that watches the specified file(s), running the "eventFunc" whenever an event for the file(s) occurs
// and the "errFunc" whenever an error is encountered. In this function, the eventFunc has access to the watcher, allowing the eventFunc
// to add new files/directories to be watched as needed.
// Example of a possible eventFunc:
// func(w *fsnotify.Watcher) error {
//		value, err := RunUpdate()
//		if err != nil {
//			return err
//		}
//		globalValue = value
//      newFiles := getNewFiles()
//      for _, file := range newFiles {
//			if err := w.Add(file); err != nil {
//				return err
//			}
// 		}
//		return nil
// }
// Example of errFunc:
// func(err error, msg string) {
//		logrus.WithError(err).Error(msg)
// }
func GetFileWatcher(eventFunc func(*fsnotify.Watcher) error, errFunc func(error, string), files ...string) (func(ctx context.Context), error) {
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		if err := w.Add(file); err != nil {
			return nil, err
		}
	}
	logrus.Debugf("Watching files: %v", files)
	return func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				if err := w.Close(); err != nil {
					errFunc(err, fmt.Sprintf("failed to close fsnotify watcher for files: %v", files))
				}
				return
			case <-w.Events:
				err := eventFunc(w)
				if err != nil {
					errFunc(err, fmt.Sprintf("event function failed watching files: %v", files))
				}
			case err := <-w.Errors:
				errFunc(err, fmt.Sprintf("received fsnotify error watching files: %v", files))
			}
		}
	}, nil
}

// ListCMsAndDirs returns a 2 sets of strings containing the paths of configmapped directories and standard
// directories respectively starting from the provided path. This can be used to watch a large number of
// files, some of which may be populated via configmaps
func ListCMsAndDirs(path string) (cms sets.String, dirs sets.String, err error) {
	cms = sets.NewString()
	dirs = sets.NewString()
	err = filepath.Walk(path, func(path string, info os.FileInfo, _ error) error {
		// We only need to watch directories as creation, deletion, and writes
		// for files in a directory trigger events for the directory
		if info != nil && info.IsDir() {
			if isCM, err := IsConfigMapMount(path); err != nil {
				return fmt.Errorf("Failed to check is path %s is configmap mounted: %v", path, err)
			} else if isCM {
				cms.Insert(path)
				// configmaps can't have nested directories
				return filepath.SkipDir
			} else {
				dirs.Insert(path)
				return nil
			}
		}
		return nil
	})
	return cms, dirs, err
}

func watchConfigs(ca *Agent, prowConfig, jobConfig string, additionals ...func(*Config) error) error {
	cmEventFunc := func() error {
		c, err := Load(prowConfig, jobConfig, additionals...)
		if err != nil {
			return err
		}
		ca.Set(c)
		return nil
	}
	// We may need to add more directories to be watched
	dirsEventFunc := func(w *fsnotify.Watcher) error {
		c, err := Load(prowConfig, jobConfig, additionals...)
		if err != nil {
			return err
		}
		ca.Set(c)
		// TODO(AlexNPavel): Is there a chance that a ConfigMap mounted directory may appear without making a new pod? If yes, handle that.
		_, dirs, err := ListCMsAndDirs(jobConfig)
		if err != nil {
			return err
		}
		for dir := range dirs {
			// Adding a file or directory that already exists in fsnotify is a no-op, so it is safe to always run Add
			if err := w.Add(dir); err != nil {
				return err
			}
		}
		return nil
	}
	errFunc := func(err error, msg string) {
		logrus.WithField("prowConfig", prowConfig).
			WithField("jobConfig", jobConfig).
			WithError(err).Error(msg)
	}
	cms := sets.NewString()
	dirs := sets.NewString()
	// TODO(AlexNPavel): allow empty jobConfig till fully migrate config to subdirs
	if jobConfig != "" {
		stat, err := os.Stat(jobConfig)
		if err != nil {
			return err
		}
		// TODO(AlexNPavel): allow single file jobConfig till fully migrate config to subdirs
		if stat.IsDir() {
			var err error
			// jobConfig points to directories of configs that may be nested
			cms, dirs, err = ListCMsAndDirs(jobConfig)
			if err != nil {
				return err
			}
		} else {
			// If jobConfig is a single file, we handle it identically to how prowConfig is handled
			if jobIsCMMounted, err := IsConfigMapMount(filepath.Dir(jobConfig)); err != nil {
				return err
			} else if jobIsCMMounted {
				cms.Insert(filepath.Dir(jobConfig))
			} else {
				dirs.Insert(jobConfig)
			}
		}
	}
	// The prow config is always a single file
	if prowIsCMMounted, err := IsConfigMapMount(filepath.Dir(prowConfig)); err != nil {
		return err
	} else if prowIsCMMounted {
		cms.Insert(filepath.Dir(prowConfig))
	} else {
		dirs.Insert(prowConfig)
	}
	for cm := range cms {
		runFunc, err := GetCMMountWatcher(cmEventFunc, errFunc, cm)
		if err != nil {
			return err
		}
		interrupts.Run(runFunc)
	}
	if len(dirs) > 0 {
		runFunc, err := GetFileWatcher(dirsEventFunc, errFunc, dirs.UnsortedList()...)
		if err != nil {
			return err
		}
		interrupts.Run(runFunc)
	}
	return nil
}

// Start will begin watching the config file at the path. If the first load
// fails, Start will return the error and abort. Future load failures will log
// the failure message but continue attempting to load.
func (ca *Agent) Start(prowConfig, jobConfig string, additionals ...func(*Config) error) error {
	c, err := Load(prowConfig, jobConfig, additionals...)
	if err != nil {
		return err
	}
	ca.Set(c)
	watchConfigs(ca, prowConfig, jobConfig, additionals...)
	return nil
}

// Subscribe registers the channel for messages on config reload.
// The caller can expect a copy of the previous and current config
// to be sent down the subscribed channel when a new configuration
// is loaded.
func (ca *Agent) Subscribe(subscription DeltaChan) {
	ca.mut.Lock()
	defer ca.mut.Unlock()
	ca.subscriptions = append(ca.subscriptions, subscription)
}

// Getter returns the current Config in a thread-safe manner.
type Getter func() *Config

// Config returns the latest config. Do not modify the config.
func (ca *Agent) Config() *Config {
	ca.mut.RLock()
	defer ca.mut.RUnlock()
	return ca.c
}

// Set sets the config. Useful for testing.
// Also used by statusreconciler to load last known config
func (ca *Agent) Set(c *Config) {
	ca.mut.Lock()
	defer ca.mut.Unlock()
	var oldConfig Config
	if ca.c != nil {
		oldConfig = *ca.c
	}
	delta := Delta{oldConfig, *c}
	ca.c = c
	for _, subscription := range ca.subscriptions {
		go func(sub DeltaChan) { // wait a minute to send each event
			end := time.NewTimer(time.Minute)
			select {
			case sub <- delta:
			case <-end.C:
			}
			if !end.Stop() { // prevent new events
				<-end.C // drain the pending event
			}
		}(subscription)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 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.
*/

// This file is a copy from upstream where it was removed in
// https://github.com/prometheus/client_golang/pull/600
package metrics

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/common/model"
)

const contentTypeHeader = "Content-Type"

func fromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
	return push(job, grouping, url, g, "PUT")
}

func push(job string, grouping map[string]string, pushURL string, g prometheus.Gatherer, method string) error {
	if !strings.Contains(pushURL, "://") {
		pushURL = "http://" + pushURL
	}
	pushURL = strings.TrimSuffix(pushURL, "/")

	if strings.Contains(job, "/") {
		return fmt.Errorf("job contains '/': %s", job)
	}
	urlComponents := []string{url.QueryEscape(job)}
	for ln, lv := range grouping {
		if !model.LabelName(ln).IsValid() {
			return fmt.Errorf("grouping label has invalid name: %s", ln)
		}
		if strings.Contains(lv, "/") {
			return fmt.Errorf("value of grouping label %s contains '/': %s", ln, lv)
		}
		urlComponents = append(urlComponents, ln, lv)
	}
	pushURL = fmt.Sprintf("%s/metrics/job/%s", pushURL, strings.Join(urlComponents, "/"))

	mfs, err := g.Gather()
	if err != nil {
		return err
	}
	buf := &bytes.Buffer{}
	enc := expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)
	// Check for pre-existing grouping labels:
	for _, mf := range mfs {
		for _, m := range mf.GetMetric() {
			for _, l := range m.GetLabel() {
				if l.GetName() == "job" {
					return fmt.Errorf("pushed metric %s (%s) already contains a job label", mf.GetName(), m)
				}
				if _, ok := grouping[l.GetName()]; ok {
					return fmt.Errorf(
						"pushed metric %s (%s) already contains grouping label %s",
						mf.GetName(), m, l.GetName(),
					)
				}
			}
		}
		enc.Encode(mf)
	}
	req, err := http.NewRequest(method, pushURL, buf)
	if err != nil {
		return err
	}
	req.Header.Set(contentTypeHeader, string(expfmt.FmtProtoDelim))
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 202 {
		body, _ := ioutil.ReadAll(resp.Body) // Ignore any further error as this is for an error message only.
		return fmt.Errorf("unexpected status code %d while pushing to %s: %s", resp.StatusCode, pushURL, body)
	}
	return nil
}

func hostnameGroupingKey() map[string]string {
	hostname, err := os.Hostname()
	if err != nil {
		return map[string]string{"instance": "unknown"}
	}
	return map[string]string{"instance": hostname}
}
<commit_msg>Accept status code 200 or 202 from pushgateway<commit_after>/*
Copyright 2019 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.
*/

// This file is a copy from upstream where it was removed in
// https://github.com/prometheus/client_golang/pull/600
package metrics

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/common/model"
)

const contentTypeHeader = "Content-Type"

func fromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
	return push(job, grouping, url, g, "PUT")
}

func push(job string, grouping map[string]string, pushURL string, g prometheus.Gatherer, method string) error {
	if !strings.Contains(pushURL, "://") {
		pushURL = "http://" + pushURL
	}
	pushURL = strings.TrimSuffix(pushURL, "/")

	if strings.Contains(job, "/") {
		return fmt.Errorf("job contains '/': %s", job)
	}
	urlComponents := []string{url.QueryEscape(job)}
	for ln, lv := range grouping {
		if !model.LabelName(ln).IsValid() {
			return fmt.Errorf("grouping label has invalid name: %s", ln)
		}
		if strings.Contains(lv, "/") {
			return fmt.Errorf("value of grouping label %s contains '/': %s", ln, lv)
		}
		urlComponents = append(urlComponents, ln, lv)
	}
	pushURL = fmt.Sprintf("%s/metrics/job/%s", pushURL, strings.Join(urlComponents, "/"))

	mfs, err := g.Gather()
	if err != nil {
		return err
	}
	buf := &bytes.Buffer{}
	enc := expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)
	// Check for pre-existing grouping labels:
	for _, mf := range mfs {
		for _, m := range mf.GetMetric() {
			for _, l := range m.GetLabel() {
				if l.GetName() == "job" {
					return fmt.Errorf("pushed metric %s (%s) already contains a job label", mf.GetName(), m)
				}
				if _, ok := grouping[l.GetName()]; ok {
					return fmt.Errorf(
						"pushed metric %s (%s) already contains grouping label %s",
						mf.GetName(), m, l.GetName(),
					)
				}
			}
		}
		enc.Encode(mf)
	}
	req, err := http.NewRequest(method, pushURL, buf)
	if err != nil {
		return err
	}
	req.Header.Set(contentTypeHeader, string(expfmt.FmtProtoDelim))
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if !(resp.StatusCode == 200 || resp.StatusCode == 202) {
		body, _ := ioutil.ReadAll(resp.Body) // Ignore any further error as this is for an error message only.
		return fmt.Errorf("unexpected status code %d while pushing to %s: %s", resp.StatusCode, pushURL, body)
	}
	return nil
}

func hostnameGroupingKey() map[string]string {
	hostname, err := os.Hostname()
	if err != nil {
		return map[string]string{"instance": "unknown"}
	}
	return map[string]string{"instance": hostname}
}
<|endoftext|>


<commit_before>package engine

// This engine buffers points and passes them through without modification. Works for queries
// that can't be aggregated locally or queries that don't require it like deletes and drops.
import (
	"common"
	"protocol"

	log "code.google.com/p/log4go"
)

type PassthroughEngine struct {
	responseChan        chan *protocol.Response
	response            *protocol.Response
	maxPointsInResponse int
	limiter             *Limiter
	responseType        *protocol.Response_Type

	// query statistics
	runStartTime  float64
	runEndTime    float64
	pointsRead    int64
	pointsWritten int64
	shardId       int
	shardLocal    bool
}

func NewPassthroughEngine(responseChan chan *protocol.Response, maxPointsInResponse int) *PassthroughEngine {
	return NewPassthroughEngineWithLimit(responseChan, maxPointsInResponse, 0)
}

func NewPassthroughEngineWithLimit(responseChan chan *protocol.Response, maxPointsInResponse, limit int) *PassthroughEngine {
	passthroughEngine := &PassthroughEngine{
		responseChan:        responseChan,
		maxPointsInResponse: maxPointsInResponse,
		limiter:             NewLimiter(limit),
		responseType:        &queryResponse,
		runStartTime:        0,
		runEndTime:          0,
		pointsRead:          0,
		pointsWritten:       0,
		shardId:             0,
		shardLocal:          false, //that really doesn't matter if it is not EXPLAIN query
	}

	return passthroughEngine
}

func (self *PassthroughEngine) YieldPoint(seriesName *string, columnNames []string, point *protocol.Point) bool {
	series := &protocol.Series{Name: seriesName, Points: []*protocol.Point{point}, Fields: columnNames}
	return self.YieldSeries(series)
}

func (self *PassthroughEngine) YieldSeries(seriesIncoming *protocol.Series) bool {
	log.Debug("PassthroughEngine YieldSeries %d", len(seriesIncoming.Points))
	if *seriesIncoming.Name == "explain query" {
		self.responseType = &explainQueryResponse
		log.Debug("Response Changed!")
	} else {
		self.responseType = &queryResponse
	}

	self.limiter.calculateLimitAndSlicePoints(seriesIncoming)
	if len(seriesIncoming.Points) == 0 {
		log.Error("Not sent == 0")
		return false
	}

	if self.response == nil {
		self.response = &protocol.Response{
			Type:   self.responseType,
			Series: seriesIncoming,
		}
	} else if self.response.Series.GetName() != seriesIncoming.GetName() {
		self.responseChan <- self.response
		self.response = &protocol.Response{
			Type:   self.responseType,
			Series: seriesIncoming,
		}
	} else if len(self.response.Series.Points) > self.maxPointsInResponse {
		self.responseChan <- self.response
		self.response = &protocol.Response{
			Type:   self.responseType,
			Series: seriesIncoming,
		}
	} else {
		self.response.Series = common.MergeSeries(self.response.Series, seriesIncoming)
	}
	return !self.limiter.hitLimit(seriesIncoming.GetName())
	//return true
}

func (self *PassthroughEngine) Close() {
	if self.response != nil && self.response.Series != nil && self.response.Series.Name != nil {
		self.responseChan <- self.response
	}
	response := &protocol.Response{Type: &endStreamResponse}
	self.responseChan <- response
}

func (self *PassthroughEngine) SetShardInfo(shardId int, shardLocal bool) {
	//EXPLAIN doens't really work with this query (yet ?)
}

func (self *PassthroughEngine) GetName() string {
	return "PassthroughEngine"
}
<commit_msg>Change log level on message of not sending points because there are 0 to yield to debug<commit_after>package engine

// This engine buffers points and passes them through without modification. Works for queries
// that can't be aggregated locally or queries that don't require it like deletes and drops.
import (
	"common"
	"protocol"

	log "code.google.com/p/log4go"
)

type PassthroughEngine struct {
	responseChan        chan *protocol.Response
	response            *protocol.Response
	maxPointsInResponse int
	limiter             *Limiter
	responseType        *protocol.Response_Type

	// query statistics
	runStartTime  float64
	runEndTime    float64
	pointsRead    int64
	pointsWritten int64
	shardId       int
	shardLocal    bool
}

func NewPassthroughEngine(responseChan chan *protocol.Response, maxPointsInResponse int) *PassthroughEngine {
	return NewPassthroughEngineWithLimit(responseChan, maxPointsInResponse, 0)
}

func NewPassthroughEngineWithLimit(responseChan chan *protocol.Response, maxPointsInResponse, limit int) *PassthroughEngine {
	passthroughEngine := &PassthroughEngine{
		responseChan:        responseChan,
		maxPointsInResponse: maxPointsInResponse,
		limiter:             NewLimiter(limit),
		responseType:        &queryResponse,
		runStartTime:        0,
		runEndTime:          0,
		pointsRead:          0,
		pointsWritten:       0,
		shardId:             0,
		shardLocal:          false, //that really doesn't matter if it is not EXPLAIN query
	}

	return passthroughEngine
}

func (self *PassthroughEngine) YieldPoint(seriesName *string, columnNames []string, point *protocol.Point) bool {
	series := &protocol.Series{Name: seriesName, Points: []*protocol.Point{point}, Fields: columnNames}
	return self.YieldSeries(series)
}

func (self *PassthroughEngine) YieldSeries(seriesIncoming *protocol.Series) bool {
	log.Debug("PassthroughEngine YieldSeries %d", len(seriesIncoming.Points))
	if *seriesIncoming.Name == "explain query" {
		self.responseType = &explainQueryResponse
		log.Debug("Response Changed!")
	} else {
		self.responseType = &queryResponse
	}

	self.limiter.calculateLimitAndSlicePoints(seriesIncoming)
	if len(seriesIncoming.Points) == 0 {
		log.Debug("Not sent == 0")
		return false
	}

	if self.response == nil {
		self.response = &protocol.Response{
			Type:   self.responseType,
			Series: seriesIncoming,
		}
	} else if self.response.Series.GetName() != seriesIncoming.GetName() {
		self.responseChan <- self.response
		self.response = &protocol.Response{
			Type:   self.responseType,
			Series: seriesIncoming,
		}
	} else if len(self.response.Series.Points) > self.maxPointsInResponse {
		self.responseChan <- self.response
		self.response = &protocol.Response{
			Type:   self.responseType,
			Series: seriesIncoming,
		}
	} else {
		self.response.Series = common.MergeSeries(self.response.Series, seriesIncoming)
	}
	return !self.limiter.hitLimit(seriesIncoming.GetName())
	//return true
}

func (self *PassthroughEngine) Close() {
	if self.response != nil && self.response.Series != nil && self.response.Series.Name != nil {
		self.responseChan <- self.response
	}
	response := &protocol.Response{Type: &endStreamResponse}
	self.responseChan <- response
}

func (self *PassthroughEngine) SetShardInfo(shardId int, shardLocal bool) {
	//EXPLAIN doens't really work with this query (yet ?)
}

func (self *PassthroughEngine) GetName() string {
	return "PassthroughEngine"
}
<|endoftext|>


<commit_before>package showallapps

import (
	"fmt"
	"github.com/Symantec/scotty/datastructs"
	"github.com/Symantec/scotty/lib/httputil"
	"html/template"
	"net/http"
	"net/url"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	htmlTemplateStr = ` \

	Total Endpoints: {{.Summary.TotalEndpoints}}

	Total Active Endpoints: {{.Summary.TotalActiveEndpoints}}

	Total Failed Endpoints: {{.Summary.TotalFailedEndpoints}}

	    Machine
	    Port
	    Name
	    Active?
	    Down?
	    Status
	    Staleness
	    Init Metric Count
	    Avg. changing metrics
	    Poll

	\ {{with $top := .}} \
	\ {{range .Apps}} \

	    {{.EndpointId.HostName}}
	    {{.EndpointId.Port}}
	    {{.Name}}
	    {{if .Active}}Yes{{else}} {{end}}
	    \ {{if .Down}} \
	      Yes

	        {{.Status}}

                {{.LastErrorTimeStr}}

		{{.LastError}}

	    \ {{else}} \
	      No
	      {{.Status}}
	    \ {{end}} \
	    {{if .Staleness}}{{.Staleness}}{{else}} {{end}}
	    {{with .InitialMetricCount}}{{.}}{{else}} {{end}}
	    {{with .AverageChangedMetrics}}{{$top.Float32 .}}{{else}} {{end}}
	    {{if .PollTime}}{{.PollTime}}{{else}} {{end}}

	\ {{end}} \
	\ {{end}} \

	  `
)

var (
	leadingWhitespace = regexp.MustCompile(`\n\s*\\ `)
	htmlTemplate      = template.Must(
		template.New("showAllApps").Parse(
			strings.Replace(
				leadingWhitespace.ReplaceAllString(
					strings.Replace(
						htmlTemplateStr,
						"\n\t",
						"\n",
						-1),
					"\n"),
				" \\\n",
				"",
				-1)))
)

type view struct {
	Apps    []*datastructs.ApplicationStatus
	Summary EndpointSummary
	History string
}

func (v *view) Float32(x float64) float32 {
	return float32(x)
}

func (v *view) Link(app *datastructs.ApplicationStatus) *url.URL {
	return httputil.NewUrl(
		fmt.Sprintf(
			"/api/hosts/%s/%s",
			app.EndpointId.HostName(),
			app.Name),
		"format", "text",
		"history", v.History)
}

type EndpointSummary struct {
	TotalEndpoints       int
	TotalActiveEndpoints int
	TotalFailedEndpoints int
}

func (e *EndpointSummary) Init(apps []*datastructs.ApplicationStatus) {
	e.TotalEndpoints = len(apps)
	e.TotalActiveEndpoints = 0
	e.TotalFailedEndpoints = 0
	for _, app := range apps {
		if !app.Active {
			continue
		}
		if app.Down {
			e.TotalFailedEndpoints++
		}
		e.TotalActiveEndpoints++
	}
}

type Handler struct {
	AS             *datastructs.ApplicationStatuses
	CollectionFreq time.Duration
}

type byNameAndPort []*datastructs.ApplicationStatus

func (b byNameAndPort) Len() int { return len(b) }

func (b byNameAndPort) Less(i, j int) bool {
	ihostname := b[i].EndpointId.HostName()
	jhostname := b[j].EndpointId.HostName()
	if ihostname < jhostname {
		return true
	} else if jhostname < ihostname {
		return false
	} else if b[i].EndpointId.Port() < b[j].EndpointId.Port() {
		return true
	}
	return false
}

func (b byNameAndPort) Swap(i, j int) {
	b[j], b[i] = b[i], b[j]
}

func sortByNameAndPort(rows []*datastructs.ApplicationStatus) {
	sort.Sort(byNameAndPort(rows))
}

func (h *Handler) ServeHTTP(
	w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	w.Header().Set("Content-Type", "text/html")
	result := h.AS.All()
	sortByNameAndPort(result)
	v := h.newView(result)
	if err := htmlTemplate.Execute(w, v); err != nil {
		fmt.Fprintln(w, "Error in template: %v\n", err)
	}
}

func (h *Handler) newView(
	apps []*datastructs.ApplicationStatus) *view {
	result := &view{
		Apps: apps,
		History: strconv.Itoa(
			int((h.CollectionFreq + time.Minute - 1) / time.Minute)),
	}
	result.Summary.Init(apps)
	return result
}
<commit_msg>Show the RPC method in status column<commit_after>package showallapps

import (
	"fmt"
	"github.com/Symantec/scotty/datastructs"
	"github.com/Symantec/scotty/lib/httputil"
	"html/template"
	"net/http"
	"net/url"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	htmlTemplateStr = ` \

	Total Endpoints: {{.Summary.TotalEndpoints}}

	Total Active Endpoints: {{.Summary.TotalActiveEndpoints}}

	Total Failed Endpoints: {{.Summary.TotalFailedEndpoints}}

	    Machine
	    Port
	    Name
	    Active?
	    Down?
	    Status
	    Staleness
	    Init Metric Count
	    Avg. changing metrics
	    Poll

	\ {{with $top := .}} \
	\ {{range .Apps}} \

	    {{.EndpointId.HostName}}
	    {{.EndpointId.Port}}
	    {{.Name}}
	    {{if .Active}}Yes{{else}} {{end}}
	    \ {{if .Down}} \
	      Yes

	        {{.Status}}

                {{.LastErrorTimeStr}}

		{{.LastError}}

	    \ {{else}} \
	      No
	      {{.Status}} {{.EndpointId.ConnectorName}}
	    \ {{end}} \
	    {{if .Staleness}}{{.Staleness}}{{else}} {{end}}
	    {{with .InitialMetricCount}}{{.}}{{else}} {{end}}
	    {{with .AverageChangedMetrics}}{{$top.Float32 .}}{{else}} {{end}}
	    {{if .PollTime}}{{.PollTime}}{{else}} {{end}}

	\ {{end}} \
	\ {{end}} \

	  `
)

var (
	leadingWhitespace = regexp.MustCompile(`\n\s*\\ `)
	htmlTemplate      = template.Must(
		template.New("showAllApps").Parse(
			strings.Replace(
				leadingWhitespace.ReplaceAllString(
					strings.Replace(
						htmlTemplateStr,
						"\n\t",
						"\n",
						-1),
					"\n"),
				" \\\n",
				"",
				-1)))
)

type view struct {
	Apps    []*datastructs.ApplicationStatus
	Summary EndpointSummary
	History string
}

func (v *view) Float32(x float64) float32 {
	return float32(x)
}

func (v *view) Link(app *datastructs.ApplicationStatus) *url.URL {
	return httputil.NewUrl(
		fmt.Sprintf(
			"/api/hosts/%s/%s",
			app.EndpointId.HostName(),
			app.Name),
		"format", "text",
		"history", v.History)
}

type EndpointSummary struct {
	TotalEndpoints       int
	TotalActiveEndpoints int
	TotalFailedEndpoints int
}

func (e *EndpointSummary) Init(apps []*datastructs.ApplicationStatus) {
	e.TotalEndpoints = len(apps)
	e.TotalActiveEndpoints = 0
	e.TotalFailedEndpoints = 0
	for _, app := range apps {
		if !app.Active {
			continue
		}
		if app.Down {
			e.TotalFailedEndpoints++
		}
		e.TotalActiveEndpoints++
	}
}

type Handler struct {
	AS             *datastructs.ApplicationStatuses
	CollectionFreq time.Duration
}

type byNameAndPort []*datastructs.ApplicationStatus

func (b byNameAndPort) Len() int { return len(b) }

func (b byNameAndPort) Less(i, j int) bool {
	ihostname := b[i].EndpointId.HostName()
	jhostname := b[j].EndpointId.HostName()
	if ihostname < jhostname {
		return true
	} else if jhostname < ihostname {
		return false
	} else if b[i].EndpointId.Port() < b[j].EndpointId.Port() {
		return true
	}
	return false
}

func (b byNameAndPort) Swap(i, j int) {
	b[j], b[i] = b[i], b[j]
}

func sortByNameAndPort(rows []*datastructs.ApplicationStatus) {
	sort.Sort(byNameAndPort(rows))
}

func (h *Handler) ServeHTTP(
	w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	w.Header().Set("Content-Type", "text/html")
	result := h.AS.All()
	sortByNameAndPort(result)
	v := h.newView(result)
	if err := htmlTemplate.Execute(w, v); err != nil {
		fmt.Fprintln(w, "Error in template: %v\n", err)
	}
}

func (h *Handler) newView(
	apps []*datastructs.ApplicationStatus) *view {
	result := &view{
		Apps: apps,
		History: strconv.Itoa(
			int((h.CollectionFreq + time.Minute - 1) / time.Minute)),
	}
	result.Summary.Init(apps)
	return result
}
<|endoftext|>


<commit_before>package presilo

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
)

func ParseSchemaFile(path string) (TypeSchema, error) {

	var context *SchemaParseContext
	var contentsBytes []byte
	var name string
	var err error

	path, err = filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	name = filepath.Base(path)

	contentsBytes, err = ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	context = NewSchemaParseContext()
	return ParseSchema(contentsBytes, name, context)
}

func ParseSchema(contentsBytes []byte, defaultTitle string, context *SchemaParseContext) (TypeSchema, error) {

	var schema TypeSchema
	var schemaTypeRaw, schemaRefRaw *json.RawMessage
	var contents map[string]*json.RawMessage
	var schemaTypeBytes, schemaRefBytes []byte
	var schemaRef string
	var schemaType string
	var present bool
	var err error

	err = json.Unmarshal(contentsBytes, &contents)
	if err != nil {
		return nil, err
	}

	// if this is a reference schema, simply return that exact schema, and do no other processing.
	schemaRefRaw, present = contents["$ref"]
	if present {

		schemaRefBytes, err = schemaRefRaw.MarshalJSON()
		if err != nil {
			return nil, err
		}

		schemaRef = string(schemaRefBytes)

		schema, present = context.SchemaDefinitions[schemaRef]
		if !present {

			errorMsg := fmt.Sprintf("Schema ref '%s' could not be resolved.", schemaRef)
			return nil, errors.New(errorMsg)
		}

		return schema, nil
	}

	// figure out type
	schemaTypeRaw, present = contents["type"]
	if !present {
		return nil, errors.New("Type was not specified")
	}
	if schemaTypeRaw == nil {
		return nil, errors.New("Schema could not be parsed, type was not specified")
	}

	schemaTypeBytes, err = schemaTypeRaw.MarshalJSON()
	if err != nil {
		return nil, err
	}

	schemaType = string(schemaTypeBytes)
	schemaType = strings.Replace(schemaType, "\"", "", -1)

	switch schemaType {

	case "integer":
		schema, err = NewIntegerSchema(contentsBytes, context)

	case "number":
		schema, err = NewNumberSchema(contentsBytes, context)

	case "string":
		schema, err = NewStringSchema(contentsBytes, context)

	case "array":
		schema, err = NewArraySchema(contentsBytes, context)

	case "object":
		schema, err = NewObjectSchema(contentsBytes, context)

	default:
		errorMsg := fmt.Sprintf("Unrecognized schema type: '%s'", schemaType)
		return nil, errors.New(errorMsg)
	}

	if err != nil {
		return nil, err
	}

	if len(schema.GetTitle()) == 0 {
		schema.SetTitle(defaultTitle)
	}

	context.SchemaDefinitions[schema.GetID()] = schema
	return schema, nil
}

/*
  Recurses the properties of the given [root],
  adding all sub-schemas to the given [schemas].
*/
func RecurseObjectSchemas(schema TypeSchema, schemas []*ObjectSchema) []*ObjectSchema {

	if schema.GetSchemaType() == SCHEMATYPE_OBJECT {
		return recurseObjectSchema(schema.(*ObjectSchema), schemas)
	}
	if schema.GetSchemaType() == SCHEMATYPE_ARRAY {
		return RecurseObjectSchemas(schema.(*ArraySchema).Items, schemas)
	}

	return schemas
}

func recurseObjectSchema(schema *ObjectSchema, schemas []*ObjectSchema) []*ObjectSchema {

	schemas = append(schemas, schema)

	for _, property := range schema.Properties {
		schemas = RecurseObjectSchemas(property, schemas)
	}

	return schemas
}
<commit_msg>Fixed parsing error for ref names<commit_after>package presilo

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
)

func ParseSchemaFile(path string) (TypeSchema, error) {

	var context *SchemaParseContext
	var contentsBytes []byte
	var name string
	var err error

	path, err = filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	name = filepath.Base(path)

	contentsBytes, err = ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	context = NewSchemaParseContext()
	return ParseSchema(contentsBytes, name, context)
}

func ParseSchema(contentsBytes []byte, defaultTitle string, context *SchemaParseContext) (TypeSchema, error) {

	var schema TypeSchema
	var contents map[string]*json.RawMessage
	var schemaRef string
	var schemaType string
	var present bool
	var err error

	err = json.Unmarshal(contentsBytes, &contents)
	if err != nil {
		return nil, err
	}

	// if this is a reference schema, simply return that exact schema, and do no other processing.
	schemaRef, err = getJsonString(contents, "$ref")
	if(err != nil) {
		return nil, err
	}

	if(len(schemaRef) > 0) {

		schema, present = context.SchemaDefinitions[schemaRef]

		if !present {
			errorMsg := fmt.Sprintf("Schema ref '%s' could not be resolved.", schemaRef)
			return nil, errors.New(errorMsg)
		}

		return schema, nil
	}

	// figure out type
	schemaType, err = getJsonString(contents, "type")
	if err != nil {
		return nil, err
	}

	if(len(schemaType) <= 0) {
		return nil, errors.New("Schema could not be parsed, type was not specified")
	}

	switch schemaType {

	case "integer":
		schema, err = NewIntegerSchema(contentsBytes, context)

	case "number":
		schema, err = NewNumberSchema(contentsBytes, context)

	case "string":
		schema, err = NewStringSchema(contentsBytes, context)

	case "array":
		schema, err = NewArraySchema(contentsBytes, context)

	case "object":
		schema, err = NewObjectSchema(contentsBytes, context)

	default:
		errorMsg := fmt.Sprintf("Unrecognized schema type: '%s'", schemaType)
		return nil, errors.New(errorMsg)
	}

	if err != nil {
		return nil, err
	}

	if len(schema.GetTitle()) == 0 {
		schema.SetTitle(defaultTitle)
	}

	context.SchemaDefinitions[schema.GetID()] = schema
	return schema, nil
}

/*
  Recurses the properties of the given [root],
  adding all sub-schemas to the given [schemas].
*/
func RecurseObjectSchemas(schema TypeSchema, schemas []*ObjectSchema) []*ObjectSchema {

	if schema.GetSchemaType() == SCHEMATYPE_OBJECT {
		return recurseObjectSchema(schema.(*ObjectSchema), schemas)
	}
	if schema.GetSchemaType() == SCHEMATYPE_ARRAY {
		return RecurseObjectSchemas(schema.(*ArraySchema).Items, schemas)
	}

	return schemas
}

func recurseObjectSchema(schema *ObjectSchema, schemas []*ObjectSchema) []*ObjectSchema {

	schemas = append(schemas, schema)

	for _, property := range schema.Properties {
		schemas = RecurseObjectSchemas(property, schemas)
	}

	return schemas
}

func getJsonString(source map[string]*json.RawMessage, key string) (string, error) {

	var ret string
	var retBytes []byte
	var message *json.RawMessage
	var err error
	var present bool

	message, present = source[key]
	if(!present) {
		return "", nil
	}

	retBytes, err = message.MarshalJSON()
	if(err != nil) {
		return "", err
	}

	ret = string(retBytes)
	return strings.Replace(ret, "\"", "", -1), nil
}
<|endoftext|>


<commit_before>package ristretto255

import (
	"bytes"
	"encoding/hex"
	"testing"

	"github.com/gtank/ristretto255/internal/edwards25519"
	"github.com/gtank/ristretto255/internal/radix51"
)

func assertFeEqual(value, expect *radix51.FieldElement) {
	if value.Equal(expect) == 1 {
		return
	} else {
		panic("failed equality assertion")
	}
}

type sqrtRatioTest struct {
	u, v     *radix51.FieldElement
	sqrt     *radix51.FieldElement
	choice   int
	negative int
}

// These tests can be found in curve25519-dalek's 'field.rs'
func TestSqrtRatioM1(t *testing.T) {
	var (
		zero, one = radix51.Zero, radix51.One

		// Two is nonsquare in our field, 4 is square
		two  = new(radix51.FieldElement).Add(one, one)
		four = new(radix51.FieldElement).Add(two, two)

		// 2*i
		twoTimesSqrtM1 = new(radix51.FieldElement).Mul(two, sqrtM1)

		sqrt2i = fieldElementFromDecimal(
			"38214883241950591754978413199355411911188925816896391856984770930832735035196")

		invSqrt4 = fieldElementFromDecimal(
			"28948022309329048855892746252171976963317496166410141009864396001978282409974")
	)

	// Check the construction of those magic numbers.
	assertFeEqual(new(radix51.FieldElement).Mul(sqrt2i, sqrt2i), twoTimesSqrtM1)
	assertFeEqual(new(radix51.FieldElement).Mul(new(radix51.FieldElement).Square(invSqrt4), four), one)

	var tests = []sqrtRatioTest{
		{u: zero, v: zero, sqrt: zero, choice: 1, negative: 0},    // 0
		{u: one, v: zero, sqrt: zero, choice: 0, negative: 0},     // 1
		{u: two, v: one, sqrt: sqrt2i, choice: 0, negative: 0},    // 2
		{u: four, v: one, sqrt: two, choice: 1, negative: 0},      // 3
		{u: one, v: four, sqrt: invSqrt4, choice: 1, negative: 0}, // 4
	}

	for idx, tt := range tests {
		sqrt := new(radix51.FieldElement)
		choice := feSqrtRatio(sqrt, tt.u, tt.v)
		if choice != tt.choice || sqrt.Equal(tt.sqrt) != 1 || sqrt.IsNegative() != tt.negative {
			t.Errorf("Failed test %d", idx)
			t.Logf("Got {u: %v, v: %v, sqrt: %v, choice: %d, neg: %d}", tt.u, tt.v, sqrt, choice, sqrt.IsNegative())
		}
	}
}

var (
	// The encoding of Ristretto element that can be represented internally by the Curve25519 base point.
	compressedRistrettoBasepoint, _ = hex.DecodeString("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76")

	// The representative Ristretto basepoint in extended coordinates.
	ristrettoBasepoint = Element{r: edwards25519.ExtendedGroupElement{
		X: radix51.FieldElement([5]uint64{426475514619346, 2063872706840040, 14628272888959, 107677749330612, 288339085807592}),
		Y: radix51.FieldElement([5]uint64{1934594822876571, 2049809580636559, 1991994783322914, 1758681962032007, 380046701118659}),
		Z: radix51.FieldElement([5]uint64{1, 0, 0, 0, 0}),
		T: radix51.FieldElement([5]uint64{410445769351754, 2235400917701188, 1495825632738689, 1351628537510093, 430502003771208}),
	}}
)

func TestRistrettoBasepointRoundTrip(t *testing.T) {
	decodedBasepoint := &Element{}
	err := decodedBasepoint.Decode(compressedRistrettoBasepoint)
	if err != nil {
		t.Fatal(err)
	}

	if decodedBasepoint.Equal(&ristrettoBasepoint) != 1 {
		t.Error("decode succeeded, but got wrong point")
	}

	roundtripBasepoint := decodedBasepoint.Encode(nil)
	if !bytes.Equal(compressedRistrettoBasepoint, roundtripBasepoint) {
		t.Error("decode<>encode roundtrip produced different results")
	}

	encodedBasepoint := ristrettoBasepoint.Encode(nil)
	if !bytes.Equal(compressedRistrettoBasepoint, encodedBasepoint) {
		t.Error("point encode produced different results")
	}
}

func TestRistrettoRandomRoundtrip(t *testing.T) {
	// TODO quickcheck
}

func TestRistrettoSmallMultiplesTestVectors(t *testing.T) {
	var testVectors = [16]string{
		// This is the identity point
		"0000000000000000000000000000000000000000000000000000000000000000",
		// This is the basepoint
		"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
		// These are small multiples of the basepoint
		"6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
		"94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259",
		"da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57",
		"e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e",
		"f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403",
		"44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d",
		"903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c",
		"02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031",
		"20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f",
		"bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42",
		"e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460",
		"aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f",
		"46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e",
		"e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e",
	}

	basepointMultiple := Element{}
	basepointMultiple.Zero()

	for i := range testVectors {
		// Grab the bytes of the encoding
		encoding, err := hex.DecodeString(testVectors[i])
		if err != nil {
			t.Fatalf("#%d: bad hex encoding in test vector: %v", i, err)
		}

		// Decode the test vector to a ristretto255 element
		decodedPoint := Element{}
		err = decodedPoint.Decode(encoding)
		if err != nil {
			t.Fatalf("#%d: could not decode test vector: %v", i, err)
		}
		// Re-encode and check round trips
		roundtripEncoding := decodedPoint.Encode(nil)
		if !bytes.Equal(encoding, roundtripEncoding) {
			t.Errorf("#%d: decode<>encode roundtrip failed", i)
		}

		// Check that the test vector encodes i * B
		if basepointMultiple.Equal(&decodedPoint) != 1 {
			t.Errorf("decoded small multiple %d * B is not %d * B", i, i)
		}
		computedEncoding := basepointMultiple.Encode(nil)
		if !bytes.Equal(encoding, computedEncoding) {
			t.Errorf("#%d: encoding computed value did not match", i)
		}

		// Ensure basepointMultiple = i * B in the next iteration
		basepointMultiple.Add(&basepointMultiple, &ristrettoBasepoint)
	}
}
<commit_msg>Add bad encoding test vectors<commit_after>package ristretto255

import (
	"bytes"
	"encoding/hex"
	"testing"

	"github.com/gtank/ristretto255/internal/edwards25519"
	"github.com/gtank/ristretto255/internal/radix51"
)

func assertFeEqual(value, expect *radix51.FieldElement) {
	if value.Equal(expect) == 1 {
		return
	} else {
		panic("failed equality assertion")
	}
}

type sqrtRatioTest struct {
	u, v     *radix51.FieldElement
	sqrt     *radix51.FieldElement
	choice   int
	negative int
}

// These tests can be found in curve25519-dalek's 'field.rs'
func TestSqrtRatioM1(t *testing.T) {
	var (
		zero, one = radix51.Zero, radix51.One

		// Two is nonsquare in our field, 4 is square
		two  = new(radix51.FieldElement).Add(one, one)
		four = new(radix51.FieldElement).Add(two, two)

		// 2*i
		twoTimesSqrtM1 = new(radix51.FieldElement).Mul(two, sqrtM1)

		sqrt2i = fieldElementFromDecimal(
			"38214883241950591754978413199355411911188925816896391856984770930832735035196")

		invSqrt4 = fieldElementFromDecimal(
			"28948022309329048855892746252171976963317496166410141009864396001978282409974")
	)

	// Check the construction of those magic numbers.
	assertFeEqual(new(radix51.FieldElement).Mul(sqrt2i, sqrt2i), twoTimesSqrtM1)
	assertFeEqual(new(radix51.FieldElement).Mul(new(radix51.FieldElement).Square(invSqrt4), four), one)

	var tests = []sqrtRatioTest{
		{u: zero, v: zero, sqrt: zero, choice: 1, negative: 0},    // 0
		{u: one, v: zero, sqrt: zero, choice: 0, negative: 0},     // 1
		{u: two, v: one, sqrt: sqrt2i, choice: 0, negative: 0},    // 2
		{u: four, v: one, sqrt: two, choice: 1, negative: 0},      // 3
		{u: one, v: four, sqrt: invSqrt4, choice: 1, negative: 0}, // 4
	}

	for idx, tt := range tests {
		sqrt := new(radix51.FieldElement)
		choice := feSqrtRatio(sqrt, tt.u, tt.v)
		if choice != tt.choice || sqrt.Equal(tt.sqrt) != 1 || sqrt.IsNegative() != tt.negative {
			t.Errorf("Failed test %d", idx)
			t.Logf("Got {u: %v, v: %v, sqrt: %v, choice: %d, neg: %d}", tt.u, tt.v, sqrt, choice, sqrt.IsNegative())
		}
	}
}

var (
	// The encoding of Ristretto element that can be represented internally by the Curve25519 base point.
	compressedRistrettoBasepoint, _ = hex.DecodeString("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76")

	// The representative Ristretto basepoint in extended coordinates.
	ristrettoBasepoint = Element{r: edwards25519.ExtendedGroupElement{
		X: radix51.FieldElement([5]uint64{426475514619346, 2063872706840040, 14628272888959, 107677749330612, 288339085807592}),
		Y: radix51.FieldElement([5]uint64{1934594822876571, 2049809580636559, 1991994783322914, 1758681962032007, 380046701118659}),
		Z: radix51.FieldElement([5]uint64{1, 0, 0, 0, 0}),
		T: radix51.FieldElement([5]uint64{410445769351754, 2235400917701188, 1495825632738689, 1351628537510093, 430502003771208}),
	}}
)

func TestRistrettoBasepointRoundTrip(t *testing.T) {
	decodedBasepoint := &Element{}
	err := decodedBasepoint.Decode(compressedRistrettoBasepoint)
	if err != nil {
		t.Fatal(err)
	}

	if decodedBasepoint.Equal(&ristrettoBasepoint) != 1 {
		t.Error("decode succeeded, but got wrong point")
	}

	roundtripBasepoint := decodedBasepoint.Encode(nil)
	if !bytes.Equal(compressedRistrettoBasepoint, roundtripBasepoint) {
		t.Error("decode<>encode roundtrip produced different results")
	}

	encodedBasepoint := ristrettoBasepoint.Encode(nil)
	if !bytes.Equal(compressedRistrettoBasepoint, encodedBasepoint) {
		t.Error("point encode produced different results")
	}
}

func TestRistrettoRandomRoundtrip(t *testing.T) {
	// TODO quickcheck
}

func TestRistrettoSmallMultiplesTestVectors(t *testing.T) {
	var testVectors = [16]string{
		// This is the identity point
		"0000000000000000000000000000000000000000000000000000000000000000",
		// This is the basepoint
		"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
		// These are small multiples of the basepoint
		"6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
		"94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259",
		"da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57",
		"e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e",
		"f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403",
		"44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d",
		"903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c",
		"02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031",
		"20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f",
		"bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42",
		"e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460",
		"aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f",
		"46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e",
		"e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e",
	}

	basepointMultiple := Element{}
	basepointMultiple.Zero()

	for i := range testVectors {
		// Grab the bytes of the encoding
		encoding, err := hex.DecodeString(testVectors[i])
		if err != nil {
			t.Fatalf("#%d: bad hex encoding in test vector: %v", i, err)
		}

		// Decode the test vector to a ristretto255 element
		decodedPoint := Element{}
		err = decodedPoint.Decode(encoding)
		if err != nil {
			t.Fatalf("#%d: could not decode test vector: %v", i, err)
		}
		// Re-encode and check round trips
		roundtripEncoding := decodedPoint.Encode(nil)
		if !bytes.Equal(encoding, roundtripEncoding) {
			t.Errorf("#%d: decode<>encode roundtrip failed", i)
		}

		// Check that the test vector encodes i * B
		if basepointMultiple.Equal(&decodedPoint) != 1 {
			t.Errorf("decoded small multiple %d * B is not %d * B", i, i)
		}
		computedEncoding := basepointMultiple.Encode(nil)
		if !bytes.Equal(encoding, computedEncoding) {
			t.Errorf("#%d: encoding computed value did not match", i)
		}

		// Ensure basepointMultiple = i * B in the next iteration
		basepointMultiple.Add(&basepointMultiple, &ristrettoBasepoint)
	}
}

func TestRistrettoBadEncodingsTestVectors(t *testing.T) {
	var testVectors = []string{
		// These are all bad because they're non-canonical field encodings.
		"00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
		"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
		"f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
		"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
		// These are all bad because they're negative field elements.
		"0100000000000000000000000000000000000000000000000000000000000000",
		"01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
		"ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20",
		"c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562",
		"c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78",
		"47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24",
		"f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72",
		"87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309",
		// These are all bad because they give a nonsquare x^2.
		"26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371",
		"4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f",
		"de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b",
		"bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042",
		"2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08",
		"f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22",
		"8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731",
		"2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b",
		// These are all bad because they give a negative xy value.
		"3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e",
		"a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220",
		"d483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e",
		"8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32",
		"32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b",
		"227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165",
		"5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e",
		"445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b",
		// This is s = -1, which causes y = 0.
		"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
	}

	basepointMultiple := Element{}
	basepointMultiple.Zero()

	for i := range testVectors {
		// Grab the bytes of the encoding
		encoding, err := hex.DecodeString(testVectors[i])
		if err != nil {
			t.Fatalf("#%d: bad hex encoding in test vector: %v", i, err)
		}

		// Attempt decoding
		decodedPoint := Element{}
		err = decodedPoint.Decode(encoding)
		if err == nil {
			t.Fatalf("#%d: did not fail on bad encoding", i)
		}
	}
}
<|endoftext|>