<commit_before>package kontrol

import (
	"database/sql"
	"errors"
	"fmt"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/hashicorp/go-version"
	sq "github.com/lann/squirrel"
	_ "github.com/lib/pq"

	"github.com/koding/kite"
	kontrolprotocol "github.com/koding/kite/kontrol/protocol"
	"github.com/koding/kite/protocol"
)

// Postgres holds Postgresql database related configuration
type PostgresConfig struct {
	Host     string
	Port     int
	Username string
	Password string
	DBName   string
}

type Postgres struct {
	DB  *sql.DB
	Log kite.Logger
}

func NewPostgres(conf *PostgresConfig, log kite.Logger) *Postgres {
	if conf == nil {
		conf = &PostgresConfig{}
	}

	if conf.Port == 0 {
		conf.Port = 5432
	}

	if conf.Host == "" {
		conf.Host = "localhost"
	}

	if conf.DBName == "" {
		conf.DBName = os.Getenv("KONTROL_POSTGRES_DBNAME")
		if conf.DBName == "" {
			panic("db name is not set for postgres kontrol storage")
		}
	}

	connString := fmt.Sprintf(
		"host=%s port=%d dbname=%s sslmode=disable",
		conf.Host, conf.Port, conf.DBName,
	)

	if conf.Password != "" {
		connString += " password=" + conf.Password
	}

	if conf.Username == "" {
		conf.Username = os.Getenv("KONTROL_POSTGRES_USERNAME")
		if conf.Username == "" {
			panic("username is not set for postgres kontrol storage")
		}
	}

	connString += " user=" + conf.Username

	db, err := sql.Open("postgres", connString)
	if err != nil {
		panic(err)
	}

	p := &Postgres{
		DB:  db,
		Log: log,
	}

	cleanInterval := 30 * time.Second  // clean every 30 second
	expireInterval := 20 * time.Second // clean rows that are 20 second old
	go p.RunCleaner(cleanInterval, expireInterval)

	return p
}

// RunCleaner delets every "interval" duration rows which are older than
// "expire" duration based on the "updated_at" field. For more info check
// CleanExpireRows which is used to delete old rows.
func (p *Postgres) RunCleaner(interval, expire time.Duration) {
	cleanFunc := func() {
		affectedRows, err := p.CleanExpiredRows(expire)
		if err != nil {
			p.Log.Warning("postgres: cleaning old rows failed: %s", err)
		} else if affectedRows != 0 {
			p.Log.Info("postgres: cleaned up %d rows", affectedRows)
		}
	}

	cleanFunc() // run for the first time
	for _ = range time.Tick(interval) {
		cleanFunc()
	}
}

// CleanExpiredRows deletes rows that are at least "expire" duration old. So if
// say an expire duration of 10 second is given, it will delete all rows that
// were updated 10 seconds ago
func (p *Postgres) CleanExpiredRows(expire time.Duration) (int64, error) {
	// See: http://stackoverflow.com/questions/14465727/how-to-insert-things-like-now-interval-2-minutes-into-php-pdo-query
	// basically by passing an integer to INTERVAL is not possible, we need to
	// cast it. However there is a more simpler way, we can multiply INTERVAL
	// with an integer so we just declare a one second INTERVAL and multiply it
	// with the amount we want.
	cleanOldRows := `DELETE FROM kite.kite WHERE updated_at < (now() at time zone 'utc') - ((INTERVAL '1 second') * $1)`

	rows, err := p.DB.Exec(cleanOldRows, int64(expire/time.Second))
	if err != nil {
		return 0, err
	}

	return rows.RowsAffected()
}

func (p *Postgres) Get(query *protocol.KontrolQuery) (Kites, error) {
	// only let query with usernames, otherwise the whole tree will be fetched
	// which is not good for us
	sqlQuery, args, err := selectQuery(query)
	if err != nil {
		return nil, err
	}

	var hasVersionConstraint bool // does query contains a constraint on version?
	var keyRest string            // query key after the version field
	var versionConstraint version.Constraints
	// NewVersion returns an error if it's a constraint, like: ">= 1.0, < 1.4"
	_, err = version.NewVersion(query.Version)
	if err != nil && query.Version != "" {
		// now parse our constraint
		versionConstraint, err = version.NewConstraint(query.Version)
		if err != nil {
			// version is a malformed, just return the error
			return nil, err
		}

		hasVersionConstraint = true
		nameQuery := &protocol.KontrolQuery{
			Username:    query.Username,
			Environment: query.Environment,
			Name:        query.Name,
		}

		// We will make a get request to all nodes under this name
		// and filter the result later.
		sqlQuery, args, err = selectQuery(nameQuery)
		if err != nil {
			return nil, err
		}

		// Rest of the key after version field
		keyRest = "/" + strings.TrimRight(
			query.Region+"/"+query.Hostname+"/"+query.ID, "/")
	}

	rows, err := p.DB.Query(sqlQuery, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var (
		username    string
		environment string
		kitename    string
		version     string
		region      string
		hostname    string
		id          string
		url         string
		updated_at  time.Time
		created_at  time.Time
	)

	kites := make(Kites, 0)

	for rows.Next() {
		err := rows.Scan(
			&username,
			&environment,
			&kitename,
			&version,
			&region,
			&hostname,
			&id,
			&url,
			&updated_at,
			&created_at,
		)
		if err != nil {
			return nil, err
		}

		kites = append(kites, &protocol.KiteWithToken{
			Kite: protocol.Kite{
				Username:    username,
				Environment: environment,
				Name:        kitename,
				Version:     version,
				Region:      region,
				Hostname:    hostname,
				ID:          id,
			},
			URL: url,
		})
	}

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

	// if it's just single result there is no need to shuffle or filter
	// according to the version constraint
	if len(kites) == 1 {
		return kites, nil
	}

	// Filter kites by version constraint
	if hasVersionConstraint {
		kites.Filter(versionConstraint, keyRest)
	}

	// randomize the result
	kites.Shuffle()

	return kites, nil
}

func (p *Postgres) Upsert(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) (err error) {
	// check that the incoming URL is valid to prevent malformed input
	_, err = url.Parse(value.URL)
	if err != nil {
		return err
	}

	// we are going to try an UPDATE, if it's not successfull we are going to
	// INSERT the document, all ine one single transaction
	tx, err := p.DB.Begin()
	if err != nil {
		return err
	}

	defer func() {
		if err != nil {
			err = tx.Rollback()
		} else {
			// it calls Rollback inside if it fails again :)
			err = tx.Commit()
		}
	}()

	res, err := tx.Exec(`UPDATE kite.kite SET url = $1, updated_at = (now() at time zone 'utc')
	WHERE id = $2`, value.URL, kiteProt.ID)
	if err != nil {
		return err
	}

	rowAffected, err := res.RowsAffected()
	if err != nil {
		return err
	}

	// we got an update! so this was successfull, just return without an error
	if rowAffected != 0 {
		return nil
	}

	insertSQL, args, err := insertQuery(kiteProt, value.URL)
	if err != nil {
		return err
	}

	_, err = tx.Exec(insertSQL, args...)
	return err
}

func (p *Postgres) Add(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) error {
	// check that the incoming URL is valid to prevent malformed input
	_, err := url.Parse(value.URL)
	if err != nil {
		return err
	}

	sqlQuery, args, err := insertQuery(kiteProt, value.URL)
	if err != nil {
		return err
	}

	_, err = p.DB.Exec(sqlQuery, args...)
	return err
}

func (p *Postgres) Update(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) error {
	// check that the incoming url is valid to prevent malformed input
	_, err := url.Parse(value.URL)
	if err != nil {
		return err
	}

	// TODO: also consider just using WHERE id = kiteProt.ID, see how it's
	// performs out
	_, err = p.DB.Exec(`UPDATE kite.kite SET url = $1, updated_at = (now() at time zone 'utc')
	WHERE id = $2`,
		value.URL, kiteProt.ID)

	return err
}

func (p *Postgres) Delete(kiteProt *protocol.Kite) error {
	deleteKite := `DELETE FROM kite.kite WHERE id = $1`
	_, err := p.DB.Exec(deleteKite, kiteProt.ID)
	return err
}

// selectQuery returns a SQL query for the given query
func selectQuery(query *protocol.KontrolQuery) (string, []interface{}, error) {
	psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)

	kites := psql.Select("*").From("kite.kite")
	fields := query.Fields()
	andQuery := sq.And{}

	// we stop for the first empty value
	for _, key := range keyOrder {
		v := fields[key]
		if v == "" {
			continue
		}

		// we are using "kitename" as the columname
		if key == "name" {
			key = "kitename"
		}

		andQuery = append(andQuery, sq.Eq{key: v})
	}

	if len(andQuery) == 0 {
		return "", nil, errors.New("all query fields are empty")
	}

	return kites.Where(andQuery).ToSql()
}

// inseryQuery
func insertQuery(kiteProt *protocol.Kite, url string) (string, []interface{}, error) {
	psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)

	kiteValues := kiteProt.Values()
	values := make([]interface{}, len(kiteValues))

	for i, kiteVal := range kiteValues {
		values[i] = kiteVal
	}

	values = append(values, url)

	return psql.Insert("kite.kite").Columns(
		"username",
		"environment",
		"kitename",
		"version",
		"region",
		"hostname",
		"id",
		"url",
	).Values(values...).ToSql()
}
<commit_msg>kontrol/postgres: use multiconfig instead of doing it by hand<commit_after>package kontrol

import (
	"database/sql"
	"errors"
	"fmt"
	"net/url"
	"strings"
	"time"

	"github.com/hashicorp/go-version"
	sq "github.com/lann/squirrel"
	_ "github.com/lib/pq"

	"github.com/koding/kite"
	kontrolprotocol "github.com/koding/kite/kontrol/protocol"
	"github.com/koding/kite/protocol"
	"github.com/koding/multiconfig"
)

// Postgres holds Postgresql database related configuration
type PostgresConfig struct {
	Host     string `default:"localhost"`
	Port     int    `default:"5432"`
	Username string `required:"true"`
	Password string `required:"true"`
	DBName   string `required:"true" `
}

type Postgres struct {
	DB  *sql.DB
	Log kite.Logger
}

func NewPostgres(conf *PostgresConfig, log kite.Logger) *Postgres {
	if conf == nil {
		conf = new(PostgresConfig)

		envLoader := &multiconfig.EnvironmentLoader{Prefix: "kontrol_postgres"}
		configLoader := multiconfig.MultiLoader(
			&multiconfig.TagLoader{}, envLoader,
		)

		if err := configLoader.Load(conf); err != nil {
			fmt.Println("Valid environment variables are: ")
			envLoader.PrintEnvs(conf)
			panic(err)
		}

		err := multiconfig.MultiValidator(&multiconfig.RequiredValidator{}).Validate(conf)
		if err != nil {
			fmt.Println("Valid environment variables are: ")
			envLoader.PrintEnvs(conf)
			panic(err)
		}
	}

	connString := fmt.Sprintf(
		"host=%s port=%d dbname=%s user=%s password=%s sslmode=disable",
		conf.Host, conf.Port, conf.DBName, conf.Username, conf.Password,
	)

	db, err := sql.Open("postgres", connString)
	if err != nil {
		panic(err)
	}

	p := &Postgres{
		DB:  db,
		Log: log,
	}

	cleanInterval := 30 * time.Second  // clean every 30 second
	expireInterval := 20 * time.Second // clean rows that are 20 second old
	go p.RunCleaner(cleanInterval, expireInterval)

	return p
}

// RunCleaner delets every "interval" duration rows which are older than
// "expire" duration based on the "updated_at" field. For more info check
// CleanExpireRows which is used to delete old rows.
func (p *Postgres) RunCleaner(interval, expire time.Duration) {
	cleanFunc := func() {
		affectedRows, err := p.CleanExpiredRows(expire)
		if err != nil {
			p.Log.Warning("postgres: cleaning old rows failed: %s", err)
		} else if affectedRows != 0 {
			p.Log.Info("postgres: cleaned up %d rows", affectedRows)
		}
	}

	cleanFunc() // run for the first time
	for _ = range time.Tick(interval) {
		cleanFunc()
	}
}

// CleanExpiredRows deletes rows that are at least "expire" duration old. So if
// say an expire duration of 10 second is given, it will delete all rows that
// were updated 10 seconds ago
func (p *Postgres) CleanExpiredRows(expire time.Duration) (int64, error) {
	// See: http://stackoverflow.com/questions/14465727/how-to-insert-things-like-now-interval-2-minutes-into-php-pdo-query
	// basically by passing an integer to INTERVAL is not possible, we need to
	// cast it. However there is a more simpler way, we can multiply INTERVAL
	// with an integer so we just declare a one second INTERVAL and multiply it
	// with the amount we want.
	cleanOldRows := `DELETE FROM kite.kite WHERE updated_at < (now() at time zone 'utc') - ((INTERVAL '1 second') * $1)`

	rows, err := p.DB.Exec(cleanOldRows, int64(expire/time.Second))
	if err != nil {
		return 0, err
	}

	return rows.RowsAffected()
}

func (p *Postgres) Get(query *protocol.KontrolQuery) (Kites, error) {
	// only let query with usernames, otherwise the whole tree will be fetched
	// which is not good for us
	sqlQuery, args, err := selectQuery(query)
	if err != nil {
		return nil, err
	}

	var hasVersionConstraint bool // does query contains a constraint on version?
	var keyRest string            // query key after the version field
	var versionConstraint version.Constraints
	// NewVersion returns an error if it's a constraint, like: ">= 1.0, < 1.4"
	_, err = version.NewVersion(query.Version)
	if err != nil && query.Version != "" {
		// now parse our constraint
		versionConstraint, err = version.NewConstraint(query.Version)
		if err != nil {
			// version is a malformed, just return the error
			return nil, err
		}

		hasVersionConstraint = true
		nameQuery := &protocol.KontrolQuery{
			Username:    query.Username,
			Environment: query.Environment,
			Name:        query.Name,
		}

		// We will make a get request to all nodes under this name
		// and filter the result later.
		sqlQuery, args, err = selectQuery(nameQuery)
		if err != nil {
			return nil, err
		}

		// Rest of the key after version field
		keyRest = "/" + strings.TrimRight(
			query.Region+"/"+query.Hostname+"/"+query.ID, "/")
	}

	rows, err := p.DB.Query(sqlQuery, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var (
		username    string
		environment string
		kitename    string
		version     string
		region      string
		hostname    string
		id          string
		url         string
		updated_at  time.Time
		created_at  time.Time
	)

	kites := make(Kites, 0)

	for rows.Next() {
		err := rows.Scan(
			&username,
			&environment,
			&kitename,
			&version,
			&region,
			&hostname,
			&id,
			&url,
			&updated_at,
			&created_at,
		)
		if err != nil {
			return nil, err
		}

		kites = append(kites, &protocol.KiteWithToken{
			Kite: protocol.Kite{
				Username:    username,
				Environment: environment,
				Name:        kitename,
				Version:     version,
				Region:      region,
				Hostname:    hostname,
				ID:          id,
			},
			URL: url,
		})
	}

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

	// if it's just single result there is no need to shuffle or filter
	// according to the version constraint
	if len(kites) == 1 {
		return kites, nil
	}

	// Filter kites by version constraint
	if hasVersionConstraint {
		kites.Filter(versionConstraint, keyRest)
	}

	// randomize the result
	kites.Shuffle()

	return kites, nil
}

func (p *Postgres) Upsert(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) (err error) {
	// check that the incoming URL is valid to prevent malformed input
	_, err = url.Parse(value.URL)
	if err != nil {
		return err
	}

	// we are going to try an UPDATE, if it's not successfull we are going to
	// INSERT the document, all ine one single transaction
	tx, err := p.DB.Begin()
	if err != nil {
		return err
	}

	defer func() {
		if err != nil {
			err = tx.Rollback()
		} else {
			// it calls Rollback inside if it fails again :)
			err = tx.Commit()
		}
	}()

	res, err := tx.Exec(`UPDATE kite.kite SET url = $1, updated_at = (now() at time zone 'utc')
	WHERE id = $2`, value.URL, kiteProt.ID)
	if err != nil {
		return err
	}

	rowAffected, err := res.RowsAffected()
	if err != nil {
		return err
	}

	// we got an update! so this was successfull, just return without an error
	if rowAffected != 0 {
		return nil
	}

	insertSQL, args, err := insertQuery(kiteProt, value.URL)
	if err != nil {
		return err
	}

	_, err = tx.Exec(insertSQL, args...)
	return err
}

func (p *Postgres) Add(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) error {
	// check that the incoming URL is valid to prevent malformed input
	_, err := url.Parse(value.URL)
	if err != nil {
		return err
	}

	sqlQuery, args, err := insertQuery(kiteProt, value.URL)
	if err != nil {
		return err
	}

	_, err = p.DB.Exec(sqlQuery, args...)
	return err
}

func (p *Postgres) Update(kiteProt *protocol.Kite, value *kontrolprotocol.RegisterValue) error {
	// check that the incoming url is valid to prevent malformed input
	_, err := url.Parse(value.URL)
	if err != nil {
		return err
	}

	// TODO: also consider just using WHERE id = kiteProt.ID, see how it's
	// performs out
	_, err = p.DB.Exec(`UPDATE kite.kite SET url = $1, updated_at = (now() at time zone 'utc')
	WHERE id = $2`,
		value.URL, kiteProt.ID)

	return err
}

func (p *Postgres) Delete(kiteProt *protocol.Kite) error {
	deleteKite := `DELETE FROM kite.kite WHERE id = $1`
	_, err := p.DB.Exec(deleteKite, kiteProt.ID)
	return err
}

// selectQuery returns a SQL query for the given query
func selectQuery(query *protocol.KontrolQuery) (string, []interface{}, error) {
	psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)

	kites := psql.Select("*").From("kite.kite")
	fields := query.Fields()
	andQuery := sq.And{}

	// we stop for the first empty value
	for _, key := range keyOrder {
		v := fields[key]
		if v == "" {
			continue
		}

		// we are using "kitename" as the columname
		if key == "name" {
			key = "kitename"
		}

		andQuery = append(andQuery, sq.Eq{key: v})
	}

	if len(andQuery) == 0 {
		return "", nil, errors.New("all query fields are empty")
	}

	return kites.Where(andQuery).ToSql()
}

// inseryQuery
func insertQuery(kiteProt *protocol.Kite, url string) (string, []interface{}, error) {
	psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar)

	kiteValues := kiteProt.Values()
	values := make([]interface{}, len(kiteValues))

	for i, kiteVal := range kiteValues {
		values[i] = kiteVal
	}

	values = append(values, url)

	return psql.Insert("kite.kite").Columns(
		"username",
		"environment",
		"kitename",
		"version",
		"region",
		"hostname",
		"id",
		"url",
	).Values(values...).ToSql()
}
<|endoftext|>


<commit_before>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package yaml

// fieldSortOrder contains the relative ordering of fields when formatting an
// object.
var fieldSortOrder = []string{
	// top-level metadata
	"name", "generateName", "namespace", "clusterName",
	"apiVersion", "kind", "metadata", "type",
	"labels", "annotations",
	"spec", "status",

	// secret and configmap
	"stringData", "data", "binaryData",

	//cronjobspec,  daemonsetspec, deploymentspec, statefulsetspec,
	// jobspec fields
	"parallelism", "completions", "activeDeadlineSeconds", "backoffLimit",
	"replicas", "selector", "manualSelector", "template",
	"ttlSecondsAfterFinished", "volumeClaimTemplates", "service", "serviceName",
	"podManagementPolicy", "updateStrategy", "strategy", "minReadySeconds",
	"revision", "revisionHistoryLimit", "paused", "progressDeadlineSeconds",

	// podspec
	// podspec scalars
	"restartPolicy", "terminationGracePeriodSeconds",
	"activeDeadlineSeconds", "dnsPolicy", "serviceAccountName",
	"serviceAccount", "automountServiceAccountToken", "nodeName",
	"hostNetwork", "hostPID", "hostIPC", "shareProcessNamespace", "hostname",
	"subdomain", "schedulerName", "priorityClassName", "priority",
	"runtimeClassName", "enableServiceLinks",

	// podspec lists and maps
	"nodeSelector", "hostAliases",

	// podspec objects
	"initContainers", "containers", "volumes", "securityContext",
	"imagePullSecrets", "affinity", "tolerations", "dnsConfig",
	"readinessGates",

	// containers
	"image", "command", "args", "workingDir", "ports", "envFrom", "env",
	"resources", "volumeMounts", "volumeDevices", "livenessProbe",
	"readinessProbe", "lifecycle", "terminationMessagePath",
	"terminationMessagePolicy", "imagePullPolicy", "securityContext",
	"stdin", "stdinOnce", "tty",

	// service
	"clusterIP", "externalIPs", "loadBalancerIP", "loadBalancerSourceRanges",
	"externalName", "externalTrafficPolicy", "sessionAffinity",

	// ports
	"protocol", "port", "targetPort", "hostPort", "containerPort", "hostIP",

	// volumemount
	"readOnly", "mountPath", "subPath", "subPathExpr", "mountPropagation",

	// envvar + envvarsource
	"value", "valueFrom", "fieldRef", "resourceFieldRef", "configMapKeyRef",
	"secretKeyRef", "prefix", "configMapRef", "secretRef",
}

type set map[string]interface{}

func newSet(values ...string) set {
	m := map[string]interface{}{}
	for _, value := range values {
		m[value] = nil
	}
	return m
}

func (s set) Has(key string) bool {
	_, found := s[key]
	return found
}

// WhitelistedListSortKinds contains the set of kinds that are whitelisted
// for sorting list field elements
var WhitelistedListSortKinds = newSet(
	"CronJob", "DaemonSet", "Deployment", "Job", "ReplicaSet", "StatefulSet",
	"ValidatingWebhookConfiguration")

// WhitelistedListSortApis contains the set of apis that are whitelisted for
// sorting list field elements
var WhitelistedListSortApis = newSet(
	"apps/v1", "apps/v1beta1", "apps/v1beta2", "batch/v1", "batch/v1beta1",
	"extensions/v1beta1", "v1", "admissionregistration.k8s.io/v1beta1")

// WhitelistedListSortFields contains json paths to list fields that should
// be sorted, and the field they should be sorted by
var WhitelistedListSortFields = map[string]string{
	".spec.template.spec.containers": "name",
	".webhooks.rules.operations":     "",
}

// FieldOrder indexes fields and maps them to relative precedence
var FieldOrder map[string]int

func init() {
	// create an index of field orderings
	FieldOrder = map[string]int{}
	for i, f := range fieldSortOrder {
		FieldOrder[f] = i + 1
	}
}
<commit_msg>Remove init function following gochecknoinits linter recommendation<commit_after>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package yaml

// fieldSortOrder contains the relative ordering of fields when formatting an
// object.
var fieldSortOrder = []string{
	// top-level metadata
	"name", "generateName", "namespace", "clusterName",
	"apiVersion", "kind", "metadata", "type",
	"labels", "annotations",
	"spec", "status",

	// secret and configmap
	"stringData", "data", "binaryData",

	//cronjobspec,  daemonsetspec, deploymentspec, statefulsetspec,
	// jobspec fields
	"parallelism", "completions", "activeDeadlineSeconds", "backoffLimit",
	"replicas", "selector", "manualSelector", "template",
	"ttlSecondsAfterFinished", "volumeClaimTemplates", "service", "serviceName",
	"podManagementPolicy", "updateStrategy", "strategy", "minReadySeconds",
	"revision", "revisionHistoryLimit", "paused", "progressDeadlineSeconds",

	// podspec
	// podspec scalars
	"restartPolicy", "terminationGracePeriodSeconds",
	"activeDeadlineSeconds", "dnsPolicy", "serviceAccountName",
	"serviceAccount", "automountServiceAccountToken", "nodeName",
	"hostNetwork", "hostPID", "hostIPC", "shareProcessNamespace", "hostname",
	"subdomain", "schedulerName", "priorityClassName", "priority",
	"runtimeClassName", "enableServiceLinks",

	// podspec lists and maps
	"nodeSelector", "hostAliases",

	// podspec objects
	"initContainers", "containers", "volumes", "securityContext",
	"imagePullSecrets", "affinity", "tolerations", "dnsConfig",
	"readinessGates",

	// containers
	"image", "command", "args", "workingDir", "ports", "envFrom", "env",
	"resources", "volumeMounts", "volumeDevices", "livenessProbe",
	"readinessProbe", "lifecycle", "terminationMessagePath",
	"terminationMessagePolicy", "imagePullPolicy", "securityContext",
	"stdin", "stdinOnce", "tty",

	// service
	"clusterIP", "externalIPs", "loadBalancerIP", "loadBalancerSourceRanges",
	"externalName", "externalTrafficPolicy", "sessionAffinity",

	// ports
	"protocol", "port", "targetPort", "hostPort", "containerPort", "hostIP",

	// volumemount
	"readOnly", "mountPath", "subPath", "subPathExpr", "mountPropagation",

	// envvar + envvarsource
	"value", "valueFrom", "fieldRef", "resourceFieldRef", "configMapKeyRef",
	"secretKeyRef", "prefix", "configMapRef", "secretRef",
}

type set map[string]interface{}

func newSet(values ...string) set {
	m := map[string]interface{}{}
	for _, value := range values {
		m[value] = nil
	}
	return m
}

func (s set) Has(key string) bool {
	_, found := s[key]
	return found
}

// WhitelistedListSortKinds contains the set of kinds that are whitelisted
// for sorting list field elements
var WhitelistedListSortKinds = newSet(
	"CronJob", "DaemonSet", "Deployment", "Job", "ReplicaSet", "StatefulSet",
	"ValidatingWebhookConfiguration")

// WhitelistedListSortApis contains the set of apis that are whitelisted for
// sorting list field elements
var WhitelistedListSortApis = newSet(
	"apps/v1", "apps/v1beta1", "apps/v1beta2", "batch/v1", "batch/v1beta1",
	"extensions/v1beta1", "v1", "admissionregistration.k8s.io/v1beta1")

// WhitelistedListSortFields contains json paths to list fields that should
// be sorted, and the field they should be sorted by
var WhitelistedListSortFields = map[string]string{
	".spec.template.spec.containers": "name",
	".webhooks.rules.operations":     "",
}

// FieldOrder indexes fields and maps them to relative precedence
var FieldOrder = func() map[string]int {
	// create an index of field orderings
	fo := map[string]int{}
	for i, f := range fieldSortOrder {
		fo[f] = i + 1
	}
	return fo
}()
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"github.com/howeyc/fsnotify"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

var (
	CompileErrors []byte
	Proxy         *httputil.ReverseProxy
	Dirty         = true
)

func main() {
	// set up logs
	log.SetFlags(0)
	log.SetPrefix("[passport] ")

	app := cli.NewApp()
	app.Name = "passport"
	app.Usage = "a Go development server"
	app.Action = DefaultAction

	app.Run(os.Args)
}

func DefaultAction(c *cli.Context) {
  url, err := url.Parse("http://localhost:3000")
  check(err)
  Proxy = httputil.NewSingleHostReverseProxy(url)

	go watch()
	go checkDirty()

	http.HandleFunc("/", MainHandler)
	err = http.ListenAndServe(":8080", nil)
	check(err)
}

func checkDirty() {
	var command *exec.Cmd
	for {
		if Dirty {
			log.Print("Restarting server...")
			build()
			if command != nil {
				command.Process.Kill()
			}
			command = run()
			Dirty = false
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func watch() {
	watcher, err := fsnotify.NewWatcher()
	check(err)

	pwd, err := os.Getwd()
	check(err)

	err = watcher.Watch(pwd)
	check(err)

	defer watcher.Close()

	for {
		select {
		case ev := <-watcher.Event:
			if ev.IsModify() && strings.HasSuffix(ev.Name, ".go") {
				Dirty = true
			}
		case err := <-watcher.Error:
			if err != nil {
				log.Print(err)
			}
		}
	}
}

func MainHandler(res http.ResponseWriter, req *http.Request) {
	if len(CompileErrors) > 0 {
		res.Write(CompileErrors)
	} else {
		// TODO: Create proxy in main
		Proxy.ServeHTTP(res, req)
	}
}

func build() {
	command := exec.Command("go", "build")

	stderr, err := command.StderrPipe()
	check(err)

	err = command.Start()
	check(err)

	CompileErrors, err = ioutil.ReadAll(stderr)
	check(err)
}

func run() *exec.Cmd {
	wd, err := os.Getwd()
	check(err)

	command := exec.Command(filepath.Join(wd, filepath.Base(wd)))
	stdout, err := command.StdoutPipe()
	check(err)

	err = command.Start()
	check(err)

	go io.Copy(os.Stdout, stdout)

	return command
}

func check(err error) {
	if err != nil {
		log.Fatalf("Error: %v", err)
	}
}
<commit_msg>Removed todo message<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"github.com/howeyc/fsnotify"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

var (
	CompileErrors []byte
	Proxy         *httputil.ReverseProxy
	Dirty         = true
)

func main() {
	// set up logs
	log.SetFlags(0)
	log.SetPrefix("[passport] ")

	app := cli.NewApp()
	app.Name = "passport"
	app.Usage = "a Go development server"
	app.Action = DefaultAction

	app.Run(os.Args)
}

func DefaultAction(c *cli.Context) {
  url, err := url.Parse("http://localhost:3000")
  check(err)
  Proxy = httputil.NewSingleHostReverseProxy(url)

	go watch()
	go checkDirty()

	http.HandleFunc("/", MainHandler)
	err = http.ListenAndServe(":8080", nil)
	check(err)
}

func checkDirty() {
	var command *exec.Cmd
	for {
		if Dirty {
			log.Print("Restarting server...")
			build()
			if command != nil {
				command.Process.Kill()
			}
			command = run()
			Dirty = false
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func watch() {
	watcher, err := fsnotify.NewWatcher()
	check(err)

	pwd, err := os.Getwd()
	check(err)

	err = watcher.Watch(pwd)
	check(err)

	defer watcher.Close()

	for {
		select {
		case ev := <-watcher.Event:
			if ev.IsModify() && strings.HasSuffix(ev.Name, ".go") {
				Dirty = true
			}
		case err := <-watcher.Error:
			if err != nil {
				log.Print(err)
			}
		}
	}
}

func MainHandler(res http.ResponseWriter, req *http.Request) {
	if len(CompileErrors) > 0 {
		res.Write(CompileErrors)
	} else {
		Proxy.ServeHTTP(res, req)
	}
}

func build() {
	command := exec.Command("go", "build")

	stderr, err := command.StderrPipe()
	check(err)

	err = command.Start()
	check(err)

	CompileErrors, err = ioutil.ReadAll(stderr)
	check(err)
}

func run() *exec.Cmd {
	wd, err := os.Getwd()
	check(err)

	command := exec.Command(filepath.Join(wd, filepath.Base(wd)))
	stdout, err := command.StdoutPipe()
	check(err)

	err = command.Start()
	check(err)

	go io.Copy(os.Stdout, stdout)

	return command
}

func check(err error) {
	if err != nil {
		log.Fatalf("Error: %v", err)
	}
}
<|endoftext|>


<commit_before>package transmission

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/longnguyen11288/go-transmission/client"
)

//TransmissionClient to talk to transmission
type TransmissionClient struct {
	apiclient client.ApiClient
}

type command struct {
	Method    string    `json:"method,omitempty"`
	Arguments arguments `json:"arguments,omitempty"`
	Result    string    `json:"result,omitempty"`
}

type arguments struct {
	Fields       []string     `json:"fields,omitempty"`
	Torrents     []Torrent    `json:"torrents,omitempty"`
	Ids          []int        `json:"ids,omitempty"`
	DeleteData   bool         `json:"delete-local-data,omitempty"`
	DownloadDir  string       `json:"download-dir,omitempty"`
	MetaInfo     string       `json:"metainfo,omitempty"`
	TorrentAdded TorrentAdded `json:"torrent-added"`
}

//Torrent struct for torrents
type Torrent struct {
	ID            int     `json:"id"`
	Name          string  `json:"name"`
	Status        int     `json:"status"`
	LeftUntilDone int     `json:"leftUntilDone"`
	Eta           int     `json:"eta"`
	UploadRatio   float64 `json:"uploadRatio"`
	RateDownload  int     `json:"rateDownload"`
	RateUpload    int     `json:"rateUpload"`
	DownloadDir   string  `json:"downloadDir"`
	IsFinished    bool    `json:"isFinished"`
	PercentDone   float64 `json:"percentDone"`
}

//TorrentAdded data returning
type TorrentAdded struct {
	HashString string `json:"hashString"`
	ID         int    `json:"id"`
	Name       string `json:"name"`
}

//New create new transmission torrent
func New(url string, username string, password string) TransmissionClient {
	apiclient := client.NewClient(url, username, password)
	tc := TransmissionClient{apiclient: apiclient}
	return tc
}

//GetTorrents get a list of torrents
func (ac *TransmissionClient) GetTorrents() ([]Torrent, error) {
	var getCommand command
	var outputCommand command
	getCommand.Method = "torrent-get"
	getCommand.Arguments.Fields = []string{"id", "name",
		"status", "leftUntilDone", "eta", "uploadRatio",
		"rateDownload", "rateUpload", "downloadDir",
		"isFinished", "percentDone"}
	body, err := json.Marshal(getCommand)
	if err != nil {
		return []Torrent{}, err
	}
	output, err := ac.apiclient.Post(string(body))
	if err != nil {
		return []Torrent{}, err
	}
	fmt.Println(string(output))
	err = json.Unmarshal(output, &outputCommand)
	if err != nil {
		return []Torrent{}, err
	}
	return outputCommand.Arguments.Torrents, nil
}

//RemoveTorrent remove the torrents
func (ac *TransmissionClient) RemoveTorrent(id int, removeFile bool) (string, error) {
	var removeCommand command
	var outputCommand command

	removeCommand.Method = "torrent-remove"
	removeCommand.Arguments.Ids = []int{id}
	removeCommand.Arguments.DeleteData = removeFile
	body, err := json.Marshal(removeCommand)
	if err != nil {
		return "", err
	}
	output, err := ac.apiclient.Post(string(body))
	if err != nil {
		return "", err
	}
	err = json.Unmarshal(output, &outputCommand)
	if err != nil {
		return "", err
	}
	return outputCommand.Result, nil
}

//AddTorrentByURL add torrent by url
func (ac *TransmissionClient) AddTorrentByURL(url string, downloadDir string) (TorrentAdded, error) {
	resp, err := http.Get(url)
	if err != nil {
		return TorrentAdded{}, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return TorrentAdded{}, err
	}
	return ac.addTorrent(body, downloadDir)
}

//AddTorrentByFile add torrent by file
func (ac *TransmissionClient) AddTorrentByFile(file string, downloadDir string) (TorrentAdded, error) {
	fileData, err := ioutil.ReadFile(file)
	if err != nil {
		return TorrentAdded{}, err
	}
	return ac.addTorrent(fileData, downloadDir)
}

func (ac *TransmissionClient) addTorrent(data []byte, downloadDir string) (TorrentAdded, error) {
	var addCommand command
	var outputCommand command

	addCommand.Method = "torrent-add"
	addCommand.Arguments.MetaInfo = base64.StdEncoding.EncodeToString(data)
	addCommand.Arguments.DownloadDir = downloadDir

	body, err := json.Marshal(addCommand)
	if err != nil {
		return TorrentAdded{}, err
	}
	output, err := ac.apiclient.Post(string(body))
	if err != nil {
		return TorrentAdded{}, err
	}
	err = json.Unmarshal(output, &outputCommand)
	if err != nil {
		return TorrentAdded{}, err
	}
	return outputCommand.Arguments.TorrentAdded, nil
}

func encodeFile(file string) (string, error) {
	fileData, err := ioutil.ReadFile(file)
	if err != nil {
		return "", err
	}

	return base64.StdEncoding.EncodeToString(fileData), nil
}
<commit_msg>bug fixes and stuff<commit_after>package transmission

import (
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"net/http"

	"github.com/longnguyen11288/go-transmission/client"
)

//TransmissionClient to talk to transmission
type TransmissionClient struct {
	apiclient client.ApiClient
}

type command struct {
	Method    string    `json:"method,omitempty"`
	Arguments arguments `json:"arguments,omitempty"`
	Result    string    `json:"result,omitempty"`
}

type arguments struct {
	Fields       []string     `json:"fields,omitempty"`
	Torrents     []Torrent    `json:"torrents,omitempty"`
	Ids          []int        `json:"ids,omitempty"`
	DeleteData   bool         `json:"delete-local-data,omitempty"`
	DownloadDir  string       `json:"download-dir,omitempty"`
	MetaInfo     string       `json:"metainfo,omitempty"`
	TorrentAdded TorrentAdded `json:"torrent-added"`
}

//Torrent struct for torrents
type Torrent struct {
	ID            int     `json:"id"`
	Name          string  `json:"name"`
	Status        int     `json:"status"`
	LeftUntilDone int     `json:"leftUntilDone"`
	Eta           int     `json:"eta"`
	UploadRatio   float64 `json:"uploadRatio"`
	RateDownload  int     `json:"rateDownload"`
	RateUpload    int     `json:"rateUpload"`
	DownloadDir   string  `json:"downloadDir"`
	IsFinished    bool    `json:"isFinished"`
	PercentDone   float64 `json:"percentDone"`
	SeedRatioMode int     `json:"seedRatioMode"`
}

//TorrentAdded data returning
type TorrentAdded struct {
	HashString string `json:"hashString"`
	ID         int    `json:"id"`
	Name       string `json:"name"`
}

//New create new transmission torrent
func New(url string, username string, password string) TransmissionClient {
	apiclient := client.NewClient(url, username, password)
	tc := TransmissionClient{apiclient: apiclient}
	return tc
}

//GetTorrents get a list of torrents
func (ac *TransmissionClient) GetTorrents() ([]Torrent, error) {
	var getCommand command
	var outputCommand command
	getCommand.Method = "torrent-get"
	getCommand.Arguments.Fields = []string{"id", "name",
		"status", "leftUntilDone", "eta", "uploadRatio",
		"rateDownload", "rateUpload", "downloadDir",
		"isFinished", "percentDone", "seedRatioMode"}
	body, err := json.Marshal(getCommand)
	if err != nil {
		return []Torrent{}, err
	}
	output, err := ac.apiclient.Post(string(body))
	if err != nil {
		return []Torrent{}, err
	}
	err = json.Unmarshal(output, &outputCommand)
	if err != nil {
		return []Torrent{}, err
	}
	return outputCommand.Arguments.Torrents, nil
}

//RemoveTorrent remove the torrents
func (ac *TransmissionClient) RemoveTorrent(id int, removeFile bool) (string, error) {
	var removeCommand command
	var outputCommand command

	removeCommand.Method = "torrent-remove"
	removeCommand.Arguments.Ids = []int{id}
	removeCommand.Arguments.DeleteData = removeFile
	body, err := json.Marshal(removeCommand)
	if err != nil {
		return "", err
	}
	output, err := ac.apiclient.Post(string(body))
	if err != nil {
		return "", err
	}
	err = json.Unmarshal(output, &outputCommand)
	if err != nil {
		return "", err
	}
	return outputCommand.Result, nil
}

//AddTorrentByURL add torrent by url
func (ac *TransmissionClient) AddTorrentByURL(url string, downloadDir string) (TorrentAdded, error) {
	resp, err := http.Get(url)
	if err != nil {
		return TorrentAdded{}, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return TorrentAdded{}, err
	}
	return ac.addTorrent(body, downloadDir)
}

//AddTorrentByFile add torrent by file
func (ac *TransmissionClient) AddTorrentByFile(file string, downloadDir string) (TorrentAdded, error) {
	fileData, err := ioutil.ReadFile(file)
	if err != nil {
		return TorrentAdded{}, err
	}
	return ac.addTorrent(fileData, downloadDir)
}

func (ac *TransmissionClient) addTorrent(data []byte, downloadDir string) (TorrentAdded, error) {
	var addCommand command
	var outputCommand command

	addCommand.Method = "torrent-add"
	addCommand.Arguments.MetaInfo = base64.StdEncoding.EncodeToString(data)
	addCommand.Arguments.DownloadDir = downloadDir

	body, err := json.Marshal(addCommand)
	if err != nil {
		return TorrentAdded{}, err
	}
	output, err := ac.apiclient.Post(string(body))
	if err != nil {
		return TorrentAdded{}, err
	}
	err = json.Unmarshal(output, &outputCommand)
	if err != nil {
		return TorrentAdded{}, err
	}
	return outputCommand.Arguments.TorrentAdded, nil
}

func encodeFile(file string) (string, error) {
	fileData, err := ioutil.ReadFile(file)
	if err != nil {
		return "", err
	}

	return base64.StdEncoding.EncodeToString(fileData), nil
}
<|endoftext|>


<commit_before>package internet

import "v2ray.com/core/common/serial"

type ConfigCreator func() interface{}

var (
	globalTransportConfigCreatorCache = make(map[string]ConfigCreator)
	globalTransportSettings           []*TransportConfig
)

const unknownProtocol = "unknown"

func transportProtocolToString(protocol TransportProtocol) string {
	switch protocol {
	case TransportProtocol_TCP:
		return "tcp"
	case TransportProtocol_UDP:
		return "udp"
	case TransportProtocol_HTTP:
		return "http"
	case TransportProtocol_MKCP:
		return "mkcp"
	case TransportProtocol_WebSocket:
		return "websocket"
	case TransportProtocol_DomainSocket:
		return "domainsocket"
	default:
		return unknownProtocol
	}
}

func RegisterProtocolConfigCreator(name string, creator ConfigCreator) error {
	if _, found := globalTransportConfigCreatorCache[name]; found {
		return newError("protocol ", name, " is already registered").AtError()
	}
	globalTransportConfigCreatorCache[name] = creator
	return nil
}

func CreateTransportConfig(name string) (interface{}, error) {
	creator, ok := globalTransportConfigCreatorCache[name]
	if !ok {
		return nil, newError("unknown transport protocol: ", name)
	}
	return creator(), nil
}

func (c *TransportConfig) GetTypedSettings() (interface{}, error) {
	return c.Settings.GetInstance()
}

func (c *TransportConfig) GetUnifiedProtocolName() string {
	if len(c.ProtocolName) > 0 {
		return c.ProtocolName
	}

	return transportProtocolToString(c.Protocol)
}

func (c *StreamConfig) GetEffectiveProtocol() string {
	if c == nil {
		return "tcp"
	}

	if len(c.ProtocolName) > 0 {
		return c.ProtocolName
	}

	return transportProtocolToString(c.Protocol)
}

func (c *StreamConfig) GetEffectiveTransportSettings() (interface{}, error) {
	protocol := c.GetEffectiveProtocol()
	return c.GetTransportSettingsFor(protocol)
}

func (c *StreamConfig) GetTransportSettingsFor(protocol string) (interface{}, error) {
	if c != nil {
		for _, settings := range c.TransportSettings {
			if settings.GetUnifiedProtocolName() == protocol {
				return settings.GetTypedSettings()
			}
		}
	}

	for _, settings := range globalTransportSettings {
		if settings.GetUnifiedProtocolName() == protocol {
			return settings.GetTypedSettings()
		}
	}

	return CreateTransportConfig(protocol)
}

func (c *StreamConfig) GetEffectiveSecuritySettings() (interface{}, error) {
	for _, settings := range c.SecuritySettings {
		if settings.Type == c.SecurityType {
			return settings.GetInstance()
		}
	}
	return serial.GetInstance(c.SecurityType)
}

func (c *StreamConfig) HasSecuritySettings() bool {
	return len(c.SecurityType) > 0
}

func ApplyGlobalTransportSettings(settings []*TransportConfig) error {
	globalTransportSettings = settings
	return nil
}

func (c *ProxyConfig) HasTag() bool {
	return c != nil && len(c.Tag) > 0
}

func (m SocketConfig_TProxyMode) IsEnabled() bool {
	return m != SocketConfig_Off
}
<commit_msg>warning message for global trasnport config<commit_after>package internet

import (
	"v2ray.com/core/common/serial"
	"v2ray.com/core/features"
)

type ConfigCreator func() interface{}

var (
	globalTransportConfigCreatorCache = make(map[string]ConfigCreator)
	globalTransportSettings           []*TransportConfig
)

const unknownProtocol = "unknown"

func transportProtocolToString(protocol TransportProtocol) string {
	switch protocol {
	case TransportProtocol_TCP:
		return "tcp"
	case TransportProtocol_UDP:
		return "udp"
	case TransportProtocol_HTTP:
		return "http"
	case TransportProtocol_MKCP:
		return "mkcp"
	case TransportProtocol_WebSocket:
		return "websocket"
	case TransportProtocol_DomainSocket:
		return "domainsocket"
	default:
		return unknownProtocol
	}
}

func RegisterProtocolConfigCreator(name string, creator ConfigCreator) error {
	if _, found := globalTransportConfigCreatorCache[name]; found {
		return newError("protocol ", name, " is already registered").AtError()
	}
	globalTransportConfigCreatorCache[name] = creator
	return nil
}

func CreateTransportConfig(name string) (interface{}, error) {
	creator, ok := globalTransportConfigCreatorCache[name]
	if !ok {
		return nil, newError("unknown transport protocol: ", name)
	}
	return creator(), nil
}

func (c *TransportConfig) GetTypedSettings() (interface{}, error) {
	return c.Settings.GetInstance()
}

func (c *TransportConfig) GetUnifiedProtocolName() string {
	if len(c.ProtocolName) > 0 {
		return c.ProtocolName
	}

	return transportProtocolToString(c.Protocol)
}

func (c *StreamConfig) GetEffectiveProtocol() string {
	if c == nil {
		return "tcp"
	}

	if len(c.ProtocolName) > 0 {
		return c.ProtocolName
	}

	return transportProtocolToString(c.Protocol)
}

func (c *StreamConfig) GetEffectiveTransportSettings() (interface{}, error) {
	protocol := c.GetEffectiveProtocol()
	return c.GetTransportSettingsFor(protocol)
}

func (c *StreamConfig) GetTransportSettingsFor(protocol string) (interface{}, error) {
	if c != nil {
		for _, settings := range c.TransportSettings {
			if settings.GetUnifiedProtocolName() == protocol {
				return settings.GetTypedSettings()
			}
		}
	}

	for _, settings := range globalTransportSettings {
		if settings.GetUnifiedProtocolName() == protocol {
			return settings.GetTypedSettings()
		}
	}

	return CreateTransportConfig(protocol)
}

func (c *StreamConfig) GetEffectiveSecuritySettings() (interface{}, error) {
	for _, settings := range c.SecuritySettings {
		if settings.Type == c.SecurityType {
			return settings.GetInstance()
		}
	}
	return serial.GetInstance(c.SecurityType)
}

func (c *StreamConfig) HasSecuritySettings() bool {
	return len(c.SecurityType) > 0
}

func ApplyGlobalTransportSettings(settings []*TransportConfig) error {
	features.PrintDeprecatedFeatureWarning("global transport settings")
	globalTransportSettings = settings
	return nil
}

func (c *ProxyConfig) HasTag() bool {
	return c != nil && len(c.Tag) > 0
}

func (m SocketConfig_TProxyMode) IsEnabled() bool {
	return m != SocketConfig_Off
}
<|endoftext|>


<commit_before>package openapi

import (
	"net/http"
	"strings"
)

var methods = []string{
	http.MethodGet,
	http.MethodPut,
	http.MethodPost,
	http.MethodDelete,
	http.MethodOptions,
	http.MethodHead,
	http.MethodPatch,
	http.MethodTrace,
}

// GetOperation returns a operation object associated with given method.
// The method is case-insensitive, converted to upper case in this function.
// If the method is invalid, this function will return nil.
func (pathItem *PathItem) GetOperation(method string) *Operation {
	switch strings.ToUpper(method) {
	case http.MethodGet:
		return pathItem.Get
	case http.MethodPost:
		return pathItem.Post
	case http.MethodPut:
		return pathItem.Put
	case http.MethodDelete:
		return pathItem.Delete
	case http.MethodOptions:
		return pathItem.Options
	case http.MethodHead:
		return pathItem.Head
	case http.MethodPatch:
		return pathItem.Patch
	case http.MethodTrace:
		return pathItem.Trace
	default:
		return nil
	}
}

// Operations returns a map containing operation object as a
// value associated with a HTTP method as a key.
func (pathItem PathItem) Operations() map[string]*Operation {
	ops := map[string]*Operation{}
	for _, method := range methods {
		if op := pathItem.GetOperation(method); op != nil {
			ops[method] = op
		}
	}
	return ops
}
<commit_msg>add comment<commit_after>package openapi

import (
	"net/http"
	"strings"
)

var methods = []string{
	http.MethodGet,
	http.MethodPut,
	http.MethodPost,
	http.MethodDelete,
	http.MethodOptions,
	http.MethodHead,
	http.MethodPatch,
	http.MethodTrace,
}

// GetOperation returns a operation object associated with given method.
// The method is case-insensitive, converted to upper case in this function.
// If the method is invalid, this function will return nil.
func (pathItem *PathItem) GetOperation(method string) *Operation {
	switch strings.ToUpper(method) {
	case http.MethodGet:
		return pathItem.Get
	case http.MethodPost:
		return pathItem.Post
	case http.MethodPut:
		return pathItem.Put
	case http.MethodDelete:
		return pathItem.Delete
	case http.MethodOptions:
		return pathItem.Options
	case http.MethodHead:
		return pathItem.Head
	case http.MethodPatch:
		return pathItem.Patch
	case http.MethodTrace:
		return pathItem.Trace
	default:
		return nil
	}
}

// Operations returns a map containing operation object as a
// value associated with a HTTP method as a key.
// If an operation is nil, it won't be added returned map, so
// the size of returned map is not same always.
func (pathItem PathItem) Operations() map[string]*Operation {
	ops := map[string]*Operation{}
	for _, method := range methods {
		if op := pathItem.GetOperation(method); op != nil {
			ops[method] = op
		}
	}
	return ops
}
<|endoftext|>


<commit_before>// Package cqrs provides a CQRS and Event Sourcing framework written in go influenced by the cqrs journey guide
//
// For a full guide visit http://github.com/andrewwebber/cqrs
//
// import "github.com/andrewwebber/cqrs"
//
// func NewAccount(firstName string, lastName string, emailAddress string, passwordHash []byte, initialBalance float64) *Account {
//   account := new(Account)
//   account.EventSourceBased = cqrs.NewEventSourceBased(account)
//
//   event := AccountCreatedEvent{firstName, lastName, emailAddress, passwordHash, initialBalance}
//   account.Update(event)
//   return account
// }

package cqrs
<commit_msg>Updated godoc<commit_after>// Package cqrs provides a CQRS and Event Sourcing framework written in go influenced by the cqrs journey guide
//
// For a full guide visit http://github.com/andrewwebber/cqrs
//
//  import "github.com/andrewwebber/cqrs"
//
//  func NewAccount(firstName string, lastName string, emailAddress string, passwordHash []byte, initialBalance float64) *Account {
//    account := new(Account)
//    account.EventSourceBased = cqrs.NewEventSourceBased(account)
//
//    event := AccountCreatedEvent{firstName, lastName, emailAddress, passwordHash, initialBalance}
//    account.Update(event)
//    return account
//  }
//
package cqrs
<|endoftext|>


<commit_before>/*
Package stringcase makes it so you can convert strings to different casing styles:

* lower case

* UPPER CASE

* Title Case

* camelCase

* PascalCase

* snake_case

* CONST_CASE

* property-case

* Header-Case

Samples
	s := stringcase.ToLowerCase("Hello world")
	// hello world

	s := stringcase.ToUpperCase("Hello world")
	// HELLO WORLD

	s := stringcase.ToTitleCase("Hello world")
	// Hello World

	s := stringcase.ToCamelCase("Hello world")
	// helloWorld

	s := stringcase.ToPascalCase("Hello world")
	// HelloWorld

	s := stringcase.ToSnakeCase("Hello world")
	// hello_world

	s := stringcase.ToConstCase("Hello world")
	// HELLO_WORLD

	s := stringcase.ToPropertyCase("Hello world")
	// hello-world

	s := stringcase.ToHeaderCase("Hello world")
	// Hello-World

*/
package stringcase
<commit_msg>tweaking style of docs<commit_after>/*
Package stringcase makes it so you can convert strings to different casing styles:

* lower case

* UPPER CASE

* Title Case

* camelCase

* PascalCase

* snake_case

* CONST_CASE

* property-case

* Header-Case

Samples
	s := stringcase.ToLowerCase("Hello world")
	// hello world
.
	s := stringcase.ToUpperCase("Hello world")
	// HELLO WORLD
.
	s := stringcase.ToTitleCase("Hello world")
	// Hello World
,
	s := stringcase.ToCamelCase("Hello world")
	// helloWorld
.
	s := stringcase.ToPascalCase("Hello world")
	// HelloWorld
.
	s := stringcase.ToSnakeCase("Hello world")
	// hello_world
.
	s := stringcase.ToConstCase("Hello world")
	// HELLO_WORLD
.
	s := stringcase.ToPropertyCase("Hello world")
	// hello-world
.
	s := stringcase.ToHeaderCase("Hello world")
	// Hello-World

*/
package stringcase
<|endoftext|>


<commit_before>package pipe

import (
	"os"
	"bufio"
	"io"
	"errors"
)

// used here to prevent os package import
var (
	Stdin = os.Stdin
	Stdout = os.Stdout
	Stderr = os.Stderr
)

// StdData used for as async communication channel
type StdDataChannel chan StdData
type StdData struct {
	Data []byte
	Err  error
}

// isNamedPipe checks whether valid pipe used
func isNamedPipe() bool {
	fileInfo, err := os.Stdin.Stat()
	if err != nil {
		return false
	}
	return ((fileInfo.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe)
}

// readChunk read from io.reader amount of bytes defined in size
// returns error if something is wrong or []byte if its fine
func readChunk(r io.Reader, size int) ([]byte, error) {
	buf := make([]byte, 0, size)
	n, err := r.Read(buf[:cap(buf)])
	if err != nil {
		return []byte{}, err
	}
	if n == 0 {
		if err == nil {
			return []byte{}, err
		}
		if err == io.EOF {
			return []byte{}, err
		}
	}
	buf = buf[:n]
	return buf, nil
}

// Read reads from io.reader one chunk as defined as bufSize
func Read(pipe io.Reader, bufSize int) ([]byte, error) {
	if !isNamedPipe() {
		return []byte{}, errors.New("Not Valid Named Pipe")
	}
	return readChunk(bufio.NewReader(pipe), bufSize)
}

// AsyncRead keeps reading from io.reader into buffer
// return data in stdData structure
// use in only with routine - ie go AsyncRead
func AsyncRead(pipe io.Reader, bufSize int, stdDataChan chan StdData) {
	if !isNamedPipe() {
		stdDataChan <- StdData{
			Err:errors.New("Not Valid Named Pipe"),
		}
	}
	r := bufio.NewReader(pipe)
	for {
		stdData := StdData{}
		data, err := readChunk(r, bufSize)
		if err != nil {
			stdData.Err = err
		}
		stdData.Data = data
		stdDataChan <- stdData
	}
}<commit_msg>golint && gofmt<commit_after>package pipe

import (
	"bufio"
	"errors"
	"io"
	"os"
)

// used here to prevent os package import
var (
	Stdin  = os.Stdin
	Stdout = os.Stdout
	Stderr = os.Stderr
)

// StdDataChannel used for as async communication channel
type StdDataChannel chan StdData

// StdData is used as channel structure
type StdData struct {
	Data []byte
	Err  error
}

// isNamedPipe checks whether valid pipe used
func isNamedPipe() bool {
	fileInfo, err := os.Stdin.Stat()
	if err != nil {
		return false
	}
	return ((fileInfo.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe)
}

// readChunk read from io.reader amount of bytes defined in size
// returns error if something is wrong or []byte if its fine
func readChunk(r io.Reader, size int) ([]byte, error) {
	buf := make([]byte, 0, size)
	n, err := r.Read(buf[:cap(buf)])
	if err != nil {
		return []byte{}, err
	}
	if n == 0 {
		if err == nil {
			return []byte{}, err
		}
		if err == io.EOF {
			return []byte{}, err
		}
	}
	buf = buf[:n]
	return buf, nil
}

// Read reads from io.reader one chunk as defined as bufSize
func Read(pipe io.Reader, bufSize int) ([]byte, error) {
	if !isNamedPipe() {
		return []byte{}, errors.New("Not Valid Named Pipe")
	}
	return readChunk(bufio.NewReader(pipe), bufSize)
}

// AsyncRead keeps reading from io.reader into buffer
// return data in stdData structure
// use in only with routine - ie go AsyncRead
func AsyncRead(pipe io.Reader, bufSize int, stdDataChan chan StdData) {
	if !isNamedPipe() {
		stdDataChan <- StdData{
			Err: errors.New("Not Valid Named Pipe"),
		}
	}
	r := bufio.NewReader(pipe)
	for {
		stdData := StdData{}
		data, err := readChunk(r, bufSize)
		if err != nil {
			stdData.Err = err
		}
		stdData.Data = data
		stdDataChan <- stdData
	}
}
<|endoftext|>


<commit_before>package overlay

import (
	"context"
	"fmt"
	"net"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/common"
)

const ovPeerTable = "overlay_peer_table"

type peerKey struct {
	peerIP  net.IP
	peerMac net.HardwareAddr
}

type peerEntry struct {
	eid        string
	vtep       net.IP
	peerIPMask net.IPMask
	inSandbox  bool
	isLocal    bool
}

type peerMap struct {
	mp map[string]peerEntry
	sync.Mutex
}

type peerNetworkMap struct {
	mp map[string]*peerMap
	sync.Mutex
}

func (pKey peerKey) String() string {
	return fmt.Sprintf("%s %s", pKey.peerIP, pKey.peerMac)
}

func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error {
	ipB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerIP = net.ParseIP(string(ipB))

	macB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerMac, err = net.ParseMAC(string(macB))
	if err != nil {
		return err
	}

	return nil
}

func (d *driver) peerDbWalk(f func(string, *peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	nids := []string{}
	for nid := range d.peerDb.mp {
		nids = append(nids, nid)
	}
	d.peerDb.Unlock()

	for _, nid := range nids {
		d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
			return f(nid, pKey, pEntry)
		})
	}
	return nil
}

func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	d.peerDb.Unlock()

	if !ok {
		return nil
	}

	mp := map[string]peerEntry{}

	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		mp[pKeyStr] = pEntry
	}
	pMap.Unlock()

	for pKeyStr, pEntry := range mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			logrus.Warnf("Peer key scan on network %s failed: %v", nid, err)
		}
		if f(&pKey, &pEntry) {
			return nil
		}
	}

	return nil
}

func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
	var (
		peerMac    net.HardwareAddr
		vtep       net.IP
		peerIPMask net.IPMask
		found      bool
	)

	err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
		if pKey.peerIP.Equal(peerIP) {
			peerMac = pKey.peerMac
			peerIPMask = pEntry.peerIPMask
			vtep = pEntry.vtep
			found = true
			return found
		}

		return found
	})

	if err != nil {
		return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
	}

	if !found {
		return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
	}

	return peerMac, peerIPMask, vtep, nil
}

func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, isLocal bool) {

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.mp[nid] = &peerMap{
			mp: make(map[string]peerEntry),
		}

		pMap = d.peerDb.mp[nid]
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pEntry := peerEntry{
		eid:        eid,
		vtep:       vtep,
		peerIPMask: peerIPMask,
		isLocal:    isLocal,
	}

	pMap.Lock()
	pMap.mp[pKey.String()] = pEntry
	pMap.Unlock()
}

func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP) peerEntry {

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return peerEntry{}
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pMap.Lock()

	pEntry, ok := pMap.mp[pKey.String()]
	if ok {
		// Mismatched endpoint ID(possibly outdated). Do not
		// delete peerdb
		if pEntry.eid != eid {
			pMap.Unlock()
			return pEntry
		}
	}

	delete(pMap.mp, pKey.String())
	pMap.Unlock()

	return pEntry
}

// The overlay uses a lazy initialization approach, this means that when a network is created
// and the driver registered the overlay does not allocate resources till the moment that a
// sandbox is actually created.
// At the moment of this call, that happens when a sandbox is initialized, is possible that
// networkDB has already delivered some events of peers already available on remote nodes,
// these peers are saved into the peerDB and this function is used to properly configure
// the network sandbox with all those peers that got previously notified.
// Note also that this method sends a single message on the channel and the go routine on the
// other side, will atomically loop on the whole table of peers and will program their state
// in one single atomic operation. This is fundamental to guarantee consistency, and avoid that
// new peerAdd or peerDelete gets reordered during the sandbox init.
func (d *driver) initSandboxPeerDB(nid string) {
	d.peerInit(nid)
}

type peerOperationType int32

const (
	peerOperationINIT peerOperationType = iota
	peerOperationADD
	peerOperationDELETE
)

type peerOperation struct {
	opType     peerOperationType
	networkID  string
	endpointID string
	peerIP     net.IP
	peerIPMask net.IPMask
	peerMac    net.HardwareAddr
	vtepIP     net.IP
	updateDB   bool
	l2Miss     bool
	l3Miss     bool
	localPeer  bool
	callerName string
}

func (d *driver) peerOpRoutine(ctx context.Context, ch chan *peerOperation) {
	var err error
	for {
		select {
		case <-ctx.Done():
			return
		case op := <-ch:
			switch op.opType {
			case peerOperationINIT:
				err = d.peerInitOp(op.networkID)
			case peerOperationADD:
				err = d.peerAddOp(op.networkID, op.endpointID, op.peerIP, op.peerIPMask, op.peerMac, op.vtepIP, op.updateDB, op.l2Miss, op.l3Miss, op.localPeer)
			case peerOperationDELETE:
				err = d.peerDeleteOp(op.networkID, op.endpointID, op.peerIP, op.peerIPMask, op.peerMac, op.vtepIP, op.localPeer)
			}
			if err != nil {
				logrus.Warnf("Peer operation failed:%s op:%v", err, op)
			}
		}
	}
}

func (d *driver) peerInit(nid string) {
	callerName := common.CallerName(1)
	d.peerOpCh <- &peerOperation{
		opType:     peerOperationINIT,
		networkID:  nid,
		callerName: callerName,
	}
}

func (d *driver) peerInitOp(nid string) error {
	return d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
		// Local entries do not need to be added
		if pEntry.isLocal {
			return false
		}

		d.peerAddOp(nid, pEntry.eid, pKey.peerIP, pEntry.peerIPMask, pKey.peerMac, pEntry.vtep, false, false, false, false)
		// return false to loop on all entries
		return false
	})
}

func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb, l2Miss, l3Miss, localPeer bool) {
	callerName := common.CallerName(1)
	d.peerOpCh <- &peerOperation{
		opType:     peerOperationADD,
		networkID:  nid,
		endpointID: eid,
		peerIP:     peerIP,
		peerIPMask: peerIPMask,
		peerMac:    peerMac,
		vtepIP:     vtep,
		updateDB:   updateDb,
		l2Miss:     l2Miss,
		l3Miss:     l3Miss,
		localPeer:  localPeer,
		callerName: callerName,
	}
}

func (d *driver) peerAddOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb, l2Miss, l3Miss, updateOnlyDB bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	if updateDb {
		d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false)
		if updateOnlyDB {
			return nil
		}
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		// We are hitting this case for all the events that are arriving before that the sandbox
		// is being created. The peer got already added into the database and the sanbox init will
		// call the peerDbUpdateSandbox that will configure all these peers from the database
		return nil
	}

	IP := &net.IPNet{
		IP:   peerIP,
		Mask: peerIPMask,
	}

	s := n.getSubnetforIP(IP)
	if s == nil {
		return fmt.Errorf("couldn't find the subnet %q in network %q", IP.String(), n.id)
	}

	if err := n.obtainVxlanID(s); err != nil {
		return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
	}

	if err := n.joinSubnetSandbox(s, false); err != nil {
		return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
	}

	if err := d.checkEncryption(nid, vtep, n.vxlanID(s), false, true); err != nil {
		logrus.Warn(err)
	}

	// Add neighbor entry for the peer IP
	if err := sbox.AddNeighbor(peerIP, peerMac, l3Miss, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
		return fmt.Errorf("could not add neighbor entry into the sandbox: %v", err)
	}

	// Add fdb entry to the bridge for the peer mac
	if err := sbox.AddNeighbor(vtep, peerMac, l2Miss, sbox.NeighborOptions().LinkName(s.vxlanName),
		sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil {
		return fmt.Errorf("could not add fdb entry into the sandbox: %v", err)
	}

	return nil
}

func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) {
	callerName := common.CallerName(1)
	d.peerOpCh <- &peerOperation{
		opType:     peerOperationDELETE,
		networkID:  nid,
		endpointID: eid,
		peerIP:     peerIP,
		peerIPMask: peerIPMask,
		peerMac:    peerMac,
		vtepIP:     vtep,
		updateDB:   updateDb,
		callerName: callerName,
	}
}

func (d *driver) peerDeleteOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	var pEntry peerEntry
	if updateDb {
		pEntry = d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	// Delete fdb entry to the bridge for the peer mac only if the
	// entry existed in local peerdb. If it is a stale delete
	// request, still call DeleteNeighbor but only to cleanup any
	// leftover sandbox neighbor cache and not actually delete the
	// kernel state.
	if (eid == pEntry.eid && vtep.Equal(pEntry.vtep)) ||
		(eid != pEntry.eid && !vtep.Equal(pEntry.vtep)) {
		if err := sbox.DeleteNeighbor(vtep, peerMac,
			eid == pEntry.eid && vtep.Equal(pEntry.vtep)); err != nil {
			return fmt.Errorf("could not delete fdb entry into the sandbox: %v", err)
		}
	}

	// Delete neighbor entry for the peer IP
	if eid == pEntry.eid {
		if err := sbox.DeleteNeighbor(peerIP, peerMac, true); err != nil {
			return fmt.Errorf("could not delete neighbor entry into the sandbox: %v", err)
		}
	}

	if err := d.checkEncryption(nid, vtep, 0, false, false); err != nil {
		logrus.Warn(err)
	}

	return nil
}

func (d *driver) pushLocalDb() {
	d.peerDbWalk(func(nid string, pKey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			d.pushLocalEndpointEvent("join", nid, pEntry.eid)
		}
		return false
	})
}

func (d *driver) peerDBUpdateSelf() {
	d.peerDbWalk(func(nid string, pkey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			pEntry.vtep = net.ParseIP(d.advertiseAddress)
		}
		return false
	})
}
<commit_msg>PeerDbDelete was passing the wrong field<commit_after>package overlay

import (
	"context"
	"fmt"
	"net"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/common"
)

const ovPeerTable = "overlay_peer_table"

type peerKey struct {
	peerIP  net.IP
	peerMac net.HardwareAddr
}

type peerEntry struct {
	eid        string
	vtep       net.IP
	peerIPMask net.IPMask
	inSandbox  bool
	isLocal    bool
}

type peerMap struct {
	mp map[string]peerEntry
	sync.Mutex
}

type peerNetworkMap struct {
	mp map[string]*peerMap
	sync.Mutex
}

func (pKey peerKey) String() string {
	return fmt.Sprintf("%s %s", pKey.peerIP, pKey.peerMac)
}

func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error {
	ipB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerIP = net.ParseIP(string(ipB))

	macB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerMac, err = net.ParseMAC(string(macB))
	if err != nil {
		return err
	}

	return nil
}

func (d *driver) peerDbWalk(f func(string, *peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	nids := []string{}
	for nid := range d.peerDb.mp {
		nids = append(nids, nid)
	}
	d.peerDb.Unlock()

	for _, nid := range nids {
		d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
			return f(nid, pKey, pEntry)
		})
	}
	return nil
}

func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	d.peerDb.Unlock()

	if !ok {
		return nil
	}

	mp := map[string]peerEntry{}

	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		mp[pKeyStr] = pEntry
	}
	pMap.Unlock()

	for pKeyStr, pEntry := range mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			logrus.Warnf("Peer key scan on network %s failed: %v", nid, err)
		}
		if f(&pKey, &pEntry) {
			return nil
		}
	}

	return nil
}

func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
	var (
		peerMac    net.HardwareAddr
		vtep       net.IP
		peerIPMask net.IPMask
		found      bool
	)

	err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
		if pKey.peerIP.Equal(peerIP) {
			peerMac = pKey.peerMac
			peerIPMask = pEntry.peerIPMask
			vtep = pEntry.vtep
			found = true
			return found
		}

		return found
	})

	if err != nil {
		return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
	}

	if !found {
		return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
	}

	return peerMac, peerIPMask, vtep, nil
}

func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, isLocal bool) {

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.mp[nid] = &peerMap{
			mp: make(map[string]peerEntry),
		}

		pMap = d.peerDb.mp[nid]
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pEntry := peerEntry{
		eid:        eid,
		vtep:       vtep,
		peerIPMask: peerIPMask,
		isLocal:    isLocal,
	}

	pMap.Lock()
	pMap.mp[pKey.String()] = pEntry
	pMap.Unlock()
}

func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP) peerEntry {

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return peerEntry{}
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pMap.Lock()

	pEntry, ok := pMap.mp[pKey.String()]
	if ok {
		// Mismatched endpoint ID(possibly outdated). Do not
		// delete peerdb
		if pEntry.eid != eid {
			pMap.Unlock()
			return pEntry
		}
	}

	delete(pMap.mp, pKey.String())
	pMap.Unlock()

	return pEntry
}

// The overlay uses a lazy initialization approach, this means that when a network is created
// and the driver registered the overlay does not allocate resources till the moment that a
// sandbox is actually created.
// At the moment of this call, that happens when a sandbox is initialized, is possible that
// networkDB has already delivered some events of peers already available on remote nodes,
// these peers are saved into the peerDB and this function is used to properly configure
// the network sandbox with all those peers that got previously notified.
// Note also that this method sends a single message on the channel and the go routine on the
// other side, will atomically loop on the whole table of peers and will program their state
// in one single atomic operation. This is fundamental to guarantee consistency, and avoid that
// new peerAdd or peerDelete gets reordered during the sandbox init.
func (d *driver) initSandboxPeerDB(nid string) {
	d.peerInit(nid)
}

type peerOperationType int32

const (
	peerOperationINIT peerOperationType = iota
	peerOperationADD
	peerOperationDELETE
)

type peerOperation struct {
	opType     peerOperationType
	networkID  string
	endpointID string
	peerIP     net.IP
	peerIPMask net.IPMask
	peerMac    net.HardwareAddr
	vtepIP     net.IP
	updateDB   bool
	l2Miss     bool
	l3Miss     bool
	localPeer  bool
	callerName string
}

func (d *driver) peerOpRoutine(ctx context.Context, ch chan *peerOperation) {
	var err error
	for {
		select {
		case <-ctx.Done():
			return
		case op := <-ch:
			switch op.opType {
			case peerOperationINIT:
				err = d.peerInitOp(op.networkID)
			case peerOperationADD:
				err = d.peerAddOp(op.networkID, op.endpointID, op.peerIP, op.peerIPMask, op.peerMac, op.vtepIP, op.updateDB, op.l2Miss, op.l3Miss, op.localPeer)
			case peerOperationDELETE:
				err = d.peerDeleteOp(op.networkID, op.endpointID, op.peerIP, op.peerIPMask, op.peerMac, op.vtepIP, op.updateDB)
			}
			if err != nil {
				logrus.Warnf("Peer operation failed:%s op:%v", err, op)
			}
		}
	}
}

func (d *driver) peerInit(nid string) {
	callerName := common.CallerName(1)
	d.peerOpCh <- &peerOperation{
		opType:     peerOperationINIT,
		networkID:  nid,
		callerName: callerName,
	}
}

func (d *driver) peerInitOp(nid string) error {
	return d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
		// Local entries do not need to be added
		if pEntry.isLocal {
			return false
		}

		d.peerAddOp(nid, pEntry.eid, pKey.peerIP, pEntry.peerIPMask, pKey.peerMac, pEntry.vtep, false, false, false, false)
		// return false to loop on all entries
		return false
	})
}

func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb, l2Miss, l3Miss, localPeer bool) {
	callerName := common.CallerName(1)
	d.peerOpCh <- &peerOperation{
		opType:     peerOperationADD,
		networkID:  nid,
		endpointID: eid,
		peerIP:     peerIP,
		peerIPMask: peerIPMask,
		peerMac:    peerMac,
		vtepIP:     vtep,
		updateDB:   updateDb,
		l2Miss:     l2Miss,
		l3Miss:     l3Miss,
		localPeer:  localPeer,
		callerName: callerName,
	}
}

func (d *driver) peerAddOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb, l2Miss, l3Miss, updateOnlyDB bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	if updateDb {
		d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false)
		if updateOnlyDB {
			return nil
		}
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		// We are hitting this case for all the events that are arriving before that the sandbox
		// is being created. The peer got already added into the database and the sanbox init will
		// call the peerDbUpdateSandbox that will configure all these peers from the database
		return nil
	}

	IP := &net.IPNet{
		IP:   peerIP,
		Mask: peerIPMask,
	}

	s := n.getSubnetforIP(IP)
	if s == nil {
		return fmt.Errorf("couldn't find the subnet %q in network %q", IP.String(), n.id)
	}

	if err := n.obtainVxlanID(s); err != nil {
		return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
	}

	if err := n.joinSubnetSandbox(s, false); err != nil {
		return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
	}

	if err := d.checkEncryption(nid, vtep, n.vxlanID(s), false, true); err != nil {
		logrus.Warn(err)
	}

	// Add neighbor entry for the peer IP
	if err := sbox.AddNeighbor(peerIP, peerMac, l3Miss, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
		return fmt.Errorf("could not add neighbor entry into the sandbox: %v", err)
	}

	// Add fdb entry to the bridge for the peer mac
	if err := sbox.AddNeighbor(vtep, peerMac, l2Miss, sbox.NeighborOptions().LinkName(s.vxlanName),
		sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil {
		return fmt.Errorf("could not add fdb entry into the sandbox: %v", err)
	}

	return nil
}

func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) {
	callerName := common.CallerName(1)
	d.peerOpCh <- &peerOperation{
		opType:     peerOperationDELETE,
		networkID:  nid,
		endpointID: eid,
		peerIP:     peerIP,
		peerIPMask: peerIPMask,
		peerMac:    peerMac,
		vtepIP:     vtep,
		updateDB:   updateDb,
		callerName: callerName,
	}
}

func (d *driver) peerDeleteOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	var pEntry peerEntry
	if updateDb {
		pEntry = d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	// Delete fdb entry to the bridge for the peer mac only if the
	// entry existed in local peerdb. If it is a stale delete
	// request, still call DeleteNeighbor but only to cleanup any
	// leftover sandbox neighbor cache and not actually delete the
	// kernel state.
	if (eid == pEntry.eid && vtep.Equal(pEntry.vtep)) ||
		(eid != pEntry.eid && !vtep.Equal(pEntry.vtep)) {
		if err := sbox.DeleteNeighbor(vtep, peerMac,
			eid == pEntry.eid && vtep.Equal(pEntry.vtep)); err != nil {
			return fmt.Errorf("could not delete fdb entry into the sandbox: %v", err)
		}
	}

	// Delete neighbor entry for the peer IP
	if eid == pEntry.eid {
		if err := sbox.DeleteNeighbor(peerIP, peerMac, true); err != nil {
			return fmt.Errorf("could not delete neighbor entry into the sandbox: %v", err)
		}
	}

	if err := d.checkEncryption(nid, vtep, 0, false, false); err != nil {
		logrus.Warn(err)
	}

	return nil
}

func (d *driver) pushLocalDb() {
	d.peerDbWalk(func(nid string, pKey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			d.pushLocalEndpointEvent("join", nid, pEntry.eid)
		}
		return false
	})
}

func (d *driver) peerDBUpdateSelf() {
	d.peerDbWalk(func(nid string, pkey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			pEntry.vtep = net.ParseIP(d.advertiseAddress)
		}
		return false
	})
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/fzzbt/radix/redis"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"syscall"
	"time"
	"crypto/tls"
	"image"
	"image/png"
	_ "image/gif"
	_ "image/jpeg"
	"bytes"
	"math"
)

// HTTP headers struct
type Headers struct {
	contentType  string
	lastModified string
	cacheControl string
}

// The URL for the default avatar
const defaultAvatarUrl = "//linuxfr.org/images/default-avatar.png"

// The maximal size for an image is 5MB
const maxSize = 5 * (1 << 20)

// The directory for caching files
var directory string

// The connection to redis
var connection *redis.Client

// Check if an URL is valid and not temporary in error
func urlStatus(uri string) error {

	str, err := connection.Get("img/err/" + uri).Str()
	if err == nil {
		return errors.New(str)
	}

	return nil
}

// Generate a key for cache from a string
func generateKeyForCache(s string) string {
	h := sha1.New()
	io.WriteString(h, s)
	key := h.Sum(nil)

	// Use 3 levels of hasing to avoid having too many files in the same directory
	return fmt.Sprintf("%s/%x/%x/%x/%x", directory, key[0:1], key[1:2], key[2:3], key[3:])
}

// Fetch image from cache
func fetchImageFromCache(uri, variation string) (headers Headers, body []byte, ok bool) {
	ok = false

	contentType, err := connection.Hget("img/"+variation+"/"+uri, "type").Str()
	if err != nil {
		return
	}

	filename := generateKeyForCache(uri)
	stat, err := os.Stat(filename)
	if err != nil {
		return
	}

	headers.contentType = contentType
	headers.lastModified = stat.ModTime().Format(time.RFC1123)

	body, err = ioutil.ReadFile(filename)
	ok = err == nil

	return
}

// Save the body and the content-type header in cache
func saveImageInCache(uri, variation string, headers Headers, body []byte) {
	go func() {
		filename := generateKeyForCache(variation+":"+uri)
		dirname := path.Dir(filename)
		err := os.MkdirAll(dirname, 0755)
		if err != nil {
			return
		}

		// Save the body on disk
		err = ioutil.WriteFile(filename, body, 0644)
		if err != nil {
			log.Printf("Error while writing %s\n", filename)
			return
		}

		// And other infos in redis
		connection.Hset("img/"+variation+"/"+uri, "type", headers.contentType)
	}()
}

// Save the error in redis for 10 minutes
func saveErrorInCache(uri string, err error) {
	go func() {
		connection.Set("img/err/"+uri, err.Error())
		connection.Expire("img/err/"+uri, 600)
	}()
}

// Fetch the image from the distant server
func fetchImageFromServer(uri string) (headers Headers, body []byte, err error) {
	// Accepts any certificate in HTTPS
	cfg := &tls.Config{InsecureSkipVerify: true}
	tr := &http.Transport{TLSClientConfig: cfg}
	client := &http.Client{Transport: tr}
	res, err := client.Get(uri)
	if err != nil {
		return
	}
	if res.StatusCode != 200 {
		log.Printf("Status code of %s is: %d\n", uri, res.StatusCode)
		err = errors.New("Unexpected status code")
		saveErrorInCache(uri, err)
		return
	}

	defer res.Body.Close()
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}
	if res.ContentLength > maxSize {
		log.Printf("Exceeded max size for %s: %d\n", uri, res.ContentLength)
		err = errors.New("Exceeded max size")
		saveErrorInCache(uri, err)
		return
	}
	contentType := res.Header.Get("Content-Type")
	if contentType[0:5] != "image" {
		log.Printf("%s has an invalid content-type: %s\n", uri, contentType)
		err = errors.New("Invalid content-type")
		saveErrorInCache(uri, err)
		return
	}
	log.Printf("Fetch %s (%s)\n", uri, contentType)

	headers.contentType = contentType
	headers.lastModified = time.Now().Format(time.RFC1123)
	if urlStatus(uri) == nil {
		saveImageInCache(uri, "orig", headers, body)
	}
	return
}

// Fetch image from cache if available, or from the server
func fetchImage(uri string) (headers Headers, body []byte, err error) {
	err = urlStatus(uri)
	if err != nil {
		return
	}

	headers, body, ok := fetchImageFromCache(uri, "orig")
	if !ok {
		headers, body, err = fetchImageFromServer(uri)
	}

	headers.cacheControl = "public, max-age=600"

	return
}

func fetchResizedImage(uri string, width, height int) (headers Headers, body []byte, err error) {

	variation := fmt.Sprintf("resize/%d/%d", width, height)
	if err != nil {
		return
	}

	headers, body, ok := fetchImageFromCache(uri, variation)

	if ok {
		return
	}

	headers, body, err = fetchImage(uri)
	if err != nil {
		return
	}

	headers, body, err = resizeImage(uri, string(body), headers, width, height)
	if (err != nil) {
		return
	}

	saveImageInCache(uri, variation, headers, body)

	return
}

func resizeImage(uri, origBody string, origHeaders Headers, width, height int) (headers Headers, body []byte, err error) {

	m, _, err := image.Decode(strings.NewReader(origBody))

	if err != nil {
		return
	}

	bounds := m.Bounds()
	origWidth, origHeight := bounds.Dx(), bounds.Dy()

	if width >= origWidth && height >= origHeight {
		headers = origHeaders
		body = []byte(origBody)
		return
	}

	ratio := math.Max(float64(origWidth), float64(origHeight)) / math.Min(float64(width), float64(height))

	newWidth := int(math.Floor(float64(origWidth) / ratio))
	newHeight := int(math.Floor(float64(origHeight) / ratio))

	log.Printf("Resize: %s to %vx%v: orig: %vx%v; new: %vx%v; ratio: %v\n", uri, width, height, origWidth, origHeight, newWidth, newHeight, ratio)

	m = Resample(m, m.Bounds(), newWidth, newHeight)
	writter := new(bytes.Buffer)

	err = png.Encode(writter, m)

	if err != nil {
		return
	}

	body = []byte(writter.String())

	headers = origHeaders
	headers.contentType = "image/png"

	return
}

// Receive an HTTP request, fetch the image and respond with it
func Image(w http.ResponseWriter, r *http.Request, fn func()) {
	query := r.URL.Query()
	encoded_url := query.Get(":encoded_url")

	strWidth, strHeight := query.Get(":width"), query.Get(":height")

	width, err := strconv.ParseInt(strWidth, 10, 32)
	if err != nil {
		log.Printf("Invalid width %s\n", strWidth)
		http.Error(w, "Invalid parameters", 400)
		return
	}

	height, err := strconv.ParseInt(strHeight, 10, 32)
	if err != nil {
		log.Printf("Invalid width %s\n", strHeight)
		http.Error(w, "Invalid parameters", 400)
		return
	}

	if (width * height > maxSize) {
		log.Printf("Requested resized image exceeds max size\n")
		http.Error(w, "Requested resized image exceeds max size", 400)
		return
	}

	chars, err := hex.DecodeString(encoded_url)
	if err != nil {
		log.Printf("Invalid URL %s\n", encoded_url)
		http.Error(w, "Invalid parameters", 400)
		return
	}
	uri := string(chars)

	headers, body, err := fetchResizedImage(uri, int(width), int(height))
	if err != nil {
		fn()
		return
	}

	if headers.lastModified == r.Header.Get("If-Modified-Since") {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	w.Header().Add("Content-Type", headers.contentType)
	w.Header().Add("Last-Modified", headers.lastModified)
	w.Header().Add("Cache-Control", headers.cacheControl)
	w.Write(body)
}

// Receive an HTTP request for an image and respond with it
func Img(w http.ResponseWriter, r *http.Request) {
	fn := func() {
		http.NotFound(w, r)
	}
	Image(w, r, fn)
}

// Returns 200 OK if the server is running (for monitoring)
func Status(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "OK")
}

func main() {
	// Parse the command-line
	var addr string
	var logs string
	var conn string
	flag.StringVar(&addr, "a", "127.0.0.1:8000", "Bind to this address:port")
	flag.StringVar(&logs, "l", "-", "Use this file for logs")
	flag.StringVar(&conn, "r", "localhost:6379/0", "The redis database to use for caching meta")
	flag.StringVar(&directory, "d", "cache", "The directory for the caching files")
	flag.Parse()

	// Logging
	if logs != "-" {
		f, err := os.OpenFile(logs, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatal("OpenFile: ", err)
		}
		syscall.Dup2(int(f.Fd()), int(os.Stdout.Fd()))
		syscall.Dup2(int(f.Fd()), int(os.Stderr.Fd()))
	}

	// Redis
	parts := strings.Split(conn, "/")
	host := parts[0]
	db := 0
	if len(parts) >= 2 {
		db, _ = strconv.Atoi(parts[1])
	}
	cfg := redis.Config{Database: db, Address: host, PoolCapacity: 4}
	connection = redis.NewClient(cfg)
	defer connection.Close()

	// Routing
	m := pat.New()
	m.Get("/status", http.HandlerFunc(Status))
	m.Get("/resize/:encoded_url/:width/:height", http.HandlerFunc(Img))
	http.Handle("/", m)

	// Start the HTTP server
	log.Printf("Listening on http://%s/\n", addr)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Invalid import fixed.<commit_after>package main

import (
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/fzzy/radix/redis"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"syscall"
	"time"
	"crypto/tls"
	"image"
	"image/png"
	_ "image/gif"
	_ "image/jpeg"
	"bytes"
	"math"
)

// HTTP headers struct
type Headers struct {
	contentType  string
	lastModified string
	cacheControl string
}

// The URL for the default avatar
const defaultAvatarUrl = "//linuxfr.org/images/default-avatar.png"

// The maximal size for an image is 5MB
const maxSize = 5 * (1 << 20)

// The directory for caching files
var directory string

// The connection to redis
var connection *redis.Client

// Check if an URL is valid and not temporary in error
func urlStatus(uri string) error {

	str, err := connection.Get("img/err/" + uri).Str()
	if err == nil {
		return errors.New(str)
	}

	return nil
}

// Generate a key for cache from a string
func generateKeyForCache(s string) string {
	h := sha1.New()
	io.WriteString(h, s)
	key := h.Sum(nil)

	// Use 3 levels of hasing to avoid having too many files in the same directory
	return fmt.Sprintf("%s/%x/%x/%x/%x", directory, key[0:1], key[1:2], key[2:3], key[3:])
}

// Fetch image from cache
func fetchImageFromCache(uri, variation string) (headers Headers, body []byte, ok bool) {
	ok = false

	contentType, err := connection.Hget("img/"+variation+"/"+uri, "type").Str()
	if err != nil {
		return
	}

	filename := generateKeyForCache(uri)
	stat, err := os.Stat(filename)
	if err != nil {
		return
	}

	headers.contentType = contentType
	headers.lastModified = stat.ModTime().Format(time.RFC1123)

	body, err = ioutil.ReadFile(filename)
	ok = err == nil

	return
}

// Save the body and the content-type header in cache
func saveImageInCache(uri, variation string, headers Headers, body []byte) {
	go func() {
		filename := generateKeyForCache(variation+":"+uri)
		dirname := path.Dir(filename)
		err := os.MkdirAll(dirname, 0755)
		if err != nil {
			return
		}

		// Save the body on disk
		err = ioutil.WriteFile(filename, body, 0644)
		if err != nil {
			log.Printf("Error while writing %s\n", filename)
			return
		}

		// And other infos in redis
		connection.Hset("img/"+variation+"/"+uri, "type", headers.contentType)
	}()
}

// Save the error in redis for 10 minutes
func saveErrorInCache(uri string, err error) {
	go func() {
		connection.Set("img/err/"+uri, err.Error())
		connection.Expire("img/err/"+uri, 600)
	}()
}

// Fetch the image from the distant server
func fetchImageFromServer(uri string) (headers Headers, body []byte, err error) {
	// Accepts any certificate in HTTPS
	cfg := &tls.Config{InsecureSkipVerify: true}
	tr := &http.Transport{TLSClientConfig: cfg}
	client := &http.Client{Transport: tr}
	res, err := client.Get(uri)
	if err != nil {
		return
	}
	if res.StatusCode != 200 {
		log.Printf("Status code of %s is: %d\n", uri, res.StatusCode)
		err = errors.New("Unexpected status code")
		saveErrorInCache(uri, err)
		return
	}

	defer res.Body.Close()
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}
	if res.ContentLength > maxSize {
		log.Printf("Exceeded max size for %s: %d\n", uri, res.ContentLength)
		err = errors.New("Exceeded max size")
		saveErrorInCache(uri, err)
		return
	}
	contentType := res.Header.Get("Content-Type")
	if contentType[0:5] != "image" {
		log.Printf("%s has an invalid content-type: %s\n", uri, contentType)
		err = errors.New("Invalid content-type")
		saveErrorInCache(uri, err)
		return
	}
	log.Printf("Fetch %s (%s)\n", uri, contentType)

	headers.contentType = contentType
	headers.lastModified = time.Now().Format(time.RFC1123)
	if urlStatus(uri) == nil {
		saveImageInCache(uri, "orig", headers, body)
	}
	return
}

// Fetch image from cache if available, or from the server
func fetchImage(uri string) (headers Headers, body []byte, err error) {
	err = urlStatus(uri)
	if err != nil {
		return
	}

	headers, body, ok := fetchImageFromCache(uri, "orig")
	if !ok {
		headers, body, err = fetchImageFromServer(uri)
	}

	headers.cacheControl = "public, max-age=600"

	return
}

func fetchResizedImage(uri string, width, height int) (headers Headers, body []byte, err error) {

	variation := fmt.Sprintf("resize/%d/%d", width, height)
	if err != nil {
		return
	}

	headers, body, ok := fetchImageFromCache(uri, variation)

	if ok {
		return
	}

	headers, body, err = fetchImage(uri)
	if err != nil {
		return
	}

	headers, body, err = resizeImage(uri, string(body), headers, width, height)
	if (err != nil) {
		return
	}

	saveImageInCache(uri, variation, headers, body)

	return
}

func resizeImage(uri, origBody string, origHeaders Headers, width, height int) (headers Headers, body []byte, err error) {

	m, _, err := image.Decode(strings.NewReader(origBody))

	if err != nil {
		return
	}

	bounds := m.Bounds()
	origWidth, origHeight := bounds.Dx(), bounds.Dy()

	if width >= origWidth && height >= origHeight {
		headers = origHeaders
		body = []byte(origBody)
		return
	}

	ratio := math.Max(float64(origWidth), float64(origHeight)) / math.Min(float64(width), float64(height))

	newWidth := int(math.Floor(float64(origWidth) / ratio))
	newHeight := int(math.Floor(float64(origHeight) / ratio))

	log.Printf("Resize: %s to %vx%v: orig: %vx%v; new: %vx%v; ratio: %v\n", uri, width, height, origWidth, origHeight, newWidth, newHeight, ratio)

	m = Resample(m, m.Bounds(), newWidth, newHeight)
	writter := new(bytes.Buffer)

	err = png.Encode(writter, m)

	if err != nil {
		return
	}

	body = []byte(writter.String())

	headers = origHeaders
	headers.contentType = "image/png"

	return
}

// Receive an HTTP request, fetch the image and respond with it
func Image(w http.ResponseWriter, r *http.Request, fn func()) {
	query := r.URL.Query()
	encoded_url := query.Get(":encoded_url")

	strWidth, strHeight := query.Get(":width"), query.Get(":height")

	width, err := strconv.ParseInt(strWidth, 10, 32)
	if err != nil {
		log.Printf("Invalid width %s\n", strWidth)
		http.Error(w, "Invalid parameters", 400)
		return
	}

	height, err := strconv.ParseInt(strHeight, 10, 32)
	if err != nil {
		log.Printf("Invalid width %s\n", strHeight)
		http.Error(w, "Invalid parameters", 400)
		return
	}

	if (width * height > maxSize) {
		log.Printf("Requested resized image exceeds max size\n")
		http.Error(w, "Requested resized image exceeds max size", 400)
		return
	}

	chars, err := hex.DecodeString(encoded_url)
	if err != nil {
		log.Printf("Invalid URL %s\n", encoded_url)
		http.Error(w, "Invalid parameters", 400)
		return
	}
	uri := string(chars)

	headers, body, err := fetchResizedImage(uri, int(width), int(height))
	if err != nil {
		fn()
		return
	}

	if headers.lastModified == r.Header.Get("If-Modified-Since") {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	w.Header().Add("Content-Type", headers.contentType)
	w.Header().Add("Last-Modified", headers.lastModified)
	w.Header().Add("Cache-Control", headers.cacheControl)
	w.Write(body)
}

// Receive an HTTP request for an image and respond with it
func Img(w http.ResponseWriter, r *http.Request) {
	fn := func() {
		http.NotFound(w, r)
	}
	Image(w, r, fn)
}

// Returns 200 OK if the server is running (for monitoring)
func Status(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "OK")
}

func main() {
	// Parse the command-line
	var addr string
	var logs string
	var conn string
	flag.StringVar(&addr, "a", "127.0.0.1:8000", "Bind to this address:port")
	flag.StringVar(&logs, "l", "-", "Use this file for logs")
	flag.StringVar(&conn, "r", "localhost:6379/0", "The redis database to use for caching meta")
	flag.StringVar(&directory, "d", "cache", "The directory for the caching files")
	flag.Parse()

	// Logging
	if logs != "-" {
		f, err := os.OpenFile(logs, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatal("OpenFile: ", err)
		}
		syscall.Dup2(int(f.Fd()), int(os.Stdout.Fd()))
		syscall.Dup2(int(f.Fd()), int(os.Stderr.Fd()))
	}

	// Redis
	parts := strings.Split(conn, "/")
	host := parts[0]
	db := 0
	if len(parts) >= 2 {
		db, _ = strconv.Atoi(parts[1])
	}
	cfg := redis.Config{Database: db, Address: host, PoolCapacity: 4}
	connection = redis.NewClient(cfg)
	defer connection.Close()

	// Routing
	m := pat.New()
	m.Get("/status", http.HandlerFunc(Status))
	m.Get("/resize/:encoded_url/:width/:height", http.HandlerFunc(Img))
	http.Handle("/", m)

	// Start the HTTP server
	log.Printf("Listening on http://%s/\n", addr)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>