<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package execution

import (
	"testing"

	"github.com/getgauge/gauge/execution/result"
	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/gauge_messages"
	"github.com/getgauge/gauge/validation"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestNumberOfStreams(c *C) {
	specs := createSpecsList(6)
	e := parallelExecution{numberOfExecutionStreams: 5, specCollection: gauge.NewSpecCollection(specs)}
	c.Assert(e.numberOfStreams(), Equals, 5)

	specs = createSpecsList(6)
	e = parallelExecution{numberOfExecutionStreams: 10, specCollection: gauge.NewSpecCollection(specs)}
	c.Assert(e.numberOfStreams(), Equals, 6)

	specs = createSpecsList(0)
	e = parallelExecution{numberOfExecutionStreams: 17, specCollection: gauge.NewSpecCollection(specs)}
	c.Assert(e.numberOfStreams(), Equals, 0)
}

func getValidationErrorMap() *validation.ValidationErrMaps {
	return &validation.ValidationErrMaps{
		SpecErrs:     make(map[*gauge.Specification][]*validation.StepValidationError),
		ScenarioErrs: make(map[*gauge.Scenario][]*validation.StepValidationError),
		StepErrs:     make(map[*gauge.Step]*validation.StepValidationError),
	}
}

func (s *MySuite) TestAggregationOfSuiteResult(c *C) {
	e := parallelExecution{errMaps: getValidationErrorMap()}
	suiteRes1 := &result.SuiteResult{ExecutionTime: 1, SpecsFailedCount: 1, IsFailed: true, SpecResults: []*result.SpecResult{&result.SpecResult{}, &result.SpecResult{}}}
	suiteRes2 := &result.SuiteResult{ExecutionTime: 3, SpecsFailedCount: 0, IsFailed: false, SpecResults: []*result.SpecResult{&result.SpecResult{}, &result.SpecResult{}}}
	suiteRes3 := &result.SuiteResult{ExecutionTime: 5, SpecsFailedCount: 0, IsFailed: false, SpecResults: []*result.SpecResult{&result.SpecResult{}, &result.SpecResult{}}}
	var suiteResults []*result.SuiteResult
	suiteResults = append(suiteResults, suiteRes1, suiteRes2, suiteRes3)
	e.aggregateResults(suiteResults)

	aggregatedRes := e.result()
	c.Assert(aggregatedRes.SpecsFailedCount, Equals, 1)
	c.Assert(aggregatedRes.IsFailed, Equals, true)
	c.Assert(len(aggregatedRes.SpecResults), Equals, 6)
	c.Assert(aggregatedRes.SpecsSkippedCount, Equals, 0)
}

func (s *MySuite) TestAggregationOfSuiteResultWithUnhandledErrors(c *C) {
	errMap := getValidationErrorMap()
	errMap.SpecErrs[&gauge.Specification{}] = make([]*validation.StepValidationError, 0)
	e := parallelExecution{errMaps: errMap}
	suiteRes1 := &result.SuiteResult{IsFailed: true, UnhandledErrors: []error{streamExecError{specsSkipped: []string{"spec1", "spec2"}, message: "Runner failed to start"}}}
	suiteRes2 := &result.SuiteResult{IsFailed: false, UnhandledErrors: []error{streamExecError{specsSkipped: []string{"spec3", "spec4"}, message: "Runner failed to start"}}}
	suiteRes3 := &result.SuiteResult{IsFailed: false}
	var suiteResults []*result.SuiteResult
	suiteResults = append(suiteResults, suiteRes1, suiteRes2, suiteRes3)
	e.aggregateResults(suiteResults)

	aggregatedRes := e.result()
	c.Assert(len(aggregatedRes.UnhandledErrors), Equals, 2)
	c.Assert(aggregatedRes.UnhandledErrors[0].Error(), Equals, "The following specifications could not be executed:\n"+
		"spec1\n"+
		"spec2\n"+
		"Reason : Runner failed to start.")
	c.Assert(aggregatedRes.UnhandledErrors[1].Error(), Equals, "The following specifications could not be executed:\n"+
		"spec3\n"+
		"spec4\n"+
		"Reason : Runner failed to start.")
	err := (aggregatedRes.UnhandledErrors[0]).(streamExecError)
	c.Assert(len(err.specsSkipped), Equals, 2)
	c.Assert(aggregatedRes.SpecsSkippedCount, Equals, 1)
}

func (s *MySuite) TestAggregationOfSuiteResultWithHook(c *C) {
	e := parallelExecution{errMaps: getValidationErrorMap()}
	suiteRes1 := &result.SuiteResult{PreSuite: &gauge_messages.ProtoHookFailure{}}
	suiteRes2 := &result.SuiteResult{PreSuite: &gauge_messages.ProtoHookFailure{}}
	suiteRes3 := &result.SuiteResult{PostSuite: &gauge_messages.ProtoHookFailure{}}
	var suiteResults []*result.SuiteResult
	suiteResults = append(suiteResults, suiteRes1, suiteRes2, suiteRes3)
	e.aggregateResults(suiteResults)

	aggregatedRes := e.result()
	c.Assert(aggregatedRes.PreSuite, Equals, suiteRes2.PreSuite)
	c.Assert(aggregatedRes.PostSuite, Equals, suiteRes3.PostSuite)
}
<commit_msg>Fixing unit tests<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package execution

import (
	"testing"

	"github.com/getgauge/gauge/execution/result"
	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/gauge_messages"
	"github.com/getgauge/gauge/validation"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestNumberOfStreams(c *C) {
	specs := createSpecsList(6)
	e := parallelExecution{numberOfExecutionStreams: 5, specCollection: gauge.NewSpecCollection(specs)}
	c.Assert(e.numberOfStreams(), Equals, 5)

	specs = createSpecsList(6)
	e = parallelExecution{numberOfExecutionStreams: 10, specCollection: gauge.NewSpecCollection(specs)}
	c.Assert(e.numberOfStreams(), Equals, 6)

	specs = createSpecsList(0)
	e = parallelExecution{numberOfExecutionStreams: 17, specCollection: gauge.NewSpecCollection(specs)}
	c.Assert(e.numberOfStreams(), Equals, 0)
}

func getValidationErrorMap() *validation.ValidationErrMaps {
	return &validation.ValidationErrMaps{
		SpecErrs:     make(map[*gauge.Specification][]*validation.StepValidationError),
		ScenarioErrs: make(map[*gauge.Scenario][]*validation.StepValidationError),
		StepErrs:     make(map[*gauge.Step]*validation.StepValidationError),
	}
}

func (s *MySuite) TestAggregationOfSuiteResult(c *C) {
	e := parallelExecution{errMaps: getValidationErrorMap()}
	suiteRes1 := &result.SuiteResult{ExecutionTime: 1, SpecsFailedCount: 1, IsFailed: true, SpecResults: []*result.SpecResult{&result.SpecResult{}, &result.SpecResult{}}}
	suiteRes2 := &result.SuiteResult{ExecutionTime: 3, SpecsFailedCount: 0, IsFailed: false, SpecResults: []*result.SpecResult{&result.SpecResult{}, &result.SpecResult{}}}
	suiteRes3 := &result.SuiteResult{ExecutionTime: 5, SpecsFailedCount: 0, IsFailed: false, SpecResults: []*result.SpecResult{&result.SpecResult{}, &result.SpecResult{}}}
	var suiteResults []*result.SuiteResult
	suiteResults = append(suiteResults, suiteRes1, suiteRes2, suiteRes3)
	e.aggregateResults(suiteResults)

	aggregatedRes := e.suiteResult
	c.Assert(aggregatedRes.SpecsFailedCount, Equals, 1)
	c.Assert(aggregatedRes.IsFailed, Equals, true)
	c.Assert(len(aggregatedRes.SpecResults), Equals, 6)
	c.Assert(aggregatedRes.SpecsSkippedCount, Equals, 0)
}

func (s *MySuite) TestAggregationOfSuiteResultWithUnhandledErrors(c *C) {
	errMap := getValidationErrorMap()
	errMap.SpecErrs[&gauge.Specification{}] = make([]*validation.StepValidationError, 0)
	e := parallelExecution{errMaps: errMap}
	suiteRes1 := &result.SuiteResult{IsFailed: true, UnhandledErrors: []error{streamExecError{specsSkipped: []string{"spec1", "spec2"}, message: "Runner failed to start"}}}
	suiteRes2 := &result.SuiteResult{IsFailed: false, UnhandledErrors: []error{streamExecError{specsSkipped: []string{"spec3", "spec4"}, message: "Runner failed to start"}}}
	suiteRes3 := &result.SuiteResult{IsFailed: false}
	var suiteResults []*result.SuiteResult
	suiteResults = append(suiteResults, suiteRes1, suiteRes2, suiteRes3)
	e.aggregateResults(suiteResults)

	aggregatedRes := e.suiteResult
	c.Assert(len(aggregatedRes.UnhandledErrors), Equals, 2)
	c.Assert(aggregatedRes.UnhandledErrors[0].Error(), Equals, "The following specifications could not be executed:\n"+
		"spec1\n"+
		"spec2\n"+
		"Reason : Runner failed to start.")
	c.Assert(aggregatedRes.UnhandledErrors[1].Error(), Equals, "The following specifications could not be executed:\n"+
		"spec3\n"+
		"spec4\n"+
		"Reason : Runner failed to start.")
	err := (aggregatedRes.UnhandledErrors[0]).(streamExecError)
	c.Assert(len(err.specsSkipped), Equals, 2)
	c.Assert(aggregatedRes.SpecsSkippedCount, Equals, 1)
}

func (s *MySuite) TestAggregationOfSuiteResultWithHook(c *C) {
	e := parallelExecution{errMaps: getValidationErrorMap()}
	suiteRes1 := &result.SuiteResult{PreSuite: &gauge_messages.ProtoHookFailure{}}
	suiteRes2 := &result.SuiteResult{PreSuite: &gauge_messages.ProtoHookFailure{}}
	suiteRes3 := &result.SuiteResult{PostSuite: &gauge_messages.ProtoHookFailure{}}
	var suiteResults []*result.SuiteResult
	suiteResults = append(suiteResults, suiteRes1, suiteRes2, suiteRes3)
	e.aggregateResults(suiteResults)

	aggregatedRes := e.suiteResult
	c.Assert(aggregatedRes.PreSuite, Equals, suiteRes2.PreSuite)
	c.Assert(aggregatedRes.PostSuite, Equals, suiteRes3.PostSuite)
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"github.com/mailgun/vulcand/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/mailgun/vulcand/engine"
	"github.com/mailgun/vulcand/plugin"
)

func NewMiddlewareCommands(cmd *Command) []cli.Command {
	out := []cli.Command{}
	for _, spec := range cmd.registry.GetSpecs() {
		if spec.CliFlags != nil && spec.FromCli != nil {
			out = append(out, makeMiddlewareCommands(cmd, spec))
		}
	}
	return out
}

func makeMiddlewareCommands(cmd *Command, spec *plugin.MiddlewareSpec) cli.Command {
	flags := append([]cli.Flag{}, spec.CliFlags...)
	flags = append(flags,
		cli.StringFlag{Name: "frontend, f", Usage: "location id"},
		cli.DurationFlag{Name: "ttl", Usage: "ttl"},
		cli.IntFlag{Name: "priority", Value: 1, Usage: "middleware priority, smaller values are lower"},
		cli.StringFlag{Name: "id", Usage: fmt.Sprintf("%s id", spec.Type)})

	return cli.Command{
		Name:  spec.Type,
		Usage: fmt.Sprintf("Operations on %s middlewares", spec.Type),
		Subcommands: []cli.Command{
			{
				Name:   "upsert",
				Usage:  fmt.Sprintf("Add new or update new %s to frontend", spec.Type),
				Flags:  flags,
				Action: makeUpsertMiddlewareAction(cmd, spec),
			},
			{
				Name:   "rm",
				Usage:  fmt.Sprintf("Remove %s from frontend", spec.Type),
				Action: makeDeleteMiddlewareAction(cmd, spec),
				Flags: []cli.Flag{
					cli.StringFlag{Name: "frontend, f", Usage: "Frontend id"},
					cli.StringFlag{Name: "id", Usage: fmt.Sprintf("%s id", spec.Type)},
				},
			},
		},
	}
}

func makeUpsertMiddlewareAction(cmd *Command, spec *plugin.MiddlewareSpec) func(c *cli.Context) {
	return func(c *cli.Context) {
		m, err := spec.FromCli(c)
		if err != nil {
			cmd.printError(err)
		} else {
			mi := engine.Middleware{Id: c.String("id"), Middleware: m, Type: spec.Type, Priority: c.Int("priority")}
			err := cmd.client.UpsertMiddleware(engine.FrontendKey{Id: c.String("frontend")}, mi, c.Duration("ttl"))
			if err != nil {
				cmd.printError(err)
				return
			}
			cmd.printOk("%v upserted", spec.Type, err)
		}
	}
}

func makeDeleteMiddlewareAction(cmd *Command, spec *plugin.MiddlewareSpec) func(c *cli.Context) {
	return func(c *cli.Context) {
		mk := engine.MiddlewareKey{FrontendKey: engine.FrontendKey{Id: c.String("frontend")}, Id: c.String("id")}
		if err := cmd.client.DeleteMiddleware(mk); err != nil {
			cmd.printError(err)
			return
		}
		cmd.printOk("%v deleted", spec.Type)
	}
}
<commit_msg>Fix noise<commit_after>package command

import (
	"fmt"
	"github.com/mailgun/vulcand/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/mailgun/vulcand/engine"
	"github.com/mailgun/vulcand/plugin"
)

func NewMiddlewareCommands(cmd *Command) []cli.Command {
	out := []cli.Command{}
	for _, spec := range cmd.registry.GetSpecs() {
		if spec.CliFlags != nil && spec.FromCli != nil {
			out = append(out, makeMiddlewareCommands(cmd, spec))
		}
	}
	return out
}

func makeMiddlewareCommands(cmd *Command, spec *plugin.MiddlewareSpec) cli.Command {
	flags := append([]cli.Flag{}, spec.CliFlags...)
	flags = append(flags,
		cli.StringFlag{Name: "frontend, f", Usage: "location id"},
		cli.DurationFlag{Name: "ttl", Usage: "ttl"},
		cli.IntFlag{Name: "priority", Value: 1, Usage: "middleware priority, smaller values are lower"},
		cli.StringFlag{Name: "id", Usage: fmt.Sprintf("%s id", spec.Type)})

	return cli.Command{
		Name:  spec.Type,
		Usage: fmt.Sprintf("Operations on %s middlewares", spec.Type),
		Subcommands: []cli.Command{
			{
				Name:   "upsert",
				Usage:  fmt.Sprintf("Add new or update new %s to frontend", spec.Type),
				Flags:  flags,
				Action: makeUpsertMiddlewareAction(cmd, spec),
			},
			{
				Name:   "rm",
				Usage:  fmt.Sprintf("Remove %s from frontend", spec.Type),
				Action: makeDeleteMiddlewareAction(cmd, spec),
				Flags: []cli.Flag{
					cli.StringFlag{Name: "frontend, f", Usage: "Frontend id"},
					cli.StringFlag{Name: "id", Usage: fmt.Sprintf("%s id", spec.Type)},
				},
			},
		},
	}
}

func makeUpsertMiddlewareAction(cmd *Command, spec *plugin.MiddlewareSpec) func(c *cli.Context) {
	return func(c *cli.Context) {
		m, err := spec.FromCli(c)
		if err != nil {
			cmd.printError(err)
		} else {
			mi := engine.Middleware{Id: c.String("id"), Middleware: m, Type: spec.Type, Priority: c.Int("priority")}
			err := cmd.client.UpsertMiddleware(engine.FrontendKey{Id: c.String("frontend")}, mi, c.Duration("ttl"))
			if err != nil {
				cmd.printError(err)
				return
			}
			cmd.printOk("%v upserted", spec.Type)
		}
	}
}

func makeDeleteMiddlewareAction(cmd *Command, spec *plugin.MiddlewareSpec) func(c *cli.Context) {
	return func(c *cli.Context) {
		mk := engine.MiddlewareKey{FrontendKey: engine.FrontendKey{Id: c.String("frontend")}, Id: c.String("id")}
		if err := cmd.client.DeleteMiddleware(mk); err != nil {
			cmd.printError(err)
			return
		}
		cmd.printOk("%v deleted", spec.Type)
	}
}
<|endoftext|>


<commit_before>package jwt

import (
	"sync"

	"github.com/micro/go-micro/v2/auth"
	"github.com/micro/go-micro/v2/auth/token"
	jwtToken "github.com/micro/go-micro/v2/auth/token/jwt"
)

// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
	j := new(jwt)
	j.Init(opts...)
	return j
}

type rule struct {
	role     string
	resource *auth.Resource
}

type jwt struct {
	options auth.Options
	jwt     token.Provider
	rules   []*rule

	sync.Mutex
}

func (j *jwt) String() string {
	return "jwt"
}

func (j *jwt) Init(opts ...auth.Option) {
	j.Lock()
	defer j.Unlock()

	for _, o := range opts {
		o(&j.options)
	}

	if len(j.options.Namespace) == 0 {
		j.options.Namespace = auth.DefaultNamespace
	}

	j.jwt = jwtToken.NewTokenProvider(
		token.WithPrivateKey(j.options.PrivateKey),
		token.WithPublicKey(j.options.PublicKey),
	)
}

func (j *jwt) Options() auth.Options {
	j.Lock()
	defer j.Unlock()
	return j.options
}

func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
	options := auth.NewGenerateOptions(opts...)
	account := &auth.Account{
		ID:        id,
		Type:      options.Type,
		Roles:     options.Roles,
		Provider:  options.Provider,
		Metadata:  options.Metadata,
		Namespace: options.Namespace,
	}

	// generate a JWT secret which can be provided to the Token() method
	// and exchanged for an access token
	secret, err := j.jwt.Generate(account)
	if err != nil {
		return nil, err
	}
	account.Secret = secret.Token

	// return the account
	return account, nil
}

func (j *jwt) Grant(role string, res *auth.Resource) error {
	j.Lock()
	defer j.Unlock()
	j.rules = append(j.rules, &rule{role, res})
	return nil
}

func (j *jwt) Revoke(role string, res *auth.Resource) error {
	j.Lock()
	defer j.Unlock()

	rules := make([]*rule, 0, len(j.rules))

	var ruleFound bool
	for _, r := range rules {
		if r.role == role && r.resource == res {
			ruleFound = true
		} else {
			rules = append(rules, r)
		}
	}

	if !ruleFound {
		return auth.ErrNotFound
	}

	j.rules = rules
	return nil
}

func (j *jwt) Verify(acc *auth.Account, res *auth.Resource) error {
	j.Lock()
	if len(res.Namespace) == 0 {
		res.Namespace = j.options.Namespace
	}
	rules := j.rules
	j.Unlock()

	for _, rule := range rules {
		// validate the rule applies to the requested resource
		if rule.resource.Namespace != "*" && rule.resource.Namespace != res.Namespace {
			continue
		}
		if rule.resource.Type != "*" && rule.resource.Type != res.Type {
			continue
		}
		if rule.resource.Name != "*" && rule.resource.Name != res.Name {
			continue
		}
		if rule.resource.Endpoint != "*" && rule.resource.Endpoint != res.Endpoint {
			continue
		}

		// a blank role indicates anyone can access the resource, even without an account
		if rule.role == "" {
			return nil
		}

		// all furter checks require an account
		if acc == nil {
			continue
		}

		// this rule allows any account access, allow the request
		if rule.role == "*" {
			return nil
		}

		// if the account has the necessary role, allow the request
		for _, r := range acc.Roles {
			if r == rule.role {
				return nil
			}
		}
	}

	// no rules matched, forbid the request
	return auth.ErrForbidden
}

func (j *jwt) Inspect(token string) (*auth.Account, error) {
	return j.jwt.Inspect(token)
}

func (j *jwt) Token(opts ...auth.TokenOption) (*auth.Token, error) {
	options := auth.NewTokenOptions(opts...)

	secret := options.RefreshToken
	if len(options.Secret) > 0 {
		secret = options.Secret
	}

	account, err := j.jwt.Inspect(secret)
	if err != nil {
		return nil, err
	}

	tok, err := j.jwt.Generate(account, token.WithExpiry(options.Expiry))
	if err != nil {
		return nil, err
	}

	return &auth.Token{
		Created:     tok.Created,
		Expiry:      tok.Expiry,
		AccessToken: tok.Token,
	}, nil
}
<commit_msg>Set RefreshToken<commit_after>package jwt

import (
	"sync"

	"github.com/micro/go-micro/v2/auth"
	"github.com/micro/go-micro/v2/auth/token"
	jwtToken "github.com/micro/go-micro/v2/auth/token/jwt"
)

// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
	j := new(jwt)
	j.Init(opts...)
	return j
}

type rule struct {
	role     string
	resource *auth.Resource
}

type jwt struct {
	options auth.Options
	jwt     token.Provider
	rules   []*rule

	sync.Mutex
}

func (j *jwt) String() string {
	return "jwt"
}

func (j *jwt) Init(opts ...auth.Option) {
	j.Lock()
	defer j.Unlock()

	for _, o := range opts {
		o(&j.options)
	}

	if len(j.options.Namespace) == 0 {
		j.options.Namespace = auth.DefaultNamespace
	}

	j.jwt = jwtToken.NewTokenProvider(
		token.WithPrivateKey(j.options.PrivateKey),
		token.WithPublicKey(j.options.PublicKey),
	)
}

func (j *jwt) Options() auth.Options {
	j.Lock()
	defer j.Unlock()
	return j.options
}

func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
	options := auth.NewGenerateOptions(opts...)
	account := &auth.Account{
		ID:        id,
		Type:      options.Type,
		Roles:     options.Roles,
		Provider:  options.Provider,
		Metadata:  options.Metadata,
		Namespace: options.Namespace,
	}

	// generate a JWT secret which can be provided to the Token() method
	// and exchanged for an access token
	secret, err := j.jwt.Generate(account)
	if err != nil {
		return nil, err
	}
	account.Secret = secret.Token

	// return the account
	return account, nil
}

func (j *jwt) Grant(role string, res *auth.Resource) error {
	j.Lock()
	defer j.Unlock()
	j.rules = append(j.rules, &rule{role, res})
	return nil
}

func (j *jwt) Revoke(role string, res *auth.Resource) error {
	j.Lock()
	defer j.Unlock()

	rules := make([]*rule, 0, len(j.rules))

	var ruleFound bool
	for _, r := range rules {
		if r.role == role && r.resource == res {
			ruleFound = true
		} else {
			rules = append(rules, r)
		}
	}

	if !ruleFound {
		return auth.ErrNotFound
	}

	j.rules = rules
	return nil
}

func (j *jwt) Verify(acc *auth.Account, res *auth.Resource) error {
	j.Lock()
	if len(res.Namespace) == 0 {
		res.Namespace = j.options.Namespace
	}
	rules := j.rules
	j.Unlock()

	for _, rule := range rules {
		// validate the rule applies to the requested resource
		if rule.resource.Namespace != "*" && rule.resource.Namespace != res.Namespace {
			continue
		}
		if rule.resource.Type != "*" && rule.resource.Type != res.Type {
			continue
		}
		if rule.resource.Name != "*" && rule.resource.Name != res.Name {
			continue
		}
		if rule.resource.Endpoint != "*" && rule.resource.Endpoint != res.Endpoint {
			continue
		}

		// a blank role indicates anyone can access the resource, even without an account
		if rule.role == "" {
			return nil
		}

		// all furter checks require an account
		if acc == nil {
			continue
		}

		// this rule allows any account access, allow the request
		if rule.role == "*" {
			return nil
		}

		// if the account has the necessary role, allow the request
		for _, r := range acc.Roles {
			if r == rule.role {
				return nil
			}
		}
	}

	// no rules matched, forbid the request
	return auth.ErrForbidden
}

func (j *jwt) Inspect(token string) (*auth.Account, error) {
	return j.jwt.Inspect(token)
}

func (j *jwt) Token(opts ...auth.TokenOption) (*auth.Token, error) {
	options := auth.NewTokenOptions(opts...)

	secret := options.RefreshToken
	if len(options.Secret) > 0 {
		secret = options.Secret
	}

	account, err := j.jwt.Inspect(secret)
	if err != nil {
		return nil, err
	}

	tok, err := j.jwt.Generate(account, token.WithExpiry(options.Expiry))
	if err != nil {
		return nil, err
	}

	return &auth.Token{
		Created:      tok.Created,
		Expiry:       tok.Expiry,
		AccessToken:  tok.Token,
		RefreshToken: tok.Token,
	}, nil
}
<|endoftext|>


<commit_before>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type patternRenamer struct {
	nameMap map[string]string
}

func newPatternRenamer() *patternRenamer {
	return &patternRenamer{map[string]string{}}
}

func (r *patternRenamer) rename(p interface{}) (interface{}, map[string]string) {
	new := r.renameNames(p)
	return new, r.nameMap
}

func (r *patternRenamer) renameNames(p interface{}) interface{} {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return x
		}

		r.nameMap[x] = gensym.GenSym(x, "renamed")
		return r.nameMap[x]
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			fallthrough
		case "$dict":
			ps := x.Arguments().Positionals()
			new := make([]ast.PositionalArgument, 0, len(ps))

			for _, p := range ps {
				new = append(new, ast.NewPositionalArgument(r.renameNames(p.Value()), p.Expanded()))
			}

			return ast.NewApp(x.Function(), ast.NewArguments(new, nil, nil), x.DebugInfo())
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<commit_msg>Refactor pattern_renamer.go<commit_after>package match

import (
	"fmt"

	"github.com/tisp-lang/tisp/src/lib/ast"
	"github.com/tisp-lang/tisp/src/lib/gensym"
	"github.com/tisp-lang/tisp/src/lib/scalar"
)

type patternRenamer struct {
	nameMap map[string]string
}

func newPatternRenamer() *patternRenamer {
	return &patternRenamer{map[string]string{}}
}

func (r *patternRenamer) rename(p interface{}) (interface{}, map[string]string) {
	q := r.renameNames(p)
	return q, r.nameMap
}

func (r *patternRenamer) renameNames(p interface{}) interface{} {
	switch x := p.(type) {
	case string:
		if scalar.Defined(x) {
			return x
		}

		r.nameMap[x] = gensym.GenSym(x, "renamed")
		return r.nameMap[x]
	case ast.App:
		switch x.Function().(string) {
		case "$list":
			fallthrough
		case "$dict":
			ps := make([]ast.PositionalArgument, 0, len(x.Arguments().Positionals()))

			for _, p := range x.Arguments().Positionals() {
				ps = append(ps, ast.NewPositionalArgument(r.renameNames(p.Value()), p.Expanded()))
			}

			return ast.NewApp(x.Function(), ast.NewArguments(ps, nil, nil), x.DebugInfo())
		}
	}

	panic(fmt.Errorf("Invalid pattern: %#v", p))
}
<|endoftext|>


<commit_before>package ui

import "net/http"

func ServeNotFound(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Not Found", http.StatusNotFound)
}

func ServeUnauthorized(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Unauthorized", http.StatusUnauthorized)
}

func ServeBadRequest(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Bad Request", http.StatusBadRequest)
}

func ServeInternalServerError(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}

func RedirectBack(w http.ResponseWriter, r *http.Request) {
	url := r.Referer()
	if url == "" {
		url = "/"
	}
	http.Redirect(w, r, url, http.StatusSeeOther)
}

func catch(r *http.Request, err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>Implement ServeForbidden func<commit_after>package ui

import "net/http"

func ServeNotFound(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Not Found", http.StatusNotFound)
}

func ServeUnauthorized(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Unauthorized", http.StatusUnauthorized)
}

func ServeBadRequest(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Bad Request", http.StatusBadRequest)
}

func ServeInternalServerError(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}

func ServeForbidden(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "Forbidden", http.StatusForbidden)
}

func RedirectBack(w http.ResponseWriter, r *http.Request) {
	url := r.Referer()
	if url == "" {
		url = "/"
	}
	http.Redirect(w, r, url, http.StatusSeeOther)
}

func catch(r *http.Request, err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package jspointer

import (
	"encoding/json"
	"errors"
	"reflect"
	"strconv"
	"strings"
	"sync"
)

var ctxPool = sync.Pool{
	New: moreCtx,
}

func moreCtx() interface{} {
	return &matchCtx{}
}

func getCtx() *matchCtx {
	return ctxPool.Get().(*matchCtx)
}

func releaseCtx(ctx *matchCtx) {
	ctx.err = nil
	ctx.set = false
	ctx.tokens = nil
	ctx.result = nil
	ctxPool.Put(ctx)
}

// New creates a new JSON pointer for given path spec. If the path fails
// to be parsed, an error is returned
func New(path string) (*JSPointer, error) {
	var p JSPointer
	if err := p.parse(path); err != nil {
		return nil, err
	}
	return &p, nil
}

func (p *JSPointer) parse(s string) error {
	if s == "" {
		return nil
	}

	if s[0] != Separator {
		return ErrInvalidPointer
	}

	prev := 0
	tokens := []string{}
	for i := 1; i < len(s); i++ {
		switch s[i] {
		case Separator:
			tokens = append(tokens, s[prev+1:i])
			prev = i
		}
	}

	if prev != len(s) {
		tokens = append(tokens, s[prev+1:])
	}

	dtokens := make([]string, 0, len(tokens))
	for _, t := range tokens {
		t = strings.Replace(strings.Replace(t, EncodedSlash, "/", -1), EncodedTilde, "~", -1)
		dtokens = append(dtokens, t)
	}

	p.tokens = dtokens
	return nil
}

// String returns the stringified version of this JSON pointer
func (p JSPointer) String() string {
	pat := ""
	for _, token := range p.tokens {
		p2 := strings.Replace(strings.Replace(token, "~", EncodedTilde, -1), "/", EncodedSlash, -1)
		pat = pat + "/" + p2
	}
	return pat
}

// Get applies the JSON pointer to the given item, and returns
// the result.
func (p JSPointer) Get(item interface{}) (interface{}, error) {
	ctx := getCtx()
	defer releaseCtx(ctx)

	ctx.tokens = p.tokens
	ctx.apply(item)
	return ctx.result, ctx.err
}

// Set applies the JSON pointer to the given item, and sets the
// value accordingly.
func (p JSPointer) Set(item interface{}, value interface{}) error {
	ctx := getCtx()
	defer releaseCtx(ctx)

	ctx.set = true
	ctx.tokens = p.tokens
	ctx.setvalue = value
	ctx.apply(item)
	return ctx.err
}

type matchCtx struct {
	err      error
	result   interface{}
	set      bool
	setvalue interface{}
	tokens   []string
}

// json element name -> field index
type fieldMap map[string]int

// struct type to -> fieldMap
var struct2FieldMap = map[reflect.Type]fieldMap{}
var fieldmapMutex = sync.Mutex{}

func getStructMap(v reflect.Value) fieldMap {
	fieldmapMutex.Lock()
	defer fieldmapMutex.Unlock()
	t := v.Type()
	fm, ok := struct2FieldMap[t]
	if ok {
		return fm
	}

	fm = fieldMap{}
	for i := 0; i < t.NumField(); i++ {
		sf := t.Field(i)
		if sf.PkgPath != "" { // unexported
			continue
		}

		tag := sf.Tag.Get("json")
		if tag == "" || tag == "-" || tag[0] == ',' {
			fm[sf.Name] = i
			continue
		}

		flen := 0
		for j := 0; j < len(tag); j++ {
			if tag[j] == ',' {
				break
			}
			flen = j
		}
		fm[tag[:flen+1]] = i
	}

	struct2FieldMap[t] = fm

	return fm
}

var strType = reflect.TypeOf("")
func (c *matchCtx) apply(item interface{}) {
	if len(c.tokens) == 0 {
		c.result = item
		return
	}

	lastidx := len(c.tokens) - 1
	node := item
	for tidx, token := range c.tokens {
		v := reflect.ValueOf(node)
		switch v.Kind() {
		case reflect.Struct:
			sm := getStructMap(v)
			i, ok := sm[token]
			if !ok {
				c.err = ErrNotFound
				return
			}
			f := v.Field(i)
			if tidx == lastidx {
				if c.set {
					if !f.CanSet() {
						c.err = ErrCanNotSet
						return
					}
					f.Set(reflect.ValueOf(c.setvalue))
					return
				}
				c.result = f.Interface()
				return
			}
			node = f.Interface()
		case reflect.Map:
			var vt reflect.Value
			// We shall try to inflate the token to its Go native
			// type if it's not a string. In other words, try not to
			// outdo yourselves.
			if t := v.Type().Key(); t != strType {
				vt = reflect.New(t).Elem()
				if err := json.Unmarshal([]byte(token), vt.Addr().Interface()); err != nil {
					name := t.PkgPath() + "." + t.Name()
					if name == "" {
						name = "(anonymous type)"
					}
					c.err = errors.New("unsupported conversion of string to " + name)
					return
				}
			} else {
				vt = reflect.ValueOf(token)
			}
			n := v.MapIndex(vt)
			if reflect.Zero(n.Type()) == n {
				c.err = ErrNotFound
				return
			}

			if tidx == lastidx {
				if c.set {
					v.SetMapIndex(vt, reflect.ValueOf(c.setvalue))
				} else {
					c.result = n.Interface()
				}
				return
			}

			node = n.Interface()
		case reflect.Slice:
			m := node.([]interface{})
			wantidx, err := strconv.Atoi(token)
			if err != nil {
				c.err = err
				return
			}

			if wantidx < 0 || len(m) <= wantidx {
				c.err = ErrSliceIndexOutOfBounds
				return
			}

			if tidx == lastidx {
				if c.set {
					m[wantidx] = c.setvalue
				} else {
					c.result = m[wantidx]
				}
				return
			}
			node = m[wantidx]
		default:
			c.err = ErrNotFound
			return
		}
	}

	// If you fell through here, there was a big problem
	c.err = ErrNotFound
}
<commit_msg>Work even if the target is a pointer<commit_after>package jspointer

import (
	"encoding/json"
	"errors"
	"reflect"
	"strconv"
	"strings"
	"sync"
)

var ctxPool = sync.Pool{
	New: moreCtx,
}

func moreCtx() interface{} {
	return &matchCtx{}
}

func getCtx() *matchCtx {
	return ctxPool.Get().(*matchCtx)
}

func releaseCtx(ctx *matchCtx) {
	ctx.err = nil
	ctx.set = false
	ctx.tokens = nil
	ctx.result = nil
	ctxPool.Put(ctx)
}

// New creates a new JSON pointer for given path spec. If the path fails
// to be parsed, an error is returned
func New(path string) (*JSPointer, error) {
	var p JSPointer
	if err := p.parse(path); err != nil {
		return nil, err
	}
	return &p, nil
}

func (p *JSPointer) parse(s string) error {
	if s == "" {
		return nil
	}

	if s[0] != Separator {
		return ErrInvalidPointer
	}

	prev := 0
	tokens := []string{}
	for i := 1; i < len(s); i++ {
		switch s[i] {
		case Separator:
			tokens = append(tokens, s[prev+1:i])
			prev = i
		}
	}

	if prev != len(s) {
		tokens = append(tokens, s[prev+1:])
	}

	dtokens := make([]string, 0, len(tokens))
	for _, t := range tokens {
		t = strings.Replace(strings.Replace(t, EncodedSlash, "/", -1), EncodedTilde, "~", -1)
		dtokens = append(dtokens, t)
	}

	p.tokens = dtokens
	return nil
}

// String returns the stringified version of this JSON pointer
func (p JSPointer) String() string {
	pat := ""
	for _, token := range p.tokens {
		p2 := strings.Replace(strings.Replace(token, "~", EncodedTilde, -1), "/", EncodedSlash, -1)
		pat = pat + "/" + p2
	}
	return pat
}

// Get applies the JSON pointer to the given item, and returns
// the result.
func (p JSPointer) Get(item interface{}) (interface{}, error) {
	ctx := getCtx()
	defer releaseCtx(ctx)

	ctx.tokens = p.tokens
	ctx.apply(item)
	return ctx.result, ctx.err
}

// Set applies the JSON pointer to the given item, and sets the
// value accordingly.
func (p JSPointer) Set(item interface{}, value interface{}) error {
	ctx := getCtx()
	defer releaseCtx(ctx)

	ctx.set = true
	ctx.tokens = p.tokens
	ctx.setvalue = value
	ctx.apply(item)
	return ctx.err
}

type matchCtx struct {
	err      error
	result   interface{}
	set      bool
	setvalue interface{}
	tokens   []string
}

// json element name -> field index
type fieldMap map[string]int

// struct type to -> fieldMap
var struct2FieldMap = map[reflect.Type]fieldMap{}
var fieldmapMutex = sync.Mutex{}

func getStructMap(v reflect.Value) fieldMap {
	fieldmapMutex.Lock()
	defer fieldmapMutex.Unlock()
	t := v.Type()
	fm, ok := struct2FieldMap[t]
	if ok {
		return fm
	}

	fm = fieldMap{}
	for i := 0; i < t.NumField(); i++ {
		sf := t.Field(i)
		if sf.PkgPath != "" { // unexported
			continue
		}

		tag := sf.Tag.Get("json")
		if tag == "" || tag == "-" || tag[0] == ',' {
			fm[sf.Name] = i
			continue
		}

		flen := 0
		for j := 0; j < len(tag); j++ {
			if tag[j] == ',' {
				break
			}
			flen = j
		}
		fm[tag[:flen+1]] = i
	}

	struct2FieldMap[t] = fm

	return fm
}

var strType = reflect.TypeOf("")

func (c *matchCtx) apply(item interface{}) {
	if len(c.tokens) == 0 {
		c.result = item
		return
	}

	lastidx := len(c.tokens) - 1
	node := item
	for tidx, token := range c.tokens {
		v := reflect.ValueOf(node)
		if v.Kind() == reflect.Ptr {
			v = v.Elem()
		}
		switch v.Kind() {
		case reflect.Struct:
			sm := getStructMap(v)
			i, ok := sm[token]
			if !ok {
				c.err = ErrNotFound
				return
			}
			f := v.Field(i)
			if tidx == lastidx {
				if c.set {
					if !f.CanSet() {
						c.err = ErrCanNotSet
						return
					}
					f.Set(reflect.ValueOf(c.setvalue))
					return
				}
				c.result = f.Interface()
				return
			}
			node = f.Interface()
		case reflect.Map:
			var vt reflect.Value
			// We shall try to inflate the token to its Go native
			// type if it's not a string. In other words, try not to
			// outdo yourselves.
			if t := v.Type().Key(); t != strType {
				vt = reflect.New(t).Elem()
				if err := json.Unmarshal([]byte(token), vt.Addr().Interface()); err != nil {
					name := t.PkgPath() + "." + t.Name()
					if name == "" {
						name = "(anonymous type)"
					}
					c.err = errors.New("unsupported conversion of string to " + name)
					return
				}
			} else {
				vt = reflect.ValueOf(token)
			}
			n := v.MapIndex(vt)
			if reflect.Zero(n.Type()) == n {
				c.err = ErrNotFound
				return
			}

			if tidx == lastidx {
				if c.set {
					v.SetMapIndex(vt, reflect.ValueOf(c.setvalue))
				} else {
					c.result = n.Interface()
				}
				return
			}

			node = n.Interface()
		case reflect.Slice:
			m := node.([]interface{})
			wantidx, err := strconv.Atoi(token)
			if err != nil {
				c.err = err
				return
			}

			if wantidx < 0 || len(m) <= wantidx {
				c.err = ErrSliceIndexOutOfBounds
				return
			}

			if tidx == lastidx {
				if c.set {
					m[wantidx] = c.setvalue
				} else {
					c.result = m[wantidx]
				}
				return
			}
			node = m[wantidx]
		default:
			c.err = ErrNotFound
			return
		}
	}

	// If you fell through here, there was a big problem
	c.err = ErrNotFound
}
<|endoftext|>


<commit_before>package judge

import (
	"fmt"
	"github.com/alekc/proxy"
	"log"
	"net"
	"net/http"
	"strings"
)

var hostnameMarkers = []string{"cache",
	"squid",
	"proxy"}

var proxyHeaderMarkers = []string{"Client-Ip",
	"HTTP_CLIENT_IP",
	"FORWARDED",
	"FORWARDED-FOR",
	"FORWARDED-FOR-IP",
	"X-FORWARDED",
	"X-FORWARDED-FOR",
	"PROXY_CONNECTION",
	"Via",
	"X-Proxy-Id",
	"X-Bluecoat-Via",
	"X-Iwproxy",
}

func (self *Judge) Start() {
	http.HandleFunc("/", self.analyzeRequest)
	err := http.ListenAndServe(fmt.Sprintf(self.ListenAddress), nil) // set listen port
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func (self *Judge) analyzeRequest(w http.ResponseWriter, req *http.Request) {
	//Debug Block
	self.debugLog(formatRequest(req))

	//set up markers
	showsRealIp := false
	showsProxyUsage := false

	result := proxy.NewJudgement()

	//if cloudflare is supported set the country
	if self.CloudFlareSupport {
		result.Country = req.Header.Get("Cf-Ipcountry")
	}

	//getRealIpFromPost
	result.RealIp = self.getRealIpFromPost(req)
	result.RemoteIp = self.getRemoteIp(req)

	//check hostnames for markers
	if msg := self.CheckReverse(result.RemoteIp.String()); len(msg) > 0 {
		showsProxyUsage = true
		result.AppendMessages(msg)
	}

	//normalize xforwardedFor removing cloudflare and trusted gateways
	self.normalizeXForwardedFor(req)

	//search our ip in all headers
	if result.RealIp != "" {
		if msg := self.checkIpInHeaders(req, result.RealIp); len(msg) > 0 {
			showsRealIp = true
			result.AppendMessages(msg)
		}
	}

	//check headers
	if msg := self.hasProxyHeaderMarkings(req); len(msg) > 0 {
		showsProxyUsage = true
		result.AppendMessages(msg)
	}

	//final judgement
	if showsRealIp {
		if showsProxyUsage {
			result.AnonType = 0
		} else {
			result.AnonType = 1
		}
	} else {
		if showsProxyUsage {
			result.AnonType = 2
		} else {
			result.AnonType = 3
		}
	}

	b, _ := result.MarshalJSON()
	w.Write(b)
}

func (self *Judge) checkIpInHeaders(req *http.Request, realIp string) []string {
	msg := make([]string, 0)
	for k, v := range req.Header {
		if strings.Contains(strings.Join(v, ","), realIp) == false {
			continue
		}
		//found our ip in the header
		msg = append(msg, fmt.Sprintf("Found real ip in the header [%s]", k))
	}
	return msg
}

//Normalize X-Forwarded-For header based on cloudflare support and trusted proxies
func (self *Judge) normalizeXForwardedFor(req *http.Request) {
	forwardedFor := make([]string, 0)

	//define acceptable xforwarded for ips
	acceptablesForwardedIps := self.TrustedGatewaysIps
	if self.CloudFlareSupport {
		ip, _, _ := net.SplitHostPort(req.RemoteAddr)
		acceptablesForwardedIps = append(acceptablesForwardedIps, ip)
	}

	//loop through ip and remove those which are acceptable
	for _, tempIp := range strings.Split(req.Header.Get("X-Forwarded-For"), ",") {
		for _, accIp := range acceptablesForwardedIps {
			if tempIp == accIp {
				continue
			}
			forwardedFor = append(forwardedFor, tempIp)
		}
	}
	//if forwardedFor is empty we can safely remove that header from our search
	//it would mean that proxy has not added any new ip
	if len(forwardedFor) == 0 {
		req.Header.Del("x-forwarded-for")
	}
}

//Gets real ip
func (self *Judge) getRealIpFromPost(req *http.Request) string {
	realIp := ""
	if err := req.ParseForm(); err == nil {
		realIp = req.Form.Get("real-ip")
	}
	return realIp
}

//
func (self *Judge) getRemoteIp(req *http.Request) net.IP {
	//get Remote ip. Replace it with cloudflare value if needed
	ip, _, _ := net.SplitHostPort(req.RemoteAddr)
	remoteIp := net.ParseIP(ip)

	//If cloudflare support is enabled, then replace remote ip with
	//contents of CF-Connecting-Ip header
	//Also add current remote ip to the array of ips which have to be removed from
	//forwarded for header
	if self.CloudFlareSupport {
		if ip = req.Header.Get("CF-Connecting-IP"); ip != "" {
			temp := net.ParseIP(ip)
			if temp != nil {
				remoteIp = temp
			}
		}
	}
	return remoteIp
}

func (self *Judge) hasProxyHeaderMarkings(req *http.Request) []string {
	msg := make([]string, 0)
	for _, marker := range proxyHeaderMarkers {
		if req.Header.Get(marker) != "" {
			msg = append(msg, fmt.Sprintf("Header [%s] is present", marker))
		}
	}
	return msg
}

//Checks if name contain certain markers
func (self *Judge) CheckReverse(ip string) []string {
	res := make([]string, 0)
	names, err := net.LookupAddr(ip)
	if err != nil {
		//self.debugLog(fmt.Sprintf("Error on resolving host for '%s' - [%+v]", ip, err))
		return res
	}
	//look for pattern
	fullNames := strings.Join(names, ",")
	for _, mark := range hostnameMarkers {
		if strings.Contains(fullNames, mark) {
			self.debugLog(fmt.Sprintf("Found marker %s in the hostname %s", mark, fullNames))
			res = append(res, fmt.Sprintf("Hostname contains %s", mark))
		}
	}
	return res
}

func (self *Judge) debugLog(msg string) {
	if self.DebugEnabled == false {
		return
	}
	fmt.Println(msg)
}
<commit_msg>Renamed receiver<commit_after>package judge

import (
	"fmt"
	"github.com/alekc/proxy"
	"log"
	"net"
	"net/http"
	"strings"
)

var hostnameMarkers = []string{"cache",
	"squid",
	"proxy"}

var proxyHeaderMarkers = []string{"Client-Ip",
	"HTTP_CLIENT_IP",
	"FORWARDED",
	"FORWARDED-FOR",
	"FORWARDED-FOR-IP",
	"X-FORWARDED",
	"X-FORWARDED-FOR",
	"PROXY_CONNECTION",
	"Via",
	"X-Proxy-Id",
	"X-Bluecoat-Via",
	"X-Iwproxy",
}

func (jd *Judge) Start() {
	http.HandleFunc("/", jd.analyzeRequest)
	err := http.ListenAndServe(fmt.Sprintf(jd.ListenAddress), nil) // set listen port
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func (jd *Judge) analyzeRequest(w http.ResponseWriter, req *http.Request) {
	//Debug Block
	jd.debugLog(formatRequest(req))

	//set up markers
	showsRealIp := false
	showsProxyUsage := false

	result := proxy.NewJudgement()

	//if cloudflare is supported set the country
	if jd.CloudFlareSupport {
		result.Country = req.Header.Get("Cf-Ipcountry")
	}

	//getRealIpFromPost
	result.RealIp = jd.getRealIpFromPost(req)
	result.RemoteIp = jd.getRemoteIp(req)

	//check hostnames for markers
	if msg := jd.CheckReverse(result.RemoteIp.String()); len(msg) > 0 {
		showsProxyUsage = true
		result.AppendMessages(msg)
	}

	//normalize xforwardedFor removing cloudflare and trusted gateways
	jd.normalizeXForwardedFor(req)

	//search our ip in all headers
	if result.RealIp != "" {
		if msg := jd.checkIpInHeaders(req, result.RealIp); len(msg) > 0 {
			showsRealIp = true
			result.AppendMessages(msg)
		}
	}

	//check headers
	if msg := jd.hasProxyHeaderMarkings(req); len(msg) > 0 {
		showsProxyUsage = true
		result.AppendMessages(msg)
	}

	//final judgement
	if showsRealIp {
		if showsProxyUsage {
			result.AnonType = 0
		} else {
			result.AnonType = 1
		}
	} else {
		if showsProxyUsage {
			result.AnonType = 2
		} else {
			result.AnonType = 3
		}
	}

	b, _ := result.MarshalJSON()
	w.Write(b)
}

func (jd *Judge) checkIpInHeaders(req *http.Request, realIp string) []string {
	msg := make([]string, 0)
	for k, v := range req.Header {
		if strings.Contains(strings.Join(v, ","), realIp) == false {
			continue
		}
		//found our ip in the header
		msg = append(msg, fmt.Sprintf("Found real ip in the header [%s]", k))
	}
	return msg
}

//Normalize X-Forwarded-For header based on cloudflare support and trusted proxies
func (jd *Judge) normalizeXForwardedFor(req *http.Request) {
	forwardedFor := make([]string, 0)

	//define acceptable xforwarded for ips
	acceptablesForwardedIps := jd.TrustedGatewaysIps
	if jd.CloudFlareSupport {
		ip, _, _ := net.SplitHostPort(req.RemoteAddr)
		acceptablesForwardedIps = append(acceptablesForwardedIps, ip)
	}

	//loop through ip and remove those which are acceptable
	for _, tempIp := range strings.Split(req.Header.Get("X-Forwarded-For"), ",") {
		for _, accIp := range acceptablesForwardedIps {
			if tempIp == accIp {
				continue
			}
			forwardedFor = append(forwardedFor, tempIp)
		}
	}
	//if forwardedFor is empty we can safely remove that header from our search
	//it would mean that proxy has not added any new ip
	if len(forwardedFor) == 0 {
		req.Header.Del("x-forwarded-for")
	}
}

//Gets real ip
func (jd *Judge) getRealIpFromPost(req *http.Request) string {
	realIp := ""
	if err := req.ParseForm(); err == nil {
		realIp = req.Form.Get("real-ip")
	}
	return realIp
}

//
func (jd *Judge) getRemoteIp(req *http.Request) net.IP {
	//get Remote ip. Replace it with cloudflare value if needed
	ip, _, _ := net.SplitHostPort(req.RemoteAddr)
	remoteIp := net.ParseIP(ip)

	//If cloudflare support is enabled, then replace remote ip with
	//contents of CF-Connecting-Ip header
	//Also add current remote ip to the array of ips which have to be removed from
	//forwarded for header
	if jd.CloudFlareSupport {
		if ip = req.Header.Get("CF-Connecting-IP"); ip != "" {
			temp := net.ParseIP(ip)
			if temp != nil {
				remoteIp = temp
			}
		}
	}
	return remoteIp
}

func (jd *Judge) hasProxyHeaderMarkings(req *http.Request) []string {
	msg := make([]string, 0)
	for _, marker := range proxyHeaderMarkers {
		if req.Header.Get(marker) != "" {
			msg = append(msg, fmt.Sprintf("Header [%s] is present", marker))
		}
	}
	return msg
}

//Checks if name contain certain markers
func (jd *Judge) CheckReverse(ip string) []string {
	res := make([]string, 0)
	names, err := net.LookupAddr(ip)
	if err != nil {
		//jd.debugLog(fmt.Sprintf("Error on resolving host for '%s' - [%+v]", ip, err))
		return res
	}
	//look for pattern
	fullNames := strings.Join(names, ",")
	for _, mark := range hostnameMarkers {
		if strings.Contains(fullNames, mark) {
			jd.debugLog(fmt.Sprintf("Found marker %s in the hostname %s", mark, fullNames))
			res = append(res, fmt.Sprintf("Hostname contains %s", mark))
		}
	}
	return res
}

func (jd *Judge) debugLog(msg string) {
	if jd.DebugEnabled == false {
		return
	}
	fmt.Println(msg)
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"fmt"
	"math"
	"sync"
	"sync/atomic"

	"github.com/hajimehoshi/ebiten/v2/internal/buffered"
	"github.com/hajimehoshi/ebiten/v2/internal/clock"
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
)

type uiContext struct {
	game      Game
	offscreen *Image
	screen    *Image

	updateCalled bool

	// scaleForWindow is the scale of a window. This doesn't represent the scale on fullscreen. This value works
	// only on desktops.
	//
	// scaleForWindow is for backward compatibility and is used to calculate the window size when SetScreenSize
	// is called.
	scaleForWindow float64

	outsideSizeUpdated bool
	outsideWidth       float64
	outsideHeight      float64

	err atomic.Value

	m sync.Mutex
}

var theUIContext = &uiContext{}

func (c *uiContext) set(game Game, scaleForWindow float64) {
	c.m.Lock()
	defer c.m.Unlock()
	c.game = game
}

func (c *uiContext) setError(err error) {
	c.err.Store(err)
}

func (c *uiContext) Layout(outsideWidth, outsideHeight float64) {
	c.outsideSizeUpdated = true
	c.outsideWidth = outsideWidth
	c.outsideHeight = outsideHeight
}

func (c *uiContext) updateOffscreen() {
	sw, sh := c.game.Layout(int(c.outsideWidth), int(c.outsideHeight))
	if sw <= 0 || sh <= 0 {
		panic("ebiten: Layout must return positive numbers")
	}

	if c.offscreen != nil && !c.outsideSizeUpdated {
		if w, h := c.offscreen.Size(); w == sw && h == sh {
			return
		}
	}
	c.outsideSizeUpdated = false

	if c.screen != nil {
		c.screen.Dispose()
		c.screen = nil
	}

	if c.offscreen != nil {
		if w, h := c.offscreen.Size(); w != sw || h != sh {
			c.offscreen.Dispose()
			c.offscreen = nil
		}
	}
	if c.offscreen == nil {
		c.offscreen = NewImage(sw, sh)
		c.offscreen.mipmap.SetVolatile(IsScreenClearedEveryFrame())
	}

	// TODO: This is duplicated with mobile/ebitenmobileview/funcs.go. Refactor this.
	d := uiDriver().DeviceScaleFactor()
	c.screen = newScreenFramebufferImage(int(c.outsideWidth*d), int(c.outsideHeight*d))

	// Do not have to update scaleForWindow since this is used only for backward compatibility.
	// Then, if a window is resizable, scaleForWindow (= ebiten.ScreenScale) might not match with the actual
	// scale. This is fine since ebiten.ScreenScale will be deprecated.
}

func (c *uiContext) setScreenClearedEveryFrame(cleared bool) {
	c.m.Lock()
	defer c.m.Unlock()

	if c.offscreen != nil {
		c.offscreen.mipmap.SetVolatile(cleared)
	}
}

func (c *uiContext) setWindowResizable(resizable bool) {
	c.m.Lock()
	defer c.m.Unlock()

	if w := uiDriver().Window(); w != nil {
		w.SetResizable(resizable)
	}
}

func (c *uiContext) screenScale() float64 {
	if c.offscreen == nil {
		return 0
	}
	sw, sh := c.offscreen.Size()
	d := uiDriver().DeviceScaleFactor()
	scaleX := c.outsideWidth / float64(sw) * d
	scaleY := c.outsideHeight / float64(sh) * d
	return math.Min(scaleX, scaleY)
}

func (c *uiContext) offsets() (float64, float64) {
	if c.offscreen == nil {
		return 0, 0
	}
	sw, sh := c.offscreen.Size()
	d := uiDriver().DeviceScaleFactor()
	s := c.screenScale()
	width := float64(sw) * s
	height := float64(sh) * s
	return (c.outsideWidth*d - width) / 2, (c.outsideHeight*d - height) / 2
}

func (c *uiContext) Update() error {
	// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.

	if err, ok := c.err.Load().(error); ok && err != nil {
		return err
	}
	if err := buffered.BeginFrame(); err != nil {
		return err
	}
	if err := c.update(); err != nil {
		return err
	}
	if err := buffered.EndFrame(); err != nil {
		return err
	}
	return nil
}

func (c *uiContext) update() error {
	// TODO: Move the clock usage to the UI driver side.
	updateCount := clock.Update(MaxTPS())

	// Ensure that Update is called once before Draw so that Update can be used for initialization.
	if !c.updateCalled && updateCount == 0 {
		updateCount = 1
		c.updateCalled = true
	}

	for i := 0; i < updateCount; i++ {
		c.updateOffscreen()

		if err := hooks.RunBeforeUpdateHooks(); err != nil {
			return err
		}
		if err := c.game.Update(); err != nil {
			return err
		}
		uiDriver().ResetForFrame()
	}

	// c.screen might be nil when updateCount is 0 in the initial state (#1039).
	if c.screen == nil {
		return nil
	}

	if IsScreenClearedEveryFrame() {
		c.offscreen.Clear()
	}
	c.game.Draw(c.offscreen)

	// This clear is needed for fullscreen mode or some mobile platforms (#622).
	c.screen.Clear()

	op := &DrawImageOptions{}

	s := c.screenScale()
	switch vd := uiDriver().Graphics().FramebufferYDirection(); vd {
	case driver.Upward:
		op.GeoM.Scale(s, -s)
		_, h := c.offscreen.Size()
		op.GeoM.Translate(0, float64(h)*s)
	case driver.Downward:
		op.GeoM.Scale(s, s)
	default:
		panic(fmt.Sprintf("ebiten: invalid v-direction: %d", vd))
	}

	op.GeoM.Translate(c.offsets())
	op.CompositeMode = CompositeModeCopy

	// filterScreen works with >=1 scale, but does not well with <1 scale.
	// Use regular FilterLinear instead so far (#669).
	if s >= 1 {
		op.Filter = filterScreen
	} else {
		op.Filter = FilterLinear
	}
	c.screen.DrawImage(c.offscreen, op)
	return nil
}

func (c *uiContext) AdjustPosition(x, y float64) (float64, float64) {
	d := uiDriver().DeviceScaleFactor()
	ox, oy := c.offsets()
	s := c.screenScale()
	return (x*d - ox) / s, (y*d - oy) / s
}
<commit_msg>ebiten: Refactoring: Reduce calls of updateOffscreen<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"fmt"
	"math"
	"sync"
	"sync/atomic"

	"github.com/hajimehoshi/ebiten/v2/internal/buffered"
	"github.com/hajimehoshi/ebiten/v2/internal/clock"
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
)

type uiContext struct {
	game      Game
	offscreen *Image
	screen    *Image

	updateCalled bool

	// scaleForWindow is the scale of a window. This doesn't represent the scale on fullscreen. This value works
	// only on desktops.
	//
	// scaleForWindow is for backward compatibility and is used to calculate the window size when SetScreenSize
	// is called.
	scaleForWindow float64

	outsideSizeUpdated bool
	outsideWidth       float64
	outsideHeight      float64

	err atomic.Value

	m sync.Mutex
}

var theUIContext = &uiContext{}

func (c *uiContext) set(game Game, scaleForWindow float64) {
	c.m.Lock()
	defer c.m.Unlock()
	c.game = game
}

func (c *uiContext) setError(err error) {
	c.err.Store(err)
}

func (c *uiContext) Layout(outsideWidth, outsideHeight float64) {
	c.outsideSizeUpdated = true
	c.outsideWidth = outsideWidth
	c.outsideHeight = outsideHeight
}

func (c *uiContext) updateOffscreen() {
	sw, sh := c.game.Layout(int(c.outsideWidth), int(c.outsideHeight))
	if sw <= 0 || sh <= 0 {
		panic("ebiten: Layout must return positive numbers")
	}

	if c.offscreen != nil && !c.outsideSizeUpdated {
		if w, h := c.offscreen.Size(); w == sw && h == sh {
			return
		}
	}
	c.outsideSizeUpdated = false

	if c.screen != nil {
		c.screen.Dispose()
		c.screen = nil
	}

	if c.offscreen != nil {
		if w, h := c.offscreen.Size(); w != sw || h != sh {
			c.offscreen.Dispose()
			c.offscreen = nil
		}
	}
	if c.offscreen == nil {
		c.offscreen = NewImage(sw, sh)
		c.offscreen.mipmap.SetVolatile(IsScreenClearedEveryFrame())
	}

	// TODO: This is duplicated with mobile/ebitenmobileview/funcs.go. Refactor this.
	d := uiDriver().DeviceScaleFactor()
	c.screen = newScreenFramebufferImage(int(c.outsideWidth*d), int(c.outsideHeight*d))

	// Do not have to update scaleForWindow since this is used only for backward compatibility.
	// Then, if a window is resizable, scaleForWindow (= ebiten.ScreenScale) might not match with the actual
	// scale. This is fine since ebiten.ScreenScale will be deprecated.
}

func (c *uiContext) setScreenClearedEveryFrame(cleared bool) {
	c.m.Lock()
	defer c.m.Unlock()

	if c.offscreen != nil {
		c.offscreen.mipmap.SetVolatile(cleared)
	}
}

func (c *uiContext) setWindowResizable(resizable bool) {
	c.m.Lock()
	defer c.m.Unlock()

	if w := uiDriver().Window(); w != nil {
		w.SetResizable(resizable)
	}
}

func (c *uiContext) screenScale() float64 {
	if c.offscreen == nil {
		return 0
	}
	sw, sh := c.offscreen.Size()
	d := uiDriver().DeviceScaleFactor()
	scaleX := c.outsideWidth / float64(sw) * d
	scaleY := c.outsideHeight / float64(sh) * d
	return math.Min(scaleX, scaleY)
}

func (c *uiContext) offsets() (float64, float64) {
	if c.offscreen == nil {
		return 0, 0
	}
	sw, sh := c.offscreen.Size()
	d := uiDriver().DeviceScaleFactor()
	s := c.screenScale()
	width := float64(sw) * s
	height := float64(sh) * s
	return (c.outsideWidth*d - width) / 2, (c.outsideHeight*d - height) / 2
}

func (c *uiContext) Update() error {
	// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.

	if err, ok := c.err.Load().(error); ok && err != nil {
		return err
	}
	if err := buffered.BeginFrame(); err != nil {
		return err
	}
	if err := c.update(); err != nil {
		return err
	}
	if err := buffered.EndFrame(); err != nil {
		return err
	}
	return nil
}

func (c *uiContext) update() error {
	// TODO: Move the clock usage to the UI driver side.
	updateCount := clock.Update(MaxTPS())

	// Ensure that Update is called once before Draw so that Update can be used for initialization.
	if !c.updateCalled && updateCount == 0 {
		updateCount = 1
		c.updateCalled = true
	}

	for i := 0; i < updateCount; i++ {
		if err := hooks.RunBeforeUpdateHooks(); err != nil {
			return err
		}
		if err := c.game.Update(); err != nil {
			return err
		}
		uiDriver().ResetForFrame()
	}

	c.updateOffscreen()

	if IsScreenClearedEveryFrame() {
		c.offscreen.Clear()
	}
	c.game.Draw(c.offscreen)

	// This clear is needed for fullscreen mode or some mobile platforms (#622).
	c.screen.Clear()

	op := &DrawImageOptions{}

	s := c.screenScale()
	switch vd := uiDriver().Graphics().FramebufferYDirection(); vd {
	case driver.Upward:
		op.GeoM.Scale(s, -s)
		_, h := c.offscreen.Size()
		op.GeoM.Translate(0, float64(h)*s)
	case driver.Downward:
		op.GeoM.Scale(s, s)
	default:
		panic(fmt.Sprintf("ebiten: invalid v-direction: %d", vd))
	}

	op.GeoM.Translate(c.offsets())
	op.CompositeMode = CompositeModeCopy

	// filterScreen works with >=1 scale, but does not well with <1 scale.
	// Use regular FilterLinear instead so far (#669).
	if s >= 1 {
		op.Filter = filterScreen
	} else {
		op.Filter = FilterLinear
	}
	c.screen.DrawImage(c.offscreen, op)
	return nil
}

func (c *uiContext) AdjustPosition(x, y float64) (float64, float64) {
	d := uiDriver().DeviceScaleFactor()
	ox, oy := c.offsets()
	s := c.screenScale()
	return (x*d - ox) / s, (y*d - oy) / s
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package api

import (
	stderrs "errors"

	"code.google.com/p/go.net/context"
)

// Context carries values across API boundaries.
type Context interface {
	Value(key interface{}) interface{}
}

// The key type is unexported to prevent collisions
type key int

// namespaceKey is the context key for the request namespace.
const namespaceKey key = 0

// NewContext instantiates a base context object for request flows.
func NewContext() Context {
	return context.TODO()
}

// NewDefaultContext instantiates a base context object for request flows in the default namespace
func NewDefaultContext() Context {
	return WithNamespace(NewContext(), NamespaceDefault)
}

// WithValue returns a copy of parent in which the value associated with key is val.
func WithValue(parent Context, key interface{}, val interface{}) Context {
	internalCtx, ok := parent.(context.Context)
	if !ok {
		panic(stderrs.New("Invalid context type"))
	}
	return context.WithValue(internalCtx, key, val)
}

// WithNamespace returns a copy of parent in which the namespace value is set
func WithNamespace(parent Context, namespace string) Context {
	return WithValue(parent, namespaceKey, namespace)
}

// NamespaceFrom returns the value of the namespace key on the ctx
func NamespaceFrom(ctx Context) (string, bool) {
	namespace, ok := ctx.Value(namespaceKey).(string)
	return namespace, ok
}

// Namespace returns the value of the namespace key on the ctx, or the empty string if none
func Namespace(ctx Context) string {
	namespace, _ := NamespaceFrom(ctx)
	return namespace
}

// ValidNamespace returns false if the namespace on the context differs from the resource.  If the resource has no namespace, it is set to the value in the context.
func ValidNamespace(ctx Context, resource *TypeMeta) bool {
	ns, ok := NamespaceFrom(ctx)
	if len(resource.Namespace) == 0 {
		resource.Namespace = ns
	}
	return ns == resource.Namespace && ok
}

// WithNamespaceDefaultIfNone returns a context whose namespace is the default if and only if the parent context has no namespace value
func WithNamespaceDefaultIfNone(parent Context) Context {
	namespace, ok := NamespaceFrom(parent)
	if !ok || len(namespace) == 0 {
		return WithNamespace(parent, NamespaceDefault)
	}
	return parent
}
<commit_msg>Change validation now that namespace is on ObjectMeta<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package api

import (
	stderrs "errors"

	"code.google.com/p/go.net/context"
)

// Context carries values across API boundaries.
type Context interface {
	Value(key interface{}) interface{}
}

// The key type is unexported to prevent collisions
type key int

// namespaceKey is the context key for the request namespace.
const namespaceKey key = 0

// NewContext instantiates a base context object for request flows.
func NewContext() Context {
	return context.TODO()
}

// NewDefaultContext instantiates a base context object for request flows in the default namespace
func NewDefaultContext() Context {
	return WithNamespace(NewContext(), NamespaceDefault)
}

// WithValue returns a copy of parent in which the value associated with key is val.
func WithValue(parent Context, key interface{}, val interface{}) Context {
	internalCtx, ok := parent.(context.Context)
	if !ok {
		panic(stderrs.New("Invalid context type"))
	}
	return context.WithValue(internalCtx, key, val)
}

// WithNamespace returns a copy of parent in which the namespace value is set
func WithNamespace(parent Context, namespace string) Context {
	return WithValue(parent, namespaceKey, namespace)
}

// NamespaceFrom returns the value of the namespace key on the ctx
func NamespaceFrom(ctx Context) (string, bool) {
	namespace, ok := ctx.Value(namespaceKey).(string)
	return namespace, ok
}

// Namespace returns the value of the namespace key on the ctx, or the empty string if none
func Namespace(ctx Context) string {
	namespace, _ := NamespaceFrom(ctx)
	return namespace
}

// ValidNamespace returns false if the namespace on the context differs from the resource.  If the resource has no namespace, it is set to the value in the context.
func ValidNamespace(ctx Context, resource *ObjectMeta) bool {
	ns, ok := NamespaceFrom(ctx)
	if len(resource.Namespace) == 0 {
		resource.Namespace = ns
	}
	return ns == resource.Namespace && ok
}

// WithNamespaceDefaultIfNone returns a context whose namespace is the default if and only if the parent context has no namespace value
func WithNamespaceDefaultIfNone(parent Context) Context {
	namespace, ok := NamespaceFrom(parent)
	if !ok || len(namespace) == 0 {
		return WithNamespace(parent, NamespaceDefault)
	}
	return parent
}
<|endoftext|>


<commit_before>// Copyright 2019 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ec2

import (
	"context"
	"fmt"
	"time"

	"github.com/cilium/cilium/pkg/aws/types"
	"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
	"github.com/cilium/cilium/pkg/spanstat"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/ec2"
	"golang.org/x/time/rate"
)

// Client represents an EC2 API client
type Client struct {
	ec2Client  *ec2.Client
	limiter    *rate.Limiter
	metricsAPI metricsAPI
}

type metricsAPI interface {
	ObserveEC2APICall(call, status string, duration float64)
	ObserveEC2RateLimit(operation string, duration time.Duration)
}

// NewClient returns a new EC2 client
func NewClient(ec2Client *ec2.Client, metrics metricsAPI, rateLimit float64, burst int) *Client {
	return &Client{
		ec2Client:  ec2Client,
		metricsAPI: metrics,
		limiter:    rate.NewLimiter(rate.Limit(rateLimit), burst),
	}
}

// deriveStatus returns a status string based on the HTTP response provided by
// the AWS API server. If no specific status is provided, either "OK" or
// "Failed" is returned based on the error variable.
func deriveStatus(req *aws.Request, err error) string {
	if req.HTTPResponse != nil {
		return req.HTTPResponse.Status
	}

	if err != nil {
		return "Failed"
	}

	return "OK"
}

func (c *Client) rateLimit(operation string) {
	r := c.limiter.Reserve()
	if delay := r.Delay(); delay != time.Duration(0) && delay != rate.InfDuration {
		c.metricsAPI.ObserveEC2RateLimit(operation, delay)
		c.limiter.Wait(context.TODO())
	}
}

// describeNetworkInterfaces lists all ENIs
func (c *Client) describeNetworkInterfaces() ([]ec2.NetworkInterface, error) {
	var (
		networkInterfaces []ec2.NetworkInterface
		nextToken         string
	)

	for {
		c.rateLimit("DescribeNetworkInterfaces")
		req := &ec2.DescribeNetworkInterfacesInput{}
		if nextToken != "" {
			req.NextToken = &nextToken
		}

		sinceStart := spanstat.Start()
		listReq := c.ec2Client.DescribeNetworkInterfacesRequest(req)
		response, err := listReq.Send(context.TODO())
		c.metricsAPI.ObserveEC2APICall("DescribeNetworkInterfaces", deriveStatus(listReq.Request, err), sinceStart.Seconds())
		if err != nil {
			return nil, err
		}

		networkInterfaces = append(networkInterfaces, response.NetworkInterfaces...)

		if response.NextToken == nil || *response.NextToken == "" {
			break
		} else {
			nextToken = *response.NextToken
		}
	}

	return networkInterfaces, nil
}

// parseENI parses a ec2.NetworkInterface as returned by the EC2 service API,
// converts it into a v2. ENI object
func parseENI(iface *ec2.NetworkInterface, vpcs types.VpcMap, subnets types.SubnetMap) (instanceID string, eni *v2.ENI, err error) {
	if iface.PrivateIpAddress == nil {
		err = fmt.Errorf("ENI has no IP address")
		return
	}

	eni = &v2.ENI{
		IP:             *iface.PrivateIpAddress,
		SecurityGroups: []string{},
		Addresses:      []string{},
	}

	if iface.MacAddress != nil {
		eni.MAC = *iface.MacAddress
	}

	if iface.NetworkInterfaceId != nil {
		eni.ID = *iface.NetworkInterfaceId
	}

	if iface.Description != nil {
		eni.Description = *iface.Description
	}

	if iface.Attachment != nil {
		if iface.Attachment.DeviceIndex != nil {
			eni.Number = int(*iface.Attachment.DeviceIndex)
		}

		if iface.Attachment.InstanceId != nil {
			instanceID = *iface.Attachment.InstanceId
		}
	}

	if iface.SubnetId != nil {
		eni.Subnet.ID = *iface.SubnetId

		if subnets != nil {
			if subnet, ok := subnets[eni.Subnet.ID]; ok {
				eni.Subnet.CIDR = subnet.CIDR
			}
		}
	}

	if iface.VpcId != nil {
		eni.VPC.ID = *iface.VpcId

		if vpcs != nil {
			if vpc, ok := vpcs[eni.VPC.ID]; ok {
				eni.VPC.PrimaryCIDR = vpc.PrimaryCIDR
			}
		}
	}

	for _, ip := range iface.PrivateIpAddresses {
		if ip.PrivateIpAddress != nil {
			eni.Addresses = append(eni.Addresses, *ip.PrivateIpAddress)
		}
	}

	for _, g := range iface.Groups {
		if g.GroupId != nil {
			eni.SecurityGroups = append(eni.SecurityGroups, *g.GroupId)
		}
	}

	return
}

// GetInstances returns the list of all instances including their ENIs as
// instanceMap
func (c *Client) GetInstances(vpcs types.VpcMap, subnets types.SubnetMap) (types.InstanceMap, error) {
	instances := types.InstanceMap{}

	networkInterfaces, err := c.describeNetworkInterfaces()
	if err != nil {
		return nil, err
	}

	for _, iface := range networkInterfaces {
		id, eni, err := parseENI(&iface, vpcs, subnets)
		if err != nil {
			return nil, err
		}

		if id != "" {
			instances.Add(id, eni)
		}
	}

	return instances, nil
}

// describeVpcs lists all VPCs
func (c *Client) describeVpcs() ([]ec2.Vpc, error) {
	var vpcs []ec2.Vpc

	c.rateLimit("DescribeVpcs")
	req := &ec2.DescribeVpcsInput{}

	sinceStart := spanstat.Start()
	listReq := c.ec2Client.DescribeVpcsRequest(req)
	response, err := listReq.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("DescribeVpcs", deriveStatus(listReq.Request, err), sinceStart.Seconds())
	if err != nil {
		return nil, err
	}

	vpcs = append(vpcs, response.Vpcs...)

	return vpcs, nil
}

// GetVpcs retrieves and returns all Vpcs
func (c *Client) GetVpcs() (types.VpcMap, error) {
	vpcs := types.VpcMap{}

	vpcList, err := c.describeVpcs()
	if err != nil {
		return nil, err
	}

	for _, v := range vpcList {
		vpc := &types.Vpc{ID: *v.VpcId}

		if v.CidrBlock != nil {
			vpc.PrimaryCIDR = *v.CidrBlock
		}

		vpcs[vpc.ID] = vpc
	}

	return vpcs, nil
}

// describeSubnets lists all subnets
func (c *Client) describeSubnets() ([]ec2.Subnet, error) {
	sinceStart := spanstat.Start()
	listReq := c.ec2Client.DescribeSubnetsRequest(&ec2.DescribeSubnetsInput{})
	result, err := listReq.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("DescribeSubnets", deriveStatus(listReq.Request, err), sinceStart.Seconds())
	if err != nil {
		return nil, err
	}

	return result.Subnets, nil
}

// GetSubnets returns all EC2 subnets as a subnetMap
func (c *Client) GetSubnets() (types.SubnetMap, error) {
	subnets := types.SubnetMap{}

	subnetList, err := c.describeSubnets()
	if err != nil {
		return nil, err
	}

	for _, s := range subnetList {
		subnet := &types.Subnet{
			ID:                 *s.SubnetId,
			CIDR:               *s.CidrBlock,
			AvailableAddresses: int(*s.AvailableIpAddressCount),
			Tags:               map[string]string{},
		}

		if s.AvailabilityZone != nil {
			subnet.AvailabilityZone = *s.AvailabilityZone
		}

		if s.VpcId != nil {
			subnet.VpcID = *s.VpcId
		}

		for _, tag := range s.Tags {
			if *tag.Key == "Name" {
				subnet.Name = *tag.Value
			} else {
				subnet.Tags[*tag.Key] = *tag.Value
			}
		}

		subnets[subnet.ID] = subnet
	}

	return subnets, nil
}

// CreateNetworkInterface creates an ENI with the given parameters
func (c *Client) CreateNetworkInterface(toAllocate int64, subnetID, desc string, groups []string) (string, *v2.ENI, error) {
	createReq := &ec2.CreateNetworkInterfaceInput{
		Description:                    &desc,
		SecondaryPrivateIpAddressCount: &toAllocate,
		SubnetId:                       &subnetID,
	}
	for _, grp := range groups {
		createReq.Groups = append(createReq.Groups, grp)
	}

	c.rateLimit("CreateNetworkInterface")
	sinceStart := spanstat.Start()
	create := c.ec2Client.CreateNetworkInterfaceRequest(createReq)
	resp, err := create.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("CreateNetworkInterfaceRequest", deriveStatus(create.Request, err), sinceStart.Seconds())
	if err != nil {
		return "", nil, err
	}

	_, eni, err := parseENI(resp.NetworkInterface, nil, nil)
	if err != nil {
		// The error is ignored on purpose. The allocation itself has
		// succeeded. The ability to parse and return the ENI
		// information is optional. Returning the ENI ID is sufficient
		// to allow for the caller to retrieve the ENI information via
		// the API or wait for a regular sync to fetch the information.
		return *resp.NetworkInterface.NetworkInterfaceId, nil, nil
	}

	return eni.ID, eni, nil

}

// DeleteNetworkInterface deletes an ENI with the specified ID
func (c *Client) DeleteNetworkInterface(eniID string) error {
	delReq := &ec2.DeleteNetworkInterfaceInput{}
	delReq.NetworkInterfaceId = &eniID

	c.rateLimit("DeleteNetworkInterface")
	sinceStart := spanstat.Start()
	req := c.ec2Client.DeleteNetworkInterfaceRequest(delReq)
	_, err := req.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("DeleteNetworkInterface", deriveStatus(req.Request, err), sinceStart.Seconds())
	return err
}

// AttachNetworkInterface attaches a previously created ENI to an instance
func (c *Client) AttachNetworkInterface(index int64, instanceID, eniID string) (string, error) {
	attachReq := &ec2.AttachNetworkInterfaceInput{
		DeviceIndex:        &index,
		InstanceId:         &instanceID,
		NetworkInterfaceId: &eniID,
	}

	c.rateLimit("AttachNetworkInterface")
	sinceStart := spanstat.Start()
	attach := c.ec2Client.AttachNetworkInterfaceRequest(attachReq)
	attachResp, err := attach.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("AttachNetworkInterface", deriveStatus(attach.Request, err), sinceStart.Seconds())
	if err != nil {
		return "", err
	}

	return *attachResp.AttachmentId, nil
}

// ModifyNetworkInterface modifies the attributes of an ENI
func (c *Client) ModifyNetworkInterface(eniID, attachmentID string, deleteOnTermination bool) error {
	changes := &ec2.NetworkInterfaceAttachmentChanges{
		AttachmentId:        &attachmentID,
		DeleteOnTermination: &deleteOnTermination,
	}

	modifyReq := &ec2.ModifyNetworkInterfaceAttributeInput{
		Attachment:         changes,
		NetworkInterfaceId: &eniID,
	}

	c.rateLimit("ModifyNetworkInterfaceAttribute")
	sinceStart := spanstat.Start()
	modify := c.ec2Client.ModifyNetworkInterfaceAttributeRequest(modifyReq)
	_, err := modify.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("ModifyNetworkInterface", deriveStatus(modify.Request, err), sinceStart.Seconds())
	return err
}

// AssignPrivateIpAddresses assigns the specified number of secondary IP
// addresses
func (c *Client) AssignPrivateIpAddresses(eniID string, addresses int64) error {
	request := ec2.AssignPrivateIpAddressesInput{
		NetworkInterfaceId:             &eniID,
		SecondaryPrivateIpAddressCount: &addresses,
	}

	c.rateLimit("AssignPrivateIpAddresses")
	sinceStart := spanstat.Start()
	req := c.ec2Client.AssignPrivateIpAddressesRequest(&request)
	_, err := req.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("AssignPrivateIpAddresses", deriveStatus(req.Request, err), sinceStart.Seconds())
	return err
}
<commit_msg>eni: Allow selecting subnet by Name tag<commit_after>// Copyright 2019 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ec2

import (
	"context"
	"fmt"
	"time"

	"github.com/cilium/cilium/pkg/aws/types"
	"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
	"github.com/cilium/cilium/pkg/spanstat"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/ec2"
	"golang.org/x/time/rate"
)

// Client represents an EC2 API client
type Client struct {
	ec2Client  *ec2.Client
	limiter    *rate.Limiter
	metricsAPI metricsAPI
}

type metricsAPI interface {
	ObserveEC2APICall(call, status string, duration float64)
	ObserveEC2RateLimit(operation string, duration time.Duration)
}

// NewClient returns a new EC2 client
func NewClient(ec2Client *ec2.Client, metrics metricsAPI, rateLimit float64, burst int) *Client {
	return &Client{
		ec2Client:  ec2Client,
		metricsAPI: metrics,
		limiter:    rate.NewLimiter(rate.Limit(rateLimit), burst),
	}
}

// deriveStatus returns a status string based on the HTTP response provided by
// the AWS API server. If no specific status is provided, either "OK" or
// "Failed" is returned based on the error variable.
func deriveStatus(req *aws.Request, err error) string {
	if req.HTTPResponse != nil {
		return req.HTTPResponse.Status
	}

	if err != nil {
		return "Failed"
	}

	return "OK"
}

func (c *Client) rateLimit(operation string) {
	r := c.limiter.Reserve()
	if delay := r.Delay(); delay != time.Duration(0) && delay != rate.InfDuration {
		c.metricsAPI.ObserveEC2RateLimit(operation, delay)
		c.limiter.Wait(context.TODO())
	}
}

// describeNetworkInterfaces lists all ENIs
func (c *Client) describeNetworkInterfaces() ([]ec2.NetworkInterface, error) {
	var (
		networkInterfaces []ec2.NetworkInterface
		nextToken         string
	)

	for {
		c.rateLimit("DescribeNetworkInterfaces")
		req := &ec2.DescribeNetworkInterfacesInput{}
		if nextToken != "" {
			req.NextToken = &nextToken
		}

		sinceStart := spanstat.Start()
		listReq := c.ec2Client.DescribeNetworkInterfacesRequest(req)
		response, err := listReq.Send(context.TODO())
		c.metricsAPI.ObserveEC2APICall("DescribeNetworkInterfaces", deriveStatus(listReq.Request, err), sinceStart.Seconds())
		if err != nil {
			return nil, err
		}

		networkInterfaces = append(networkInterfaces, response.NetworkInterfaces...)

		if response.NextToken == nil || *response.NextToken == "" {
			break
		} else {
			nextToken = *response.NextToken
		}
	}

	return networkInterfaces, nil
}

// parseENI parses a ec2.NetworkInterface as returned by the EC2 service API,
// converts it into a v2. ENI object
func parseENI(iface *ec2.NetworkInterface, vpcs types.VpcMap, subnets types.SubnetMap) (instanceID string, eni *v2.ENI, err error) {
	if iface.PrivateIpAddress == nil {
		err = fmt.Errorf("ENI has no IP address")
		return
	}

	eni = &v2.ENI{
		IP:             *iface.PrivateIpAddress,
		SecurityGroups: []string{},
		Addresses:      []string{},
	}

	if iface.MacAddress != nil {
		eni.MAC = *iface.MacAddress
	}

	if iface.NetworkInterfaceId != nil {
		eni.ID = *iface.NetworkInterfaceId
	}

	if iface.Description != nil {
		eni.Description = *iface.Description
	}

	if iface.Attachment != nil {
		if iface.Attachment.DeviceIndex != nil {
			eni.Number = int(*iface.Attachment.DeviceIndex)
		}

		if iface.Attachment.InstanceId != nil {
			instanceID = *iface.Attachment.InstanceId
		}
	}

	if iface.SubnetId != nil {
		eni.Subnet.ID = *iface.SubnetId

		if subnets != nil {
			if subnet, ok := subnets[eni.Subnet.ID]; ok {
				eni.Subnet.CIDR = subnet.CIDR
			}
		}
	}

	if iface.VpcId != nil {
		eni.VPC.ID = *iface.VpcId

		if vpcs != nil {
			if vpc, ok := vpcs[eni.VPC.ID]; ok {
				eni.VPC.PrimaryCIDR = vpc.PrimaryCIDR
			}
		}
	}

	for _, ip := range iface.PrivateIpAddresses {
		if ip.PrivateIpAddress != nil {
			eni.Addresses = append(eni.Addresses, *ip.PrivateIpAddress)
		}
	}

	for _, g := range iface.Groups {
		if g.GroupId != nil {
			eni.SecurityGroups = append(eni.SecurityGroups, *g.GroupId)
		}
	}

	return
}

// GetInstances returns the list of all instances including their ENIs as
// instanceMap
func (c *Client) GetInstances(vpcs types.VpcMap, subnets types.SubnetMap) (types.InstanceMap, error) {
	instances := types.InstanceMap{}

	networkInterfaces, err := c.describeNetworkInterfaces()
	if err != nil {
		return nil, err
	}

	for _, iface := range networkInterfaces {
		id, eni, err := parseENI(&iface, vpcs, subnets)
		if err != nil {
			return nil, err
		}

		if id != "" {
			instances.Add(id, eni)
		}
	}

	return instances, nil
}

// describeVpcs lists all VPCs
func (c *Client) describeVpcs() ([]ec2.Vpc, error) {
	var vpcs []ec2.Vpc

	c.rateLimit("DescribeVpcs")
	req := &ec2.DescribeVpcsInput{}

	sinceStart := spanstat.Start()
	listReq := c.ec2Client.DescribeVpcsRequest(req)
	response, err := listReq.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("DescribeVpcs", deriveStatus(listReq.Request, err), sinceStart.Seconds())
	if err != nil {
		return nil, err
	}

	vpcs = append(vpcs, response.Vpcs...)

	return vpcs, nil
}

// GetVpcs retrieves and returns all Vpcs
func (c *Client) GetVpcs() (types.VpcMap, error) {
	vpcs := types.VpcMap{}

	vpcList, err := c.describeVpcs()
	if err != nil {
		return nil, err
	}

	for _, v := range vpcList {
		vpc := &types.Vpc{ID: *v.VpcId}

		if v.CidrBlock != nil {
			vpc.PrimaryCIDR = *v.CidrBlock
		}

		vpcs[vpc.ID] = vpc
	}

	return vpcs, nil
}

// describeSubnets lists all subnets
func (c *Client) describeSubnets() ([]ec2.Subnet, error) {
	sinceStart := spanstat.Start()
	listReq := c.ec2Client.DescribeSubnetsRequest(&ec2.DescribeSubnetsInput{})
	result, err := listReq.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("DescribeSubnets", deriveStatus(listReq.Request, err), sinceStart.Seconds())
	if err != nil {
		return nil, err
	}

	return result.Subnets, nil
}

// GetSubnets returns all EC2 subnets as a subnetMap
func (c *Client) GetSubnets() (types.SubnetMap, error) {
	subnets := types.SubnetMap{}

	subnetList, err := c.describeSubnets()
	if err != nil {
		return nil, err
	}

	for _, s := range subnetList {
		subnet := &types.Subnet{
			ID:                 *s.SubnetId,
			CIDR:               *s.CidrBlock,
			AvailableAddresses: int(*s.AvailableIpAddressCount),
			Tags:               map[string]string{},
		}

		if s.AvailabilityZone != nil {
			subnet.AvailabilityZone = *s.AvailabilityZone
		}

		if s.VpcId != nil {
			subnet.VpcID = *s.VpcId
		}

		for _, tag := range s.Tags {
			if *tag.Key == "Name" {
				subnet.Name = *tag.Value
			}
			subnet.Tags[*tag.Key] = *tag.Value
		}

		subnets[subnet.ID] = subnet
	}

	return subnets, nil
}

// CreateNetworkInterface creates an ENI with the given parameters
func (c *Client) CreateNetworkInterface(toAllocate int64, subnetID, desc string, groups []string) (string, *v2.ENI, error) {
	createReq := &ec2.CreateNetworkInterfaceInput{
		Description:                    &desc,
		SecondaryPrivateIpAddressCount: &toAllocate,
		SubnetId:                       &subnetID,
	}
	for _, grp := range groups {
		createReq.Groups = append(createReq.Groups, grp)
	}

	c.rateLimit("CreateNetworkInterface")
	sinceStart := spanstat.Start()
	create := c.ec2Client.CreateNetworkInterfaceRequest(createReq)
	resp, err := create.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("CreateNetworkInterfaceRequest", deriveStatus(create.Request, err), sinceStart.Seconds())
	if err != nil {
		return "", nil, err
	}

	_, eni, err := parseENI(resp.NetworkInterface, nil, nil)
	if err != nil {
		// The error is ignored on purpose. The allocation itself has
		// succeeded. The ability to parse and return the ENI
		// information is optional. Returning the ENI ID is sufficient
		// to allow for the caller to retrieve the ENI information via
		// the API or wait for a regular sync to fetch the information.
		return *resp.NetworkInterface.NetworkInterfaceId, nil, nil
	}

	return eni.ID, eni, nil

}

// DeleteNetworkInterface deletes an ENI with the specified ID
func (c *Client) DeleteNetworkInterface(eniID string) error {
	delReq := &ec2.DeleteNetworkInterfaceInput{}
	delReq.NetworkInterfaceId = &eniID

	c.rateLimit("DeleteNetworkInterface")
	sinceStart := spanstat.Start()
	req := c.ec2Client.DeleteNetworkInterfaceRequest(delReq)
	_, err := req.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("DeleteNetworkInterface", deriveStatus(req.Request, err), sinceStart.Seconds())
	return err
}

// AttachNetworkInterface attaches a previously created ENI to an instance
func (c *Client) AttachNetworkInterface(index int64, instanceID, eniID string) (string, error) {
	attachReq := &ec2.AttachNetworkInterfaceInput{
		DeviceIndex:        &index,
		InstanceId:         &instanceID,
		NetworkInterfaceId: &eniID,
	}

	c.rateLimit("AttachNetworkInterface")
	sinceStart := spanstat.Start()
	attach := c.ec2Client.AttachNetworkInterfaceRequest(attachReq)
	attachResp, err := attach.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("AttachNetworkInterface", deriveStatus(attach.Request, err), sinceStart.Seconds())
	if err != nil {
		return "", err
	}

	return *attachResp.AttachmentId, nil
}

// ModifyNetworkInterface modifies the attributes of an ENI
func (c *Client) ModifyNetworkInterface(eniID, attachmentID string, deleteOnTermination bool) error {
	changes := &ec2.NetworkInterfaceAttachmentChanges{
		AttachmentId:        &attachmentID,
		DeleteOnTermination: &deleteOnTermination,
	}

	modifyReq := &ec2.ModifyNetworkInterfaceAttributeInput{
		Attachment:         changes,
		NetworkInterfaceId: &eniID,
	}

	c.rateLimit("ModifyNetworkInterfaceAttribute")
	sinceStart := spanstat.Start()
	modify := c.ec2Client.ModifyNetworkInterfaceAttributeRequest(modifyReq)
	_, err := modify.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("ModifyNetworkInterface", deriveStatus(modify.Request, err), sinceStart.Seconds())
	return err
}

// AssignPrivateIpAddresses assigns the specified number of secondary IP
// addresses
func (c *Client) AssignPrivateIpAddresses(eniID string, addresses int64) error {
	request := ec2.AssignPrivateIpAddressesInput{
		NetworkInterfaceId:             &eniID,
		SecondaryPrivateIpAddressCount: &addresses,
	}

	c.rateLimit("AssignPrivateIpAddresses")
	sinceStart := spanstat.Start()
	req := c.ec2Client.AssignPrivateIpAddressesRequest(&request)
	_, err := req.Send(context.TODO())
	c.metricsAPI.ObserveEC2APICall("AssignPrivateIpAddresses", deriveStatus(req.Request, err), sinceStart.Seconds())
	return err
}
<|endoftext|>


<commit_before>// Copyright 2017 Kohei YOSHIDA <https://yosida95.com/>.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package chame

import (
	"encoding/json"
	"strconv"
	"time"

	"github.com/pkg/errors"
)

type Token struct {
	now time.Time

	Issuer    string   `json:"iss"`
	Subject   string   `json:"sub"`
	NotBefore JWTEpoch `json:"nbf"`
	Expiry    JWTEpoch `json:"exp"`
}

func (tok *Token) Valid() error {
	const leeway = 1 * time.Minute

	if nbf := tok.NotBefore.Time(); !nbf.IsZero() && nbf.Add(-leeway).After(tok.now) {
		return errors.New("chame: expired token")
	}

	if exp := tok.Expiry.Time(); !exp.IsZero() && !exp.Add(leeway).After(tok.now) {
		return errors.New("chame: expired token")
	}

	return nil
}

type JWTEpoch int64

func (epoch JWTEpoch) Time() time.Time {
	return time.Unix(int64(epoch), 0)
}

var _ json.Marshaler = (*JWTEpoch)(nil)

func (epoch JWTEpoch) MarshalJSON() ([]byte, error) {
	v := strconv.FormatInt(int64(epoch), 10)
	return []byte(v), nil
}

var _ json.Unmarshaler = (*JWTEpoch)(nil)

func (epoch *JWTEpoch) UnmarshalJSON(data []byte) error {
	i, err := strconv.ParseInt(string(data), 10, 64)
	if err != nil {
		return err
	}
	*epoch = JWTEpoch(i)
	return nil
}
<commit_msg>chame: regard JWTEpoch(0) as time.Time of zero<commit_after>// Copyright 2017 Kohei YOSHIDA <https://yosida95.com/>.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package chame

import (
	"encoding/json"
	"strconv"
	"time"

	"github.com/pkg/errors"
)

type Token struct {
	now time.Time

	Issuer    string   `json:"iss"`
	Subject   string   `json:"sub"`
	NotBefore JWTEpoch `json:"nbf"`
	Expiry    JWTEpoch `json:"exp"`
}

func (tok *Token) Valid() error {
	const leeway = 1 * time.Minute

	if nbf := tok.NotBefore.Time(); !nbf.IsZero() && nbf.Add(-leeway).After(tok.now) {
		return errors.New("chame: expired token")
	}

	if exp := tok.Expiry.Time(); !exp.IsZero() && !exp.Add(leeway).After(tok.now) {
		return errors.New("chame: expired token")
	}

	return nil
}

type JWTEpoch int64

func (epoch JWTEpoch) Time() time.Time {
	if epoch == 0 {
		return time.Time{}
	}
	return time.Unix(int64(epoch), 0)
}

var _ json.Marshaler = (*JWTEpoch)(nil)

func (epoch JWTEpoch) MarshalJSON() ([]byte, error) {
	v := strconv.FormatInt(int64(epoch), 10)
	return []byte(v), nil
}

var _ json.Unmarshaler = (*JWTEpoch)(nil)

func (epoch *JWTEpoch) UnmarshalJSON(data []byte) error {
	i, err := strconv.ParseInt(string(data), 10, 64)
	if err != nil {
		return err
	}
	*epoch = JWTEpoch(i)
	return nil
}
<|endoftext|>


<commit_before>package cli

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/cheggaaa/pb"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/taku-k/polymerase/pkg/base"
	"github.com/taku-k/polymerase/pkg/storage/storagepb"
	"github.com/taku-k/polymerase/pkg/utils/dirutil"
	"github.com/taku-k/polymerase/pkg/utils/exec"
	"github.com/taku-k/polymerase/pkg/utils/log"
	"golang.org/x/sync/errgroup"
)

const (
	progressBarWidth = 80
)

type restoreContext struct {
	*base.Config

	from string

	applyPrepare bool
}

var restoreCmd = &cobra.Command{
	Use:   "restore",
	Short: "Receives backup data to restore from a polymerase server",
	RunE:  runRestore,
}

func runRestore(cmd *cobra.Command, args []string) error {
	if len(args) > 0 {
		return usageAndError(cmd)
	}

	if restoreCtx.from == "" {
		return errors.New("You must specify `from`")
	}
	if db == "" {
		return errors.New("You must specify `db`")
	}

	// Signal
	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

	errCh := make(chan error, 1)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		scli, err := getStorageClient(ctx, nil)
		if err != nil {
			errCh <- err
		}
		res, err := scli.GetKeysAtPoint(context.Background(), &storagepb.GetKeysAtPointRequest{
			Db:   db,
			From: restoreCtx.from,
		})

		restoreDir, err := filepath.Abs("polymerase-restore")
		if err != nil {
			errCh <- err
		}
		if err := dirutil.MkdirAllWithLog(restoreDir); err != nil {
			errCh <- err
		}
		log.Info("Restore data directory: ", restoreDir)

		pbs := make([]*pb.ProgressBar, len(res.Keys))
		pbs[len(res.Keys)-1] = pb.New64(int64(res.Keys[len(res.Keys)-1].Size)).Prefix("base | ")
		for inc, idx := len(res.Keys)-1, 0; inc > 0; inc -= 1 {
			info := res.Keys[idx]
			pbs[idx] = pb.New64(int64(info.Size)).Prefix(fmt.Sprintf("inc%d | ", inc))
		}
		for _, bar := range pbs {
			bar.SetWidth(progressBarWidth)
			bar.SetUnits(pb.U_BYTES)
		}

		pool, err := pb.StartPool(pbs...)
		if err != nil {
			errCh <- err
		}

		g, _ := errgroup.WithContext(ctx)

		for inc, idx := len(res.Keys)-1, 0; inc > 0; inc -= 1 {
			info := res.Keys[idx]
			bar := pbs[idx]
			inc := inc
			g.Go(func() error {
				return getIncBackup(scli, info, restoreDir, inc, bar)
			})
			idx += 1
		}
		g.Go(func() error {
			return getFullBackup(scli, res.Keys[len(res.Keys)-1], restoreDir, pbs[len(res.Keys)-1])
		})
		if err := g.Wait(); err != nil {
			pool.Stop()
			errCh <- err
		}
		pool.Stop()

		// Automatically preparing backups only when applyPrepare flag is true.
		if restoreCtx.applyPrepare {
			os.Chdir(restoreDir)
			c := exec.PrepareBaseBackup(ctx, xtrabackupCfg)
			if err := c.Run(); err != nil {
				errCh <- errors.Wrap(err, fmt.Sprintf("failed preparing base: %v", c.Args))
			}
			for inc := 1; inc < len(res.Keys); inc += 1 {
				c := exec.PrepareIncBackup(ctx, inc, xtrabackupCfg)
				if err := c.Run(); err != nil {
					errCh <- errors.Wrap(err, fmt.Sprintf("failed preparing inc%d: %v", inc, c.Args))
				}
			}
		}
	}()

	select {
	case err := <-errCh:
		return err
	case _ = <-signalCh:
	}
	return nil
}

func getIncBackup(cli storagepb.StorageServiceClient, info *storagepb.BackupFileInfo, restoreDir string, inc int, bar *pb.ProgressBar) error {
	fn := filepath.Join(restoreDir, fmt.Sprintf("inc%d.xb.gz", inc))
	if err := getBackup(cli, info, fn, bar); err != nil {
		return err
	}
	if err := exec.UnzipIncBackupCmd(context.TODO(), fn, restoreDir, inc); err != nil {
		return err
	}
	return nil
}

func getFullBackup(cli storagepb.StorageServiceClient, info *storagepb.BackupFileInfo, restoreDir string, bar *pb.ProgressBar) error {
	fn := filepath.Join(restoreDir, "base.tar.gz")
	if err := getBackup(cli, info, fn, bar); err != nil {
		return err
	}
	if err := exec.UnzipFullBackupCmd(context.TODO(), fn, restoreDir); err != nil {
		return err
	}
	return nil
}

func getBackup(
	cli storagepb.StorageServiceClient,
	info *storagepb.BackupFileInfo,
	fn string,
	bar *pb.ProgressBar,
) error {
	stream, err := cli.GetFileByKey(context.Background(), &storagepb.GetFileByKeyRequest{
		Key:         info.Key,
		StorageType: info.StorageType,
	})
	if err != nil {
		return err
	}
	f, err := os.Create(fn)
	if err != nil {
		return err
	}
	bufw := bufio.NewWriter(f)
	multiw := io.MultiWriter(bufw, bar)
	for {
		fs, err := stream.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		multiw.Write(fs.Content)
	}
	bar.Finish()
	return nil
}
<commit_msg>Fix bug<commit_after>package cli

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/cheggaaa/pb"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/taku-k/polymerase/pkg/base"
	"github.com/taku-k/polymerase/pkg/storage/storagepb"
	"github.com/taku-k/polymerase/pkg/utils/dirutil"
	"github.com/taku-k/polymerase/pkg/utils/exec"
	"github.com/taku-k/polymerase/pkg/utils/log"
	"golang.org/x/sync/errgroup"
)

const (
	progressBarWidth = 80
)

type restoreContext struct {
	*base.Config

	from string

	applyPrepare bool
}

var restoreCmd = &cobra.Command{
	Use:   "restore",
	Short: "Receives backup data to restore from a polymerase server",
	RunE:  runRestore,
}

func runRestore(cmd *cobra.Command, args []string) error {
	if len(args) > 0 {
		return usageAndError(cmd)
	}

	if restoreCtx.from == "" {
		return errors.New("You must specify `from`")
	}
	if db == "" {
		return errors.New("You must specify `db`")
	}

	// Signal
	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

	errCh := make(chan error, 1)
	finishCh := make(chan struct{})

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() {
		scli, err := getStorageClient(ctx, nil)
		if err != nil {
			errCh <- err
		}
		res, err := scli.GetKeysAtPoint(context.Background(), &storagepb.GetKeysAtPointRequest{
			Db:   db,
			From: restoreCtx.from,
		})

		restoreDir, err := filepath.Abs("polymerase-restore")
		if err != nil {
			errCh <- err
		}
		if err := dirutil.MkdirAllWithLog(restoreDir); err != nil {
			errCh <- err
		}
		log.Info("Restore data directory: ", restoreDir)

		pbs := make([]*pb.ProgressBar, len(res.Keys))
		pbs[len(res.Keys)-1] = pb.New64(int64(res.Keys[len(res.Keys)-1].Size)).Prefix("base | ")
		for inc, idx := len(res.Keys)-1, 0; inc > 0; inc -= 1 {
			info := res.Keys[idx]
			pbs[idx] = pb.New64(int64(info.Size)).Prefix(fmt.Sprintf("inc%d | ", inc))
		}
		for _, bar := range pbs {
			bar.SetWidth(progressBarWidth)
			bar.SetUnits(pb.U_BYTES)
		}

		pool, err := pb.StartPool(pbs...)
		if err != nil {
			errCh <- err
		}

		g, _ := errgroup.WithContext(ctx)

		for inc, idx := len(res.Keys)-1, 0; inc > 0; inc -= 1 {
			info := res.Keys[idx]
			bar := pbs[idx]
			inc := inc
			g.Go(func() error {
				return getIncBackup(scli, info, restoreDir, inc, bar)
			})
			idx += 1
		}
		g.Go(func() error {
			return getFullBackup(scli, res.Keys[len(res.Keys)-1], restoreDir, pbs[len(res.Keys)-1])
		})
		if err := g.Wait(); err != nil {
			pool.Stop()
			errCh <- err
		}
		pool.Stop()

		// Automatically preparing backups only when applyPrepare flag is true.
		if restoreCtx.applyPrepare {
			os.Chdir(restoreDir)
			c := exec.PrepareBaseBackup(ctx, xtrabackupCfg)
			if err := c.Run(); err != nil {
				errCh <- errors.Wrap(err, fmt.Sprintf("failed preparing base: %v", c.Args))
			}
			for inc := 1; inc < len(res.Keys); inc += 1 {
				c := exec.PrepareIncBackup(ctx, inc, xtrabackupCfg)
				if err := c.Run(); err != nil {
					errCh <- errors.Wrap(err, fmt.Sprintf("failed preparing inc%d: %v", inc, c.Args))
				}
			}
		}

		finishCh <- struct{}{}
	}()

	select {
	case err := <-errCh:
		return err
	case <-signalCh:
	case <-finishCh:
	}
	return nil
}

func getIncBackup(cli storagepb.StorageServiceClient, info *storagepb.BackupFileInfo, restoreDir string, inc int, bar *pb.ProgressBar) error {
	fn := filepath.Join(restoreDir, fmt.Sprintf("inc%d.xb.gz", inc))
	if err := getBackup(cli, info, fn, bar); err != nil {
		return err
	}
	if err := exec.UnzipIncBackupCmd(context.TODO(), fn, restoreDir, inc); err != nil {
		return err
	}
	return nil
}

func getFullBackup(cli storagepb.StorageServiceClient, info *storagepb.BackupFileInfo, restoreDir string, bar *pb.ProgressBar) error {
	fn := filepath.Join(restoreDir, "base.tar.gz")
	if err := getBackup(cli, info, fn, bar); err != nil {
		return err
	}
	if err := exec.UnzipFullBackupCmd(context.TODO(), fn, restoreDir); err != nil {
		return err
	}
	return nil
}

func getBackup(
	cli storagepb.StorageServiceClient,
	info *storagepb.BackupFileInfo,
	fn string,
	bar *pb.ProgressBar,
) error {
	stream, err := cli.GetFileByKey(context.Background(), &storagepb.GetFileByKeyRequest{
		Key:         info.Key,
		StorageType: info.StorageType,
	})
	if err != nil {
		return err
	}
	f, err := os.Create(fn)
	if err != nil {
		return err
	}
	bufw := bufio.NewWriter(f)
	multiw := io.MultiWriter(bufw, bar)
	for {
		fs, err := stream.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		multiw.Write(fs.Content)
	}
	bar.Finish()
	return nil
}
<|endoftext|>