<commit_before>package rbac

import (
	"github.com/mikespook/gorbac"
)

var Rbac *gorbac.RBAC

func init() {
	Rbac = gorbac.New()

	// roles
	rAdmin := gorbac.NewStdRole("admin")
	rUser := gorbac.NewStdRole("user")

	// Being on the box automatically enables permissions and access to
	// some endpoints
	rLocal := gorbac.NewStdRole("localhost")

	// regarding system settings
	prSettings := gorbac.NewStdPermission("r-settings") // read
	pwSettings := gorbac.NewStdPermission("w-settings") // write

	// regarding configuring other admin users
	prAdmins := gorbac.NewStdPermission("r-admins") // read
	pwAdmins := gorbac.NewStdPermission("w-admins") // write
	plAdmins := gorbac.NewStdPermission("l-admins") // list

	// regarding configuring other users
	prUsers := gorbac.NewStdPermission("r-users")
	pwUsers := gorbac.NewStdPermission("w-users")
	plUsers := gorbac.NewStdPermission("l-users")

	// regarding self
	pSelf := gorbac.NewStdPermission("self")

	// assigning permissions to roles
	rAdmin.Assign(prSettings)
	rAdmin.Assign(pwSettings)
	rAdmin.Assign(prAdmins)
	rAdmin.Assign(pwAdmins)
	rAdmin.Assign(plAdmins)
	rAdmin.Assign(prUsers)
	rAdmin.Assign(pwUsers)
	rAdmin.Assign(plUsers)
	rAdmin.Assign(pSelf)

	rUser.Assign(plUsers)
	rUser.Assign(pSelf)

	rLocal.Assign(plUsers)

	// add roles to rbac object
	Rbac.Add(rAdmin)
	Rbac.Add(rUser)
	rbac.Add(rLocalp)
}
<commit_msg>Reworked RBAC<commit_after>package rbac

import (
	"github.com/mikespook/gorbac"
)

var Rbac *gorbac.RBAC

const (
	// roles
	RoleAdmin = gorbac.NewStdRole("admin")
	RoleUser  = gorbac.NewStdRole("user")

	// Being on the box (localhost) automatically enables
	// permissions and access to some endpoints
	RoleLocal = gorbac.NewStdRole("local")

	// regarding system settings
	PermRSettings = gorbac.NewStdPermission("r-settings") // read
	PermWSettings = gorbac.NewStdPermission("w-settings") // write

	// regarding configuring other admin users
	PermRAdmins = gorbac.NewStdPermission("r-admins") // read
	PermWAdmins = gorbac.NewStdPermission("w-admins") // write
	PermLAdmins = gorbac.NewStdPermission("l-admins") // list

	// regarding configuring other users
	PermRUsers = gorbac.NewStdPermission("r-users")
	PermWUsers = gorbac.NewStdPermission("w-users")
	PermLUsers = gorbac.NewStdPermission("l-users")
)

func init() {
	Rbac = gorbac.New()

	// assigning permissions to roles
	RoleAdmin.Assign(PermRSettings)
	RoleAdmin.Assign(PermWSettings)
	RoleAdmin.Assign(PermRAdmins)
	RoleAdmin.Assign(PermWAdmins)
	RoleAdmin.Assign(PermLAdmins)
	RoleAdmin.Assign(PermRUsers)
	RoleAdmin.Assign(PermWUsers)
	RoleAdmin.Assign(PermLUsers)

	RoleUser.Assign(PermLUsers)

	RoleLocal.Assign(PermLUsers)

	// add roles to rbac object
	Rbac.Add(RoleAdmin)
	Rbac.Add(RoleUser)
	Rbac.Add(RoleLocal)
}
<|endoftext|>


<commit_before>package format

import (
	"bytes"
	"testing"
	"time"

	"collectd.org/api"
)

func TestWrite(t *testing.T) {
	vl := api.ValueList{
		Identifier: api.Identifier{
			Host:   "example.com",
			Plugin: "golang",
			Type:   "gauge",
		},
		Time:     time.Unix(1426975989, 1),
		Interval: 10 * time.Second,
		Values:   []api.Value{api.Gauge(42)},
	}

	buf := &bytes.Buffer{}

	g := &Graphite{
		W:                 buf,
		Prefix:            "-->",
		Suffix:            "<--",
		EscapeChar:        "_",
		SeparateInstances: false,
		AlwaysAppendDS:    true,
	}

	if err := g.Write(vl); err != nil {
		t.Errorf("got %v, want %v", err, nil)
	}

	want := "-->example_com<--.golang.gauge.value 42 1426975989\r\n"
	got := buf.String()
	if got != want {
		t.Errorf("got %q, want %q", got, want)
	}
}
<commit_msg>format: Extend the Graphite test cases.<commit_after>package format

import (
	"bytes"
	"testing"
	"time"

	"collectd.org/api"
)

func TestWrite(t *testing.T) {
	cases := []struct {
		ValueList api.ValueList
		Graphite  *Graphite
		Want      string
	}{
		{ // case 0
			ValueList: api.ValueList{
				Identifier: api.Identifier{
					Host:           "example.com",
					Plugin:         "golang",
					PluginInstance: "example",
					Type:           "gauge",
					TypeInstance:   "answer",
				},
				Time:     time.Unix(1426975989, 1),
				Interval: 10 * time.Second,
				Values:   []api.Value{api.Gauge(42)},
			},
			Graphite: &Graphite{
				Prefix:            "-->",
				Suffix:            "<--",
				EscapeChar:        "_",
				SeparateInstances: false,
				AlwaysAppendDS:    true,
			},
			Want: "-->example_com<--.golang-example.gauge-answer.value 42 1426975989\r\n",
		},
		{ // case 1
			ValueList: api.ValueList{
				Identifier: api.Identifier{
					Host:           "example.com",
					Plugin:         "golang",
					PluginInstance: "example",
					Type:           "gauge",
					TypeInstance:   "answer",
				},
				Time:     time.Unix(1426975989, 1),
				Interval: 10 * time.Second,
				Values:   []api.Value{api.Derive(1337)},
			},
			Graphite: &Graphite{
				Prefix:            "collectd.",
				Suffix:            "",
				EscapeChar:        "@",
				SeparateInstances: true,
				AlwaysAppendDS:    false,
			},
			Want: "collectd.example@com.golang.example.gauge.answer 1337 1426975989\r\n",
		},
		{ // case 2
			ValueList: api.ValueList{
				Identifier: api.Identifier{
					Host:   "example.com",
					Plugin: "golang",
					Type:   "gauge",
				},
				Time:     time.Unix(1426975989, 1),
				Interval: 10 * time.Second,
				Values:   []api.Value{api.Gauge(42), api.Derive(1337)},
			},
			Graphite: &Graphite{
				Prefix:            "collectd.",
				Suffix:            "",
				EscapeChar:        "_",
				SeparateInstances: true,
				AlwaysAppendDS:    false,
			},
			Want: "collectd.example_com.golang.gauge.0 42 1426975989\r\n" +
				"collectd.example_com.golang.gauge.1 1337 1426975989\r\n",
		},
	}

	for i, c := range cases {
		buf := &bytes.Buffer{}
		c.Graphite.W = buf

		if err := c.Graphite.Write(c.ValueList); err != nil {
			t.Errorf("case %d: got %v, want %v", i, err, nil)
		}

		got := buf.String()
		if got != c.Want {
			t.Errorf("got %q, want %q", got, c.Want)
		}
	}
}
<|endoftext|>


<commit_before>package qb

import (
	"fmt"
	"strings"
)

// NewBuilder generates a new builder struct
func NewBuilder(driver string) *Builder {
	return &Builder{
		query:   NewQuery(),
		adapter: NewAdapter(driver),
	}
}

// Builder is a struct that holds an active query that it is used for building common sql queries
// it has all the common functions except multiple statements & table crudders
type Builder struct {
	query   *Query
	adapter Adapter
}

// SetEscaping sets the escaping parameter of current adapter
func (b *Builder) SetEscaping(escaping bool) {
	b.adapter.SetEscaping(escaping)
}

// Adapter returns the active adapter of builder
func (b *Builder) Adapter() Adapter {
	return b.adapter
}

// Reset clears query bindings and its errors
func (b *Builder) Reset() {
	b.query = NewQuery()
	b.adapter.Reset()
}

// Query returns the active query and resets the query.
// The query clauses and returns the sql and bindings
func (b *Builder) Query() *Query {
	query := b.query
	b.Reset()
	fmt.Printf("\n%s\n%s\n", query.SQL(), query.Bindings())
	return query
}

// Insert generates an "insert into %s(%s)" statement
func (b *Builder) Insert(table string) *Builder {
	clause := fmt.Sprintf("INSERT INTO %s", b.adapter.Escape(table))
	b.query.AddClause(clause)
	return b
}

// Values generates "values(%s)" statement and add bindings for each value
func (b *Builder) Values(m map[string]interface{}) *Builder {

	keys := []string{}
	values := []interface{}{}
	for k, v := range m {
		keys = append(keys, b.adapter.Escape(k))
		values = append(values, v)
		b.query.AddBinding(v)
	}

	b.query.AddClause(fmt.Sprintf("(%s)", strings.Join(keys, ", ")))

	placeholders := []string{}

	for _ = range values {
		placeholders = append(placeholders, b.adapter.Placeholder())
	}
	clause := fmt.Sprintf("VALUES (%s)", strings.Join(placeholders, ", "))
	b.query.AddClause(clause)
	return b
}

// Returning generates RETURNING statement for postgres only
// NOTE: Do not use it with sqlite, mysql or other drivers
func (b *Builder) Returning(cols ...string) *Builder {
	cols = b.adapter.EscapeAll(cols)
	clause := fmt.Sprintf("RETURNING %s", strings.Join(cols, ", "))
	b.query.AddClause(clause)
	return b
}

// Update generates "update %s" statement
func (b *Builder) Update(table string) *Builder {
	clause := fmt.Sprintf("UPDATE %s", b.adapter.Escape(table))
	b.query.AddClause(clause)
	return b
}

// Set generates "set a = placeholder" statement for each key a and add bindings for map value
func (b *Builder) Set(m map[string]interface{}) *Builder {
	updates := []string{}
	for k, v := range m {
		// check if aliasing exists
		if strings.Contains(k, ".") {
			kpieces := strings.Split(k, ".")
			k = fmt.Sprintf("%s.%s", kpieces[0], b.adapter.Escape(kpieces[1]))
		} else {
			k = b.adapter.Escape(k)
		}
		updates = append(updates, fmt.Sprintf("%s = %s", k, b.adapter.Placeholder()))
		b.query.AddBinding(v)
	}
	clause := fmt.Sprintf("SET %s", strings.Join(updates, ", "))
	b.query.AddClause(clause)
	return b
}

// Delete generates "delete" statement
func (b *Builder) Delete(table string) *Builder {
	b.query.AddClause(fmt.Sprintf("DELETE FROM %s", b.adapter.Escape(table)))
	return b
}

// Select generates "select %s" statement
func (b *Builder) Select(columns ...string) *Builder {
	clause := fmt.Sprintf("SELECT %s", strings.Join(columns, ", "))
	b.query.AddClause(clause)
	return b
}

// From generates "from %s" statement for each table name
func (b *Builder) From(tables ...string) *Builder {
	tbls := []string{}
	for _, v := range tables {
		tablePieces := strings.Split(v, " ")
		v = b.adapter.Escape(tablePieces[0])
		if len(tablePieces) > 1 {
			v = fmt.Sprintf("%s %s", v, tablePieces[1])
		}
		tbls = append(tbls, v)
	}
	b.query.AddClause(fmt.Sprintf("FROM %s", strings.Join(tbls, ", ")))
	return b
}

// InnerJoin generates "inner join %s on %s" statement for each expression
func (b *Builder) InnerJoin(table string, expressions ...string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}
	b.query.AddClause(fmt.Sprintf("INNER JOIN %s ON %s", v, strings.Join(expressions, " ")))
	return b
}

// CrossJoin generates "cross join %s" statement for table
func (b *Builder) CrossJoin(table string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}

	b.query.AddClause(fmt.Sprintf("CROSS JOIN %s", v))
	return b
}

// LeftOuterJoin generates "left outer join %s on %s" statement for each expression
func (b *Builder) LeftOuterJoin(table string, expressions ...string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}

	b.query.AddClause(fmt.Sprintf("LEFT OUTER JOIN %s ON %s", v, strings.Join(expressions, " ")))
	return b
}

// RightOuterJoin generates "right outer join %s on %s" statement for each expression
func (b *Builder) RightOuterJoin(table string, expressions ...string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}
	b.query.AddClause(fmt.Sprintf("RIGHT OUTER JOIN %s ON %s", v, strings.Join(expressions, " ")))
	return b
}

// FullOuterJoin generates "full outer join %s on %s" for each expression
func (b *Builder) FullOuterJoin(table string, expressions ...string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}
	b.query.AddClause(fmt.Sprintf("FULL OUTER JOIN %s ON %s", v, strings.Join(expressions, " ")))
	return b
}

// Where generates "where %s" for the expression and adds bindings for each value
func (b *Builder) Where(expression string, bindings ...interface{}) *Builder {
	if expression == "" {
		return b
	}
	b.query.AddClause(fmt.Sprintf("WHERE %s", expression))
	b.query.AddBinding(bindings...)
	return b
}

// OrderBy generates "order by %s" for each expression
func (b *Builder) OrderBy(expressions ...string) *Builder {
	b.query.AddClause(fmt.Sprintf("ORDER BY %s", strings.Join(expressions, ", ")))
	return b
}

// GroupBy generates "group by %s" for each column
func (b *Builder) GroupBy(columns ...string) *Builder {
	b.query.AddClause(fmt.Sprintf("GROUP BY %s", strings.Join(columns, ", ")))
	return b
}

// Having generates "having %s" for each expression
func (b *Builder) Having(expressions ...string) *Builder {
	b.query.AddClause(fmt.Sprintf("HAVING %s", strings.Join(expressions, ", ")))
	return b
}

// Limit generates limit %d offset %d for offset and count
func (b *Builder) Limit(offset int, count int) *Builder {
	b.query.AddClause(fmt.Sprintf("LIMIT %d OFFSET %d", count, offset))
	return b
}

// aggregates

// Avg function generates "avg(%s)" statement for column
func (b *Builder) Avg(column string) string {
	return fmt.Sprintf("AVG(%s)", b.adapter.Escape(column))
}

// Count function generates "count(%s)" statement for column
func (b *Builder) Count(column string) string {
	return fmt.Sprintf("COUNT(%s)", b.adapter.Escape(column))
}

// Sum function generates "sum(%s)" statement for column
func (b *Builder) Sum(column string) string {
	return fmt.Sprintf("SUM(%s)", b.adapter.Escape(column))
}

// Min function generates "min(%s)" statement for column
func (b *Builder) Min(column string) string {
	return fmt.Sprintf("MIN(%s)", b.adapter.Escape(column))
}

// Max function generates "max(%s)" statement for column
func (b *Builder) Max(column string) string {
	return fmt.Sprintf("MAX(%s)", b.adapter.Escape(column))
}

// expressions

// NotIn function generates "%s not in (%s)" for key and adds bindings for each value
func (b *Builder) NotIn(key string, values ...interface{}) string {
	b.query.AddBinding(values...)
	return fmt.Sprintf("%s NOT IN (%s)", b.adapter.Escape(key), strings.Join(b.adapter.Placeholders(values...), ","))
}

// In function generates "%s in (%s)" for key and adds bindings for each value
func (b *Builder) In(key string, values ...interface{}) string {
	b.query.AddBinding(values...)
	return fmt.Sprintf("%s IN (%s)", b.adapter.Escape(key), strings.Join(b.adapter.Placeholders(values...), ","))
}

// NotEq function generates "%s != placeholder" for key and adds binding for value
func (b *Builder) NotEq(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s != %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// Eq function generates "%s = placeholder" for key and adds binding for value
func (b *Builder) Eq(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s = %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// Gt function generates "%s > placeholder" for key and adds binding for value
func (b *Builder) Gt(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s > %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// Gte function generates "%s >= placeholder" for key and adds binding for value
func (b *Builder) Gte(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s >= %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// St function generates "%s < placeholder" for key and adds binding for value
func (b *Builder) St(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s < %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// Ste function generates "%s <= placeholder" for key and adds binding for value
func (b *Builder) Ste(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s <= %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// And function generates " AND " between any number of expressions
func (b *Builder) And(expressions ...string) string {
	if len(expressions) == 0 {
		return ""
	} else if len(expressions) == 1 {
		return expressions[0]
	}
	return fmt.Sprintf("(%s)", strings.Join(expressions, " AND "))
}

// Or function generates " OR " between any number of expressions
func (b *Builder) Or(expressions ...string) string {
	return strings.Join(expressions, " OR ")
}

// CreateTable generates generic CREATE TABLE statement
func (b *Builder) CreateTable(table string, fields []string, constraints []string) *Builder {

	b.query.AddClause(fmt.Sprintf("CREATE TABLE %s(", b.adapter.Escape(table)))

	for k, f := range fields {
		clause := fmt.Sprintf("\t%s", f)
		if len(fields)-1 > k || len(constraints) > 0 {
			clause += ","
		}
		b.query.AddClause(clause)
	}

	for k, c := range constraints {
		constraint := fmt.Sprintf("\t%s", c)
		if len(constraints)-1 > k {
			constraint += ","
		}
		b.query.AddClause(fmt.Sprintf("%s", constraint))
	}

	b.query.AddClause(")")
	return b
}

// AlterTable generates generic ALTER TABLE statement
func (b *Builder) AlterTable(table string) *Builder {
	b.query.AddClause(fmt.Sprintf("ALTER TABLE %s", table))
	return b
}

// DropTable generates generic DROP TABLE statement
func (b *Builder) DropTable(table string) *Builder {
	b.query.AddClause(fmt.Sprintf("DROP TABLE %s", b.adapter.Escape(table)))
	return b
}

// Add generates generic ADD COLUMN statement
func (b *Builder) Add(colName string, colType string) *Builder {
	b.query.AddClause(fmt.Sprintf("ADD %s %s", colName, colType))
	return b
}

// Drop generates generic DROP COLUMN statement
func (b *Builder) Drop(colName string) *Builder {
	b.query.AddClause(fmt.Sprintf("DROP %s", colName))
	return b
}

// CreateIndex generates an index on columns
func (b *Builder) CreateIndex(indexName string, tableName string, columns ...string) *Builder {
	b.query.AddClause(fmt.Sprintf("CREATE INDEX %s ON %s (%s)", indexName, tableName, strings.Join(b.adapter.EscapeAll(columns), ",")))
	return b
}
<commit_msg>disable debugging in builder<commit_after>package qb

import (
	"fmt"
	"strings"
)

// NewBuilder generates a new builder struct
func NewBuilder(driver string) *Builder {
	return &Builder{
		query:   NewQuery(),
		adapter: NewAdapter(driver),
	}
}

// Builder is a struct that holds an active query that it is used for building common sql queries
// it has all the common functions except multiple statements & table crudders
type Builder struct {
	query   *Query
	adapter Adapter
}

// SetEscaping sets the escaping parameter of current adapter
func (b *Builder) SetEscaping(escaping bool) {
	b.adapter.SetEscaping(escaping)
}

// Adapter returns the active adapter of builder
func (b *Builder) Adapter() Adapter {
	return b.adapter
}

// Reset clears query bindings and its errors
func (b *Builder) Reset() {
	b.query = NewQuery()
	b.adapter.Reset()
}

// Query returns the active query and resets the query.
// The query clauses and returns the sql and bindings
func (b *Builder) Query() *Query {
	query := b.query
	b.Reset()
	//fmt.Printf("\n%s\n%s\n", query.SQL(), query.Bindings())
	return query
}

// Insert generates an "insert into %s(%s)" statement
func (b *Builder) Insert(table string) *Builder {
	clause := fmt.Sprintf("INSERT INTO %s", b.adapter.Escape(table))
	b.query.AddClause(clause)
	return b
}

// Values generates "values(%s)" statement and add bindings for each value
func (b *Builder) Values(m map[string]interface{}) *Builder {

	keys := []string{}
	values := []interface{}{}
	for k, v := range m {
		keys = append(keys, b.adapter.Escape(k))
		values = append(values, v)
		b.query.AddBinding(v)
	}

	b.query.AddClause(fmt.Sprintf("(%s)", strings.Join(keys, ", ")))

	placeholders := []string{}

	for _ = range values {
		placeholders = append(placeholders, b.adapter.Placeholder())
	}
	clause := fmt.Sprintf("VALUES (%s)", strings.Join(placeholders, ", "))
	b.query.AddClause(clause)
	return b
}

// Returning generates RETURNING statement for postgres only
// NOTE: Do not use it with sqlite, mysql or other drivers
func (b *Builder) Returning(cols ...string) *Builder {
	cols = b.adapter.EscapeAll(cols)
	clause := fmt.Sprintf("RETURNING %s", strings.Join(cols, ", "))
	b.query.AddClause(clause)
	return b
}

// Update generates "update %s" statement
func (b *Builder) Update(table string) *Builder {
	clause := fmt.Sprintf("UPDATE %s", b.adapter.Escape(table))
	b.query.AddClause(clause)
	return b
}

// Set generates "set a = placeholder" statement for each key a and add bindings for map value
func (b *Builder) Set(m map[string]interface{}) *Builder {
	updates := []string{}
	for k, v := range m {
		// check if aliasing exists
		if strings.Contains(k, ".") {
			kpieces := strings.Split(k, ".")
			k = fmt.Sprintf("%s.%s", kpieces[0], b.adapter.Escape(kpieces[1]))
		} else {
			k = b.adapter.Escape(k)
		}
		updates = append(updates, fmt.Sprintf("%s = %s", k, b.adapter.Placeholder()))
		b.query.AddBinding(v)
	}
	clause := fmt.Sprintf("SET %s", strings.Join(updates, ", "))
	b.query.AddClause(clause)
	return b
}

// Delete generates "delete" statement
func (b *Builder) Delete(table string) *Builder {
	b.query.AddClause(fmt.Sprintf("DELETE FROM %s", b.adapter.Escape(table)))
	return b
}

// Select generates "select %s" statement
func (b *Builder) Select(columns ...string) *Builder {
	clause := fmt.Sprintf("SELECT %s", strings.Join(columns, ", "))
	b.query.AddClause(clause)
	return b
}

// From generates "from %s" statement for each table name
func (b *Builder) From(tables ...string) *Builder {
	tbls := []string{}
	for _, v := range tables {
		tablePieces := strings.Split(v, " ")
		v = b.adapter.Escape(tablePieces[0])
		if len(tablePieces) > 1 {
			v = fmt.Sprintf("%s %s", v, tablePieces[1])
		}
		tbls = append(tbls, v)
	}
	b.query.AddClause(fmt.Sprintf("FROM %s", strings.Join(tbls, ", ")))
	return b
}

// InnerJoin generates "inner join %s on %s" statement for each expression
func (b *Builder) InnerJoin(table string, expressions ...string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}
	b.query.AddClause(fmt.Sprintf("INNER JOIN %s ON %s", v, strings.Join(expressions, " ")))
	return b
}

// CrossJoin generates "cross join %s" statement for table
func (b *Builder) CrossJoin(table string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}

	b.query.AddClause(fmt.Sprintf("CROSS JOIN %s", v))
	return b
}

// LeftOuterJoin generates "left outer join %s on %s" statement for each expression
func (b *Builder) LeftOuterJoin(table string, expressions ...string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}

	b.query.AddClause(fmt.Sprintf("LEFT OUTER JOIN %s ON %s", v, strings.Join(expressions, " ")))
	return b
}

// RightOuterJoin generates "right outer join %s on %s" statement for each expression
func (b *Builder) RightOuterJoin(table string, expressions ...string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}
	b.query.AddClause(fmt.Sprintf("RIGHT OUTER JOIN %s ON %s", v, strings.Join(expressions, " ")))
	return b
}

// FullOuterJoin generates "full outer join %s on %s" for each expression
func (b *Builder) FullOuterJoin(table string, expressions ...string) *Builder {
	tablePieces := strings.Split(table, " ")

	v := b.adapter.Escape(tablePieces[0])
	if len(tablePieces) > 1 {
		v = fmt.Sprintf("%s %s", v, tablePieces[1])
	}
	b.query.AddClause(fmt.Sprintf("FULL OUTER JOIN %s ON %s", v, strings.Join(expressions, " ")))
	return b
}

// Where generates "where %s" for the expression and adds bindings for each value
func (b *Builder) Where(expression string, bindings ...interface{}) *Builder {
	if expression == "" {
		return b
	}
	b.query.AddClause(fmt.Sprintf("WHERE %s", expression))
	b.query.AddBinding(bindings...)
	return b
}

// OrderBy generates "order by %s" for each expression
func (b *Builder) OrderBy(expressions ...string) *Builder {
	b.query.AddClause(fmt.Sprintf("ORDER BY %s", strings.Join(expressions, ", ")))
	return b
}

// GroupBy generates "group by %s" for each column
func (b *Builder) GroupBy(columns ...string) *Builder {
	b.query.AddClause(fmt.Sprintf("GROUP BY %s", strings.Join(columns, ", ")))
	return b
}

// Having generates "having %s" for each expression
func (b *Builder) Having(expressions ...string) *Builder {
	b.query.AddClause(fmt.Sprintf("HAVING %s", strings.Join(expressions, ", ")))
	return b
}

// Limit generates limit %d offset %d for offset and count
func (b *Builder) Limit(offset int, count int) *Builder {
	b.query.AddClause(fmt.Sprintf("LIMIT %d OFFSET %d", count, offset))
	return b
}

// aggregates

// Avg function generates "avg(%s)" statement for column
func (b *Builder) Avg(column string) string {
	return fmt.Sprintf("AVG(%s)", b.adapter.Escape(column))
}

// Count function generates "count(%s)" statement for column
func (b *Builder) Count(column string) string {
	return fmt.Sprintf("COUNT(%s)", b.adapter.Escape(column))
}

// Sum function generates "sum(%s)" statement for column
func (b *Builder) Sum(column string) string {
	return fmt.Sprintf("SUM(%s)", b.adapter.Escape(column))
}

// Min function generates "min(%s)" statement for column
func (b *Builder) Min(column string) string {
	return fmt.Sprintf("MIN(%s)", b.adapter.Escape(column))
}

// Max function generates "max(%s)" statement for column
func (b *Builder) Max(column string) string {
	return fmt.Sprintf("MAX(%s)", b.adapter.Escape(column))
}

// expressions

// NotIn function generates "%s not in (%s)" for key and adds bindings for each value
func (b *Builder) NotIn(key string, values ...interface{}) string {
	b.query.AddBinding(values...)
	return fmt.Sprintf("%s NOT IN (%s)", b.adapter.Escape(key), strings.Join(b.adapter.Placeholders(values...), ","))
}

// In function generates "%s in (%s)" for key and adds bindings for each value
func (b *Builder) In(key string, values ...interface{}) string {
	b.query.AddBinding(values...)
	return fmt.Sprintf("%s IN (%s)", b.adapter.Escape(key), strings.Join(b.adapter.Placeholders(values...), ","))
}

// NotEq function generates "%s != placeholder" for key and adds binding for value
func (b *Builder) NotEq(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s != %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// Eq function generates "%s = placeholder" for key and adds binding for value
func (b *Builder) Eq(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s = %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// Gt function generates "%s > placeholder" for key and adds binding for value
func (b *Builder) Gt(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s > %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// Gte function generates "%s >= placeholder" for key and adds binding for value
func (b *Builder) Gte(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s >= %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// St function generates "%s < placeholder" for key and adds binding for value
func (b *Builder) St(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s < %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// Ste function generates "%s <= placeholder" for key and adds binding for value
func (b *Builder) Ste(key string, value interface{}) string {
	b.query.AddBinding(value)
	return fmt.Sprintf("%s <= %s", b.adapter.Escape(key), b.adapter.Placeholder())
}

// And function generates " AND " between any number of expressions
func (b *Builder) And(expressions ...string) string {
	if len(expressions) == 0 {
		return ""
	} else if len(expressions) == 1 {
		return expressions[0]
	}
	return fmt.Sprintf("(%s)", strings.Join(expressions, " AND "))
}

// Or function generates " OR " between any number of expressions
func (b *Builder) Or(expressions ...string) string {
	return strings.Join(expressions, " OR ")
}

// CreateTable generates generic CREATE TABLE statement
func (b *Builder) CreateTable(table string, fields []string, constraints []string) *Builder {

	b.query.AddClause(fmt.Sprintf("CREATE TABLE %s(", b.adapter.Escape(table)))

	for k, f := range fields {
		clause := fmt.Sprintf("\t%s", f)
		if len(fields)-1 > k || len(constraints) > 0 {
			clause += ","
		}
		b.query.AddClause(clause)
	}

	for k, c := range constraints {
		constraint := fmt.Sprintf("\t%s", c)
		if len(constraints)-1 > k {
			constraint += ","
		}
		b.query.AddClause(fmt.Sprintf("%s", constraint))
	}

	b.query.AddClause(")")
	return b
}

// AlterTable generates generic ALTER TABLE statement
func (b *Builder) AlterTable(table string) *Builder {
	b.query.AddClause(fmt.Sprintf("ALTER TABLE %s", table))
	return b
}

// DropTable generates generic DROP TABLE statement
func (b *Builder) DropTable(table string) *Builder {
	b.query.AddClause(fmt.Sprintf("DROP TABLE %s", b.adapter.Escape(table)))
	return b
}

// Add generates generic ADD COLUMN statement
func (b *Builder) Add(colName string, colType string) *Builder {
	b.query.AddClause(fmt.Sprintf("ADD %s %s", colName, colType))
	return b
}

// Drop generates generic DROP COLUMN statement
func (b *Builder) Drop(colName string) *Builder {
	b.query.AddClause(fmt.Sprintf("DROP %s", colName))
	return b
}

// CreateIndex generates an index on columns
func (b *Builder) CreateIndex(indexName string, tableName string, columns ...string) *Builder {
	b.query.AddClause(fmt.Sprintf("CREATE INDEX %s ON %s (%s)", indexName, tableName, strings.Join(b.adapter.EscapeAll(columns), ",")))
	return b
}
<|endoftext|>


<commit_before>package cluster

import (
	"fmt"

	"github.com/openshift/installer/pkg/asset"
	"github.com/openshift/installer/pkg/asset/installconfig"
	"github.com/openshift/installer/pkg/types/config"
)

const (
	tfvarsFilename  = "terraform.tfvars"
	tfvarsAssetName = "Terraform Variables"
)

// TerraformVariables depends on InstallConfig and
// Ignition to generate the terrafor.tfvars.
type TerraformVariables struct {
	// The Assets that this tfvars file depends.
	installConfig     asset.Asset
	bootstrapIgnition asset.Asset
	masterIgnition    asset.Asset
	workerIgnition    asset.Asset
}

var _ asset.Asset = (*TerraformVariables)(nil)

// Name returns the human-friendly name of the asset.
func (t *TerraformVariables) Name() string {
	return tfvarsAssetName
}

// Dependencies returns the dependency of the TerraformVariable
func (t *TerraformVariables) Dependencies() []asset.Asset {
	return []asset.Asset{t.installConfig, t.bootstrapIgnition, t.masterIgnition, t.workerIgnition}
}

// Generate generates the terraform.tfvars file.
func (t *TerraformVariables) Generate(parents map[asset.Asset]*asset.State) (*asset.State, error) {
	installCfg, err := installconfig.GetInstallConfig(t.installConfig, parents)
	if err != nil {
		return nil, fmt.Errorf("failed to get install config state in the parent asset states")
	}

	contents := map[asset.Asset][]string{}

	for _, ign := range []asset.Asset{
		t.bootstrapIgnition,
		t.masterIgnition,
		t.workerIgnition,
	} {
		state, ok := parents[ign]
		if !ok {
			return nil, fmt.Errorf("failed to get the ignition state for %v in the parent asset states", ign)
		}

		for _, content := range state.Contents {
			contents[ign] = append(contents[ign], string(content.Data))
		}
	}

	cluster, err := config.ConvertInstallConfigToTFVars(installCfg, contents[t.bootstrapIgnition][0], contents[t.masterIgnition], contents[t.workerIgnition][0])
	if err != nil {
		return nil, err
	}

	data, err := cluster.TFVars()
	if err != nil {
		return nil, err
	}

	return &asset.State{
		Contents: []asset.Content{
			{
				Name: tfvarsFilename,
				Data: []byte(data),
			},
		},
	}, nil
}
<commit_msg>asset/cluster: use cache for libvirt images<commit_after>package cluster

import (
	"fmt"

	"github.com/openshift/installer/pkg/asset"
	"github.com/openshift/installer/pkg/asset/installconfig"
	"github.com/openshift/installer/pkg/types/config"
)

const (
	tfvarsFilename  = "terraform.tfvars"
	tfvarsAssetName = "Terraform Variables"
)

// TerraformVariables depends on InstallConfig and
// Ignition to generate the terrafor.tfvars.
type TerraformVariables struct {
	// The Assets that this tfvars file depends.
	installConfig     asset.Asset
	bootstrapIgnition asset.Asset
	masterIgnition    asset.Asset
	workerIgnition    asset.Asset
}

var _ asset.Asset = (*TerraformVariables)(nil)

// Name returns the human-friendly name of the asset.
func (t *TerraformVariables) Name() string {
	return tfvarsAssetName
}

// Dependencies returns the dependency of the TerraformVariable
func (t *TerraformVariables) Dependencies() []asset.Asset {
	return []asset.Asset{t.installConfig, t.bootstrapIgnition, t.masterIgnition, t.workerIgnition}
}

// Generate generates the terraform.tfvars file.
func (t *TerraformVariables) Generate(parents map[asset.Asset]*asset.State) (*asset.State, error) {
	installCfg, err := installconfig.GetInstallConfig(t.installConfig, parents)
	if err != nil {
		return nil, fmt.Errorf("failed to get install config state in the parent asset states")
	}

	contents := map[asset.Asset][]string{}

	for _, ign := range []asset.Asset{
		t.bootstrapIgnition,
		t.masterIgnition,
		t.workerIgnition,
	} {
		state, ok := parents[ign]
		if !ok {
			return nil, fmt.Errorf("failed to get the ignition state for %v in the parent asset states", ign)
		}

		for _, content := range state.Contents {
			contents[ign] = append(contents[ign], string(content.Data))
		}
	}

	cluster, err := config.ConvertInstallConfigToTFVars(installCfg, contents[t.bootstrapIgnition][0], contents[t.masterIgnition], contents[t.workerIgnition][0])
	if err != nil {
		return nil, err
	}

	if cluster.Platform == config.PlatformLibvirt {
		if err := cluster.Libvirt.UseCachedImage(); err != nil {
			return nil, err
		}
	}

	data, err := cluster.TFVars()
	if err != nil {
		return nil, err
	}

	return &asset.State{
		Contents: []asset.Content{
			{
				Name: tfvarsFilename,
				Data: []byte(data),
			},
		},
	}, nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"context"
	"github.com/containers/image/v5/transports/alltransports"
	"github.com/containers/image/v5/types"
	"github.com/spf13/cobra"
	"strings"

	"github.com/openshift/source-to-image/pkg/api"
	"github.com/openshift/source-to-image/pkg/api/constants"
	"github.com/openshift/source-to-image/pkg/build/strategies/dockerfile"
	"github.com/openshift/source-to-image/pkg/util/fs"
)

// getImageLabels attempts to inspect an image existing in a remote registry.
func getImageLabels(ctx context.Context, ref types.ImageReference) (map[string]string, error) {
	img, err := ref.NewImage(ctx, &types.SystemContext{})
	if err != nil {
		return nil, err
	}

	imageMetadata, err := img.Inspect(ctx)
	if err != nil {
		return nil, err
	}

	return imageMetadata.Labels, nil
}

// generateDockerfile generates a Dockerfile with the given configuration.
func generateDockerfile(cfg *api.Config) error {
	fileSystem := fs.NewFileSystem()
	builder, err := dockerfile.New(cfg, fileSystem)
	if err != nil {
		return err
	}

	_, err = builder.Build(cfg)
	if err != nil {
		return err
	}

	return nil
}

// adjustConfigWithImageLabels adjusts the configuration with given labels.
func adjustConfigWithImageLabels(cfg *api.Config, labels map[string]string) {
	if v, ok := labels[constants.ScriptsURLLabel]; ok {
		cfg.ScriptsURL = v
	}

	if v, ok := labels[constants.DestinationLabel]; ok {
		cfg.Destination = v
	}

}

// CanonizeBuilderImageArg appends 'docker://' if the builder image doesn't contain the a schema.
func CanonizeBuilderImageArg(builderImage string) string {
	if strings.Contains(builderImage, "://") {
		return builderImage
	}
	return "docker://" + builderImage
}

// NewCmdGenerate implements the S2I cli generate command.
func NewCmdGenerate(cfg *api.Config) *cobra.Command {
	generateCmd := &cobra.Command{
		Use: "generate <image> <dockerfile>",
		Short: "Generate a Dockerfile using an existing S2I builder	image " +
			"that can be used to produce an image by any application " +
			"supporting the format.",
		Example: `
# Generate a Dockerfile for the centos/nodejs-10-centos7 builder image:
$ s2i generate docker://docker.io/centos/nodejs-10-centos7 Dockerfile.gen
`,
		RunE: func(cmd *cobra.Command, _ []string) error {
			if cmd.Flags().NArg() != 2 {
				return cmd.Help()
			}

			builderImageArg := CanonizeBuilderImageArg(cmd.Flags().Arg(0))

			ref, err := alltransports.ParseImageName(builderImageArg)
			if err != nil {
				return err
			}

			cfg.BuilderImage = ref.DockerReference().Name()
			cfg.AsDockerfile = cmd.Flags().Arg(1)

			ctx := context.Background()
			var imageLabels map[string]string
			if imageLabels, err = getImageLabels(ctx, ref); err != nil {
				return err
			}

			adjustConfigWithImageLabels(cfg, imageLabels)
			return generateDockerfile(cfg)
		},
	}

	generateCmd.Flags().BoolVarP(&(cfg.Quiet), "quiet", "q", false, "Operate quietly. Suppress all non-error output.")
	generateCmd.Flags().VarP(&(cfg.Environment), "env", "e", "Specify an single environment variable in NAME=VALUE format")
	generateCmd.Flags().StringVarP(&(cfg.AssembleUser), "assemble-user", "", "", "Specify the user to run assemble with")
	generateCmd.Flags().StringVarP(&(cfg.AssembleRuntimeUser), "assemble-runtime-user", "", "", "Specify the user to run assemble-runtime with")

	return generateCmd
}
<commit_msg>Sync names in program from documentation<commit_after>package cmd

import (
	"context"
	"github.com/containers/image/v5/transports/alltransports"
	"github.com/containers/image/v5/types"
	"github.com/spf13/cobra"
	"strings"

	"github.com/openshift/source-to-image/pkg/api"
	"github.com/openshift/source-to-image/pkg/api/constants"
	"github.com/openshift/source-to-image/pkg/build/strategies/dockerfile"
	"github.com/openshift/source-to-image/pkg/util/fs"
)

// getImageLabels attempts to inspect an image existing in a remote registry.
func getImageLabels(ctx context.Context, ref types.ImageReference) (map[string]string, error) {
	img, err := ref.NewImage(ctx, &types.SystemContext{})
	if err != nil {
		return nil, err
	}

	imageMetadata, err := img.Inspect(ctx)
	if err != nil {
		return nil, err
	}

	return imageMetadata.Labels, nil
}

// generateDockerfile generates a Dockerfile with the given configuration.
func generateDockerfile(cfg *api.Config) error {
	fileSystem := fs.NewFileSystem()
	builder, err := dockerfile.New(cfg, fileSystem)
	if err != nil {
		return err
	}

	_, err = builder.Build(cfg)
	if err != nil {
		return err
	}

	return nil
}

// adjustConfigWithImageLabels adjusts the configuration with given labels.
func adjustConfigWithImageLabels(cfg *api.Config, labels map[string]string) {
	if v, ok := labels[constants.ScriptsURLLabel]; ok {
		cfg.ScriptsURL = v
	}

	if v, ok := labels[constants.DestinationLabel]; ok {
		cfg.Destination = v
	}

}

// CanonizeBuilderImageArg appends 'docker://' if the builder image doesn't contain the a schema.
func CanonizeBuilderImageArg(builderImage string) string {
	if strings.Contains(builderImage, "://") {
		return builderImage
	}
	return "docker://" + builderImage
}

// NewCmdGenerate implements the S2I cli generate command.
func NewCmdGenerate(cfg *api.Config) *cobra.Command {
	generateCmd := &cobra.Command{
		Use: "generate <builder image> <output file>",
		Short: "Generate a Dockerfile using an existing S2I builder	image " +
			"that can be used to produce an image by any application " +
			"supporting the format.",
		Example: `
# Generate a Dockerfile for the centos/nodejs-10-centos7 builder image:
$ s2i generate docker.io/centos/nodejs-10-centos7 Dockerfile.gen
`,
		RunE: func(cmd *cobra.Command, _ []string) error {
			if cmd.Flags().NArg() != 2 {
				return cmd.Help()
			}

			builderImageArg := CanonizeBuilderImageArg(cmd.Flags().Arg(0))

			ref, err := alltransports.ParseImageName(builderImageArg)
			if err != nil {
				return err
			}

			cfg.BuilderImage = ref.DockerReference().Name()
			cfg.AsDockerfile = cmd.Flags().Arg(1)

			ctx := context.Background()
			var imageLabels map[string]string
			if imageLabels, err = getImageLabels(ctx, ref); err != nil {
				return err
			}

			adjustConfigWithImageLabels(cfg, imageLabels)
			return generateDockerfile(cfg)
		},
	}

	generateCmd.Flags().BoolVarP(&(cfg.Quiet), "quiet", "q", false, "Operate quietly. Suppress all non-error output.")
	generateCmd.Flags().VarP(&(cfg.Environment), "env", "e", "Specify an single environment variable in NAME=VALUE format")
	generateCmd.Flags().StringVarP(&(cfg.AssembleUser), "assemble-user", "", "", "Specify the user to run assemble with")
	generateCmd.Flags().StringVarP(&(cfg.AssembleRuntimeUser), "assemble-runtime-user", "", "", "Specify the user to run assemble-runtime with")

	return generateCmd
}
<|endoftext|>


<commit_before>package containerfs // import "github.com/docker/docker/pkg/containerfs"

import (
	"archive/tar"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/idtools"
	"github.com/docker/docker/pkg/system"
	"github.com/sirupsen/logrus"
)

// TarFunc provides a function definition for a custom Tar function
type TarFunc func(string, *archive.TarOptions) (io.ReadCloser, error)

// UntarFunc provides a function definition for a custom Untar function
type UntarFunc func(io.Reader, string, *archive.TarOptions) error

// Archiver provides a similar implementation of the archive.Archiver package with the rootfs abstraction
type Archiver struct {
	SrcDriver Driver
	DstDriver Driver
	Tar       TarFunc
	Untar     UntarFunc
	IDMapping *idtools.IdentityMapping
}

// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
// If either Tar or Untar fails, TarUntar aborts and returns the error.
func (archiver *Archiver) TarUntar(src, dst string) error {
	logrus.Debugf("TarUntar(%s %s)", src, dst)
	tarArchive, err := archiver.Tar(src, &archive.TarOptions{Compression: archive.Uncompressed})
	if err != nil {
		return err
	}
	defer tarArchive.Close()
	options := &archive.TarOptions{
		UIDMaps: archiver.IDMapping.UIDs(),
		GIDMaps: archiver.IDMapping.GIDs(),
	}
	return archiver.Untar(tarArchive, dst, options)
}

// UntarPath untar a file from path to a destination, src is the source tar file path.
func (archiver *Archiver) UntarPath(src, dst string) error {
	tarArchive, err := archiver.SrcDriver.Open(src)
	if err != nil {
		return err
	}
	defer tarArchive.Close()
	options := &archive.TarOptions{
		UIDMaps: archiver.IDMapping.UIDs(),
		GIDMaps: archiver.IDMapping.GIDs(),
	}
	return archiver.Untar(tarArchive, dst, options)
}

// CopyWithTar creates a tar archive of filesystem path `src`, and
// unpacks it at filesystem path `dst`.
// The archive is streamed directly with fixed buffering and no
// intermediary disk IO.
func (archiver *Archiver) CopyWithTar(src, dst string) error {
	srcSt, err := archiver.SrcDriver.Stat(src)
	if err != nil {
		return err
	}
	if !srcSt.IsDir() {
		return archiver.CopyFileWithTar(src, dst)
	}

	// if this archiver is set up with ID mapping we need to create
	// the new destination directory with the remapped root UID/GID pair
	// as owner

	identity := idtools.Identity{UID: archiver.IDMapping.RootPair().UID, GID: archiver.IDMapping.RootPair().GID}

	// Create dst, copy src's content into it
	if err := idtools.MkdirAllAndChownNew(dst, 0755, identity); err != nil {
		return err
	}
	logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
	return archiver.TarUntar(src, dst)
}

// CopyFileWithTar emulates the behavior of the 'cp' command-line
// for a single file. It copies a regular file from path `src` to
// path `dst`, and preserves all its metadata.
func (archiver *Archiver) CopyFileWithTar(src, dst string) (retErr error) {
	logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst)
	srcDriver := archiver.SrcDriver
	dstDriver := archiver.DstDriver

	srcSt, retErr := srcDriver.Stat(src)
	if retErr != nil {
		return retErr
	}

	if srcSt.IsDir() {
		return fmt.Errorf("Can't copy a directory")
	}

	// Clean up the trailing slash. This must be done in an operating
	// system specific manner.
	if dst[len(dst)-1] == dstDriver.Separator() {
		dst = dstDriver.Join(dst, srcDriver.Base(src))
	}

	// The original call was system.MkdirAll, which is just
	// os.MkdirAll on not-Windows and changed for Windows.
	if dstDriver.OS() == "windows" {
		// Now we are WCOW
		if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil {
			return err
		}
	} else {
		// We can just use the driver.MkdirAll function
		if err := dstDriver.MkdirAll(dstDriver.Dir(dst), 0700); err != nil {
			return err
		}
	}

	r, w := io.Pipe()
	errC := make(chan error, 1)

	go func() {
		defer close(errC)
		errC <- func() error {
			defer w.Close()

			srcF, err := srcDriver.Open(src)
			if err != nil {
				return err
			}
			defer srcF.Close()

			hdr, err := tar.FileInfoHeader(srcSt, "")
			if err != nil {
				return err
			}
			hdr.Format = tar.FormatPAX
			hdr.ModTime = hdr.ModTime.Truncate(time.Second)
			hdr.AccessTime = time.Time{}
			hdr.ChangeTime = time.Time{}
			hdr.Name = dstDriver.Base(dst)
			if dstDriver.OS() == "windows" {
				hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
			} else {
				hdr.Mode = int64(os.FileMode(hdr.Mode))
			}

			if err := remapIDs(archiver.IDMapping, hdr); err != nil {
				return err
			}

			tw := tar.NewWriter(w)
			defer tw.Close()
			if err := tw.WriteHeader(hdr); err != nil {
				return err
			}
			if _, err := io.Copy(tw, srcF); err != nil {
				return err
			}
			return nil
		}()
	}()
	defer func() {
		if err := <-errC; retErr == nil && err != nil {
			retErr = err
		}
	}()

	retErr = archiver.Untar(r, dstDriver.Dir(dst), nil)
	if retErr != nil {
		r.CloseWithError(retErr)
	}
	return retErr
}

// IdentityMapping returns the IdentityMapping of the archiver.
func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping {
	return archiver.IDMapping
}

func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error {
	ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
	hdr.Uid, hdr.Gid = ids.UID, ids.GID
	return err
}

// chmodTarEntry is used to adjust the file permissions used in tar header based
// on the platform the archival is done.
func chmodTarEntry(perm os.FileMode) os.FileMode {
	//perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.)
	permPart := perm & os.ModePerm
	noPermPart := perm &^ os.ModePerm
	// Add the x bit: make everything +x from windows
	permPart |= 0111
	permPart &= 0755

	return noPermPart | permPart
}
<commit_msg>pkg/containerfs: normalize comment formatting<commit_after>package containerfs // import "github.com/docker/docker/pkg/containerfs"

import (
	"archive/tar"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/idtools"
	"github.com/docker/docker/pkg/system"
	"github.com/sirupsen/logrus"
)

// TarFunc provides a function definition for a custom Tar function
type TarFunc func(string, *archive.TarOptions) (io.ReadCloser, error)

// UntarFunc provides a function definition for a custom Untar function
type UntarFunc func(io.Reader, string, *archive.TarOptions) error

// Archiver provides a similar implementation of the archive.Archiver package with the rootfs abstraction
type Archiver struct {
	SrcDriver Driver
	DstDriver Driver
	Tar       TarFunc
	Untar     UntarFunc
	IDMapping *idtools.IdentityMapping
}

// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
// If either Tar or Untar fails, TarUntar aborts and returns the error.
func (archiver *Archiver) TarUntar(src, dst string) error {
	logrus.Debugf("TarUntar(%s %s)", src, dst)
	tarArchive, err := archiver.Tar(src, &archive.TarOptions{Compression: archive.Uncompressed})
	if err != nil {
		return err
	}
	defer tarArchive.Close()
	options := &archive.TarOptions{
		UIDMaps: archiver.IDMapping.UIDs(),
		GIDMaps: archiver.IDMapping.GIDs(),
	}
	return archiver.Untar(tarArchive, dst, options)
}

// UntarPath untar a file from path to a destination, src is the source tar file path.
func (archiver *Archiver) UntarPath(src, dst string) error {
	tarArchive, err := archiver.SrcDriver.Open(src)
	if err != nil {
		return err
	}
	defer tarArchive.Close()
	options := &archive.TarOptions{
		UIDMaps: archiver.IDMapping.UIDs(),
		GIDMaps: archiver.IDMapping.GIDs(),
	}
	return archiver.Untar(tarArchive, dst, options)
}

// CopyWithTar creates a tar archive of filesystem path `src`, and
// unpacks it at filesystem path `dst`.
// The archive is streamed directly with fixed buffering and no
// intermediary disk IO.
func (archiver *Archiver) CopyWithTar(src, dst string) error {
	srcSt, err := archiver.SrcDriver.Stat(src)
	if err != nil {
		return err
	}
	if !srcSt.IsDir() {
		return archiver.CopyFileWithTar(src, dst)
	}

	// if this archiver is set up with ID mapping we need to create
	// the new destination directory with the remapped root UID/GID pair
	// as owner

	identity := idtools.Identity{UID: archiver.IDMapping.RootPair().UID, GID: archiver.IDMapping.RootPair().GID}

	// Create dst, copy src's content into it
	if err := idtools.MkdirAllAndChownNew(dst, 0755, identity); err != nil {
		return err
	}
	logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
	return archiver.TarUntar(src, dst)
}

// CopyFileWithTar emulates the behavior of the 'cp' command-line
// for a single file. It copies a regular file from path `src` to
// path `dst`, and preserves all its metadata.
func (archiver *Archiver) CopyFileWithTar(src, dst string) (retErr error) {
	logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst)
	srcDriver := archiver.SrcDriver
	dstDriver := archiver.DstDriver

	srcSt, retErr := srcDriver.Stat(src)
	if retErr != nil {
		return retErr
	}

	if srcSt.IsDir() {
		return fmt.Errorf("Can't copy a directory")
	}

	// Clean up the trailing slash. This must be done in an operating
	// system specific manner.
	if dst[len(dst)-1] == dstDriver.Separator() {
		dst = dstDriver.Join(dst, srcDriver.Base(src))
	}

	// The original call was system.MkdirAll, which is just
	// os.MkdirAll on not-Windows and changed for Windows.
	if dstDriver.OS() == "windows" {
		// Now we are WCOW
		if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil {
			return err
		}
	} else {
		// We can just use the driver.MkdirAll function
		if err := dstDriver.MkdirAll(dstDriver.Dir(dst), 0700); err != nil {
			return err
		}
	}

	r, w := io.Pipe()
	errC := make(chan error, 1)

	go func() {
		defer close(errC)
		errC <- func() error {
			defer w.Close()

			srcF, err := srcDriver.Open(src)
			if err != nil {
				return err
			}
			defer srcF.Close()

			hdr, err := tar.FileInfoHeader(srcSt, "")
			if err != nil {
				return err
			}
			hdr.Format = tar.FormatPAX
			hdr.ModTime = hdr.ModTime.Truncate(time.Second)
			hdr.AccessTime = time.Time{}
			hdr.ChangeTime = time.Time{}
			hdr.Name = dstDriver.Base(dst)
			if dstDriver.OS() == "windows" {
				hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
			} else {
				hdr.Mode = int64(os.FileMode(hdr.Mode))
			}

			if err := remapIDs(archiver.IDMapping, hdr); err != nil {
				return err
			}

			tw := tar.NewWriter(w)
			defer tw.Close()
			if err := tw.WriteHeader(hdr); err != nil {
				return err
			}
			if _, err := io.Copy(tw, srcF); err != nil {
				return err
			}
			return nil
		}()
	}()
	defer func() {
		if err := <-errC; retErr == nil && err != nil {
			retErr = err
		}
	}()

	retErr = archiver.Untar(r, dstDriver.Dir(dst), nil)
	if retErr != nil {
		r.CloseWithError(retErr)
	}
	return retErr
}

// IdentityMapping returns the IdentityMapping of the archiver.
func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping {
	return archiver.IDMapping
}

func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error {
	ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
	hdr.Uid, hdr.Gid = ids.UID, ids.GID
	return err
}

// chmodTarEntry is used to adjust the file permissions used in tar header based
// on the platform the archival is done.
func chmodTarEntry(perm os.FileMode) os.FileMode {
	// perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.)
	permPart := perm & os.ModePerm
	noPermPart := perm &^ os.ModePerm
	// Add the x bit: make everything +x from windows
	permPart |= 0111
	permPart &= 0755

	return noPermPart | permPart
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The cert-manager 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 http

import (
	"context"
	"fmt"
	"hash/adler32"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/utils/pointer"

	cmacme "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
	logf "github.com/cert-manager/cert-manager/pkg/logs"
)

func podLabels(ch *cmacme.Challenge) map[string]string {
	domainHash := fmt.Sprintf("%d", adler32.Checksum([]byte(ch.Spec.DNSName)))
	tokenHash := fmt.Sprintf("%d", adler32.Checksum([]byte(ch.Spec.Token)))
	solverIdent := "true"
	return map[string]string{
		// TODO: we need to support domains longer than 63 characters
		// this value should probably be hashed, and then the full plain text
		// value stored as an annotation to make it easier for users to read
		// see #425 for details: https://github.com/cert-manager/cert-manager/issues/425
		cmacme.DomainLabelKey:               domainHash,
		cmacme.TokenLabelKey:                tokenHash,
		cmacme.SolverIdentificationLabelKey: solverIdent,
	}
}

func (s *Solver) ensurePod(ctx context.Context, ch *cmacme.Challenge) (*corev1.Pod, error) {
	log := logf.FromContext(ctx).WithName("ensurePod")

	log.V(logf.DebugLevel).Info("checking for existing HTTP01 solver pods")
	existingPods, err := s.getPodsForChallenge(ctx, ch)
	if err != nil {
		return nil, err
	}
	if len(existingPods) == 1 {
		logf.WithRelatedResource(log, existingPods[0]).Info("found one existing HTTP01 solver pod")
		return existingPods[0], nil
	}
	if len(existingPods) > 1 {
		log.V(logf.InfoLevel).Info("multiple challenge solver pods found for challenge. cleaning up all existing pods.")
		err := s.cleanupPods(ctx, ch)
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("multiple existing challenge solver pods found and cleaned up. retrying challenge sync")
	}

	log.V(logf.InfoLevel).Info("creating HTTP01 challenge solver pod")

	return s.createPod(ctx, ch)
}

// getPodsForChallenge returns a list of pods that were created to solve
// the given challenge
func (s *Solver) getPodsForChallenge(ctx context.Context, ch *cmacme.Challenge) ([]*corev1.Pod, error) {
	log := logf.FromContext(ctx)

	podLabels := podLabels(ch)
	orderSelector := labels.NewSelector()
	for key, val := range podLabels {
		req, err := labels.NewRequirement(key, selection.Equals, []string{val})
		if err != nil {
			return nil, err
		}
		orderSelector = orderSelector.Add(*req)
	}

	podList, err := s.podLister.Pods(ch.Namespace).List(orderSelector)
	if err != nil {
		return nil, err
	}

	var relevantPods []*corev1.Pod
	for _, pod := range podList {
		if !metav1.IsControlledBy(pod, ch) {
			logf.WithRelatedResource(log, pod).Info("found existing solver pod for this challenge resource, however " +
				"it does not have an appropriate OwnerReference referencing this challenge. Skipping it altogether.")
			continue
		}
		relevantPods = append(relevantPods, pod)
	}

	return relevantPods, nil
}

func (s *Solver) cleanupPods(ctx context.Context, ch *cmacme.Challenge) error {
	log := logf.FromContext(ctx, "cleanupPods")

	pods, err := s.getPodsForChallenge(ctx, ch)
	if err != nil {
		return err
	}
	var errs []error
	for _, pod := range pods {
		log := logf.WithRelatedResource(log, pod).V(logf.DebugLevel)
		log.V(logf.InfoLevel).Info("deleting pod resource")

		err := s.Client.CoreV1().Pods(pod.Namespace).Delete(ctx, pod.Name, metav1.DeleteOptions{})
		if err != nil {
			log.V(logf.WarnLevel).Info("failed to delete pod resource", "error", err)
			errs = append(errs, err)
			continue
		}
		log.V(logf.InfoLevel).Info("successfully deleted pod resource")
	}

	return utilerrors.NewAggregate(errs)
}

// createPod will create a challenge solving pod for the given certificate,
// domain, token and key.
func (s *Solver) createPod(ctx context.Context, ch *cmacme.Challenge) (*corev1.Pod, error) {
	return s.Client.CoreV1().Pods(ch.Namespace).Create(
		ctx,
		s.buildPod(ch),
		metav1.CreateOptions{})
}

// buildPod will build a challenge solving pod for the given certificate,
// domain, token and key. It will not create it in the API server
func (s *Solver) buildPod(ch *cmacme.Challenge) *corev1.Pod {
	pod := s.buildDefaultPod(ch)

	// Override defaults if they have changed in the pod template.
	if ch.Spec.Solver.HTTP01 != nil {
		if ch.Spec.Solver.HTTP01.Ingress != nil {
			pod = s.mergePodObjectMetaWithPodTemplate(pod,
				ch.Spec.Solver.HTTP01.Ingress.PodTemplate)
		}
	}

	return pod
}

func (s *Solver) buildDefaultPod(ch *cmacme.Challenge) *corev1.Pod {
	podLabels := podLabels(ch)

	return &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "cm-acme-http-solver-",
			Namespace:    ch.Namespace,
			Labels:       podLabels,
			Annotations: map[string]string{
				"sidecar.istio.io/inject": "false",
			},
			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(ch, challengeGvk)},
		},
		Spec: corev1.PodSpec{
			RestartPolicy: corev1.RestartPolicyOnFailure,
			SecurityContext: &corev1.PodSecurityContext{
				RunAsNonRoot: pointer.BoolPtr(true),
			},
			Containers: []corev1.Container{
				{
					Name: "acmesolver",
					// TODO: use an image as specified as a config option
					Image:           s.Context.HTTP01SolverImage,
					ImagePullPolicy: corev1.PullIfNotPresent,
					// TODO: replace this with some kind of cmdline generator
					Args: []string{
						fmt.Sprintf("--listen-port=%d", acmeSolverListenPort),
						fmt.Sprintf("--domain=%s", ch.Spec.DNSName),
						fmt.Sprintf("--token=%s", ch.Spec.Token),
						fmt.Sprintf("--key=%s", ch.Spec.Key),
					},
					Resources: corev1.ResourceRequirements{
						Requests: corev1.ResourceList{
							corev1.ResourceCPU:    s.ACMEOptions.HTTP01SolverResourceRequestCPU,
							corev1.ResourceMemory: s.ACMEOptions.HTTP01SolverResourceRequestMemory,
						},
						Limits: corev1.ResourceList{
							corev1.ResourceCPU:    s.ACMEOptions.HTTP01SolverResourceLimitsCPU,
							corev1.ResourceMemory: s.ACMEOptions.HTTP01SolverResourceLimitsMemory,
						},
					},
					Ports: []corev1.ContainerPort{
						{
							Name:          "http",
							ContainerPort: acmeSolverListenPort,
						},
					},
				},
			},
		},
	}
}

// Merge object meta from the pod template. Fall back to default values.
func (s *Solver) mergePodObjectMetaWithPodTemplate(pod *corev1.Pod, podTempl *cmacme.ACMEChallengeSolverHTTP01IngressPodTemplate) *corev1.Pod {
	if podTempl == nil {
		return pod
	}

	if pod.Labels == nil {
		pod.Labels = make(map[string]string)
	}

	for k, v := range podTempl.Labels {
		pod.Labels[k] = v
	}

	if pod.Annotations == nil {
		pod.Annotations = make(map[string]string)
	}

	for k, v := range podTempl.Annotations {
		pod.Annotations[k] = v
	}

	if pod.Spec.NodeSelector == nil {
		pod.Spec.NodeSelector = make(map[string]string)
	}

	for k, v := range podTempl.Spec.NodeSelector {
		pod.Spec.NodeSelector[k] = v
	}

	if pod.Spec.Tolerations == nil {
		pod.Spec.Tolerations = []corev1.Toleration{}
	}

	pod.Spec.Tolerations = append(pod.Spec.Tolerations, podTempl.Spec.Tolerations...)

	if podTempl.Spec.Affinity != nil {
		pod.Spec.Affinity = podTempl.Spec.Affinity
	}

	if podTempl.Spec.PriorityClassName != "" {
		pod.Spec.PriorityClassName = podTempl.Spec.PriorityClassName
	}

	if podTempl.Spec.ServiceAccountName != "" {
		pod.Spec.ServiceAccountName = podTempl.Spec.ServiceAccountName
	}

	return pod
}
<commit_msg>update: Setting allowPrivilegeEscalation to false<commit_after>/*
Copyright 2020 The cert-manager 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 http

import (
	"context"
	"fmt"
	"hash/adler32"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/utils/pointer"

	cmacme "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
	logf "github.com/cert-manager/cert-manager/pkg/logs"
)

func podLabels(ch *cmacme.Challenge) map[string]string {
	domainHash := fmt.Sprintf("%d", adler32.Checksum([]byte(ch.Spec.DNSName)))
	tokenHash := fmt.Sprintf("%d", adler32.Checksum([]byte(ch.Spec.Token)))
	solverIdent := "true"
	return map[string]string{
		// TODO: we need to support domains longer than 63 characters
		// this value should probably be hashed, and then the full plain text
		// value stored as an annotation to make it easier for users to read
		// see #425 for details: https://github.com/cert-manager/cert-manager/issues/425
		cmacme.DomainLabelKey:               domainHash,
		cmacme.TokenLabelKey:                tokenHash,
		cmacme.SolverIdentificationLabelKey: solverIdent,
	}
}

func (s *Solver) ensurePod(ctx context.Context, ch *cmacme.Challenge) (*corev1.Pod, error) {
	log := logf.FromContext(ctx).WithName("ensurePod")

	log.V(logf.DebugLevel).Info("checking for existing HTTP01 solver pods")
	existingPods, err := s.getPodsForChallenge(ctx, ch)
	if err != nil {
		return nil, err
	}
	if len(existingPods) == 1 {
		logf.WithRelatedResource(log, existingPods[0]).Info("found one existing HTTP01 solver pod")
		return existingPods[0], nil
	}
	if len(existingPods) > 1 {
		log.V(logf.InfoLevel).Info("multiple challenge solver pods found for challenge. cleaning up all existing pods.")
		err := s.cleanupPods(ctx, ch)
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("multiple existing challenge solver pods found and cleaned up. retrying challenge sync")
	}

	log.V(logf.InfoLevel).Info("creating HTTP01 challenge solver pod")

	return s.createPod(ctx, ch)
}

// getPodsForChallenge returns a list of pods that were created to solve
// the given challenge
func (s *Solver) getPodsForChallenge(ctx context.Context, ch *cmacme.Challenge) ([]*corev1.Pod, error) {
	log := logf.FromContext(ctx)

	podLabels := podLabels(ch)
	orderSelector := labels.NewSelector()
	for key, val := range podLabels {
		req, err := labels.NewRequirement(key, selection.Equals, []string{val})
		if err != nil {
			return nil, err
		}
		orderSelector = orderSelector.Add(*req)
	}

	podList, err := s.podLister.Pods(ch.Namespace).List(orderSelector)
	if err != nil {
		return nil, err
	}

	var relevantPods []*corev1.Pod
	for _, pod := range podList {
		if !metav1.IsControlledBy(pod, ch) {
			logf.WithRelatedResource(log, pod).Info("found existing solver pod for this challenge resource, however " +
				"it does not have an appropriate OwnerReference referencing this challenge. Skipping it altogether.")
			continue
		}
		relevantPods = append(relevantPods, pod)
	}

	return relevantPods, nil
}

func (s *Solver) cleanupPods(ctx context.Context, ch *cmacme.Challenge) error {
	log := logf.FromContext(ctx, "cleanupPods")

	pods, err := s.getPodsForChallenge(ctx, ch)
	if err != nil {
		return err
	}
	var errs []error
	for _, pod := range pods {
		log := logf.WithRelatedResource(log, pod).V(logf.DebugLevel)
		log.V(logf.InfoLevel).Info("deleting pod resource")

		err := s.Client.CoreV1().Pods(pod.Namespace).Delete(ctx, pod.Name, metav1.DeleteOptions{})
		if err != nil {
			log.V(logf.WarnLevel).Info("failed to delete pod resource", "error", err)
			errs = append(errs, err)
			continue
		}
		log.V(logf.InfoLevel).Info("successfully deleted pod resource")
	}

	return utilerrors.NewAggregate(errs)
}

// createPod will create a challenge solving pod for the given certificate,
// domain, token and key.
func (s *Solver) createPod(ctx context.Context, ch *cmacme.Challenge) (*corev1.Pod, error) {
	return s.Client.CoreV1().Pods(ch.Namespace).Create(
		ctx,
		s.buildPod(ch),
		metav1.CreateOptions{})
}

// buildPod will build a challenge solving pod for the given certificate,
// domain, token and key. It will not create it in the API server
func (s *Solver) buildPod(ch *cmacme.Challenge) *corev1.Pod {
	pod := s.buildDefaultPod(ch)

	// Override defaults if they have changed in the pod template.
	if ch.Spec.Solver.HTTP01 != nil {
		if ch.Spec.Solver.HTTP01.Ingress != nil {
			pod = s.mergePodObjectMetaWithPodTemplate(pod,
				ch.Spec.Solver.HTTP01.Ingress.PodTemplate)
		}
	}

	return pod
}

func (s *Solver) buildDefaultPod(ch *cmacme.Challenge) *corev1.Pod {
	podLabels := podLabels(ch)

	return &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "cm-acme-http-solver-",
			Namespace:    ch.Namespace,
			Labels:       podLabels,
			Annotations: map[string]string{
				"sidecar.istio.io/inject": "false",
			},
			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(ch, challengeGvk)},
		},
		Spec: corev1.PodSpec{
			RestartPolicy: corev1.RestartPolicyOnFailure,
			SecurityContext: &corev1.PodSecurityContext{
				RunAsNonRoot: pointer.BoolPtr(true),
			},
			Containers: []corev1.Container{
				{
					Name: "acmesolver",
					// TODO: use an image as specified as a config option
					Image:           s.Context.HTTP01SolverImage,
					ImagePullPolicy: corev1.PullIfNotPresent,
					// TODO: replace this with some kind of cmdline generator
					Args: []string{
						fmt.Sprintf("--listen-port=%d", acmeSolverListenPort),
						fmt.Sprintf("--domain=%s", ch.Spec.DNSName),
						fmt.Sprintf("--token=%s", ch.Spec.Token),
						fmt.Sprintf("--key=%s", ch.Spec.Key),
					},
					Resources: corev1.ResourceRequirements{
						Requests: corev1.ResourceList{
							corev1.ResourceCPU:    s.ACMEOptions.HTTP01SolverResourceRequestCPU,
							corev1.ResourceMemory: s.ACMEOptions.HTTP01SolverResourceRequestMemory,
						},
						Limits: corev1.ResourceList{
							corev1.ResourceCPU:    s.ACMEOptions.HTTP01SolverResourceLimitsCPU,
							corev1.ResourceMemory: s.ACMEOptions.HTTP01SolverResourceLimitsMemory,
						},
					},
					Ports: []corev1.ContainerPort{
						{
							Name:          "http",
							ContainerPort: acmeSolverListenPort,
						},
					},
					SecurityContext: &corev1.SecurityContext{
						AllowPrivilegeEscalation: pointer.BoolPtr(false),
					},
				},
			},
		},
	}
}

// Merge object meta from the pod template. Fall back to default values.
func (s *Solver) mergePodObjectMetaWithPodTemplate(pod *corev1.Pod, podTempl *cmacme.ACMEChallengeSolverHTTP01IngressPodTemplate) *corev1.Pod {
	if podTempl == nil {
		return pod
	}

	if pod.Labels == nil {
		pod.Labels = make(map[string]string)
	}

	for k, v := range podTempl.Labels {
		pod.Labels[k] = v
	}

	if pod.Annotations == nil {
		pod.Annotations = make(map[string]string)
	}

	for k, v := range podTempl.Annotations {
		pod.Annotations[k] = v
	}

	if pod.Spec.NodeSelector == nil {
		pod.Spec.NodeSelector = make(map[string]string)
	}

	for k, v := range podTempl.Spec.NodeSelector {
		pod.Spec.NodeSelector[k] = v
	}

	if pod.Spec.Tolerations == nil {
		pod.Spec.Tolerations = []corev1.Toleration{}
	}

	pod.Spec.Tolerations = append(pod.Spec.Tolerations, podTempl.Spec.Tolerations...)

	if podTempl.Spec.Affinity != nil {
		pod.Spec.Affinity = podTempl.Spec.Affinity
	}

	if podTempl.Spec.PriorityClassName != "" {
		pod.Spec.PriorityClassName = podTempl.Spec.PriorityClassName
	}

	if podTempl.Spec.ServiceAccountName != "" {
		pod.Spec.ServiceAccountName = podTempl.Spec.ServiceAccountName
	}

	return pod
}
<|endoftext|>


<commit_before>/*
Copyright 2020 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 audit

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"os/user"
	"strings"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/version"
)

var mutex sync.Mutex

// userName pulls the user flag, if empty gets the os username.
func userName() string {
	u := viper.GetString(config.UserFlag)
	if u != "" {
		return u
	}
	osUser, err := user.Current()
	if err != nil {
		return "UNKNOWN"
	}
	return osUser.Username
}

// args concats the args into space delimited string.
func args() string {
	// first arg is binary and second is command, anything beyond is a minikube arg
	if len(os.Args) < 3 {
		return ""
	}
	return strings.Join(os.Args[2:], " ")
}

// Log details about the executed command.
func LogCommandStart() (string, error) {
	mutex.Lock()
	defer mutex.Unlock()
	if len(os.Args) < 2 || !shouldLog() {
		return "", nil
	}
	id := uuid.New().String()
	r := newRow(pflag.Arg(0), args(), userName(), version.GetVersion(), time.Now(), id)
	if err := appendToLog(r); err != nil {
		return "", err
	}
	return r.id, nil
}

func LogCommandEnd(id string) error {
	mutex.Lock()
	defer mutex.Unlock()
	if id == "" {
		return nil
	}
	if currentLogFile == nil {
		if err := setLogFile(); err != nil {
			return fmt.Errorf("failed to set the log file: %v", err)
		}
	}
	if err := seekToBeginning(); err != nil {
		return err
	}
	var logs []string
	s := bufio.NewScanner(currentLogFile)
	for s.Scan() {
		logs = append(logs, s.Text())
	}
	if err := s.Err(); err != nil {
		return fmt.Errorf("failed to read from audit file: %v", err)
	}
	rowSlice, err := logsToRows(logs)
	if err != nil {
		return fmt.Errorf("failed to convert logs to rows: %v", err)
	}
	auditContents := ""
	var entriesNeedsToUpdate int
	for _, v := range rowSlice {
		if v.id == id {
			v.endTime = time.Now().Format(constants.TimeFormat)
			v.Data = v.toMap()
			entriesNeedsToUpdate++
		}
		auditLog, err := json.Marshal(v)
		if err != nil {
			return err
		}
		auditContents += string(auditLog) + "\n"
	}
	if entriesNeedsToUpdate == 0 {
		return fmt.Errorf("failed to find a log row with id equals to %v", id)
	}
	if err := currentLogFile.Truncate(0); err != nil {
		return err
	}
	_, err = currentLogFile.Write([]byte(auditContents))
	if err != nil {
		return err
	}
	return nil
}

// shouldLog returns if the command should be logged.
func shouldLog() bool {
	// in rare chance we get here without a command, don't log
	if pflag.NArg() == 0 {
		return false
	}

	if isDeletePurge() {
		return false
	}

	// commands that should not be logged.
	no := []string{"status", "version", "logs", "generate-docs"}
	a := pflag.Arg(0)
	for _, c := range no {
		if a == c {
			return false
		}
	}
	return true
}

// isDeletePurge return true if command is delete with purge flag.
func isDeletePurge() bool {
	return pflag.Arg(0) == "delete" && viper.GetBool("purge")
}
<commit_msg>fix truncating on Windows<commit_after>/*
Copyright 2020 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 audit

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"os/user"
	"strings"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/version"
)

var mutex sync.Mutex

// userName pulls the user flag, if empty gets the os username.
func userName() string {
	u := viper.GetString(config.UserFlag)
	if u != "" {
		return u
	}
	osUser, err := user.Current()
	if err != nil {
		return "UNKNOWN"
	}
	return osUser.Username
}

// args concats the args into space delimited string.
func args() string {
	// first arg is binary and second is command, anything beyond is a minikube arg
	if len(os.Args) < 3 {
		return ""
	}
	return strings.Join(os.Args[2:], " ")
}

// Log details about the executed command.
func LogCommandStart() (string, error) {
	mutex.Lock()
	defer mutex.Unlock()
	if len(os.Args) < 2 || !shouldLog() {
		return "", nil
	}
	id := uuid.New().String()
	r := newRow(pflag.Arg(0), args(), userName(), version.GetVersion(), time.Now(), id)
	if err := appendToLog(r); err != nil {
		return "", err
	}
	return r.id, nil
}

func LogCommandEnd(id string) error {
	mutex.Lock()
	defer mutex.Unlock()
	if id == "" {
		return nil
	}
	if currentLogFile == nil {
		if err := setLogFile(); err != nil {
			return fmt.Errorf("failed to set the log file: %v", err)
		}
	}
	if err := seekToBeginning(); err != nil {
		return err
	}
	var logs []string
	s := bufio.NewScanner(currentLogFile)
	for s.Scan() {
		logs = append(logs, s.Text())
	}
	if err := s.Err(); err != nil {
		return fmt.Errorf("failed to read from audit file: %v", err)
	}
	rowSlice, err := logsToRows(logs)
	if err != nil {
		return fmt.Errorf("failed to convert logs to rows: %v", err)
	}
	auditContents := ""
	var entriesNeedsToUpdate int
	for _, v := range rowSlice {
		if v.id == id {
			v.endTime = time.Now().Format(constants.TimeFormat)
			v.Data = v.toMap()
			entriesNeedsToUpdate++
		}
		auditLog, err := json.Marshal(v)
		if err != nil {
			return err
		}
		auditContents += string(auditLog) + "\n"
	}
	if entriesNeedsToUpdate == 0 {
		return fmt.Errorf("failed to find a log row with id equals to %v", id)
	}
	// have to close the audit file, truncate it, then reopen it as Windows can't truncate an open file
	if err := currentLogFile.Close(); err != nil {
		return fmt.Errorf("failed to close audit file: %v", err)
	}
	if err := os.Truncate(localpath.AuditLog(), 0); err != nil {
		return fmt.Errorf("failed to truncate audit file: %v", err)
	}
	if err := setLogFile(); err != nil {
		return err
	}
	_, err = currentLogFile.Write([]byte(auditContents))
	if err != nil {
		return err
	}
	return nil
}

// shouldLog returns if the command should be logged.
func shouldLog() bool {
	// in rare chance we get here without a command, don't log
	if pflag.NArg() == 0 {
		return false
	}

	if isDeletePurge() {
		return false
	}

	// commands that should not be logged.
	no := []string{"status", "version", "logs", "generate-docs"}
	a := pflag.Arg(0)
	for _, c := range no {
		if a == c {
			return false
		}
	}
	return true
}

// isDeletePurge return true if command is delete with purge flag.
func isDeletePurge() bool {
	return pflag.Arg(0) == "delete" && viper.GetBool("purge")
}
<|endoftext|>


<commit_before>// +build !android

package platform

import (
	"fmt"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
)

type Platform struct{}

const name = "platform"

func (self *Platform) Name() string {
	return name
}

func (self *Platform) Collect() (result interface{}, err error) {
	result, err = getPlatformInfo()
	return
}

func getPlatformInfo() (platformInfo map[string]interface{}, err error) {

	// collect each portion, and allow the parts that succeed (even if some
	// parts fail.)  For this check, it does have the (small) liability
	// that if both the ArchInfo() and the PythonVersion() fail, the error
	// from the ArchInfo() will be lost

	// for this, no error check.  The successful results will be added
	// to the return value, and the error stored.
	platformInfo, err = GetArchInfo()

	platformInfo["goV"] = strings.Replace(runtime.Version(), "go", "", -1)
	// If this errors, swallow the error.
	// It will usually mean that Python is not on the PATH
	// and we don't care about that.
	pythonV, e := getPythonVersion()

	// if there was no failure, add the python variables to the platformInfo
	if e == nil {
		platformInfo["pythonV"] = pythonV
	}

	platformInfo["GOOS"] = runtime.GOOS
	platformInfo["GOOARCH"] = runtime.GOARCH

	return
}

func getPythonVersion() (string, error) {
	out, err := exec.Command("python", "-V").CombinedOutput()
	if err != nil {
		return "", err
	}
	version := fmt.Sprintf("%s", out)
	values := regexp.MustCompile("Python (.*)\n").FindStringSubmatch(version)
	return strings.Trim(values[1], "\r"), nil
}
<commit_msg>[platform] Fix nil map dereference<commit_after>// +build !android

package platform

import (
	"fmt"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
)

type Platform struct{}

const name = "platform"

func (self *Platform) Name() string {
	return name
}

func (self *Platform) Collect() (result interface{}, err error) {
	result, err = getPlatformInfo()
	return
}

func getPlatformInfo() (platformInfo map[string]interface{}, err error) {

	// collect each portion, and allow the parts that succeed (even if some
	// parts fail.)  For this check, it does have the (small) liability
	// that if both the ArchInfo() and the PythonVersion() fail, the error
	// from the ArchInfo() will be lost

	platformInfo = make(map[string]interface{})
	// for this, no error check.  The successful results will be added
	// to the return value, and the error stored.
	platformInfo, err = GetArchInfo()

	platformInfo["goV"] = strings.Replace(runtime.Version(), "go", "", -1)
	// If this errors, swallow the error.
	// It will usually mean that Python is not on the PATH
	// and we don't care about that.
	pythonV, e := getPythonVersion()

	// if there was no failure, add the python variables to the platformInfo
	if e == nil {
		platformInfo["pythonV"] = pythonV
	}

	platformInfo["GOOS"] = runtime.GOOS
	platformInfo["GOOARCH"] = runtime.GOARCH

	return
}

func getPythonVersion() (string, error) {
	out, err := exec.Command("python", "-V").CombinedOutput()
	if err != nil {
		return "", err
	}
	version := fmt.Sprintf("%s", out)
	values := regexp.MustCompile("Python (.*)\n").FindStringSubmatch(version)
	return strings.Trim(values[1], "\r"), nil
}
<|endoftext|>


<commit_before>package rproxy

import (
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"

	"github.com/prometheus/client_golang/prometheus/promhttp"
)

type AdminUI struct {
	Addr net.Addr

	proxy  *Proxy
	server *http.Server
}

func NewAdminUI(proxy *Proxy) *AdminUI {
	return &AdminUI{proxy: proxy}
}

func (a *AdminUI) Start() error {
	config := a.proxy.GetConfig()

	ln, err := config.Admin.Listen()
	if err != nil {
		return err
	}
	a.Addr = ln.Addr()

	proto := "http"
	if config.Admin.TLS {
		proto = "https"
	}
	log.Printf("Admin URL: %s://%s/\n", proto, config.Admin.Addr)

	a.server = &http.Server{
		Addr:      config.Admin.Addr,
		TLSConfig: config.Admin.GetTLSConfig(),
		Handler:   a.buildMux(),
	}

	go func() {
		err := a.server.Serve(ln)
		if err != http.ErrServerClosed {
			log.Fatal("server.Serve returned error: ", err)
		}
	}()

	return nil
}

func (a *AdminUI) Stop() {
	a.server.Close()
}

func (a *AdminUI) buildMux() *http.ServeMux {
	mux := http.NewServeMux()
	mux.HandleFunc("/cmd/", a.handleHTTPCmd)
	mux.HandleFunc("/status.json", a.handleHTTPStatusJSON)
	mux.HandleFunc("/info.json", a.handleHTTPInfo)
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			http.NotFound(w, r)
			return
		}
		a.handleHTTPStatusHTML(w, r)
	})
	mux.Handle("/metrics/", promhttp.Handler())
	return mux
}

var statusTemplate *template.Template

const statusHtml = `<!DOCTYPE html>

		<title>Proxy status</title>

State: {{.stateStr}}
{{.info}}

As JSON: here

Metrics: prometheus endpoint

		<form action="/cmd/" method="POST">
			<button type="submit" name="cmd" value="pause">pause</button>
			<button type="submit" name="cmd" value="unpause">unpause</button>
			<button type="submit" name="cmd" value="reload">reload [=pause+reload config+unpause]</button>
			<button type="submit" name="cmd" value="terminate-raw-connections">terminate raw connections</button>
		</form>

`

func (a *AdminUI) handleHTTPStatusHTML(w http.ResponseWriter, r *http.Request) {
	info := a.proxy.GetInfo()
	infoBytes, _ := json.MarshalIndent(info, "", "    ")

	err := statusTemplate.Execute(w, map[string]interface{}{
		"info": string(infoBytes),
	})
	if err != nil {
		panic(err)
	}
}

func (a *AdminUI) handleHTTPStatusJSON(w http.ResponseWriter, r *http.Request) {
	info := a.proxy.GetInfo()
	infoBytes, _ := json.MarshalIndent(map[string]interface{}{
		"Warning":         "DEPRECATED, DO NOT USE THIS FILE, use /info.json instead",
		"ActiveRequests":  info.ActiveRequests,
		"WaitingRequests": info.WaitingRequests,
		"State":           info.State,
		"Config":          info.Config,
		"RawConnections":  info.RawConnections,
	}, "", "    ")
	w.Write(infoBytes)
}

func (a *AdminUI) handleHTTPInfo(w http.ResponseWriter, r *http.Request) {
	infoBytes, _ := json.MarshalIndent(a.proxy.GetInfo(), "", "    ")
	w.Write(infoBytes)
}

type JsonHttpResponse struct {
	Ok    bool   `json:"ok"`
	Error string `json:"error,omitempty"`
}

func respond(w http.ResponseWriter, status int, errStr string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(JsonHttpResponse{
		Ok:    status == http.StatusOK,
		Error: errStr,
	})
}

func call(w http.ResponseWriter, block func() error) {
	defer func() {
		err := recover()
		if err != nil {
			log.Printf("Caught an internal error while handling API call: %s", err)
			respond(w, http.StatusInternalServerError, "Internal error; try again later")
		}
	}()

	err := block()
	if err != nil {
		respond(w, http.StatusBadRequest, err.Error())
	} else {
		respond(w, http.StatusOK, "")
	}
}

func (a *AdminUI) handleHTTPCmd(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Redirect(w, r, "/", http.StatusSeeOther)
		return
	}

	r.ParseForm()
	cmd := r.Form["cmd"][0]
	switch cmd {
	case "pause":
		call(w, a.proxy.Pause)
	case "unpause":
		call(w, a.proxy.Unpause)
	case "reload":
		call(w, a.proxy.Reload)
	case "terminate-raw-connections":
		call(w, a.proxy.TerminateRawConnections)
	default:
		respond(w, http.StatusBadRequest, fmt.Sprintf("Unknown cmd: '%s'", cmd))
	}
}

func init() {
	var err error
	statusTemplate, err = template.New("status").Parse(statusHtml)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Move status.json warning to top of file (sorted alphabetically).<commit_after>package rproxy

import (
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"

	"github.com/prometheus/client_golang/prometheus/promhttp"
)

type AdminUI struct {
	Addr net.Addr

	proxy  *Proxy
	server *http.Server
}

func NewAdminUI(proxy *Proxy) *AdminUI {
	return &AdminUI{proxy: proxy}
}

func (a *AdminUI) Start() error {
	config := a.proxy.GetConfig()

	ln, err := config.Admin.Listen()
	if err != nil {
		return err
	}
	a.Addr = ln.Addr()

	proto := "http"
	if config.Admin.TLS {
		proto = "https"
	}
	log.Printf("Admin URL: %s://%s/\n", proto, config.Admin.Addr)

	a.server = &http.Server{
		Addr:      config.Admin.Addr,
		TLSConfig: config.Admin.GetTLSConfig(),
		Handler:   a.buildMux(),
	}

	go func() {
		err := a.server.Serve(ln)
		if err != http.ErrServerClosed {
			log.Fatal("server.Serve returned error: ", err)
		}
	}()

	return nil
}

func (a *AdminUI) Stop() {
	a.server.Close()
}

func (a *AdminUI) buildMux() *http.ServeMux {
	mux := http.NewServeMux()
	mux.HandleFunc("/cmd/", a.handleHTTPCmd)
	mux.HandleFunc("/status.json", a.handleHTTPStatusJSON)
	mux.HandleFunc("/info.json", a.handleHTTPInfo)
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			http.NotFound(w, r)
			return
		}
		a.handleHTTPStatusHTML(w, r)
	})
	mux.Handle("/metrics/", promhttp.Handler())
	return mux
}

var statusTemplate *template.Template

const statusHtml = `<!DOCTYPE html>

		<title>Proxy status</title>

State: {{.stateStr}}
{{.info}}

As JSON: here

Metrics: prometheus endpoint

		<form action="/cmd/" method="POST">
			<button type="submit" name="cmd" value="pause">pause</button>
			<button type="submit" name="cmd" value="unpause">unpause</button>
			<button type="submit" name="cmd" value="reload">reload [=pause+reload config+unpause]</button>
			<button type="submit" name="cmd" value="terminate-raw-connections">terminate raw connections</button>
		</form>

`

func (a *AdminUI) handleHTTPStatusHTML(w http.ResponseWriter, r *http.Request) {
	info := a.proxy.GetInfo()
	infoBytes, _ := json.MarshalIndent(info, "", "    ")

	err := statusTemplate.Execute(w, map[string]interface{}{
		"info": string(infoBytes),
	})
	if err != nil {
		panic(err)
	}
}

func (a *AdminUI) handleHTTPStatusJSON(w http.ResponseWriter, r *http.Request) {
	info := a.proxy.GetInfo()
	infoBytes, _ := json.MarshalIndent(map[string]interface{}{
		"!Warning":        "DEPRECATED, DO NOT USE THIS FILE, use /info.json instead",
		"ActiveRequests":  info.ActiveRequests,
		"WaitingRequests": info.WaitingRequests,
		"State":           info.State,
		"Config":          info.Config,
		"RawConnections":  info.RawConnections,
	}, "", "    ")
	w.Write(infoBytes)
}

func (a *AdminUI) handleHTTPInfo(w http.ResponseWriter, r *http.Request) {
	infoBytes, _ := json.MarshalIndent(a.proxy.GetInfo(), "", "    ")
	w.Write(infoBytes)
}

type JsonHttpResponse struct {
	Ok    bool   `json:"ok"`
	Error string `json:"error,omitempty"`
}

func respond(w http.ResponseWriter, status int, errStr string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(JsonHttpResponse{
		Ok:    status == http.StatusOK,
		Error: errStr,
	})
}

func call(w http.ResponseWriter, block func() error) {
	defer func() {
		err := recover()
		if err != nil {
			log.Printf("Caught an internal error while handling API call: %s", err)
			respond(w, http.StatusInternalServerError, "Internal error; try again later")
		}
	}()

	err := block()
	if err != nil {
		respond(w, http.StatusBadRequest, err.Error())
	} else {
		respond(w, http.StatusOK, "")
	}
}

func (a *AdminUI) handleHTTPCmd(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Redirect(w, r, "/", http.StatusSeeOther)
		return
	}

	r.ParseForm()
	cmd := r.Form["cmd"][0]
	switch cmd {
	case "pause":
		call(w, a.proxy.Pause)
	case "unpause":
		call(w, a.proxy.Unpause)
	case "reload":
		call(w, a.proxy.Reload)
	case "terminate-raw-connections":
		call(w, a.proxy.TerminateRawConnections)
	default:
		respond(w, http.StatusBadRequest, fmt.Sprintf("Unknown cmd: '%s'", cmd))
	}
}

func init() {
	var err error
	statusTemplate, err = template.New("status").Parse(statusHtml)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
  "database/sql"
  "errors"
  "flag"
  "io"
  "log"
  "net/http"
  "os"
  "path"
  "strings"
  "time"

  _ "github.com/mattn/go-sqlite3"
  "github.com/ungerik/go-rss"
)

// CREATE TABLE feeds (name TEXT PRIMARY KEY, url TEXT NOT NULL, dayOfWeek INTEGER NOT NULL,
// seconds INTEGER NOT NULL, lastTitle TEXT NOT NULL);

type updatedTitleMessage struct {
  Name  string
  Title string
}

// Flag specifications.
var dbFilename = flag.String("db_file", "feeds.db", "filename of database to use")
var target = flag.String("target", "", "target directory to download to")
var checkInterval = flag.Int(
  "check_interval", 3600, "seconds between checks during normal operation")
var rapidCheckInterval = flag.Int(
  "rapid_check_interval", 60, "seconds between checks when we suspect there will be a new item")
var rapidCheckDuration = flag.Int(
  "rapid_check_duration", 3600, "seconds that we suspect there will be a new item")

func downloadUrl(url string) error {
  // Figure out the filename to download to.
  lastSeparatorIndex := strings.LastIndex(url, "/")
  if lastSeparatorIndex == -1 {
    return errors.New("malformed url (no slash!?)")
  }
  filename := url[lastSeparatorIndex+1:]
  if len(filename) == 0 {
    return errors.New("malformed url (no filename)")
  }
  filepath := path.Join(*target, filename)

  // Actually download it.
  resp, err := http.Get(url)
  if err != nil {
    return err
  }
  defer resp.Body.Close()

  file, err := os.Create(filepath)
  if err != nil {
    return err
  }
  defer file.Close()

  _, err = io.Copy(file, resp.Body)
  if err != nil {
    return err
  }
  return nil
}

func lastRapidStartTime(fromTime time.Time, dayOfWeek int, seconds int) time.Time {
  dayDiff := dayOfWeek - int(fromTime.Weekday())
  if dayDiff > 0 {
    dayDiff -= 7
  }

  if dayDiff == 0 {
    if fromTime.Sub(time.Date(fromTime.Year(), fromTime.Month(), fromTime.Day(), 0, 0, 0, 0, time.Local)).Seconds() < float64(seconds) {
      dayDiff -= 7
    }
  }

  return time.Date(
    fromTime.Year(), fromTime.Month(), fromTime.Day()+dayDiff, 0, 0, seconds, 0, time.Local)
}

func nextRapidStartTime(fromTime time.Time, dayOfWeek int, seconds int) time.Time {
  return lastRapidStartTime(fromTime.AddDate(0, 0, 7), dayOfWeek, seconds)
}

func isRapid(fromTime time.Time, dayOfWeek int, seconds int) bool {
  secondsSinceRapidStart := fromTime.Sub(lastRapidStartTime(fromTime, dayOfWeek, seconds)).Seconds()
  return secondsSinceRapidStart < float64(*rapidCheckDuration)
}

func watchFeed(
  messages chan updatedTitleMessage, name string, feedUrl string, dayOfWeek int, seconds int,
  lastTitle string) {
  log.Printf("[%s] Starting watch.", name)

  checkTime := time.Now()

  // Main loop.
  for {
    log.Printf("[%s] Checking for new items.", name)

    // Fetch RSS.
    feed, err := rss.Read(feedUrl)
    if err != nil {
      log.Printf("[%s] Error fetching RSS: %s", name, err)
    } else {
      // Download any new files.
      for i := 0; i < len(feed.Item); i++ {
        if feed.Item[i].Title == lastTitle {
          break
        }

        log.Printf("[%s] Fetching %s.", name, feed.Item[i].Title)
        go func(url string) {
          err := downloadUrl(url)
          if err != nil {
            log.Printf("[%s] Error downloading %s: %s", name, url, err)
          }
        }(feed.Item[i].Link)
      }

      // Update last seen title.
      if len(feed.Item) > 0 {
        newTitle := feed.Item[0].Title
        if lastTitle != newTitle {
          lastTitle = newTitle
          messages <- updatedTitleMessage{name, lastTitle}
        }
      }
    }

    // Determine next wait time & wait.
    if isRapid(checkTime, dayOfWeek, seconds) {
      checkTime = checkTime.Add(time.Duration(*rapidCheckInterval) * time.Second)
    } else {
      checkTime = checkTime.Add(time.Duration(*checkInterval) * time.Second)
    }

    nextRapidTime := nextRapidStartTime(checkTime, dayOfWeek, seconds)
    if checkTime.After(nextRapidTime) {
      checkTime = nextRapidTime
    }
    time.Sleep(checkTime.Sub(time.Now()))
  }
}

func main() {
  log.Print("Starting rss-downloader.")

  // Check flags.
  flag.Parse()
  if *target == "" {
    log.Fatal("Flag 'target' is required.")
  }

  // Connect to database.
  db, err := sql.Open("sqlite3", *dbFilename)
  if err != nil {
    log.Fatalf("Error opening database connection: %s", err)
  }
  defer db.Close()

  // Start watching.
  messages := make(chan updatedTitleMessage)
  rows, err := db.Query("SELECT name, url, dayOfWeek, seconds, lastTitle FROM feeds")
  if err != nil {
    log.Fatalf("Error reading RSS feeds: %s", err)
  }
  for rows.Next() {
    var name string
    var url string
    var dayOfWeek int
    var seconds int
    var lastTitle string

    if err := rows.Scan(&name, &url, &dayOfWeek, &seconds, &lastTitle); err != nil {
      log.Fatalf("Error reading RSS feeds: %s", err)
    }

    go watchFeed(messages, name, url, dayOfWeek, seconds, lastTitle)
  }
  if err := rows.Err(); err != nil {
    log.Fatalf("Error reading RSS feeds: %s", err)
  }

  for {
    msg := <-messages
    _, err := db.Exec(
      "UPDATE feeds SET lastTitle = ? WHERE name = ?", msg.Title, msg.Name)
    if err != nil {
      log.Printf("[%s] Error updating last title: %s", err)
    }
  }
}
<commit_msg>Add --log_file flag to allow logfile to be specified.<commit_after>package main

import (
  "database/sql"
  "errors"
  "flag"
  "io"
  "log"
  "net/http"
  "os"
  "path"
  "strings"
  "time"

  _ "github.com/mattn/go-sqlite3"
  "github.com/ungerik/go-rss"
)

// CREATE TABLE feeds (name TEXT PRIMARY KEY, url TEXT NOT NULL, dayOfWeek INTEGER NOT NULL,
// seconds INTEGER NOT NULL, lastTitle TEXT NOT NULL);

type updatedTitleMessage struct {
  Name  string
  Title string
}

// Flag specifications.
var dbFilename = flag.String("db_file", "feeds.db", "filename of database to use")
var logFilename = flag.String("log_file", "", "filename to log to")
var target = flag.String("target", "", "target directory to download to")
var checkInterval = flag.Int(
  "check_interval", 3600, "seconds between checks during normal operation")
var rapidCheckInterval = flag.Int(
  "rapid_check_interval", 60, "seconds between checks when we suspect there will be a new item")
var rapidCheckDuration = flag.Int(
  "rapid_check_duration", 3600, "seconds that we suspect there will be a new item")

func downloadUrl(url string) error {
  // Figure out the filename to download to.
  lastSeparatorIndex := strings.LastIndex(url, "/")
  if lastSeparatorIndex == -1 {
    return errors.New("malformed url (no slash!?)")
  }
  filename := url[lastSeparatorIndex+1:]
  if len(filename) == 0 {
    return errors.New("malformed url (no filename)")
  }
  filepath := path.Join(*target, filename)

  // Actually download it.
  resp, err := http.Get(url)
  if err != nil {
    return err
  }
  defer resp.Body.Close()

  file, err := os.Create(filepath)
  if err != nil {
    return err
  }
  defer file.Close()

  _, err = io.Copy(file, resp.Body)
  if err != nil {
    return err
  }
  return nil
}

func lastRapidStartTime(fromTime time.Time, dayOfWeek int, seconds int) time.Time {
  dayDiff := dayOfWeek - int(fromTime.Weekday())
  if dayDiff > 0 {
    dayDiff -= 7
  }

  if dayDiff == 0 {
    if fromTime.Sub(time.Date(fromTime.Year(), fromTime.Month(), fromTime.Day(), 0, 0, 0, 0, time.Local)).Seconds() < float64(seconds) {
      dayDiff -= 7
    }
  }

  return time.Date(
    fromTime.Year(), fromTime.Month(), fromTime.Day()+dayDiff, 0, 0, seconds, 0, time.Local)
}

func nextRapidStartTime(fromTime time.Time, dayOfWeek int, seconds int) time.Time {
  return lastRapidStartTime(fromTime.AddDate(0, 0, 7), dayOfWeek, seconds)
}

func isRapid(fromTime time.Time, dayOfWeek int, seconds int) bool {
  secondsSinceRapidStart := fromTime.Sub(lastRapidStartTime(fromTime, dayOfWeek, seconds)).Seconds()
  return secondsSinceRapidStart < float64(*rapidCheckDuration)
}

func watchFeed(
  messages chan updatedTitleMessage, name string, feedUrl string, dayOfWeek int, seconds int,
  lastTitle string) {
  log.Printf("[%s] Starting watch.", name)

  checkTime := time.Now()

  // Main loop.
  for {
    log.Printf("[%s] Checking for new items.", name)

    // Fetch RSS.
    feed, err := rss.Read(feedUrl)
    if err != nil {
      log.Printf("[%s] Error fetching RSS: %s", name, err)
    } else {
      // Download any new files.
      for i := 0; i < len(feed.Item); i++ {
        if feed.Item[i].Title == lastTitle {
          break
        }

        log.Printf("[%s] Fetching %s.", name, feed.Item[i].Title)
        go func(url string) {
          err := downloadUrl(url)
          if err != nil {
            log.Printf("[%s] Error downloading %s: %s", name, url, err)
          }
        }(feed.Item[i].Link)
      }

      // Update last seen title.
      if len(feed.Item) > 0 {
        newTitle := feed.Item[0].Title
        if lastTitle != newTitle {
          lastTitle = newTitle
          messages <- updatedTitleMessage{name, lastTitle}
        }
      }
    }

    // Determine next wait time & wait.
    if isRapid(checkTime, dayOfWeek, seconds) {
      checkTime = checkTime.Add(time.Duration(*rapidCheckInterval) * time.Second)
    } else {
      checkTime = checkTime.Add(time.Duration(*checkInterval) * time.Second)
    }

    nextRapidTime := nextRapidStartTime(checkTime, dayOfWeek, seconds)
    if checkTime.After(nextRapidTime) {
      checkTime = nextRapidTime
    }
    time.Sleep(checkTime.Sub(time.Now()))
  }
}

func main() {
  // Check flags.
  flag.Parse()
  if *target == "" {
    log.Fatal("--target is required.")
  }

  // Set up logging.
  if *logFilename != "" {
    logWriter, err := os.OpenFile(*logFilename, os.O_RDWR | os.O_APPEND | os.O_CREATE, 0666)
    if err != nil {
      log.Fatal("--log_file could not be opened")
    }

    log.SetOutput(logWriter)
    defer logWriter.Close()
  }

  log.Print("Starting rss-downloader.")

  // Connect to database.
  db, err := sql.Open("sqlite3", *dbFilename)
  if err != nil {
    log.Fatalf("Error opening database connection: %s", err)
  }
  defer db.Close()

  // Start watching.
  messages := make(chan updatedTitleMessage)
  rows, err := db.Query("SELECT name, url, dayOfWeek, seconds, lastTitle FROM feeds")
  if err != nil {
    log.Fatalf("Error reading RSS feeds: %s", err)
  }
  for rows.Next() {
    var name string
    var url string
    var dayOfWeek int
    var seconds int
    var lastTitle string

    if err := rows.Scan(&name, &url, &dayOfWeek, &seconds, &lastTitle); err != nil {
      log.Fatalf("Error reading RSS feeds: %s", err)
    }

    go watchFeed(messages, name, url, dayOfWeek, seconds, lastTitle)
  }
  if err := rows.Err(); err != nil {
    log.Fatalf("Error reading RSS feeds: %s", err)
  }

  for {
    msg := <-messages
    _, err := db.Exec(
      "UPDATE feeds SET lastTitle = ? WHERE name = ?", msg.Title, msg.Name)
    if err != nil {
      log.Printf("[%s] Error updating last title: %s", err)
    }
  }
}
<|endoftext|>