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

package lxc

import (
	"errors"
	"fmt"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/fs"
	"github.com/globocom/tsuru/log"
	"io/ioutil"
	"net"
	"os/exec"
	"strings"
	"time"
)

var fsystem fs.Fs

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}

// container represents an lxc container with the given name.
type container struct {
	name string
	ip   string
}

// runCmd executes commands and log the given stdout and stderror.
func runCmd(cmd string, args ...string) error {
	command := exec.Command(cmd, args...)
	out, err := command.CombinedOutput()
	log.Printf("running the cmd: %s with the args: %s", cmd, args)
	log.Print(string(out))
	return err
}

// Ip returns the ip for the container.
func (c *container) Ip() string {
	if c.ip != "" {
		return c.ip
	}
	timeout, err := config.GetInt("lxc:ip-timeout")
	if err != nil {
		timeout = 60
	}
	quit := time.After(time.Duration(timeout) * time.Second)
	tick := time.Tick(2 * time.Second)
	stop := false
	for !stop {
		select {
		case <-tick:
			file, _ := filesystem().Open("/var/lib/misc/dnsmasq.leases")
			data, _ := ioutil.ReadAll(file)
			log.Print("dnsmasq.leases")
			log.Print(string(data))
			for _, line := range strings.Split(string(data), "\n") {
				if strings.Index(line, c.name) != -1 {
					log.Printf("ip in %s", line)
					c.ip = strings.Split(line, " ")[2]
					return c.ip
				}
			}
		case <-quit:
			stop = true
		default:
			time.Sleep(1 * time.Second)
		}
	}
	return ""
}

// create creates a lxc container with ubuntu template by default.
func (c *container) create() error {
	keyPath, err := config.GetString("lxc:authorized-key-path")
	if err != nil {
		return err
	}
	return runCmd("sudo", "lxc-create", "-t", "ubuntu-cloud", "-n", c.name, "--", "-S", keyPath)
}

// start starts a lxc container.
func (c *container) start() error {
	return runCmd("sudo", "lxc-start", "--daemon", "-n", c.name)
}

// stop stops a lxc container.
func (c *container) stop() error {
	return runCmd("sudo", "lxc-stop", "-n", c.name)
}

// destroy destory a lxc container.
func (c *container) destroy() error {
	return runCmd("sudo", "lxc-destroy", "-n", c.name)
}

// waitForNetwork waits the container network is up.
func (c *container) waitForNetwork() error {
	t, err := config.GetInt("lxc:ip-timeout")
	if err != nil {
		t = 60
	}
	timeout := time.After(time.Duration(t) * time.Second)
	done := make(chan bool)
	go func(c *container) {
		for {
			port, err := config.GetInt("lxc:ssh-port")
			if err != nil {
				port = 22
			}
			addr := fmt.Sprintf("%s:%d", c.Ip(), port)
			conn, err := net.Dial("tcp", addr)
			if err == nil {
				conn.Close()
				done <- true
				break
			}
		}
	}(c)
	select {
	case <-done:
		return nil
	case <-timeout:
		return errors.New("timeout")
	}
	return nil
}
<commit_msg>lxc: simplifying.<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package lxc

import (
	"errors"
	"fmt"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/fs"
	"github.com/globocom/tsuru/log"
	"io/ioutil"
	"net"
	"os/exec"
	"strings"
	"time"
)

var fsystem fs.Fs

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}

// container represents an lxc container with the given name.
type container struct {
	name string
	ip   string
}

// runCmd executes commands and log the given stdout and stderror.
func runCmd(cmd string, args ...string) error {
	command := exec.Command(cmd, args...)
	out, err := command.CombinedOutput()
	log.Printf("running the cmd: %s with the args: %s", cmd, args)
	log.Print(string(out))
	return err
}

// Ip returns the ip for the container.
func (c *container) Ip() string {
	if c.ip != "" {
		return c.ip
	}
	timeout, err := config.GetInt("lxc:ip-timeout")
	if err != nil {
		timeout = 60
	}
	quit := time.After(time.Duration(timeout) * time.Second)
	tick := time.Tick(2 * time.Second)
	stop := false
	for !stop {
		select {
		case <-tick:
			file, _ := filesystem().Open("/var/lib/misc/dnsmasq.leases")
			data, _ := ioutil.ReadAll(file)
			log.Print("dnsmasq.leases")
			log.Print(string(data))
			for _, line := range strings.Split(string(data), "\n") {
				if strings.Index(line, c.name) != -1 {
					log.Printf("ip in %s", line)
					c.ip = strings.Split(line, " ")[2]
					return c.ip
				}
			}
		case <-quit:
			stop = true
		default:
			time.Sleep(1 * time.Second)
		}
	}
	return ""
}

// create creates a lxc container with ubuntu template by default.
func (c *container) create() error {
	keyPath, err := config.GetString("lxc:authorized-key-path")
	if err != nil {
		return err
	}
	return runCmd("sudo", "lxc-create", "-t", "ubuntu-cloud", "-n", c.name, "--", "-S", keyPath)
}

// start starts a lxc container.
func (c *container) start() error {
	return runCmd("sudo", "lxc-start", "--daemon", "-n", c.name)
}

// stop stops a lxc container.
func (c *container) stop() error {
	return runCmd("sudo", "lxc-stop", "-n", c.name)
}

// destroy destory a lxc container.
func (c *container) destroy() error {
	return runCmd("sudo", "lxc-destroy", "-n", c.name)
}

// waitForNetwork waits the container network is up.
func (c *container) waitForNetwork() error {
	t, err := config.GetInt("lxc:ip-timeout")
	if err != nil {
		t = 60
	}
	timeout := time.After(time.Duration(t) * time.Second)
	done := make(chan bool)
	go func(c *container) {
		for {
			port, err := config.GetInt("lxc:ssh-port")
			if err != nil {
				port = 22
			}
			addr := fmt.Sprintf("%s:%d", c.Ip(), port)
			conn, err := net.Dial("tcp", addr)
			if err == nil {
				conn.Close()
				done <- true
				break
			}
		}
	}(c)
	select {
	case <-done:
	case <-timeout:
		return errors.New("timeout")
	}
	return nil
}
<|endoftext|>


<commit_before>package drivers

import (
	"database/sql"
	"fmt"

	// Side-effect import sql driver
	_ "github.com/lib/pq"
	"github.com/nullbio/sqlboiler/bdb"
)

// PostgresDriver holds the database connection string and a handle
// to the database connection.
type PostgresDriver struct {
	connStr string
	dbConn  *sql.DB
}

// NewPostgresDriver takes the database connection details as parameters and
// returns a pointer to a PostgresDriver object. Note that it is required to
// call PostgresDriver.Open() and PostgresDriver.Close() to open and close
// the database connection once an object has been obtained.
func NewPostgresDriver(user, pass, dbname, host string, port int) *PostgresDriver {
	driver := PostgresDriver{
		connStr: fmt.Sprintf("user=%s password=%s dbname=%s host=%s port=%d",
			user, pass, dbname, host, port),
	}

	return &driver
}

// Open opens the database connection using the connection string
func (p *PostgresDriver) Open() error {
	var err error
	p.dbConn, err = sql.Open("postgres", p.connStr)
	if err != nil {
		return err
	}

	return nil
}

// Close closes the database connection
func (p *PostgresDriver) Close() {
	p.dbConn.Close()
}

// TableNames connects to the postgres database and
// retrieves all table names from the information_schema where the
// table schema is public. It excludes common migration tool tables
// such as gorp_migrations
func (p *PostgresDriver) TableNames() ([]string, error) {
	var names []string

	rows, err := p.dbConn.Query(`
		select table_name from information_schema.tables
		where table_schema='public' and table_name not like '%migrations%'
	`)

	if err != nil {
		return nil, err
	}

	defer rows.Close()
	for rows.Next() {
		var name string
		if err := rows.Scan(&name); err != nil {
			return nil, err
		}
		names = append(names, name)
	}

	return names, nil
}

// Columns takes a table name and attempts to retrieve the table information
// from the database information_schema.columns. It retrieves the column names
// and column types and returns those as a []Column after TranslateColumnType()
// converts the SQL types to Go types, for example: "varchar" to "string"
func (p *PostgresDriver) Columns(tableName string) ([]bdb.Column, error) {
	var columns []bdb.Column

	rows, err := p.dbConn.Query(`
	select column_name, data_type, column_default, is_nullable
	from information_schema.columns where table_name=$1
	`, tableName)

	if err != nil {
		return nil, err
	}

	defer rows.Close()
	for rows.Next() {
		var colName, colType, colDefault, isNullable string
		var defaultPtr *string
		if err := rows.Scan(&colName, &colType, &defaultPtr, &isNullable); err != nil {
			return nil, fmt.Errorf("unable to scan for table %s: %s", tableName, err)
		}

		if defaultPtr == nil {
			colDefault = ""
		} else {
			colDefault = *defaultPtr
		}

		column := bdb.Column{
			Name:       colName,
			Type:       colType,
			Default:    colDefault,
			IsNullable: isNullable == "YES",
		}
		columns = append(columns, column)
	}

	return columns, nil
}

// PrimaryKeyInfo looks up the primary key for a table.
func (p *PostgresDriver) PrimaryKeyInfo(tableName string) (*bdb.PrimaryKey, error) {
	pkey := &bdb.PrimaryKey{}
	var err error

	query := `
	select tc.constraint_name
	from information_schema.table_constraints as tc
	where tc.table_name = $1 and tc.constraint_type = 'PRIMARY KEY';`

	row := p.dbConn.QueryRow(query, tableName)
	if err = row.Scan(&pkey.Name); err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	queryColumns := `
	select kcu.column_name
	from   information_schema.key_column_usage as kcu
	where  constraint_name = $1;`

	var rows *sql.Rows
	if rows, err = p.dbConn.Query(queryColumns, pkey.Name); err != nil {
		return nil, err
	}

	var columns []string
	for rows.Next() {
		var column string

		err = rows.Scan(&column)
		if err != nil {
			return nil, err
		}

		columns = append(columns, column)
	}

	if err = rows.Err(); err != nil {
		return nil, err
	}

	pkey.Columns = columns

	return pkey, nil
}

// ForeignKeyInfo retrieves the foreign keys for a given table name.
func (p *PostgresDriver) ForeignKeyInfo(tableName string) ([]bdb.ForeignKey, error) {
	var fkeys []bdb.ForeignKey

	query := `
	select
		tc.constraint_name,
		kcu.table_name as source_table,
		kcu.column_name as source_column,
		ccu.table_name as dest_table,
		ccu.column_name as dest_column
	from information_schema.table_constraints as tc
		inner join information_schema.key_column_usage as kcu ON tc.constraint_name = kcu.constraint_name
		inner join information_schema.constraint_column_usage as ccu ON tc.constraint_name = ccu.constraint_name
	where tc.table_name = $1 and tc.constraint_type = 'FOREIGN KEY';`

	var rows *sql.Rows
	var err error
	if rows, err = p.dbConn.Query(query, tableName); err != nil {
		return nil, err
	}

	for rows.Next() {
		var fkey bdb.ForeignKey
		var sourceTable string

		err = rows.Scan(&fkey.Name, &sourceTable, &fkey.Column, &fkey.ForeignTable, &fkey.ForeignColumn)
		if err != nil {
			return nil, err
		}

		fkeys = append(fkeys, fkey)
	}

	if err = rows.Err(); err != nil {
		return nil, err
	}

	return fkeys, nil
}

// TranslateColumnType converts postgres database types to Go types, for example
// "varchar" to "string" and "bigint" to "int64". It returns this parsed data
// as a Column object.
func (p *PostgresDriver) TranslateColumnType(c bdb.Column) bdb.Column {
	if c.IsNullable {
		switch c.Type {
		case "bigint", "bigserial":
			c.Type = "null.Int64"
		case "integer", "serial":
			c.Type = "null.Int32"
		case "smallint", "smallserial":
			c.Type = "null.Int16"
		case "decimal", "numeric", "double precision", "money":
			c.Type = "null.Float64"
		case "real":
			c.Type = "null.Float32"
		case "bit", "bit varying", "character", "character varying", "cidr", "inet", "json", "macaddr", "text", "uuid", "xml":
			c.Type = "null.String"
		case "boolean":
			c.Type = "null.Bool"
		case "date", "interval", "time", "timestamp without time zone", "timestamp with time zone":
			c.Type = "null.Time"
		default:
			c.Type = "null.String"
		}
	} else {
		switch c.Type {
		case "bigint", "bigserial":
			c.Type = "int64"
		case "integer", "serial":
			c.Type = "int32"
		case "smallint", "smallserial":
			c.Type = "int16"
		case "decimal", "numeric", "double precision", "money":
			c.Type = "float64"
		case "real":
			c.Type = "float32"
		case "bit", "bit varying", "character", "character varying", "cidr", "inet", "json", "macaddr", "text", "uuid", "xml":
			c.Type = "string"
		case "bytea":
			c.Type = "[]byte"
		case "boolean":
			c.Type = "bool"
		case "date", "interval", "time", "timestamp without time zone", "timestamp with time zone":
			c.Type = "time.Time"
		default:
			c.Type = "string"
		}
	}

	return c
}
<commit_msg>Remove system tables from postgres driver lookups<commit_after>package drivers

import (
	"database/sql"
	"fmt"

	// Side-effect import sql driver
	_ "github.com/lib/pq"
	"github.com/nullbio/sqlboiler/bdb"
)

// PostgresDriver holds the database connection string and a handle
// to the database connection.
type PostgresDriver struct {
	connStr string
	dbConn  *sql.DB
}

// NewPostgresDriver takes the database connection details as parameters and
// returns a pointer to a PostgresDriver object. Note that it is required to
// call PostgresDriver.Open() and PostgresDriver.Close() to open and close
// the database connection once an object has been obtained.
func NewPostgresDriver(user, pass, dbname, host string, port int) *PostgresDriver {
	driver := PostgresDriver{
		connStr: fmt.Sprintf("user=%s password=%s dbname=%s host=%s port=%d",
			user, pass, dbname, host, port),
	}

	return &driver
}

// Open opens the database connection using the connection string
func (p *PostgresDriver) Open() error {
	var err error
	p.dbConn, err = sql.Open("postgres", p.connStr)
	if err != nil {
		return err
	}

	return nil
}

// Close closes the database connection
func (p *PostgresDriver) Close() {
	p.dbConn.Close()
}

// TableNames connects to the postgres database and
// retrieves all table names from the information_schema where the
// table schema is public. It excludes common migration tool tables
// such as gorp_migrations
func (p *PostgresDriver) TableNames() ([]string, error) {
	var names []string

	rows, err := p.dbConn.Query(`
		select table_name from information_schema.tables
		where table_schema = 'public' and table_name not like '%migrations%'
	`)

	if err != nil {
		return nil, err
	}

	defer rows.Close()
	for rows.Next() {
		var name string
		if err := rows.Scan(&name); err != nil {
			return nil, err
		}
		names = append(names, name)
	}

	return names, nil
}

// Columns takes a table name and attempts to retrieve the table information
// from the database information_schema.columns. It retrieves the column names
// and column types and returns those as a []Column after TranslateColumnType()
// converts the SQL types to Go types, for example: "varchar" to "string"
func (p *PostgresDriver) Columns(tableName string) ([]bdb.Column, error) {
	var columns []bdb.Column

	rows, err := p.dbConn.Query(`
	select column_name, data_type, column_default, is_nullable
	from information_schema.columns
	where table_name=$1 and table_schema = 'public'
	`, tableName)

	if err != nil {
		return nil, err
	}

	defer rows.Close()
	for rows.Next() {
		var colName, colType, colDefault, isNullable string
		var defaultPtr *string
		if err := rows.Scan(&colName, &colType, &defaultPtr, &isNullable); err != nil {
			return nil, fmt.Errorf("unable to scan for table %s: %s", tableName, err)
		}

		if defaultPtr == nil {
			colDefault = ""
		} else {
			colDefault = *defaultPtr
		}

		column := bdb.Column{
			Name:       colName,
			Type:       colType,
			Default:    colDefault,
			IsNullable: isNullable == "YES",
		}
		columns = append(columns, column)
	}

	return columns, nil
}

// PrimaryKeyInfo looks up the primary key for a table.
func (p *PostgresDriver) PrimaryKeyInfo(tableName string) (*bdb.PrimaryKey, error) {
	pkey := &bdb.PrimaryKey{}
	var err error

	query := `
	select tc.constraint_name
	from information_schema.table_constraints as tc
	where tc.table_name = $1 and tc.constraint_type = 'PRIMARY KEY' and tc.table_schema = 'public';`

	row := p.dbConn.QueryRow(query, tableName)
	if err = row.Scan(&pkey.Name); err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	queryColumns := `
	select kcu.column_name
	from   information_schema.key_column_usage as kcu
	where  constraint_name = $1 and table_schema = 'public';`

	var rows *sql.Rows
	if rows, err = p.dbConn.Query(queryColumns, pkey.Name); err != nil {
		return nil, err
	}

	var columns []string
	for rows.Next() {
		var column string

		err = rows.Scan(&column)
		if err != nil {
			return nil, err
		}

		columns = append(columns, column)
	}

	if err = rows.Err(); err != nil {
		return nil, err
	}

	pkey.Columns = columns

	return pkey, nil
}

// ForeignKeyInfo retrieves the foreign keys for a given table name.
func (p *PostgresDriver) ForeignKeyInfo(tableName string) ([]bdb.ForeignKey, error) {
	var fkeys []bdb.ForeignKey

	query := `
	select
		tc.constraint_name,
		kcu.table_name as source_table,
		kcu.column_name as source_column,
		ccu.table_name as dest_table,
		ccu.column_name as dest_column
	from information_schema.table_constraints as tc
		inner join information_schema.key_column_usage as kcu ON tc.constraint_name = kcu.constraint_name
		inner join information_schema.constraint_column_usage as ccu ON tc.constraint_name = ccu.constraint_name
	where tc.table_name = $1 and tc.constraint_type = 'FOREIGN KEY' and tc.table_schema = 'information_schema';`

	var rows *sql.Rows
	var err error
	if rows, err = p.dbConn.Query(query, tableName); err != nil {
		return nil, err
	}

	for rows.Next() {
		var fkey bdb.ForeignKey
		var sourceTable string

		err = rows.Scan(&fkey.Name, &sourceTable, &fkey.Column, &fkey.ForeignTable, &fkey.ForeignColumn)
		if err != nil {
			return nil, err
		}

		fkeys = append(fkeys, fkey)
	}

	if err = rows.Err(); err != nil {
		return nil, err
	}

	return fkeys, nil
}

// TranslateColumnType converts postgres database types to Go types, for example
// "varchar" to "string" and "bigint" to "int64". It returns this parsed data
// as a Column object.
func (p *PostgresDriver) TranslateColumnType(c bdb.Column) bdb.Column {
	if c.IsNullable {
		switch c.Type {
		case "bigint", "bigserial":
			c.Type = "null.Int64"
		case "integer", "serial":
			c.Type = "null.Int32"
		case "smallint", "smallserial":
			c.Type = "null.Int16"
		case "decimal", "numeric", "double precision", "money":
			c.Type = "null.Float64"
		case "real":
			c.Type = "null.Float32"
		case "bit", "bit varying", "character", "character varying", "cidr", "inet", "json", "macaddr", "text", "uuid", "xml":
			c.Type = "null.String"
		case "boolean":
			c.Type = "null.Bool"
		case "date", "interval", "time", "timestamp without time zone", "timestamp with time zone":
			c.Type = "null.Time"
		default:
			c.Type = "null.String"
		}
	} else {
		switch c.Type {
		case "bigint", "bigserial":
			c.Type = "int64"
		case "integer", "serial":
			c.Type = "int32"
		case "smallint", "smallserial":
			c.Type = "int16"
		case "decimal", "numeric", "double precision", "money":
			c.Type = "float64"
		case "real":
			c.Type = "float32"
		case "bit", "bit varying", "character", "character varying", "cidr", "inet", "json", "macaddr", "text", "uuid", "xml":
			c.Type = "string"
		case "bytea":
			c.Type = "[]byte"
		case "boolean":
			c.Type = "bool"
		case "date", "interval", "time", "timestamp without time zone", "timestamp with time zone":
			c.Type = "time.Time"
		default:
			c.Type = "string"
		}
	}

	return c
}
<|endoftext|>


<commit_before>// $G $D/$F.go

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

package main

type T struct {
  s string;
}

func main() {
  s := "";
  l1 := len(s);
  l2 := len(T.s);  // BUG: cannot take len() of a string field
}

/*
uetli:/home/gri/go/test/bugs gri$ 6g bug057.go
bug057.go:13: syntax error
*/
<commit_msg>code had syntax error masking real bug<commit_after>// $G $D/$F.go

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

package main

type T struct {
  s string;
}

func main() {
  s := "";
  l1 := len(s);
  var t T;
  l2 := len(T.s);  // BUG: cannot take len() of a string field
}

/*
uetli:/home/gri/go/test/bugs gri$ 6g bug057.go
bug057.go:14: syntax error
*/
<|endoftext|>


<commit_before>package proxy

import (
	"context"
	"fmt"
	"regexp"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/aws/aws-sdk-go/service/sts/stsiface"
	"github.com/pkg/errors"
)

const (
	maxSessionNameLen int = 32
)

var (
	// matches char that is not valid in a STS role session name
	invalidSessionNameRegexp = regexp.MustCompile(`[^\w+=,.@-]`)

	sessionExpiration = 5 * time.Minute
)

type credentials struct {
	AccessKey   string
	Expiration  time.Time
	GeneratedAt time.Time
	RoleArn     roleArn
	SecretKey   string
	Token       string
}

func (c credentials) ExpiredNow() bool {
	return c.ExpiredAt(time.Now())
}

func (c credentials) ExpiredAt(at time.Time) bool {
	return at.After(c.Expiration)
}

func (c credentials) ExpiresIn(d time.Duration) bool {
	return c.ExpiredAt(time.Now().Add(d))
}

type containerCredentials struct {
	containerInfo
	credentials
}

func (c containerCredentials) IsValid(container containerInfo) bool {
	return c.containerInfo.IamRole.Equals(container.IamRole) &&
		c.containerInfo.ID == container.ID &&
		!c.credentials.ExpiresIn(sessionExpiration)
}

type credentialsProvider struct {
	container            containerService
	awsSts               stsiface.STSAPI
	defaultIamRoleArn    roleArn
	defaultIamPolicy     string
	containerCredentials map[string]containerCredentials
	lock                 sync.Mutex
}

func newCredentialsProvider(stsSvc stsiface.STSAPI, container containerService, defaultIamRoleArn roleArn, defaultIamPolicy string) *credentialsProvider {
	return &credentialsProvider{
		container:            container,
		awsSts:               stsSvc,
		defaultIamRoleArn:    defaultIamRoleArn,
		defaultIamPolicy:     defaultIamPolicy,
		containerCredentials: make(map[string]containerCredentials),
	}
}

// CredentialsForIP resolves the IP to a specific container, then attempts to assume the role
// specified in the container's metadata. Role specific credentials are returned.
//
// If the cache contains no fresh and valid role credentials, a fresh set is requested from
// AWS and cached.
func (c *credentialsProvider) CredentialsForIP(ctx context.Context, containerIP string) (credentials, error) {
	c.lock.Lock()
	defer c.lock.Unlock()

	container, err := c.container.ContainerForIP(ctx, containerIP)
	if err != nil {
		return credentials{}, errors.Wrapf(err, "Error finding container with IP [%s]", containerIP)
	}

	oldCredentials, found := c.containerCredentials[containerIP]

	if !found || !oldCredentials.IsValid(container) {
		arn := container.IamRole
		iamPolicy := container.IamPolicy

		if arn.Empty() {
			arn = c.defaultIamRoleArn

			if len(iamPolicy) == 0 {
				iamPolicy = c.defaultIamPolicy
			}
		}

		role, err := c.AssumeRole(arn, iamPolicy, generateSessionName(c.container.TypeName(), container.ID))

		if err != nil {
			return credentials{}, errors.Wrapf(err, "Error assuming role [%s] for container [%s] at IP {%s]", arn, container.Name, containerIP)
		}

		oldCredentials = containerCredentials{container, role}
		c.containerCredentials[containerIP] = oldCredentials
	}

	return oldCredentials.credentials, nil
}

func (c *credentialsProvider) AssumeRole(role roleArn, iamPolicy, sessionName string) (credentials, error) {
	var policy *string

	if len(iamPolicy) > 0 {
		policy = aws.String(iamPolicy)
	}

	resp, err := c.awsSts.AssumeRole(&sts.AssumeRoleInput{
		DurationSeconds: aws.Int64(3600), // Max is 1 hour
		Policy:          policy,
		RoleArn:         aws.String(role.String()),
		RoleSessionName: aws.String(sessionName),
	})

	if err != nil {
		return credentials{}, errors.Wrapf(err, "Error assuming role [%s] with policy [%s] and session name [%s]]", role, iamPolicy, sessionName)
	}

	return credentials{
		AccessKey:   *resp.Credentials.AccessKeyId,
		SecretKey:   *resp.Credentials.SecretAccessKey,
		Token:       *resp.Credentials.SessionToken,
		Expiration:  *resp.Credentials.Expiration,
		GeneratedAt: time.Now(),
		RoleArn:     role,
	}, nil
}

func generateSessionName(platform, containerID string) string {
	sessionName := fmt.Sprintf("%s-%s", platform, containerID)
	return invalidSessionNameRegexp.ReplaceAllString(sessionName, "_")[0:maxSessionNameLen]
}
<commit_msg>fix: default policy only applied w/ default role<commit_after>package proxy

import (
	"context"
	"fmt"
	"regexp"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/aws/aws-sdk-go/service/sts/stsiface"
	"github.com/pkg/errors"
)

const (
	maxSessionNameLen int = 32
)

var (
	// matches char that is not valid in a STS role session name
	invalidSessionNameRegexp = regexp.MustCompile(`[^\w+=,.@-]`)

	sessionExpiration = 5 * time.Minute
)

type credentials struct {
	AccessKey   string
	Expiration  time.Time
	GeneratedAt time.Time
	RoleArn     roleArn
	SecretKey   string
	Token       string
}

func (c credentials) ExpiredNow() bool {
	return c.ExpiredAt(time.Now())
}

func (c credentials) ExpiredAt(at time.Time) bool {
	return at.After(c.Expiration)
}

func (c credentials) ExpiresIn(d time.Duration) bool {
	return c.ExpiredAt(time.Now().Add(d))
}

type containerCredentials struct {
	containerInfo
	credentials
}

func (c containerCredentials) IsValid(container containerInfo) bool {
	return c.containerInfo.IamRole.Equals(container.IamRole) &&
		c.containerInfo.ID == container.ID &&
		!c.credentials.ExpiresIn(sessionExpiration)
}

type credentialsProvider struct {
	container            containerService
	awsSts               stsiface.STSAPI
	defaultIamRoleArn    roleArn
	defaultIamPolicy     string
	containerCredentials map[string]containerCredentials
	lock                 sync.Mutex
}

func newCredentialsProvider(stsSvc stsiface.STSAPI, container containerService, defaultIamRoleArn roleArn, defaultIamPolicy string) *credentialsProvider {
	return &credentialsProvider{
		container:            container,
		awsSts:               stsSvc,
		defaultIamRoleArn:    defaultIamRoleArn,
		defaultIamPolicy:     defaultIamPolicy,
		containerCredentials: make(map[string]containerCredentials),
	}
}

// CredentialsForIP resolves the IP to a specific container, then attempts to assume the role
// specified in the container's metadata. Role specific credentials are returned.
//
// If the cache contains no fresh and valid role credentials, a fresh set is requested from
// AWS and cached.
func (c *credentialsProvider) CredentialsForIP(ctx context.Context, containerIP string) (credentials, error) {
	c.lock.Lock()
	defer c.lock.Unlock()

	container, err := c.container.ContainerForIP(ctx, containerIP)
	if err != nil {
		return credentials{}, errors.Wrapf(err, "Error finding container with IP [%s]", containerIP)
	}

	oldCredentials, found := c.containerCredentials[containerIP]

	if !found || !oldCredentials.IsValid(container) {
		arn := container.IamRole
		iamPolicy := container.IamPolicy

		if arn.Empty() {
			arn = c.defaultIamRoleArn
		}

		if len(iamPolicy) == 0 {
			iamPolicy = c.defaultIamPolicy
		}

		role, err := c.AssumeRole(arn, iamPolicy, generateSessionName(c.container.TypeName(), container.ID))

		if err != nil {
			return credentials{}, errors.Wrapf(err, "Error assuming role [%s] for container [%s] at IP {%s]", arn, container.Name, containerIP)
		}

		oldCredentials = containerCredentials{container, role}
		c.containerCredentials[containerIP] = oldCredentials
	}

	return oldCredentials.credentials, nil
}

func (c *credentialsProvider) AssumeRole(role roleArn, iamPolicy, sessionName string) (credentials, error) {
	var policy *string

	if len(iamPolicy) > 0 {
		policy = aws.String(iamPolicy)
	}

	resp, err := c.awsSts.AssumeRole(&sts.AssumeRoleInput{
		DurationSeconds: aws.Int64(3600), // Max is 1 hour
		Policy:          policy,
		RoleArn:         aws.String(role.String()),
		RoleSessionName: aws.String(sessionName),
	})

	if err != nil {
		return credentials{}, errors.Wrapf(err, "Error assuming role [%s] with policy [%s] and session name [%s]]", role, iamPolicy, sessionName)
	}

	return credentials{
		AccessKey:   *resp.Credentials.AccessKeyId,
		SecretKey:   *resp.Credentials.SecretAccessKey,
		Token:       *resp.Credentials.SessionToken,
		Expiration:  *resp.Credentials.Expiration,
		GeneratedAt: time.Now(),
		RoleArn:     role,
	}, nil
}

func generateSessionName(platform, containerID string) string {
	sessionName := fmt.Sprintf("%s-%s", platform, containerID)
	return invalidSessionNameRegexp.ReplaceAllString(sessionName, "_")[0:maxSessionNameLen]
}
<|endoftext|>


<commit_before>// Copyright 2020 The LUCI 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 backend

import (
	"context"
	"net/http"
	"time"

	"google.golang.org/api/googleapi"

	"go.chromium.org/luci/common/data/rand/mathrand"
	"go.chromium.org/luci/common/retry"
)

// quotaErrorIterator is an retry.Iterator implementation that only retries errors
// if they are tagged with errTag.
type quotaErrorIterator struct {
	delay    time.Duration
	maxDelay time.Duration
}

// Next implements exponential backoff retry.
// Not use retry.ExponentialBackOff because we don't want to limited by
// number of retries, and also add jitter.
func (it *quotaErrorIterator) Next(ctx context.Context, err error) time.Duration {
	if apiErr, ok := err.(*googleapi.Error); !ok || apiErr.Code != http.StatusForbidden || !hasReason(apiErr, "quotaExceeded") {
		return retry.Stop
	}

	delay := it.delay
	switch {
	case delay > it.maxDelay:
		delay = it.maxDelay
	default:
		nextDelay := delay * 2
		// +-10% of next delay.
		nextDelay = nextDelay - nextDelay/10 + time.Duration(mathrand.Intn(ctx, int(nextDelay/5)))
		it.delay = nextDelay
	}

	return delay
}

func quotaErrorIteratorFactory() retry.Factory {
	return func() retry.Iterator {
		return &quotaErrorIterator{
			delay:    time.Second,
			maxDelay: 10 * time.Second,
		}
	}
}
<commit_msg>[resultdb] Minor changes in quota iterator<commit_after>// Copyright 2020 The LUCI 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 backend

import (
	"context"
	"net/http"
	"time"

	"google.golang.org/api/googleapi"

	"go.chromium.org/luci/common/data/rand/mathrand"
	"go.chromium.org/luci/common/retry"
)

// quotaErrorIterator is an retry.Iterator implementation that only retries
// Google API quota errors.
type quotaErrorIterator struct {
	delay    time.Duration
	maxDelay time.Duration
}

// Next implements exponential backoff retry.
// Not use retry.ExponentialBackOff because we don't want to limited by
// number of retries, and also add jitter.
func (it *quotaErrorIterator) Next(ctx context.Context, err error) time.Duration {
	if apiErr, ok := err.(*googleapi.Error); !ok || apiErr.Code != http.StatusForbidden || !hasReason(apiErr, "quotaExceeded") {
		return retry.Stop
	}

	delay := it.delay
	if  delay > it.maxDelay {
		delay = it.maxDelay
	} else {
		nextDelay := delay * 2
		// +-10% of next delay.
		nextDelay = nextDelay - nextDelay/10 + time.Duration(mathrand.Intn(ctx, int(nextDelay/5)))
		it.delay = nextDelay
	}

	return delay
}

func quotaErrorIteratorFactory() retry.Factory {
	return func() retry.Iterator {
		return &quotaErrorIterator{
			delay:    time.Second,
			maxDelay: 10 * time.Second,
		}
	}
}
<|endoftext|>


<commit_before>package updater

import (
	"context"
	"fmt"
	"log"

	"golang.org/x/sync/errgroup"

	"github.com/Alkemic/webrss/feed_fetcher"
	"github.com/Alkemic/webrss/repository"
)

type feedRepository interface {
	List(ctx context.Context) ([]repository.Feed, error)
}

type webrssService interface {
	SaveEntries(ctx context.Context, feedID int64, entries []repository.Entry) error
}

type feedFetcher interface {
	Fetch(ctx context.Context, url string) (feed_fetcher.Feed, error)
}

type UpdateService struct {
	feedRepository feedRepository
	webrssService  webrssService
	feedFetcher    feedFetcher
	logger         *log.Logger
}

func New(feedRepository feedRepository, webrssService webrssService, feedFetcher feedFetcher, logger *log.Logger) UpdateService {
	return UpdateService{
		feedRepository: feedRepository,
		webrssService:  webrssService,
		feedFetcher:    feedFetcher,
		logger:         logger,
	}
}

func (u UpdateService) Run(ctx context.Context) error {
	g, ctx := errgroup.WithContext(ctx)
	feeds, err := u.feedRepository.List(ctx)
	if err != nil {
		return fmt.Errorf("cannot select feeds: %w", err)
	}
	for _, feed := range feeds {
		feed := feed
		g.Go(func() error {
			feeder, err := u.feedFetcher.Fetch(ctx, feed.FeedUrl)
			if err != nil {
				u.logger.Println("error fetching feed:", err)
				return nil
			}
			entries := feeder.Entries(ctx)
			if err := u.webrssService.SaveEntries(ctx, feed.ID, entries); err != nil {
				return fmt.Errorf("cannot save entry: %w", err)
			}
			return nil
		})
	}
	if err := g.Wait(); err != nil {
		return fmt.Errorf("got error during processing feeds: %w", err)
	}
	return nil
}
<commit_msg>Log url of failed feed<commit_after>package updater

import (
	"context"
	"fmt"
	"log"

	"golang.org/x/sync/errgroup"

	"github.com/Alkemic/webrss/feed_fetcher"
	"github.com/Alkemic/webrss/repository"
)

type feedRepository interface {
	List(ctx context.Context) ([]repository.Feed, error)
}

type webrssService interface {
	SaveEntries(ctx context.Context, feedID int64, entries []repository.Entry) error
}

type feedFetcher interface {
	Fetch(ctx context.Context, url string) (feed_fetcher.Feed, error)
}

type UpdateService struct {
	feedRepository feedRepository
	webrssService  webrssService
	feedFetcher    feedFetcher
	logger         *log.Logger
}

func New(feedRepository feedRepository, webrssService webrssService, feedFetcher feedFetcher, logger *log.Logger) UpdateService {
	return UpdateService{
		feedRepository: feedRepository,
		webrssService:  webrssService,
		feedFetcher:    feedFetcher,
		logger:         logger,
	}
}

func (u UpdateService) Run(ctx context.Context) error {
	g, ctx := errgroup.WithContext(ctx)
	feeds, err := u.feedRepository.List(ctx)
	if err != nil {
		return fmt.Errorf("cannot select feeds: %w", err)
	}
	for _, feed := range feeds {
		feed := feed
		g.Go(func() error {
			feeder, err := u.feedFetcher.Fetch(ctx, feed.FeedUrl)
			if err != nil {
				u.logger.Printf("error fetching feed %s: %v\n", feed.FeedUrl, err)
				return nil
			}
			entries := feeder.Entries(ctx)
			if err := u.webrssService.SaveEntries(ctx, feed.ID, entries); err != nil {
				return fmt.Errorf("cannot save entry: %w", err)
			}
			return nil
		})
	}
	if err := g.Wait(); err != nil {
		return fmt.Errorf("got error during processing feeds: %w", err)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/rnd"
)

// Parameters hold all configuration parameters
type Parameters struct {

	// sizes
	Nova int // number of objective values
	Noor int // number of out-of-range values
	Nsol int // total number of solutions
	Ncpu int // number of cpus

	// time
	Tf    int // final time
	DtExc int // delta time for exchange
	DtOut int // delta time for output

	// options
	DEC      float64 // C-coefficient for differential evolution
	Pll      bool    // parallel
	Seed     int     // seed for random numbers generator
	GenType  string  // generation type: "latin", "halton", "rnd"
	LatinDup int     // Latin Hypercube duplicates number
	EpsH     float64 // minimum value for 'h' constraints
	Verbose  bool    // show messages
	GenAll   bool    // generate all solutions together; i.e. not within each group/CPU
	Nsamples int     // run many samples
	BinInt   int     // flag that integers represent binary numbers if BinInt > 0; thus Nint=BinInt
	ClearFlt bool    // clear flt if corresponding int is 0
	ExcTour  bool    // use exchange via tournament
	ExcOne   bool    // use exchange one randomly

	// crossover and mutation of integers
	IntPc       float64 // probability of crossover for ints
	IntNcuts    int     // number of cuts in crossover of ints
	IntPm       float64 // probability of mutation for ints
	IntNchanges int     // number of changes during mutation of ints

	// range
	FltMin []float64 // minimum float allowed
	FltMax []float64 // maximum float allowed
	IntMin []int     // minimum int allowed
	IntMax []int     // maximum int allowed

	// derived
	Nflt   int       // number of floats
	Nint   int       // number of integers
	DelFlt []float64 // max float range
	DelInt []int     // max int range

	// extra variables not direclty related to GOGA (for convenience of having a reader already)
	Strategy int  // strategy
	PlotSet1 bool // plot set of graphs 1
	PlotSet2 bool // plot set of graphs 2
}

// Default sets default parameters
func (o *Parameters) Default() {

	// sizes
	o.Nova = 1
	o.Noor = 0
	o.Nsol = 40
	o.Ncpu = 4

	// time
	o.Tf = 100
	o.DtExc = -1
	o.DtOut = -1

	// options
	o.DEC = 0.8
	o.Pll = true
	o.Seed = 0
	o.GenType = "latin"
	o.LatinDup = 2
	o.EpsH = 0.1
	o.Verbose = true
	o.GenAll = false
	o.Nsamples = 10
	o.BinInt = 0
	o.ClearFlt = false
	o.ExcTour = true
	o.ExcOne = true

	// crossover and mutation of integers
	o.IntPc = 0.8
	o.IntNcuts = 1
	o.IntPm = 0.01
	o.IntNchanges = 1
}

// Read reads configuration parameters from JSON file
func (o *Parameters) Read(filenamepath string) {
	o.Default()
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}
	err = json.Unmarshal(b, o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}
	return
}

// CalcDerived computes derived variables and checks consistency
func (o *Parameters) CalcDerived() {

	// check
	if o.Nova < 1 {
		chk.Panic("number of objective values (nova) must be greater than 0")
	}
	if o.Nsol < 6 {
		chk.Panic("number of solutions must greater than 6. Nsol = %d is invalid", o.Nsol)
	}
	if o.Ncpu < 2 {
		o.Ncpu = 1
		o.Pll = false
		o.DtExc = 1
	}
	if o.Ncpu > o.Nsol/2 {
		chk.Panic("number of CPU must be smaller than or equal to half the number of solutions. Ncpu=%d > Nsol/2=%d", o.Ncpu, o.Nsol/2)
	}
	if o.Tf < 1 {
		o.Tf = 1
	}
	if o.DtExc < 1 {
		o.DtExc = o.Tf / 10
	}
	if o.DtOut < 1 {
		o.DtOut = o.Tf / 5
	}

	// derived
	o.Nflt = len(o.FltMin)
	o.Nint = len(o.IntMin)
	if o.BinInt > 0 {
		o.Nint = o.BinInt
	}
	if o.Nflt == 0 && o.Nint == 0 {
		chk.Panic("either floats and ints must be set (via FltMin/Max or IntMin/Max)")
	}

	// floats
	if o.Nflt > 0 {
		chk.IntAssert(len(o.FltMax), o.Nflt)
		o.DelFlt = make([]float64, o.Nflt)
		for i := 0; i < o.Nflt; i++ {
			o.DelFlt[i] = o.FltMax[i] - o.FltMin[i]
		}
	}

	// generic ints
	if o.BinInt == 0 && o.Nint > 0 {
		chk.IntAssert(len(o.IntMax), o.Nint)
		o.DelInt = make([]int, o.Nint)
		for i := 0; i < o.Nint; i++ {
			o.DelInt[i] = o.IntMax[i] - o.IntMin[i]
		}
	}
	if o.Nint != o.Nflt {
		o.ClearFlt = false
	}

	// number of cuts and changes in ints
	if o.Nint > 0 {
		if o.IntNcuts > o.Nint {
			o.IntNcuts = o.Nint
		}
		if o.IntNchanges > o.Nint {
			o.IntNchanges = o.Nint
		}
	}

	// initialise random numbers generator
	rnd.Init(o.Seed)
}

// EnforceRange makes sure x is within given range
func (o *Parameters) EnforceRange(i int, x float64) float64 {
	if x < o.FltMin[i] {
		return o.FltMin[i]
	}
	if x > o.FltMax[i] {
		return o.FltMax[i]
	}
	return x
}

// LogParams returns a log with current parameters
func (o *Parameters) LogParams() (l string) {

	// sizes
	l += io.ArgsTable("SIZES",
		"number of objective values", "Nova", o.Nova,
		"number of out-of-range values", "Noor", o.Noor,
		"total number of solutions", "Nsol", o.Nsol,
		"number of cpus", "Ncpu", o.Ncpu,
	)

	// time
	l += "\n"
	l += io.ArgsTable("TIME",
		"final time", "Tf", o.Tf,
		"delta time for exchange", "DtExc", o.DtExc,
		"delta time for output", "DtOut", o.DtOut,
	)

	// options
	l += "\n"
	l += io.ArgsTable("OPTIONS",
		"C-coefficient for differential evolution", "DEC", o.DEC,
		"parallel", "Pll", o.Pll,
		"seed for random numbers generator", "Seed", o.Seed,
		"generation type: 'latin', 'halton', 'rnd'", "GenType", o.GenType,
		"Latin Hypercube duplicates number", "LatinDup", o.LatinDup,
		"minimum value for 'h' constraints", "EpsH", o.EpsH,
		"show messages", "Verbose", o.Verbose,
		"generate all solutions together", "GenAll", o.GenAll,
		"run many trials", "Nsamples", o.Nsamples,
		"integers represent binary numbers", "BinInt", o.BinInt,
		"clear flt if corresponding int is 0", "ClearFlt", o.ClearFlt,
		"use exchange via tournament", "ExcTour", o.ExcTour,
		"use exchange one randomly", "ExcOne", o.ExcOne,
	)

	// crossover and mutation of integers
	l += "\n"
	l += io.ArgsTable("CROSSOVER AND MUTATION OF INTS",
		"probability of crossover for ints", "IntPc", o.IntPc,
		"number of cuts in crossover of ints", "IntNcuts", o.IntNcuts,
		"probability of mutation for ints", "IntPm", o.IntPm,
		"number of changes during mutation of ints", "IntNchanges", o.IntNchanges,
	)

	// derived
	l += "\n"
	l += io.ArgsTable("DERIVED",
		"number of floats", "Nflt", o.Nflt,
		"number of integers", "Nint", o.Nint,
	)

	// extra
	l += "\n"
	l += io.ArgsTable("EXTRA",
		"strategy", "Strategy", o.Strategy,
		"plot set of graphs 1", "PlotSet1", o.PlotSet1,
		"plot set of graphs 2", "PlotSet2", o.PlotSet2,
	)
	return
}
<commit_msg>prob num added to options<commit_after>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/rnd"
)

// Parameters hold all configuration parameters
type Parameters struct {

	// sizes
	Nova int // number of objective values
	Noor int // number of out-of-range values
	Nsol int // total number of solutions
	Ncpu int // number of cpus

	// time
	Tf    int // final time
	DtExc int // delta time for exchange
	DtOut int // delta time for output

	// options
	DEC      float64 // C-coefficient for differential evolution
	Pll      bool    // parallel
	Seed     int     // seed for random numbers generator
	GenType  string  // generation type: "latin", "halton", "rnd"
	LatinDup int     // Latin Hypercube duplicates number
	EpsH     float64 // minimum value for 'h' constraints
	Verbose  bool    // show messages
	GenAll   bool    // generate all solutions together; i.e. not within each group/CPU
	Nsamples int     // run many samples
	BinInt   int     // flag that integers represent binary numbers if BinInt > 0; thus Nint=BinInt
	ClearFlt bool    // clear flt if corresponding int is 0
	ExcTour  bool    // use exchange via tournament
	ExcOne   bool    // use exchange one randomly

	// crossover and mutation of integers
	IntPc       float64 // probability of crossover for ints
	IntNcuts    int     // number of cuts in crossover of ints
	IntPm       float64 // probability of mutation for ints
	IntNchanges int     // number of changes during mutation of ints

	// range
	FltMin []float64 // minimum float allowed
	FltMax []float64 // maximum float allowed
	IntMin []int     // minimum int allowed
	IntMax []int     // maximum int allowed

	// derived
	Nflt   int       // number of floats
	Nint   int       // number of integers
	DelFlt []float64 // max float range
	DelInt []int     // max int range

	// extra variables not direclty related to GOGA (for convenience of having a reader already)
	Strategy int  // strategy
	PlotSet1 bool // plot set of graphs 1
	PlotSet2 bool // plot set of graphs 2
	ProbNum  int  // problem number
}

// Default sets default parameters
func (o *Parameters) Default() {

	// sizes
	o.Nova = 1
	o.Noor = 0
	o.Nsol = 40
	o.Ncpu = 4

	// time
	o.Tf = 100
	o.DtExc = -1
	o.DtOut = -1

	// options
	o.DEC = 0.8
	o.Pll = true
	o.Seed = 0
	o.GenType = "latin"
	o.LatinDup = 2
	o.EpsH = 0.1
	o.Verbose = true
	o.GenAll = false
	o.Nsamples = 10
	o.BinInt = 0
	o.ClearFlt = false
	o.ExcTour = true
	o.ExcOne = true

	// crossover and mutation of integers
	o.IntPc = 0.8
	o.IntNcuts = 1
	o.IntPm = 0.01
	o.IntNchanges = 1
}

// Read reads configuration parameters from JSON file
func (o *Parameters) Read(filenamepath string) {
	o.Default()
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}
	err = json.Unmarshal(b, o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}
	return
}

// CalcDerived computes derived variables and checks consistency
func (o *Parameters) CalcDerived() {

	// check
	if o.Nova < 1 {
		chk.Panic("number of objective values (nova) must be greater than 0")
	}
	if o.Nsol < 6 {
		chk.Panic("number of solutions must greater than 6. Nsol = %d is invalid", o.Nsol)
	}
	if o.Ncpu < 2 {
		o.Ncpu = 1
		o.Pll = false
		o.DtExc = 1
	}
	if o.Ncpu > o.Nsol/2 {
		chk.Panic("number of CPU must be smaller than or equal to half the number of solutions. Ncpu=%d > Nsol/2=%d", o.Ncpu, o.Nsol/2)
	}
	if o.Tf < 1 {
		o.Tf = 1
	}
	if o.DtExc < 1 {
		o.DtExc = o.Tf / 10
	}
	if o.DtOut < 1 {
		o.DtOut = o.Tf / 5
	}

	// derived
	o.Nflt = len(o.FltMin)
	o.Nint = len(o.IntMin)
	if o.BinInt > 0 {
		o.Nint = o.BinInt
	}
	if o.Nflt == 0 && o.Nint == 0 {
		chk.Panic("either floats and ints must be set (via FltMin/Max or IntMin/Max)")
	}

	// floats
	if o.Nflt > 0 {
		chk.IntAssert(len(o.FltMax), o.Nflt)
		o.DelFlt = make([]float64, o.Nflt)
		for i := 0; i < o.Nflt; i++ {
			o.DelFlt[i] = o.FltMax[i] - o.FltMin[i]
		}
	}

	// generic ints
	if o.BinInt == 0 && o.Nint > 0 {
		chk.IntAssert(len(o.IntMax), o.Nint)
		o.DelInt = make([]int, o.Nint)
		for i := 0; i < o.Nint; i++ {
			o.DelInt[i] = o.IntMax[i] - o.IntMin[i]
		}
	}
	if o.Nint != o.Nflt {
		o.ClearFlt = false
	}

	// number of cuts and changes in ints
	if o.Nint > 0 {
		if o.IntNcuts > o.Nint {
			o.IntNcuts = o.Nint
		}
		if o.IntNchanges > o.Nint {
			o.IntNchanges = o.Nint
		}
	}

	// initialise random numbers generator
	rnd.Init(o.Seed)
}

// EnforceRange makes sure x is within given range
func (o *Parameters) EnforceRange(i int, x float64) float64 {
	if x < o.FltMin[i] {
		return o.FltMin[i]
	}
	if x > o.FltMax[i] {
		return o.FltMax[i]
	}
	return x
}

// LogParams returns a log with current parameters
func (o *Parameters) LogParams() (l string) {

	// sizes
	l += io.ArgsTable("SIZES",
		"number of objective values", "Nova", o.Nova,
		"number of out-of-range values", "Noor", o.Noor,
		"total number of solutions", "Nsol", o.Nsol,
		"number of cpus", "Ncpu", o.Ncpu,
	)

	// time
	l += "\n"
	l += io.ArgsTable("TIME",
		"final time", "Tf", o.Tf,
		"delta time for exchange", "DtExc", o.DtExc,
		"delta time for output", "DtOut", o.DtOut,
	)

	// options
	l += "\n"
	l += io.ArgsTable("OPTIONS",
		"C-coefficient for differential evolution", "DEC", o.DEC,
		"parallel", "Pll", o.Pll,
		"seed for random numbers generator", "Seed", o.Seed,
		"generation type: 'latin', 'halton', 'rnd'", "GenType", o.GenType,
		"Latin Hypercube duplicates number", "LatinDup", o.LatinDup,
		"minimum value for 'h' constraints", "EpsH", o.EpsH,
		"show messages", "Verbose", o.Verbose,
		"generate all solutions together", "GenAll", o.GenAll,
		"run many trials", "Nsamples", o.Nsamples,
		"integers represent binary numbers", "BinInt", o.BinInt,
		"clear flt if corresponding int is 0", "ClearFlt", o.ClearFlt,
		"use exchange via tournament", "ExcTour", o.ExcTour,
		"use exchange one randomly", "ExcOne", o.ExcOne,
	)

	// crossover and mutation of integers
	l += "\n"
	l += io.ArgsTable("CROSSOVER AND MUTATION OF INTS",
		"probability of crossover for ints", "IntPc", o.IntPc,
		"number of cuts in crossover of ints", "IntNcuts", o.IntNcuts,
		"probability of mutation for ints", "IntPm", o.IntPm,
		"number of changes during mutation of ints", "IntNchanges", o.IntNchanges,
	)

	// derived
	l += "\n"
	l += io.ArgsTable("DERIVED",
		"number of floats", "Nflt", o.Nflt,
		"number of integers", "Nint", o.Nint,
	)

	// extra
	l += "\n"
	l += io.ArgsTable("EXTRA",
		"strategy", "Strategy", o.Strategy,
		"plot set of graphs 1", "PlotSet1", o.PlotSet1,
		"plot set of graphs 2", "PlotSet2", o.PlotSet2,
		"problem number", "ProbNum", o.ProbNum,
	)
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
)

// Params is an auxiliary structure to hold input parameters for setting the GA up
type Params struct {
	Nislands  int     // number of islands
	Ninds     int     // number of individuals: population size
	Nbases    int     // number of bases in chromosome
	Tf        int     // number of generations
	Dtout     int     // increment of time for output
	Dtmig     int     // increment of time for migration
	Pc        float64 // probability of crossover
	Pm        float64 // probability of mutation
	Elite     bool    // use elitism
	Rws       bool    // use Roulette-Wheel selection method
	Rnk       bool    // ranking
	RnkSP     float64 // selective pressure for ranking
	Fnkey     string  // key for reports' filenames
	UseIntRnd bool    // generate random integers instead of selecting from grid
	UseFltRnd bool    // generate random float point numbers instead of selecting from grid
}

// ReadParams reads parameters from JSON file
func ReadParams(filenamepath string) *Params {

	// new params
	var o Params

	// set default values
	o.Nislands = 1
	o.Ninds = 10
	o.Nbases = 10
	o.Tf = 100
	o.Dtout = 10
	o.Dtmig = 40
	o.Pc = 0.8
	o.Pm = 0.01
	o.Elite = true
	o.Rws = false
	o.Rnk = true
	o.RnkSP = 1.2
	o.Fnkey = ""
	o.UseIntRnd = false
	o.UseFltRnd = false

	// read file
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}

	// decode
	err = json.Unmarshal(b, &o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}

	// results
	return &o
}
<commit_msg>doplot added to parameters<commit_after>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
)

// Params is an auxiliary structure to hold input parameters for setting the GA up
type Params struct {
	Nislands  int     // number of islands
	Ninds     int     // number of individuals: population size
	Nbases    int     // number of bases in chromosome
	Tf        int     // number of generations
	Dtout     int     // increment of time for output
	Dtmig     int     // increment of time for migration
	Pc        float64 // probability of crossover
	Pm        float64 // probability of mutation
	Elite     bool    // use elitism
	Rws       bool    // use Roulette-Wheel selection method
	Rnk       bool    // ranking
	RnkSP     float64 // selective pressure for ranking
	Fnkey     string  // key for reports' filenames
	UseIntRnd bool    // generate random integers instead of selecting from grid
	UseFltRnd bool    // generate random float point numbers instead of selecting from grid
	DoPlot    bool    // plot results
}

// ReadParams reads parameters from JSON file
func ReadParams(filenamepath string) *Params {

	// new params
	var o Params

	// set default values
	o.Nislands = 1
	o.Ninds = 10
	o.Nbases = 10
	o.Tf = 100
	o.Dtout = 10
	o.Dtmig = 40
	o.Pc = 0.8
	o.Pm = 0.01
	o.Elite = true
	o.Rws = false
	o.Rnk = true
	o.RnkSP = 1.2
	o.Fnkey = ""
	o.UseIntRnd = false
	o.UseFltRnd = false
	o.DoPlot = false

	// read file
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}

	// decode
	err = json.Unmarshal(b, &o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}

	// results
	return &o
}
<|endoftext|>


<commit_before>// Package martini is a powerful package for quickly writing modular web applications/services in Golang.
//
// For a full guide visit http://github.com/codegangsta/martini
//
//  package main
//
//  import "github.com/codegangsta/martini"
//
//  func main() {
//    m := martini.Classic()
//
//    m.Get("/", func() string {
//      return "Hello world!"
//    })
//
//    m.Run()
//  }
package martini

import (
	"github.com/codegangsta/inject"
	"log"
	"net/http"
	"os"
	"reflect"
)

// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level.
type Martini struct {
	inject.Injector
	handlers []Handler
	action   Handler
	logger   *log.Logger
}

// New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used.
func New() *Martini {
	m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)}
	m.Map(m.logger)
	m.Map(defaultReturnHandler())
	return m
}

// Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers.
// Will panic if any of the handlers is not a callable function
func (m *Martini) Handlers(handlers ...Handler) {
	m.handlers = make([]Handler, 0)
	for _, handler := range handlers {
		m.Use(handler)
	}
}

// Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic().
func (m *Martini) Action(handler Handler) {
	validateHandler(handler)
	m.action = handler
}

// Use adds a middleware Handler to the stack. Will panic if the handler is not a callable func. Middleware Handlers are invoked in the order that they are added.
func (m *Martini) Use(handler Handler) {
	validateHandler(handler)

	m.handlers = append(m.handlers, handler)
}

// ServeHTTP is the HTTP Entry point for a Martini instance. Useful if you want to control your own HTTP server.
func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	m.createContext(res, req).run()
}

// Run the http server. Listening on os.GetEnv("PORT") or 3000 by default.
func (m *Martini) Run() {
	port := os.Getenv("PORT")
	if len(port) == 0 {
		port = "3000"
	}

	host := os.Getenv("HOST")
	if len(host) == 0 {
		host = ""
	}

	m.logger.Println("listening on host:port " + host + ":" + port)
	m.logger.Fatalln(http.ListenAndServe(host+":"+port, m))
}

func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
	c := &context{inject.New(), append(m.handlers, m.action), NewResponseWriter(res), 0}
	c.SetParent(m)
	c.MapTo(c, (*Context)(nil))
	c.MapTo(c.rw, (*http.ResponseWriter)(nil))
	c.Map(req)
	return c
}

// ClassicMartini represents a Martini with some reasonable defaults. Embeds the router functions for convenience.
type ClassicMartini struct {
	*Martini
	Router
}

// Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery, and martini.Static.
func Classic() *ClassicMartini {
	r := NewRouter()
	m := New()
	m.Use(Logger())
	m.Use(Recovery())
	m.Use(Static("public"))
	m.Action(r.Handle)
	return &ClassicMartini{m, r}
}

// Handler can be any callable function. Martini attempts to inject services into the handler's argument list.
// Martini will panic if an argument could not be fullfilled via dependency injection.
type Handler interface{}

func validateHandler(handler Handler) {
	if reflect.TypeOf(handler).Kind() != reflect.Func {
		panic("martini handler must be a callable func")
	}
}

// Context represents a request context. Services can be mapped on the request level from this interface.
type Context interface {
	inject.Injector
	// Next is an optional function that Middleware Handlers can call to yield the until after
	// the other Handlers have been executed. This works really well for any operations that must
	// happen after an http request
	Next()
	// Written returns whether or not the response for this context has been written.
	Written() bool
}

type context struct {
	inject.Injector
	handlers []Handler
	rw       ResponseWriter
	index    int
}

func (c *context) Next() {
	c.index += 1
	c.run()
}

func (c *context) Written() bool {
	return c.rw.Written()
}

func (c *context) run() {
	for c.index < len(c.handlers) {
		_, err := c.Invoke(c.handlers[c.index])
		if err != nil {
			panic(err)
		}
		c.index += 1

		if c.Written() {
			return
		}
	}
}
<commit_msg>Fix data race by not appending to m.handlers but passing m.action instead to the context.<commit_after>// Package martini is a powerful package for quickly writing modular web applications/services in Golang.
//
// For a full guide visit http://github.com/codegangsta/martini
//
//  package main
//
//  import "github.com/codegangsta/martini"
//
//  func main() {
//    m := martini.Classic()
//
//    m.Get("/", func() string {
//      return "Hello world!"
//    })
//
//    m.Run()
//  }
package martini

import (
	"log"
	"net/http"
	"os"
	"reflect"

	"github.com/codegangsta/inject"
)

// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level.
type Martini struct {
	inject.Injector
	handlers []Handler
	action   Handler
	logger   *log.Logger
}

// New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used.
func New() *Martini {
	m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)}
	m.Map(m.logger)
	m.Map(defaultReturnHandler())
	return m
}

// Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers.
// Will panic if any of the handlers is not a callable function
func (m *Martini) Handlers(handlers ...Handler) {
	m.handlers = make([]Handler, 0)
	for _, handler := range handlers {
		m.Use(handler)
	}
}

// Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic().
func (m *Martini) Action(handler Handler) {
	validateHandler(handler)
	m.action = handler
}

// Use adds a middleware Handler to the stack. Will panic if the handler is not a callable func. Middleware Handlers are invoked in the order that they are added.
func (m *Martini) Use(handler Handler) {
	validateHandler(handler)

	m.handlers = append(m.handlers, handler)
}

// ServeHTTP is the HTTP Entry point for a Martini instance. Useful if you want to control your own HTTP server.
func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	m.createContext(res, req).run()
}

// Run the http server. Listening on os.GetEnv("PORT") or 3000 by default.
func (m *Martini) Run() {
	port := os.Getenv("PORT")
	if len(port) == 0 {
		port = "3000"
	}

	host := os.Getenv("HOST")
	if len(host) == 0 {
		host = ""
	}

	m.logger.Println("listening on host:port " + host + ":" + port)
	m.logger.Fatalln(http.ListenAndServe(host+":"+port, m))
}

func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
	c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0}
	c.SetParent(m)
	c.MapTo(c, (*Context)(nil))
	c.MapTo(c.rw, (*http.ResponseWriter)(nil))
	c.Map(req)
	return c
}

// ClassicMartini represents a Martini with some reasonable defaults. Embeds the router functions for convenience.
type ClassicMartini struct {
	*Martini
	Router
}

// Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery, and martini.Static.
func Classic() *ClassicMartini {
	r := NewRouter()
	m := New()
	m.Use(Logger())
	m.Use(Recovery())
	m.Use(Static("public"))
	m.Action(r.Handle)
	return &ClassicMartini{m, r}
}

// Handler can be any callable function. Martini attempts to inject services into the handler's argument list.
// Martini will panic if an argument could not be fullfilled via dependency injection.
type Handler interface{}

func validateHandler(handler Handler) {
	if reflect.TypeOf(handler).Kind() != reflect.Func {
		panic("martini handler must be a callable func")
	}
}

// Context represents a request context. Services can be mapped on the request level from this interface.
type Context interface {
	inject.Injector
	// Next is an optional function that Middleware Handlers can call to yield the until after
	// the other Handlers have been executed. This works really well for any operations that must
	// happen after an http request
	Next()
	// Written returns whether or not the response for this context has been written.
	Written() bool
}

type context struct {
	inject.Injector
	handlers []Handler
	action   Handler
	rw       ResponseWriter
	index    int
}

func (c *context) handler() Handler {
	if c.index < len(c.handlers) {
		return c.handlers[c.index]
	}
	if c.index == len(c.handlers) {
		return c.action
	}
	panic("invalid index for context handler")
}

func (c *context) Next() {
	c.index += 1
	c.run()
}

func (c *context) Written() bool {
	return c.rw.Written()
}

func (c *context) run() {
	for c.index <= len(c.handlers) {
		_, err := c.Invoke(c.handler())
		if err != nil {
			panic(err)
		}
		c.index += 1

		if c.Written() {
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/catatsuy/isucon6-final/bench/fails"
	"github.com/catatsuy/isucon6-final/bench/scenario"
	"github.com/catatsuy/isucon6-final/bench/score"
)

var BenchmarkTimeout int
var InitialCheckOnly bool
var MatsuriNum = 10
var LoadIndexPageNum = 10
var DrawOnRandomRoomNum = 2

func main() {

	var urls string

	flag.StringVar(&urls, "urls", "", "ベンチマーク対象のURL（scheme, host, portまで。カンマ区切りで複数可。例： https://xxx.xxx.xxx.xxx,https://xxx.xxx.xxx.xxx:1443）")
	flag.IntVar(&BenchmarkTimeout, "timeout", 60, "ソフトタイムアウト")
	flag.BoolVar(&InitialCheckOnly, "initialcheck", false, "初期チェックだけ行う")

	flag.Parse()

	origins, err := makeOrigins(urls)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
	}

	initialCheck(origins)

	// 初期チェックのみモードではない、かつ、この時点でcriticalが出ていなければ負荷をかけにいく
	if !InitialCheckOnly && !fails.GetIsCritical() {
		benchmark(origins)
	}

	output()
}

func makeOrigins(urls string) ([]string, error) {
	if urls == "" {
		return nil, errors.New("urlsが指定されていません")
	}
	origins := strings.Split(urls, ",")
	for _, origin := range origins {
		u, err := url.Parse(origin)
		if err != nil {
			return nil, err
		}
		if u.Scheme != "https" || u.Host == "" {
			return nil, errors.New("urlsの指定が間違っています")
		}
	}
	return origins, nil
}

func initialCheck(origins []string) {
	scenario.CSRFTokenRefreshed(origins)
	scenario.StrokeReflectedToTop(origins)
	scenario.RoomWithoutStrokeNotShownAtTop(origins)
	scenario.StrokeReflectedToSVG(origins)
	scenario.CantDrawFirstStrokeOnSomeoneElsesRoom(origins)
	scenario.TopPageContent(origins)
	scenario.APIAndHTMLMustBeConsistent(origins)
	scenario.CheckStaticFiles(origins)
}

func benchmark(origins []string) {
	var wg sync.WaitGroup
	for i := 0; i < MatsuriNum; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			scenario.Matsuri(origins, BenchmarkTimeout-5)
		}()
	}

	loadIndexPageCh := makeChan(LoadIndexPageNum)
	drawOnRandomRoomCh := makeChan(DrawOnRandomRoomNum)
	timeoutCh := time.After(time.Duration(BenchmarkTimeout) * time.Second)

L:
	for {
		select {
		case <-loadIndexPageCh:
			go func() {
				scenario.LoadIndexPage(origins)
				time.Sleep(100 * time.Millisecond)
				loadIndexPageCh <- struct{}{}
			}()
		case <-drawOnRandomRoomCh:
			go func() {
				scenario.DrawOnRandomRoom(origins)
				time.Sleep(500 * time.Millisecond)
				drawOnRandomRoomCh <- struct{}{}
			}()
		case <-timeoutCh:
			break L
		}
	}

	wg.Wait()
}

func output() {
	b, _ := json.Marshal(score.Output{
		Pass:     !fails.GetIsCritical(),
		Score:    score.Get(),
		Messages: fails.GetUnique(),
	})

	fmt.Println(string(b))
}

func makeChan(len int) chan struct{} {
	ch := make(chan struct{}, len)
	for i := 0; i < len; i++ {
		ch <- struct{}{}
	}
	return ch
}
<commit_msg>Adjust param 10 threads * every 100 ms -> 20 threads * every 200 ms<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/catatsuy/isucon6-final/bench/fails"
	"github.com/catatsuy/isucon6-final/bench/scenario"
	"github.com/catatsuy/isucon6-final/bench/score"
)

var BenchmarkTimeout int
var InitialCheckOnly bool
var MatsuriNum = 10
var LoadIndexPageNum = 20
var DrawOnRandomRoomNum = 2

func main() {

	var urls string

	flag.StringVar(&urls, "urls", "", "ベンチマーク対象のURL（scheme, host, portまで。カンマ区切りで複数可。例： https://xxx.xxx.xxx.xxx,https://xxx.xxx.xxx.xxx:1443）")
	flag.IntVar(&BenchmarkTimeout, "timeout", 60, "ソフトタイムアウト")
	flag.BoolVar(&InitialCheckOnly, "initialcheck", false, "初期チェックだけ行う")

	flag.Parse()

	origins, err := makeOrigins(urls)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
	}

	initialCheck(origins)

	// 初期チェックのみモードではない、かつ、この時点でcriticalが出ていなければ負荷をかけにいく
	if !InitialCheckOnly && !fails.GetIsCritical() {
		benchmark(origins)
	}

	output()
}

func makeOrigins(urls string) ([]string, error) {
	if urls == "" {
		return nil, errors.New("urlsが指定されていません")
	}
	origins := strings.Split(urls, ",")
	for _, origin := range origins {
		u, err := url.Parse(origin)
		if err != nil {
			return nil, err
		}
		if u.Scheme != "https" || u.Host == "" {
			return nil, errors.New("urlsの指定が間違っています")
		}
	}
	return origins, nil
}

func initialCheck(origins []string) {
	scenario.CSRFTokenRefreshed(origins)
	scenario.StrokeReflectedToTop(origins)
	scenario.RoomWithoutStrokeNotShownAtTop(origins)
	scenario.StrokeReflectedToSVG(origins)
	scenario.CantDrawFirstStrokeOnSomeoneElsesRoom(origins)
	scenario.TopPageContent(origins)
	scenario.APIAndHTMLMustBeConsistent(origins)
	scenario.CheckStaticFiles(origins)
}

func benchmark(origins []string) {
	var wg sync.WaitGroup
	for i := 0; i < MatsuriNum; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			scenario.Matsuri(origins, BenchmarkTimeout-5)
		}()
	}

	loadIndexPageCh := makeChan(LoadIndexPageNum)
	drawOnRandomRoomCh := makeChan(DrawOnRandomRoomNum)
	timeoutCh := time.After(time.Duration(BenchmarkTimeout) * time.Second)

L:
	for {
		select {
		case <-loadIndexPageCh:
			go func() {
				scenario.LoadIndexPage(origins)
				time.Sleep(200 * time.Millisecond)
				loadIndexPageCh <- struct{}{}
			}()
		case <-drawOnRandomRoomCh:
			go func() {
				scenario.DrawOnRandomRoom(origins)
				time.Sleep(500 * time.Millisecond)
				drawOnRandomRoomCh <- struct{}{}
			}()
		case <-timeoutCh:
			break L
		}
	}

	wg.Wait()
}

func output() {
	b, _ := json.Marshal(score.Output{
		Pass:     !fails.GetIsCritical(),
		Score:    score.Get(),
		Messages: fails.GetUnique(),
	})

	fmt.Println(string(b))
}

func makeChan(len int) chan struct{} {
	ch := make(chan struct{}, len)
	for i := 0; i < len; i++ {
		ch <- struct{}{}
	}
	return ch
}
<|endoftext|>


<commit_before>// Copyright 2014 The Cockroach 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.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package rpc

import (
	"net"
	"testing"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/leaktest"
	"github.com/cockroachdb/cockroach/util/stop"
)

func newTestServer(t *testing.T, ctx *Context, manual bool) (*grpc.Server, net.Listener) {
	tlsConfig, err := ctx.GetServerTLSConfig()
	if err != nil {
		t.Fatal(err)
	}
	s := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig)))

	ln, err := util.ListenAndServeGRPC(ctx.Stopper, s, util.TestAddr)
	if err != nil {
		t.Fatal(err)
	}

	return s, ln
}

func TestOffsetMeasurement(t *testing.T) {
	defer leaktest.AfterTest(t)()

	stopper := stop.NewStopper()
	defer stopper.Stop()

	serverManual := hlc.NewManualClock(10)
	serverClock := hlc.NewClock(serverManual.UnixNano)
	ctx := newNodeTestContext(serverClock, stopper)
	s, ln := newTestServer(t, ctx, true)
	remoteAddr := ln.Addr().String()

	heartbeat := &HeartbeatService{
		clock:              serverClock,
		remoteClockMonitor: ctx.RemoteClocks,
	}
	RegisterHeartbeatServer(s, heartbeat)

	// Create a client that is 10 nanoseconds behind the server.
	// Use the server context (heartbeat is node-to-node).
	advancing := AdvancingClock{time: 0, advancementInterval: 10}
	clientClock := hlc.NewClock(advancing.UnixNano)
	context := newNodeTestContext(clientClock, stopper)
	_, err := context.GRPCDial(remoteAddr)
	if err != nil {
		t.Fatal(err)
	}

	expectedOffset := RemoteOffset{Offset: 5, Uncertainty: 5, MeasuredAt: 10}
	util.SucceedsSoon(t, func() error {
		context.RemoteClocks.mu.Lock()
		defer context.RemoteClocks.mu.Unlock()

		if o := context.RemoteClocks.mu.offsets[remoteAddr]; o != expectedOffset {
			return util.Errorf("expected:\n%v\nactual:\n%v", expectedOffset, o)
		}
		return nil
	})
}

// TestDelayedOffsetMeasurement tests that the client will record a
// zero offset if the heartbeat reply exceeds the
// maximumClockReadingDelay, but not the heartbeat timeout.
func TestDelayedOffsetMeasurement(t *testing.T) {
	defer leaktest.AfterTest(t)()

	stopper := stop.NewStopper()
	defer stopper.Stop()

	serverManual := hlc.NewManualClock(10)
	serverClock := hlc.NewClock(serverManual.UnixNano)
	ctx := newNodeTestContext(serverClock, stopper)
	s, ln := newTestServer(t, ctx, true)
	remoteAddr := ln.Addr().String()

	heartbeat := &HeartbeatService{
		clock:              serverClock,
		remoteClockMonitor: ctx.RemoteClocks,
	}
	RegisterHeartbeatServer(s, heartbeat)

	// Create a client that receives a heartbeat right after the
	// maximumClockReadingDelay.
	advancing := AdvancingClock{
		time:                0,
		advancementInterval: maximumClockReadingDelay.Nanoseconds() + 1,
	}
	clientClock := hlc.NewClock(advancing.UnixNano)
	context := newNodeTestContext(clientClock, stopper)
	_, err := context.GRPCDial(remoteAddr)
	if err != nil {
		t.Fatal(err)
	}

	// Since the reply took too long, we should have a zero offset, even
	// though the client is still healthy because it received a heartbeat
	// reply.
	context.RemoteClocks.mu.Lock()
	if o, ok := context.RemoteClocks.mu.offsets[remoteAddr]; ok {
		t.Errorf("expected offset to not exist, but found %v", o)
	}
	context.RemoteClocks.mu.Unlock()
}

func TestFailedOffsetMeasurement(t *testing.T) {
	defer leaktest.AfterTest(t)()

	stopper := stop.NewStopper()
	defer stopper.Stop()

	serverManual := hlc.NewManualClock(0)
	serverClock := hlc.NewClock(serverManual.UnixNano)
	ctx := newNodeTestContext(serverClock, stopper)
	s, ln := newTestServer(t, ctx, true)
	remoteAddr := ln.Addr().String()

	heartbeat := &ManualHeartbeatService{
		clock:              serverClock,
		remoteClockMonitor: ctx.RemoteClocks,
		ready:              make(chan struct{}),
		stopper:            stopper,
	}
	RegisterHeartbeatServer(s, heartbeat)

	// Create a client that never receives a heartbeat after the first.
	clientManual := hlc.NewManualClock(0)
	clientClock := hlc.NewClock(clientManual.UnixNano)
	context := newNodeTestContext(clientClock, stopper)
	// Increase the timeout so that failure arises from exceeding the maximum
	// clock reading delay, not the timeout.
	context.HeartbeatTimeout = 20 * context.HeartbeatInterval
	_, err := context.GRPCDial(remoteAddr)
	if err != nil {
		t.Fatal(err)
	}
	heartbeat.ready <- struct{}{} // Allow one heartbeat for initialization.

	util.SucceedsSoon(t, func() error {
		context.RemoteClocks.mu.Lock()
		defer context.RemoteClocks.mu.Unlock()

		if _, ok := context.RemoteClocks.mu.offsets[remoteAddr]; !ok {
			return util.Errorf("expected offset of %s to be initialized, but it was not", remoteAddr)
		}
		return nil
	})

	expectedOffset := RemoteOffset{}
	util.SucceedsSoon(t, func() error {
		context.RemoteClocks.mu.Lock()
		defer context.RemoteClocks.mu.Unlock()

		if o := context.RemoteClocks.mu.offsets[remoteAddr]; o != expectedOffset {
			return util.Errorf("expected offset of %s to be empty, got %v", remoteAddr, o)
		}
		return nil
	})
}

type AdvancingClock struct {
	time                int64
	advancementInterval int64
}

func (ac *AdvancingClock) UnixNano() int64 {
	time := ac.time
	ac.time = time + ac.advancementInterval
	return time
}
<commit_msg>rpc: remove unused locals<commit_after>// Copyright 2014 The Cockroach 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.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package rpc

import (
	"net"
	"testing"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/leaktest"
	"github.com/cockroachdb/cockroach/util/stop"
)

func newTestServer(t *testing.T, ctx *Context, manual bool) (*grpc.Server, net.Listener) {
	tlsConfig, err := ctx.GetServerTLSConfig()
	if err != nil {
		t.Fatal(err)
	}
	s := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig)))

	ln, err := util.ListenAndServeGRPC(ctx.Stopper, s, util.TestAddr)
	if err != nil {
		t.Fatal(err)
	}

	return s, ln
}

func TestOffsetMeasurement(t *testing.T) {
	defer leaktest.AfterTest(t)()

	stopper := stop.NewStopper()
	defer stopper.Stop()

	serverManual := hlc.NewManualClock(10)
	serverClock := hlc.NewClock(serverManual.UnixNano)
	ctx := newNodeTestContext(serverClock, stopper)
	s, ln := newTestServer(t, ctx, true)
	remoteAddr := ln.Addr().String()

	RegisterHeartbeatServer(s, &HeartbeatService{
		clock:              serverClock,
		remoteClockMonitor: ctx.RemoteClocks,
	})

	// Create a client that is 10 nanoseconds behind the server.
	// Use the server context (heartbeat is node-to-node).
	advancing := AdvancingClock{time: 0, advancementInterval: 10}
	clientClock := hlc.NewClock(advancing.UnixNano)
	context := newNodeTestContext(clientClock, stopper)
	_, err := context.GRPCDial(remoteAddr)
	if err != nil {
		t.Fatal(err)
	}

	expectedOffset := RemoteOffset{Offset: 5, Uncertainty: 5, MeasuredAt: 10}
	util.SucceedsSoon(t, func() error {
		context.RemoteClocks.mu.Lock()
		defer context.RemoteClocks.mu.Unlock()

		if o := context.RemoteClocks.mu.offsets[remoteAddr]; o != expectedOffset {
			return util.Errorf("expected:\n%v\nactual:\n%v", expectedOffset, o)
		}
		return nil
	})
}

// TestDelayedOffsetMeasurement tests that the client will record a
// zero offset if the heartbeat reply exceeds the
// maximumClockReadingDelay, but not the heartbeat timeout.
func TestDelayedOffsetMeasurement(t *testing.T) {
	defer leaktest.AfterTest(t)()

	stopper := stop.NewStopper()
	defer stopper.Stop()

	serverManual := hlc.NewManualClock(10)
	serverClock := hlc.NewClock(serverManual.UnixNano)
	ctx := newNodeTestContext(serverClock, stopper)
	s, ln := newTestServer(t, ctx, true)
	remoteAddr := ln.Addr().String()

	RegisterHeartbeatServer(s, &HeartbeatService{
		clock:              serverClock,
		remoteClockMonitor: ctx.RemoteClocks,
	})

	// Create a client that receives a heartbeat right after the
	// maximumClockReadingDelay.
	advancing := AdvancingClock{
		time:                0,
		advancementInterval: maximumClockReadingDelay.Nanoseconds() + 1,
	}
	clientClock := hlc.NewClock(advancing.UnixNano)
	context := newNodeTestContext(clientClock, stopper)
	_, err := context.GRPCDial(remoteAddr)
	if err != nil {
		t.Fatal(err)
	}

	// Since the reply took too long, we should have a zero offset, even
	// though the client is still healthy because it received a heartbeat
	// reply.
	context.RemoteClocks.mu.Lock()
	if o, ok := context.RemoteClocks.mu.offsets[remoteAddr]; ok {
		t.Errorf("expected offset to not exist, but found %v", o)
	}
	context.RemoteClocks.mu.Unlock()
}

func TestFailedOffsetMeasurement(t *testing.T) {
	defer leaktest.AfterTest(t)()

	stopper := stop.NewStopper()
	defer stopper.Stop()

	serverManual := hlc.NewManualClock(0)
	serverClock := hlc.NewClock(serverManual.UnixNano)
	ctx := newNodeTestContext(serverClock, stopper)
	s, ln := newTestServer(t, ctx, true)
	remoteAddr := ln.Addr().String()

	heartbeat := &ManualHeartbeatService{
		clock:              serverClock,
		remoteClockMonitor: ctx.RemoteClocks,
		ready:              make(chan struct{}),
		stopper:            stopper,
	}
	RegisterHeartbeatServer(s, heartbeat)

	// Create a client that never receives a heartbeat after the first.
	clientManual := hlc.NewManualClock(0)
	clientClock := hlc.NewClock(clientManual.UnixNano)
	context := newNodeTestContext(clientClock, stopper)
	// Increase the timeout so that failure arises from exceeding the maximum
	// clock reading delay, not the timeout.
	context.HeartbeatTimeout = 20 * context.HeartbeatInterval
	_, err := context.GRPCDial(remoteAddr)
	if err != nil {
		t.Fatal(err)
	}
	heartbeat.ready <- struct{}{} // Allow one heartbeat for initialization.

	util.SucceedsSoon(t, func() error {
		context.RemoteClocks.mu.Lock()
		defer context.RemoteClocks.mu.Unlock()

		if _, ok := context.RemoteClocks.mu.offsets[remoteAddr]; !ok {
			return util.Errorf("expected offset of %s to be initialized, but it was not", remoteAddr)
		}
		return nil
	})

	expectedOffset := RemoteOffset{}
	util.SucceedsSoon(t, func() error {
		context.RemoteClocks.mu.Lock()
		defer context.RemoteClocks.mu.Unlock()

		if o := context.RemoteClocks.mu.offsets[remoteAddr]; o != expectedOffset {
			return util.Errorf("expected offset of %s to be empty, got %v", remoteAddr, o)
		}
		return nil
	})
}

type AdvancingClock struct {
	time                int64
	advancementInterval int64
}

func (ac *AdvancingClock) UnixNano() int64 {
	time := ac.time
	ac.time = time + ac.advancementInterval
	return time
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 mungers

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"k8s.io/contrib/mungegithub/features"
	"k8s.io/contrib/mungegithub/github"

	"github.com/golang/glog"
	githubapi "github.com/google/go-github/github"
	"github.com/spf13/cobra"
)

const (
	day              = time.Hour * 24
	keepOpenLabel    = "keep-open"
	stalePullRequest = 90 * day // Close the PR if no human interaction for `stalePullRequest`
	startWarning     = 60 * day
	remindWarning    = 30 * day
	closingComment   = `This PR hasn't been active in %s. Closing this PR. Please reopen if you would like to work towards merging this change, if/when the PR is ready for the next round of review.

%s
You can add 'keep-open' label to prevent this from happening again, or add a comment to keep it open another 90 days`
	warningComment = `This PR hasn't been active in %s. It will be closed in %s.

%s
You can add 'keep-open' label to prevent this from happening, or add a comment to keep it open another 90 days`
)

var (
	closingCommentRE = regexp.MustCompile(`This PR hasn't been active in \d+ days?\..*label to prevent this from happening again.`)
	warningCommentRE = regexp.MustCompile(`This PR hasn't been active in \d+ days?\..*be closed in \d+ days?\.`)
)

// CloseStalePR will ask the Bot to close any PullRequest that didn't
// have any human interactions in `stalePullRequest` duration.
//
// This is done by checking both review and issue comments, and by
// ignoring comments done with a bot name. We also consider re-open on the PR.
type CloseStalePR struct{}

func init() {
	s := CloseStalePR{}
	RegisterMungerOrDie(s)
	RegisterStaleComments(s)
}

// Name is the name usable in --pr-mungers
func (CloseStalePR) Name() string { return "close-stale-pr" }

// RequiredFeatures is a slice of 'features' that must be provided
func (CloseStalePR) RequiredFeatures() []string { return []string{} }

// Initialize will initialize the munger
func (CloseStalePR) Initialize(config *github.Config, features *features.Features) error {
	return nil
}

// EachLoop is called at the start of every munge loop
func (CloseStalePR) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (CloseStalePR) AddFlags(cmd *cobra.Command, config *github.Config) {}

func findLastHumanPullRequestUpdate(obj *github.MungeObject) (*time.Time, error) {
	pr, err := obj.GetPR()
	if err != nil {
		return nil, err
	}

	comments, err := obj.ListReviewComments()
	if err != nil {
		return nil, err
	}

	lastHuman := pr.CreatedAt
	for i := range comments {
		comment := comments[i]
		if comment.User == nil || comment.User.Login == nil || comment.CreatedAt == nil || comment.Body == nil {
			continue
		}
		if *comment.User.Login == botName || *comment.User.Login == jenkinsBotName {
			continue
		}
		if lastHuman.Before(*comment.UpdatedAt) {
			lastHuman = comment.UpdatedAt
		}
	}

	return lastHuman, nil
}

func findLastHumanIssueUpdate(obj *github.MungeObject) (*time.Time, error) {
	lastHuman := obj.Issue.CreatedAt

	comments, err := obj.ListComments()
	if err != nil {
		return nil, err
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if mergeBotComment(comment) || jenkinsBotComment(comment) {
			continue
		}
		if lastHuman.Before(*comment.UpdatedAt) {
			lastHuman = comment.UpdatedAt
		}
	}

	return lastHuman, nil
}

func findLastInterestingEventUpdate(obj *github.MungeObject) (*time.Time, error) {
	lastInteresting := obj.Issue.CreatedAt

	events, err := obj.GetEvents()
	if err != nil {
		return nil, err
	}

	for i := range events {
		event := events[i]
		if event.Event == nil || *event.Event != "reopened" {
			continue
		}

		if lastInteresting.Before(*event.CreatedAt) {
			lastInteresting = event.CreatedAt
		}
	}

	return lastInteresting, nil
}

func findLastModificationTime(obj *github.MungeObject) (*time.Time, error) {
	lastHumanIssue, err := findLastHumanIssueUpdate(obj)
	if err != nil {
		return nil, err
	}
	lastHumanPR, err := findLastHumanPullRequestUpdate(obj)
	if err != nil {
		return nil, err
	}
	lastInterestingEvent, err := findLastInterestingEventUpdate(obj)
	if err != nil {
		return nil, err
	}

	lastModif := lastHumanPR
	if lastHumanIssue.After(*lastModif) {
		lastModif = lastHumanIssue
	}
	if lastInterestingEvent.After(*lastModif) {
		lastModif = lastInterestingEvent
	}

	return lastModif, nil
}

func findLatestWarningComment(obj *github.MungeObject) *githubapi.IssueComment {
	var lastFoundComment *githubapi.IssueComment

	comments, err := obj.ListComments()
	if err != nil {
		return nil
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if !mergeBotComment(comment) {
			continue
		}

		if !warningCommentRE.MatchString(*comment.Body) {
			continue
		}

		if lastFoundComment == nil || lastFoundComment.CreatedAt.Before(*comment.UpdatedAt) {
			if lastFoundComment != nil {
				obj.DeleteComment(lastFoundComment)
			}
			lastFoundComment = comment
		}
	}

	return lastFoundComment
}

func durationToDays(duration time.Duration) string {
	days := duration / day
	dayString := "days"
	if days == 1 || days == -1 {
		dayString = "day"
	}
	return fmt.Sprintf("%d %s", days, dayString)
}

func closePullRequest(obj *github.MungeObject, inactiveFor time.Duration) {
	mention := mentionUsers(getInvolvedUsers(obj))
	if mention != "" {
		mention = "cc " + mention + "\n"
	}

	comment := findLatestWarningComment(obj)
	if comment != nil {
		obj.DeleteComment(comment)
	}

	obj.WriteComment(fmt.Sprintf(closingComment, durationToDays(inactiveFor), mention))
	obj.ClosePR()
}

func getInvolvedUsers(obj *github.MungeObject) []string {
	var users []string

	var user string
	if obj.Issue.User != nil && obj.Issue.User.Login != nil {
		user = *obj.Issue.User.Login
		users = append(users, user)
	}

	for _, assignee := range obj.Issue.Assignees {
		if assignee.Login == nil || *assignee.Login == user {
			continue
		}
		users = append(users, *assignee.Login)
	}

	return users
}

func mentionUsers(users []string) string {
	var mentions []string
	for _, user := range users {
		mentions = append(mentions, "@"+user)
	}

	return strings.Join(mentions, " ")
}

func postWarningComment(obj *github.MungeObject, inactiveFor time.Duration, closeIn time.Duration) {
	mention := mentionUsers(getInvolvedUsers(obj))
	if mention != "" {
		mention = "cc " + mention + "\n"
	}

	obj.WriteComment(fmt.Sprintf(
		warningComment,
		durationToDays(inactiveFor),
		durationToDays(closeIn),
		mention,
	))
}

func checkAndWarn(obj *github.MungeObject, inactiveFor time.Duration, closeIn time.Duration) {
	if closeIn < day {
		// We are going to close the PR in less than a day. Too late to warn
		return
	}
	comment := findLatestWarningComment(obj)
	if comment == nil {
		// We don't already have the comment. Post it
		postWarningComment(obj, inactiveFor, closeIn)
	} else if time.Since(*comment.UpdatedAt) > remindWarning {
		// It's time to warn again
		obj.DeleteComment(comment)
		postWarningComment(obj, inactiveFor, closeIn)
	} else {
		// We already have a warning, and it's not expired. Do nothing
	}
}

// Munge is the workhorse that will actually close the PRs
func (CloseStalePR) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	if obj.HasLabel(keepOpenLabel) {
		return
	}

	lastModif, err := findLastModificationTime(obj)
	if err != nil {
		glog.Errorf("Failed to find last modification: %v", err)
		return
	}

	closeIn := -time.Since(lastModif.Add(stalePullRequest))
	inactiveFor := time.Since(*lastModif)
	if closeIn <= 0 {
		closePullRequest(obj, inactiveFor)
	} else if closeIn <= startWarning {
		checkAndWarn(obj, inactiveFor, closeIn)
	} else {
		// Pull-request is active. Remove previous potential warning
		comment := findLatestWarningComment(obj)
		if comment != nil {
			obj.DeleteComment(comment)
		}
	}
}

func (CloseStalePR) isStaleComment(obj *github.MungeObject, comment *githubapi.IssueComment) bool {
	if !mergeBotComment(comment) {
		return false
	}

	if !closingCommentRE.MatchString(*comment.Body) {
		return false
	}

	return true
}

// StaleComments returns a slice of stale comments
func (s CloseStalePR) StaleComments(obj *github.MungeObject, comments []*githubapi.IssueComment) []*githubapi.IssueComment {
	return forEachCommentTest(obj, comments, s.isStaleComment)
}
<commit_msg>Add date in close-pr warning<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 mungers

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"k8s.io/contrib/mungegithub/features"
	"k8s.io/contrib/mungegithub/github"

	"github.com/golang/glog"
	githubapi "github.com/google/go-github/github"
	"github.com/spf13/cobra"
)

const (
	day              = time.Hour * 24
	keepOpenLabel    = "keep-open"
	stalePullRequest = 90 * day // Close the PR if no human interaction for `stalePullRequest`
	startWarning     = 60 * day
	remindWarning    = 30 * day
	closingComment   = `This PR hasn't been active in %s. Closing this PR. Please reopen if you would like to work towards merging this change, if/when the PR is ready for the next round of review.

%s
You can add 'keep-open' label to prevent this from happening again, or add a comment to keep it open another 90 days`
	warningComment = `This PR hasn't been active in %s. It will be closed in %s (%s).

%s
You can add 'keep-open' label to prevent this from happening, or add a comment to keep it open another 90 days`
)

var (
	closingCommentRE = regexp.MustCompile(`This PR hasn't been active in \d+ days?\..*label to prevent this from happening again.`)
	warningCommentRE = regexp.MustCompile(`This PR hasn't been active in \d+ days?\..*be closed in \d+ days?\.`)
)

// CloseStalePR will ask the Bot to close any PullRequest that didn't
// have any human interactions in `stalePullRequest` duration.
//
// This is done by checking both review and issue comments, and by
// ignoring comments done with a bot name. We also consider re-open on the PR.
type CloseStalePR struct{}

func init() {
	s := CloseStalePR{}
	RegisterMungerOrDie(s)
	RegisterStaleComments(s)
}

// Name is the name usable in --pr-mungers
func (CloseStalePR) Name() string { return "close-stale-pr" }

// RequiredFeatures is a slice of 'features' that must be provided
func (CloseStalePR) RequiredFeatures() []string { return []string{} }

// Initialize will initialize the munger
func (CloseStalePR) Initialize(config *github.Config, features *features.Features) error {
	return nil
}

// EachLoop is called at the start of every munge loop
func (CloseStalePR) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (CloseStalePR) AddFlags(cmd *cobra.Command, config *github.Config) {}

func findLastHumanPullRequestUpdate(obj *github.MungeObject) (*time.Time, error) {
	pr, err := obj.GetPR()
	if err != nil {
		return nil, err
	}

	comments, err := obj.ListReviewComments()
	if err != nil {
		return nil, err
	}

	lastHuman := pr.CreatedAt
	for i := range comments {
		comment := comments[i]
		if comment.User == nil || comment.User.Login == nil || comment.CreatedAt == nil || comment.Body == nil {
			continue
		}
		if *comment.User.Login == botName || *comment.User.Login == jenkinsBotName {
			continue
		}
		if lastHuman.Before(*comment.UpdatedAt) {
			lastHuman = comment.UpdatedAt
		}
	}

	return lastHuman, nil
}

func findLastHumanIssueUpdate(obj *github.MungeObject) (*time.Time, error) {
	lastHuman := obj.Issue.CreatedAt

	comments, err := obj.ListComments()
	if err != nil {
		return nil, err
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if mergeBotComment(comment) || jenkinsBotComment(comment) {
			continue
		}
		if lastHuman.Before(*comment.UpdatedAt) {
			lastHuman = comment.UpdatedAt
		}
	}

	return lastHuman, nil
}

func findLastInterestingEventUpdate(obj *github.MungeObject) (*time.Time, error) {
	lastInteresting := obj.Issue.CreatedAt

	events, err := obj.GetEvents()
	if err != nil {
		return nil, err
	}

	for i := range events {
		event := events[i]
		if event.Event == nil || *event.Event != "reopened" {
			continue
		}

		if lastInteresting.Before(*event.CreatedAt) {
			lastInteresting = event.CreatedAt
		}
	}

	return lastInteresting, nil
}

func findLastModificationTime(obj *github.MungeObject) (*time.Time, error) {
	lastHumanIssue, err := findLastHumanIssueUpdate(obj)
	if err != nil {
		return nil, err
	}
	lastHumanPR, err := findLastHumanPullRequestUpdate(obj)
	if err != nil {
		return nil, err
	}
	lastInterestingEvent, err := findLastInterestingEventUpdate(obj)
	if err != nil {
		return nil, err
	}

	lastModif := lastHumanPR
	if lastHumanIssue.After(*lastModif) {
		lastModif = lastHumanIssue
	}
	if lastInterestingEvent.After(*lastModif) {
		lastModif = lastInterestingEvent
	}

	return lastModif, nil
}

func findLatestWarningComment(obj *github.MungeObject) *githubapi.IssueComment {
	var lastFoundComment *githubapi.IssueComment

	comments, err := obj.ListComments()
	if err != nil {
		return nil
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if !mergeBotComment(comment) {
			continue
		}

		if !warningCommentRE.MatchString(*comment.Body) {
			continue
		}

		if lastFoundComment == nil || lastFoundComment.CreatedAt.Before(*comment.UpdatedAt) {
			if lastFoundComment != nil {
				obj.DeleteComment(lastFoundComment)
			}
			lastFoundComment = comment
		}
	}

	return lastFoundComment
}

func durationToDays(duration time.Duration) string {
	days := duration / day
	dayString := "days"
	if days == 1 || days == -1 {
		dayString = "day"
	}
	return fmt.Sprintf("%d %s", days, dayString)
}

func closePullRequest(obj *github.MungeObject, inactiveFor time.Duration) {
	mention := mentionUsers(getInvolvedUsers(obj))
	if mention != "" {
		mention = "cc " + mention + "\n"
	}

	comment := findLatestWarningComment(obj)
	if comment != nil {
		obj.DeleteComment(comment)
	}

	obj.WriteComment(fmt.Sprintf(closingComment, durationToDays(inactiveFor), mention))
	obj.ClosePR()
}

func getInvolvedUsers(obj *github.MungeObject) []string {
	var users []string

	var user string
	if obj.Issue.User != nil && obj.Issue.User.Login != nil {
		user = *obj.Issue.User.Login
		users = append(users, user)
	}

	for _, assignee := range obj.Issue.Assignees {
		if assignee.Login == nil || *assignee.Login == user {
			continue
		}
		users = append(users, *assignee.Login)
	}

	return users
}

func mentionUsers(users []string) string {
	var mentions []string
	for _, user := range users {
		mentions = append(mentions, "@"+user)
	}

	return strings.Join(mentions, " ")
}

func postWarningComment(obj *github.MungeObject, inactiveFor time.Duration, closeIn time.Duration) {
	mention := mentionUsers(getInvolvedUsers(obj))
	if mention != "" {
		mention = "cc " + mention + "\n"
	}

	closeDate := time.Now().Add(closeIn).Format("Jan 2, 2006")

	obj.WriteComment(fmt.Sprintf(
		warningComment,
		durationToDays(inactiveFor),
		durationToDays(closeIn),
		closeDate,
		mention,
	))
}

func checkAndWarn(obj *github.MungeObject, inactiveFor time.Duration, closeIn time.Duration) {
	if closeIn < day {
		// We are going to close the PR in less than a day. Too late to warn
		return
	}
	comment := findLatestWarningComment(obj)
	if comment == nil {
		// We don't already have the comment. Post it
		postWarningComment(obj, inactiveFor, closeIn)
	} else if time.Since(*comment.UpdatedAt) > remindWarning {
		// It's time to warn again
		obj.DeleteComment(comment)
		postWarningComment(obj, inactiveFor, closeIn)
	} else {
		// We already have a warning, and it's not expired. Do nothing
	}
}

// Munge is the workhorse that will actually close the PRs
func (CloseStalePR) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	if obj.HasLabel(keepOpenLabel) {
		return
	}

	lastModif, err := findLastModificationTime(obj)
	if err != nil {
		glog.Errorf("Failed to find last modification: %v", err)
		return
	}

	closeIn := -time.Since(lastModif.Add(stalePullRequest))
	inactiveFor := time.Since(*lastModif)
	if closeIn <= 0 {
		closePullRequest(obj, inactiveFor)
	} else if closeIn <= startWarning {
		checkAndWarn(obj, inactiveFor, closeIn)
	} else {
		// Pull-request is active. Remove previous potential warning
		comment := findLatestWarningComment(obj)
		if comment != nil {
			obj.DeleteComment(comment)
		}
	}
}

func (CloseStalePR) isStaleComment(obj *github.MungeObject, comment *githubapi.IssueComment) bool {
	if !mergeBotComment(comment) {
		return false
	}

	if !closingCommentRE.MatchString(*comment.Body) {
		return false
	}

	return true
}

// StaleComments returns a slice of stale comments
func (s CloseStalePR) StaleComments(obj *github.MungeObject, comments []*githubapi.IssueComment) []*githubapi.IssueComment {
	return forEachCommentTest(obj, comments, s.isStaleComment)
}
<|endoftext|>