<commit_before>package decor

import (
	"fmt"
	"io"
	"math"
	"strconv"
	"strings"
	"time"

	"github.com/VividCortex/ewma"
)

type SpeedKiB float64

func (s SpeedKiB) Format(st fmt.State, verb rune) {
	prec, ok := st.Precision()

	if verb == 'd' || !ok {
		prec = 0
	}
	if verb == 'f' && !ok {
		prec = 6
	}
	// retain old beahavior if s verb used
	if verb == 's' {
		prec = 1
	}

	var res, unit string
	switch {
	case s >= TiB:
		unit = "TiB/s"
		res = strconv.FormatFloat(float64(s)/TiB, 'f', prec, 64)
	case s >= GiB:
		unit = "GiB/s"
		res = strconv.FormatFloat(float64(s)/GiB, 'f', prec, 64)
	case s >= MiB:
		unit = "MiB/s"
		res = strconv.FormatFloat(float64(s)/MiB, 'f', prec, 64)
	case s >= KiB:
		unit = "KiB/s"
		res = strconv.FormatFloat(float64(s)/KiB, 'f', prec, 64)
	default:
		unit = "b/s"
		res = strconv.FormatInt(int64(s), 10)
	}

	if st.Flag(' ') {
		res += " "
	}
	res += unit

	if w, ok := st.Width(); ok {
		if len(res) < w {
			pad := strings.Repeat(" ", w-len(res))
			if st.Flag(int('-')) {
				res += pad
			} else {
				res = pad + res
			}
		}
	}

	io.WriteString(st, res)
}

type SpeedKB float64

func (s SpeedKB) Format(st fmt.State, verb rune) {
	prec, ok := st.Precision()

	if verb == 'd' || !ok {
		prec = 0
	}
	if verb == 'f' && !ok {
		prec = 6
	}
	// retain old beahavior if s verb used
	if verb == 's' {
		prec = 1
	}

	var res, unit string
	switch {
	case s >= TB:
		unit = "TB/s"
		res = strconv.FormatFloat(float64(s)/TB, 'f', prec, 64)
	case s >= GB:
		unit = "GB/s"
		res = strconv.FormatFloat(float64(s)/GB, 'f', prec, 64)
	case s >= MB:
		unit = "MB/s"
		res = strconv.FormatFloat(float64(s)/MB, 'f', prec, 64)
	case s >= KB:
		unit = "kB/s"
		res = strconv.FormatFloat(float64(s)/KB, 'f', prec, 64)
	default:
		unit = "b/s"
		res = strconv.FormatInt(int64(s), 10)
	}

	if st.Flag(' ') {
		res += " "
	}
	res += unit

	if w, ok := st.Width(); ok {
		if len(res) < w {
			pad := strings.Repeat(" ", w-len(res))
			if st.Flag(int('-')) {
				res += pad
			} else {
				res = pad + res
			}
		}
	}

	io.WriteString(st, res)
}

// EwmaSpeed exponential-weighted-moving-average based speed decorator,
// with dynamic unit measure adjustment.
//
//	`unit` one of [0|UnitKiB|UnitKB] zero for no unit
//
//	`unitFormat` printf compatible verb for value, like "%f" or "%d"
//
//	`average` MovingAverage implementation
//
//	`wcc` optional WC config
//
// unitFormat example if UnitKiB is chosen:
//
//	"%.1f" = "1.0MiB/s" or "% .1f" = "1.0 MiB/s"
func EwmaSpeed(unit int, unitFormat string, age float64, wcc ...WC) Decorator {
	return MovingAverageSpeed(unit, unitFormat, ewma.NewMovingAverage(age), wcc...)
}

// MovingAverageSpeed decorator relies on MovingAverage implementation
// to calculate its average.
//
//	`unit` one of [0|UnitKiB|UnitKB] zero for no unit
//
//	`unitFormat` printf compatible verb for value, like "%f" or "%d"
//
//	`average` MovingAverage implementation
//
//	`wcc` optional WC config
func MovingAverageSpeed(unit int, unitFormat string, average MovingAverage, wcc ...WC) Decorator {
	var wc WC
	for _, widthConf := range wcc {
		wc = widthConf
	}
	wc.Init()
	d := &movingAverageSpeed{
		WC:         wc,
		unit:       unit,
		unitFormat: unitFormat,
		average:    average,
	}
	return d
}

type movingAverageSpeed struct {
	WC
	unit        int
	unitFormat  string
	average     ewma.MovingAverage
	msg         string
	completeMsg *string
}

func (d *movingAverageSpeed) Decor(st *Statistics) string {
	if st.Completed {
		if d.completeMsg != nil {
			return d.FormatMsg(*d.completeMsg)
		}
		return d.FormatMsg(d.msg)
	}

	speed := d.average.Value()
	switch d.unit {
	case UnitKiB:
		d.msg = fmt.Sprintf(d.unitFormat, SpeedKiB(speed))
	case UnitKB:
		d.msg = fmt.Sprintf(d.unitFormat, SpeedKB(speed))
	default:
		d.msg = fmt.Sprintf(d.unitFormat, speed)
	}

	return d.FormatMsg(d.msg)
}

func (s *movingAverageSpeed) NextAmount(n int, wdd ...time.Duration) {
	var workDuration time.Duration
	for _, wd := range wdd {
		workDuration = wd
	}
	speed := float64(n) / workDuration.Seconds() / 1000
	if math.IsInf(speed, 0) || math.IsNaN(speed) {
		return
	}
	s.average.Add(speed)
}

func (d *movingAverageSpeed) OnCompleteMessage(msg string) {
	d.completeMsg = &msg
}

// AverageSpeed decorator with dynamic unit measure adjustment.
//
//	`unit` one of [0|UnitKiB|UnitKB] zero for no unit
//
//	`unitFormat` printf compatible verb for value, like "%f" or "%d"
//
//	`wcc` optional WC config
//
// unitFormat example if UnitKiB is chosen:
//
//	"%.1f" = "1.0MiB/s" or "% .1f" = "1.0 MiB/s"
func AverageSpeed(unit int, unitFormat string, wcc ...WC) Decorator {
	var wc WC
	for _, widthConf := range wcc {
		wc = widthConf
	}
	wc.Init()
	d := &averageSpeed{
		WC:         wc,
		unit:       unit,
		unitFormat: unitFormat,
		startTime:  time.Now(),
	}
	return d
}

type averageSpeed struct {
	WC
	unit        int
	unitFormat  string
	startTime   time.Time
	msg         string
	completeMsg *string
}

func (d *averageSpeed) Decor(st *Statistics) string {
	if st.Completed {
		if d.completeMsg != nil {
			return d.FormatMsg(*d.completeMsg)
		}
		return d.FormatMsg(d.msg)
	}

	timeElapsed := time.Since(d.startTime)
	speed := float64(st.Current) / timeElapsed.Seconds()

	switch d.unit {
	case UnitKiB:
		d.msg = fmt.Sprintf(d.unitFormat, SpeedKiB(speed))
	case UnitKB:
		d.msg = fmt.Sprintf(d.unitFormat, SpeedKB(speed))
	default:
		d.msg = fmt.Sprintf(d.unitFormat, speed)
	}

	return d.FormatMsg(d.msg)
}

func (d *averageSpeed) OnCompleteMessage(msg string) {
	d.completeMsg = &msg
}
<commit_msg>receiver name s should be consistent with previous receiver name d<commit_after>package decor

import (
	"fmt"
	"io"
	"math"
	"strconv"
	"strings"
	"time"

	"github.com/VividCortex/ewma"
)

type SpeedKiB float64

func (s SpeedKiB) Format(st fmt.State, verb rune) {
	prec, ok := st.Precision()

	if verb == 'd' || !ok {
		prec = 0
	}
	if verb == 'f' && !ok {
		prec = 6
	}
	// retain old beahavior if s verb used
	if verb == 's' {
		prec = 1
	}

	var res, unit string
	switch {
	case s >= TiB:
		unit = "TiB/s"
		res = strconv.FormatFloat(float64(s)/TiB, 'f', prec, 64)
	case s >= GiB:
		unit = "GiB/s"
		res = strconv.FormatFloat(float64(s)/GiB, 'f', prec, 64)
	case s >= MiB:
		unit = "MiB/s"
		res = strconv.FormatFloat(float64(s)/MiB, 'f', prec, 64)
	case s >= KiB:
		unit = "KiB/s"
		res = strconv.FormatFloat(float64(s)/KiB, 'f', prec, 64)
	default:
		unit = "b/s"
		res = strconv.FormatInt(int64(s), 10)
	}

	if st.Flag(' ') {
		res += " "
	}
	res += unit

	if w, ok := st.Width(); ok {
		if len(res) < w {
			pad := strings.Repeat(" ", w-len(res))
			if st.Flag(int('-')) {
				res += pad
			} else {
				res = pad + res
			}
		}
	}

	io.WriteString(st, res)
}

type SpeedKB float64

func (s SpeedKB) Format(st fmt.State, verb rune) {
	prec, ok := st.Precision()

	if verb == 'd' || !ok {
		prec = 0
	}
	if verb == 'f' && !ok {
		prec = 6
	}
	// retain old beahavior if s verb used
	if verb == 's' {
		prec = 1
	}

	var res, unit string
	switch {
	case s >= TB:
		unit = "TB/s"
		res = strconv.FormatFloat(float64(s)/TB, 'f', prec, 64)
	case s >= GB:
		unit = "GB/s"
		res = strconv.FormatFloat(float64(s)/GB, 'f', prec, 64)
	case s >= MB:
		unit = "MB/s"
		res = strconv.FormatFloat(float64(s)/MB, 'f', prec, 64)
	case s >= KB:
		unit = "kB/s"
		res = strconv.FormatFloat(float64(s)/KB, 'f', prec, 64)
	default:
		unit = "b/s"
		res = strconv.FormatInt(int64(s), 10)
	}

	if st.Flag(' ') {
		res += " "
	}
	res += unit

	if w, ok := st.Width(); ok {
		if len(res) < w {
			pad := strings.Repeat(" ", w-len(res))
			if st.Flag(int('-')) {
				res += pad
			} else {
				res = pad + res
			}
		}
	}

	io.WriteString(st, res)
}

// EwmaSpeed exponential-weighted-moving-average based speed decorator,
// with dynamic unit measure adjustment.
//
//	`unit` one of [0|UnitKiB|UnitKB] zero for no unit
//
//	`unitFormat` printf compatible verb for value, like "%f" or "%d"
//
//	`average` MovingAverage implementation
//
//	`wcc` optional WC config
//
// unitFormat example if UnitKiB is chosen:
//
//	"%.1f" = "1.0MiB/s" or "% .1f" = "1.0 MiB/s"
func EwmaSpeed(unit int, unitFormat string, age float64, wcc ...WC) Decorator {
	return MovingAverageSpeed(unit, unitFormat, ewma.NewMovingAverage(age), wcc...)
}

// MovingAverageSpeed decorator relies on MovingAverage implementation
// to calculate its average.
//
//	`unit` one of [0|UnitKiB|UnitKB] zero for no unit
//
//	`unitFormat` printf compatible verb for value, like "%f" or "%d"
//
//	`average` MovingAverage implementation
//
//	`wcc` optional WC config
func MovingAverageSpeed(unit int, unitFormat string, average MovingAverage, wcc ...WC) Decorator {
	var wc WC
	for _, widthConf := range wcc {
		wc = widthConf
	}
	wc.Init()
	d := &movingAverageSpeed{
		WC:         wc,
		unit:       unit,
		unitFormat: unitFormat,
		average:    average,
	}
	return d
}

type movingAverageSpeed struct {
	WC
	unit        int
	unitFormat  string
	average     ewma.MovingAverage
	msg         string
	completeMsg *string
}

func (d *movingAverageSpeed) Decor(st *Statistics) string {
	if st.Completed {
		if d.completeMsg != nil {
			return d.FormatMsg(*d.completeMsg)
		}
		return d.FormatMsg(d.msg)
	}

	speed := d.average.Value()
	switch d.unit {
	case UnitKiB:
		d.msg = fmt.Sprintf(d.unitFormat, SpeedKiB(speed))
	case UnitKB:
		d.msg = fmt.Sprintf(d.unitFormat, SpeedKB(speed))
	default:
		d.msg = fmt.Sprintf(d.unitFormat, speed)
	}

	return d.FormatMsg(d.msg)
}

func (d *movingAverageSpeed) NextAmount(n int, wdd ...time.Duration) {
	var workDuration time.Duration
	for _, wd := range wdd {
		workDuration = wd
	}
	speed := float64(n) / workDuration.Seconds() / 1000
	if math.IsInf(speed, 0) || math.IsNaN(speed) {
		return
	}
	d.average.Add(speed)
}

func (d *movingAverageSpeed) OnCompleteMessage(msg string) {
	d.completeMsg = &msg
}

// AverageSpeed decorator with dynamic unit measure adjustment.
//
//	`unit` one of [0|UnitKiB|UnitKB] zero for no unit
//
//	`unitFormat` printf compatible verb for value, like "%f" or "%d"
//
//	`wcc` optional WC config
//
// unitFormat example if UnitKiB is chosen:
//
//	"%.1f" = "1.0MiB/s" or "% .1f" = "1.0 MiB/s"
func AverageSpeed(unit int, unitFormat string, wcc ...WC) Decorator {
	var wc WC
	for _, widthConf := range wcc {
		wc = widthConf
	}
	wc.Init()
	d := &averageSpeed{
		WC:         wc,
		unit:       unit,
		unitFormat: unitFormat,
		startTime:  time.Now(),
	}
	return d
}

type averageSpeed struct {
	WC
	unit        int
	unitFormat  string
	startTime   time.Time
	msg         string
	completeMsg *string
}

func (d *averageSpeed) Decor(st *Statistics) string {
	if st.Completed {
		if d.completeMsg != nil {
			return d.FormatMsg(*d.completeMsg)
		}
		return d.FormatMsg(d.msg)
	}

	timeElapsed := time.Since(d.startTime)
	speed := float64(st.Current) / timeElapsed.Seconds()

	switch d.unit {
	case UnitKiB:
		d.msg = fmt.Sprintf(d.unitFormat, SpeedKiB(speed))
	case UnitKB:
		d.msg = fmt.Sprintf(d.unitFormat, SpeedKB(speed))
	default:
		d.msg = fmt.Sprintf(d.unitFormat, speed)
	}

	return d.FormatMsg(d.msg)
}

func (d *averageSpeed) OnCompleteMessage(msg string) {
	d.completeMsg = &msg
}
<|endoftext|>


<commit_before>package inmem

import (
	"context"
	"errors"
	"strings"
	"sync"
	"sync/atomic"

	log "github.com/hashicorp/go-hclog"
	"github.com/hashicorp/vault/physical"

	"github.com/armon/go-radix"
)

// Verify interfaces are satisfied
var _ physical.Backend = (*InmemBackend)(nil)
var _ physical.HABackend = (*InmemHABackend)(nil)
var _ physical.HABackend = (*TransactionalInmemHABackend)(nil)
var _ physical.Lock = (*InmemLock)(nil)
var _ physical.Transactional = (*TransactionalInmemBackend)(nil)
var _ physical.Transactional = (*TransactionalInmemHABackend)(nil)

var (
	PutDisabledError    = errors.New("put operations disabled in inmem backend")
	GetDisabledError    = errors.New("get operations disabled in inmem backend")
	DeleteDisabledError = errors.New("delete operations disabled in inmem backend")
	ListDisabledError   = errors.New("list operations disabled in inmem backend")
)

// InmemBackend is an in-memory only physical backend. It is useful
// for testing and development situations where the data is not
// expected to be durable.
type InmemBackend struct {
	sync.RWMutex
	root       *radix.Tree
	permitPool *physical.PermitPool
	logger     log.Logger
	failGet    *uint32
	failPut    *uint32
	failDelete *uint32
	failList   *uint32
}

type TransactionalInmemBackend struct {
	InmemBackend
}

// NewInmem constructs a new in-memory backend
func NewInmem(_ map[string]string, logger log.Logger) (physical.Backend, error) {
	in := &InmemBackend{
		root:       radix.New(),
		permitPool: physical.NewPermitPool(physical.DefaultParallelOperations),
		logger:     logger,
		failGet:    new(uint32),
		failPut:    new(uint32),
		failDelete: new(uint32),
		failList:   new(uint32),
	}
	return in, nil
}

// Basically for now just creates a permit pool of size 1 so only one operation
// can run at a time
func NewTransactionalInmem(_ map[string]string, logger log.Logger) (physical.Backend, error) {
	in := &TransactionalInmemBackend{
		InmemBackend: InmemBackend{
			root:       radix.New(),
			permitPool: physical.NewPermitPool(1),
			logger:     logger,
		},
	}
	return in, nil
}

// Put is used to insert or update an entry
func (i *InmemBackend) Put(ctx context.Context, entry *physical.Entry) error {
	i.permitPool.Acquire()
	defer i.permitPool.Release()

	i.Lock()
	defer i.Unlock()

	return i.PutInternal(ctx, entry)
}

func (i *InmemBackend) PutInternal(ctx context.Context, entry *physical.Entry) error {
	if atomic.LoadUint32(i.failPut) != 0 {
		return PutDisabledError
	}

	i.root.Insert(entry.Key, entry.Value)
	return nil
}

func (i *InmemBackend) FailPut(fail bool) {
	var val uint32
	if fail {
		val = 1
	}
	atomic.StoreUint32(i.failPut, val)
}

// Get is used to fetch an entry
func (i *InmemBackend) Get(ctx context.Context, key string) (*physical.Entry, error) {
	i.permitPool.Acquire()
	defer i.permitPool.Release()

	i.RLock()
	defer i.RUnlock()

	return i.GetInternal(ctx, key)
}

func (i *InmemBackend) GetInternal(ctx context.Context, key string) (*physical.Entry, error) {
	if atomic.LoadUint32(i.failGet) != 0 {
		return nil, GetDisabledError
	}

	if raw, ok := i.root.Get(key); ok {
		return &physical.Entry{
			Key:   key,
			Value: raw.([]byte),
		}, nil
	}
	return nil, nil
}

func (i *InmemBackend) FailGet(fail bool) {
	var val uint32
	if fail {
		val = 1
	}
	atomic.StoreUint32(i.failGet, val)
}

// Delete is used to permanently delete an entry
func (i *InmemBackend) Delete(ctx context.Context, key string) error {
	i.permitPool.Acquire()
	defer i.permitPool.Release()

	i.Lock()
	defer i.Unlock()

	return i.DeleteInternal(ctx, key)
}

func (i *InmemBackend) DeleteInternal(ctx context.Context, key string) error {
	if atomic.LoadUint32(i.failDelete) != 0 {
		return DeleteDisabledError
	}

	i.root.Delete(key)
	return nil
}

func (i *InmemBackend) FailDelete(fail bool) {
	var val uint32
	if fail {
		val = 1
	}
	atomic.StoreUint32(i.failDelete, val)
}

// List is used ot list all the keys under a given
// prefix, up to the next prefix.
func (i *InmemBackend) List(ctx context.Context, prefix string) ([]string, error) {
	i.permitPool.Acquire()
	defer i.permitPool.Release()

	i.RLock()
	defer i.RUnlock()

	return i.ListInternal(prefix)
}

func (i *InmemBackend) ListInternal(prefix string) ([]string, error) {
	if atomic.LoadUint32(i.failList) != 0 {
		return nil, ListDisabledError
	}

	var out []string
	seen := make(map[string]interface{})
	walkFn := func(s string, v interface{}) bool {
		trimmed := strings.TrimPrefix(s, prefix)
		sep := strings.Index(trimmed, "/")
		if sep == -1 {
			out = append(out, trimmed)
		} else {
			trimmed = trimmed[:sep+1]
			if _, ok := seen[trimmed]; !ok {
				out = append(out, trimmed)
				seen[trimmed] = struct{}{}
			}
		}
		return false
	}
	i.root.WalkPrefix(prefix, walkFn)

	return out, nil
}

func (i *InmemBackend) FailList(fail bool) {
	var val uint32
	if fail {
		val = 1
	}
	atomic.StoreUint32(i.failList, val)
}

// Implements the transaction interface
func (t *TransactionalInmemBackend) Transaction(ctx context.Context, txns []*physical.TxnEntry) error {
	t.permitPool.Acquire()
	defer t.permitPool.Release()

	t.Lock()
	defer t.Unlock()

	return physical.GenericTransactionHandler(ctx, t, txns)
}
<commit_msg>Fix nil pointer in transactional_inmem<commit_after>package inmem

import (
	"context"
	"errors"
	"strings"
	"sync"
	"sync/atomic"

	log "github.com/hashicorp/go-hclog"
	"github.com/hashicorp/vault/physical"

	"github.com/armon/go-radix"
)

// Verify interfaces are satisfied
var _ physical.Backend = (*InmemBackend)(nil)
var _ physical.HABackend = (*InmemHABackend)(nil)
var _ physical.HABackend = (*TransactionalInmemHABackend)(nil)
var _ physical.Lock = (*InmemLock)(nil)
var _ physical.Transactional = (*TransactionalInmemBackend)(nil)
var _ physical.Transactional = (*TransactionalInmemHABackend)(nil)

var (
	PutDisabledError    = errors.New("put operations disabled in inmem backend")
	GetDisabledError    = errors.New("get operations disabled in inmem backend")
	DeleteDisabledError = errors.New("delete operations disabled in inmem backend")
	ListDisabledError   = errors.New("list operations disabled in inmem backend")
)

// InmemBackend is an in-memory only physical backend. It is useful
// for testing and development situations where the data is not
// expected to be durable.
type InmemBackend struct {
	sync.RWMutex
	root       *radix.Tree
	permitPool *physical.PermitPool
	logger     log.Logger
	failGet    *uint32
	failPut    *uint32
	failDelete *uint32
	failList   *uint32
}

type TransactionalInmemBackend struct {
	InmemBackend
}

// NewInmem constructs a new in-memory backend
func NewInmem(_ map[string]string, logger log.Logger) (physical.Backend, error) {
	in := &InmemBackend{
		root:       radix.New(),
		permitPool: physical.NewPermitPool(physical.DefaultParallelOperations),
		logger:     logger,
		failGet:    new(uint32),
		failPut:    new(uint32),
		failDelete: new(uint32),
		failList:   new(uint32),
	}
	return in, nil
}

// Basically for now just creates a permit pool of size 1 so only one operation
// can run at a time
func NewTransactionalInmem(_ map[string]string, logger log.Logger) (physical.Backend, error) {
	in := &TransactionalInmemBackend{
		InmemBackend: InmemBackend{
			root:       radix.New(),
			permitPool: physical.NewPermitPool(1),
			logger:     logger,
			failGet:    new(uint32),
			failPut:    new(uint32),
			failDelete: new(uint32),
			failList:   new(uint32),
		},
	}
	return in, nil
}

// Put is used to insert or update an entry
func (i *InmemBackend) Put(ctx context.Context, entry *physical.Entry) error {
	i.permitPool.Acquire()
	defer i.permitPool.Release()

	i.Lock()
	defer i.Unlock()

	return i.PutInternal(ctx, entry)
}

func (i *InmemBackend) PutInternal(ctx context.Context, entry *physical.Entry) error {
	if atomic.LoadUint32(i.failPut) != 0 {
		return PutDisabledError
	}

	i.root.Insert(entry.Key, entry.Value)
	return nil
}

func (i *InmemBackend) FailPut(fail bool) {
	var val uint32
	if fail {
		val = 1
	}
	atomic.StoreUint32(i.failPut, val)
}

// Get is used to fetch an entry
func (i *InmemBackend) Get(ctx context.Context, key string) (*physical.Entry, error) {
	i.permitPool.Acquire()
	defer i.permitPool.Release()

	i.RLock()
	defer i.RUnlock()

	return i.GetInternal(ctx, key)
}

func (i *InmemBackend) GetInternal(ctx context.Context, key string) (*physical.Entry, error) {
	if atomic.LoadUint32(i.failGet) != 0 {
		return nil, GetDisabledError
	}

	if raw, ok := i.root.Get(key); ok {
		return &physical.Entry{
			Key:   key,
			Value: raw.([]byte),
		}, nil
	}
	return nil, nil
}

func (i *InmemBackend) FailGet(fail bool) {
	var val uint32
	if fail {
		val = 1
	}
	atomic.StoreUint32(i.failGet, val)
}

// Delete is used to permanently delete an entry
func (i *InmemBackend) Delete(ctx context.Context, key string) error {
	i.permitPool.Acquire()
	defer i.permitPool.Release()

	i.Lock()
	defer i.Unlock()

	return i.DeleteInternal(ctx, key)
}

func (i *InmemBackend) DeleteInternal(ctx context.Context, key string) error {
	if atomic.LoadUint32(i.failDelete) != 0 {
		return DeleteDisabledError
	}

	i.root.Delete(key)
	return nil
}

func (i *InmemBackend) FailDelete(fail bool) {
	var val uint32
	if fail {
		val = 1
	}
	atomic.StoreUint32(i.failDelete, val)
}

// List is used ot list all the keys under a given
// prefix, up to the next prefix.
func (i *InmemBackend) List(ctx context.Context, prefix string) ([]string, error) {
	i.permitPool.Acquire()
	defer i.permitPool.Release()

	i.RLock()
	defer i.RUnlock()

	return i.ListInternal(prefix)
}

func (i *InmemBackend) ListInternal(prefix string) ([]string, error) {
	if atomic.LoadUint32(i.failList) != 0 {
		return nil, ListDisabledError
	}

	var out []string
	seen := make(map[string]interface{})
	walkFn := func(s string, v interface{}) bool {
		trimmed := strings.TrimPrefix(s, prefix)
		sep := strings.Index(trimmed, "/")
		if sep == -1 {
			out = append(out, trimmed)
		} else {
			trimmed = trimmed[:sep+1]
			if _, ok := seen[trimmed]; !ok {
				out = append(out, trimmed)
				seen[trimmed] = struct{}{}
			}
		}
		return false
	}
	i.root.WalkPrefix(prefix, walkFn)

	return out, nil
}

func (i *InmemBackend) FailList(fail bool) {
	var val uint32
	if fail {
		val = 1
	}
	atomic.StoreUint32(i.failList, val)
}

// Implements the transaction interface
func (t *TransactionalInmemBackend) Transaction(ctx context.Context, txns []*physical.TxnEntry) error {
	t.permitPool.Acquire()
	defer t.permitPool.Release()

	t.Lock()
	defer t.Unlock()

	return physical.GenericTransactionHandler(ctx, t, txns)
}
<|endoftext|>


<commit_before>package gosf

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
)

/*************************************/
/************** Operator *************/
/*************************************/

// Operator can make a *http.Request instance according to given request ctx
// for http.Client to send request to salesforce or returns an error.
// It can also handle the response responded from salesforce.
type Operator interface {
	// Make request by given request context.
	Make(*RequestCtx) (*Request, error)
	// Handle success response from salesforce.
	Handle(*http.Response) error
}

var (
	_ Operator = &opCreate{}
	_ Operator = &opUpdate{}
	_ Operator = &opDelete{}
	_ Operator = &opGet{}
	_ Operator = &OpQuery{}
)

/************************************/
/********** CREATE SOBJECT **********/
/************************************/

type (
	// opCreate is a request for creating SObject.
	opCreate struct {
		sobjectName string
		sobject     interface{}
		result      *opCreateResult
	}

	opCreateResult struct {
		ID      string   `json:"id"`
		Errors  []string `json:"errors"`
		Success bool     `json:"success"`
	}
)

func (op *opCreate) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case op.sobject == nil:
		return nil, errors.New("missing Sobject")
	default:
		return NewRequest(http.MethodPost, ctx.SobjectURLWithName(op.sobjectName), op.sobject), nil
	}
}

func (op *opCreate) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusCreated {
		return fmt.Errorf("create operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusCreated)
	}
	return json.NewDecoder(resp.Body).Decode(&op.result)
}

/************************************/
/********** UPDATE SOBJECT **********/
/************************************/

// opUpdate is a request for updating SObject.
type opUpdate struct {
	sobjectName string
	sobjectID   string
	sobject     interface{}
}

func (op *opUpdate) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case op.sobjectID == "":
		return nil, errors.New("missing Sobject id")
	case op.sobject == nil:
		return nil, errors.New("missing Sobject")
	default:
		return NewRequest(http.MethodPatch, ctx.SobjectURLWithID(op.sobjectName, op.sobjectID), op.sobject), nil
	}
}

func (op *opUpdate) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("update operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusOK)
	}
	return nil
}

/************************************/
/********** DELETE SOBJECT **********/
/************************************/

// opDelete is a request for deleting SObject.
type opDelete struct {
	sobjectName string
	sobjectID   string
}

func (op *opDelete) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case op.sobjectID == "":
		return nil, errors.New("missing Sobject id")
	default:
		return NewRequest(http.MethodDelete, ctx.SobjectURLWithID(op.sobjectName, op.sobjectID), nil), nil
	}
}

func (op *opDelete) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusNoContent {
		return fmt.Errorf("delete operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusNoContent)
	}
	return nil
}

/************************************/
/*********** GET SOBJECT ************/
/************************************/

// opGet is a request for getting SObject.
type opGet struct {
	sobjectName string
	sobjectID   string
	target      interface{}
}

func (op *opGet) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case op.sobjectID == "":
		return nil, errors.New("missing Sobject id")
	default:
		return NewRequest(http.MethodGet, ctx.SobjectURLWithID(op.sobjectName, op.sobjectID), nil), nil
	}
}

func (op *opGet) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("get operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusOK)
	}
	return json.NewDecoder(resp.Body).Decode(&op.target)
}

/***********************************/
/********** QUERY SOBJECT **********/
/***********************************/

type (
	// OpQuery is a request for quering SObject.
	// Set which Sobject type and fileds to query by Select() and From() is necessary.
	// The reset operations below is optional:
	// 	- OrderDesc(), OrderAsc(), OrderReset(), OrderNullFirst(), OrderNullLast() to control ORDER key.
	//  - Limit() to control LIMIT key
	// See the methods' doc  for more details.
	OpQuery struct {
		sobjectName  string
		selectFileds []string
		whereClauses []whereClause
		order        string
		nullPriority string
		limit        int
		target       interface{}
	}

	whereClause struct {
		field     string
		condition interface{}
	}
)

// IsValid returns true if whereClause's condition is valid.
// In SOQL, condition in where clause can only be number, boolean or string.
func (c *whereClause) IsValid() bool {
	switch c.condition.(type) {
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
		return true
	case bool:
		return true
	case string:
		return true
	default:
		return false
	}
}

// Make request by given request context.
func (op *OpQuery) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case len(op.selectFileds) <= 0:
		return nil, errors.New("missing select fields")
	default:
		return NewRequest(http.MethodGet, ctx.QueryURL(op.makeQueryStatment()), nil), nil
	}
}

// Handle success response from salesforce.
func (op *OpQuery) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("get operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusOK)
	}
	return json.NewDecoder(resp.Body).Decode(&op.target)
}

// Select defines which columns of SObject will be return in result.
func (op *OpQuery) Select(fields ...string) *OpQuery {
	if op.selectFileds == nil {
		op.selectFileds = make([]string, 0)
	}
	op.selectFileds = append(op.selectFileds, fields...)
	return op
}

// From defines which SObject will be query.
func (op *OpQuery) From(sobjectName string) *OpQuery {
	op.sobjectName = sobjectName
	return op
}

// OrderDesc defines the order column to sort results descendant.
func (op *OpQuery) OrderDesc(field string) *OpQuery {
	op.order = fmt.Sprintf("%s DESC", field)
	return op
}

// OrderAsc defines the order column to sort results ascendant.
func (op *OpQuery) OrderAsc(field string) *OpQuery {
	op.order = fmt.Sprintf("%s ASC", field)
	return op
}

// OrderReset resets the order statment=="".
func (op *OpQuery) OrderReset() *OpQuery {
	op.order = ""
	return op
}

// OrderNullFirst make null null column value first in query results when given order column.
func (op *OpQuery) OrderNullFirst() *OpQuery {
	op.nullPriority = "NULL FIRST"
	return op
}

// OrderNullLast make null column value last in query results when given order column.
func (op *OpQuery) OrderNullLast() *OpQuery {
	op.nullPriority = "NULL LAST"
	return op
}

// Limit defines the max records will be return.
// if n==0, will treat it as a signal to reset limitation to unlimited.
func (op *OpQuery) Limit(n int) *OpQuery {
	op.limit = n
	return op
}

func (op *OpQuery) makeQueryStatment() string {
	baseQuery := fmt.Sprintf("SELECT %s FROM %s", op.makeSelectStatment(), op.sobjectName)
	return strings.Join([]string{
		baseQuery,
		op.makeWhereCluasesStatment(),
		op.makeOrderStatment(),
		op.makeLimitStatment(),
	}, " ")
}

// makeSelectStatment renders statment as below if r.selectFileds has elements:
// SELECT <FIELD1> [,<FIELD2>]...
func (op *OpQuery) makeSelectStatment() string {
	if len(op.selectFileds) == 0 {
		defaultLogger.Print("[QuerySObjectRequest] Missing Select fields")
	}
	return strings.Join(op.selectFileds, ",")
}

// makeWhereCluasesStatment renders statment as below if r.whereClauses has elements:
// WHERE <FIELD1>=<CODITION1> [,<FIELD2>=<CODITION2>]...
func (op *OpQuery) makeWhereCluasesStatment() string {
	var data = make(map[string]interface{})
	for _, clause := range op.whereClauses {
		if !clause.IsValid() {
			defaultLogger.Printf(
				"[QuerySObjectRequest] Found invalid where-clause which should be type of number, boolean or string. QuerySObject=%s, Field=%s, ConditionType=%T",
				op.sobjectName, clause.field, clause.condition,
			)
			continue
		}
		data[clause.field] = clause.condition
	}

	if len(data) == 0 {
		return ""
	}

	var filters = make([]string, 0)
	for field, cond := range data {
		filters = append(filters, fmt.Sprintf("%s=%v", field, cond))
	}
	return fmt.Sprintf("WHERE %s", strings.Join(filters, ","))
}

// makeOrderStatment renders statment as below if r.order!="":
// ORDER BY <FIELD> <DESC|ASC> NULL <FIRST|LAST>
func (op *OpQuery) makeOrderStatment() string {
	if op.order == "" {
		return ""
	}

	var nullPriority = op.nullPriority
	if op.nullPriority == "" {
		nullPriority = "NULL FIRST"
	}
	return fmt.Sprintf("ORDER BY %s %s", op.order, nullPriority)
}

// makeLimitStatment renders statment as below if r.limit>0:
// LIMIT <LIMITATION>
func (op *OpQuery) makeLimitStatment() string {
	if op.limit > 0 {
		return fmt.Sprintf("LIMIT %d", op.limit)
	}
	return ""
}
<commit_msg>add NewOpQuery method<commit_after>package gosf

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
)

/*************************************/
/************** Operator *************/
/*************************************/

// Operator can make a *http.Request instance according to given request ctx
// for http.Client to send request to salesforce or returns an error.
// It can also handle the response responded from salesforce.
type Operator interface {
	// Make request by given request context.
	Make(*RequestCtx) (*Request, error)
	// Handle success response from salesforce.
	Handle(*http.Response) error
}

var (
	_ Operator = &opCreate{}
	_ Operator = &opUpdate{}
	_ Operator = &opDelete{}
	_ Operator = &opGet{}
	_ Operator = &OpQuery{}
)

/************************************/
/********** CREATE SOBJECT **********/
/************************************/

type (
	// opCreate is a request for creating SObject.
	opCreate struct {
		sobjectName string
		sobject     interface{}
		result      *opCreateResult
	}

	opCreateResult struct {
		ID      string   `json:"id"`
		Errors  []string `json:"errors"`
		Success bool     `json:"success"`
	}
)

func (op *opCreate) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case op.sobject == nil:
		return nil, errors.New("missing Sobject")
	default:
		return NewRequest(http.MethodPost, ctx.SobjectURLWithName(op.sobjectName), op.sobject), nil
	}
}

func (op *opCreate) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusCreated {
		return fmt.Errorf("create operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusCreated)
	}
	return json.NewDecoder(resp.Body).Decode(&op.result)
}

/************************************/
/********** UPDATE SOBJECT **********/
/************************************/

// opUpdate is a request for updating SObject.
type opUpdate struct {
	sobjectName string
	sobjectID   string
	sobject     interface{}
}

func (op *opUpdate) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case op.sobjectID == "":
		return nil, errors.New("missing Sobject id")
	case op.sobject == nil:
		return nil, errors.New("missing Sobject")
	default:
		return NewRequest(http.MethodPatch, ctx.SobjectURLWithID(op.sobjectName, op.sobjectID), op.sobject), nil
	}
}

func (op *opUpdate) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("update operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusOK)
	}
	return nil
}

/************************************/
/********** DELETE SOBJECT **********/
/************************************/

// opDelete is a request for deleting SObject.
type opDelete struct {
	sobjectName string
	sobjectID   string
}

func (op *opDelete) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case op.sobjectID == "":
		return nil, errors.New("missing Sobject id")
	default:
		return NewRequest(http.MethodDelete, ctx.SobjectURLWithID(op.sobjectName, op.sobjectID), nil), nil
	}
}

func (op *opDelete) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusNoContent {
		return fmt.Errorf("delete operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusNoContent)
	}
	return nil
}

/************************************/
/*********** GET SOBJECT ************/
/************************************/

// opGet is a request for getting SObject.
type opGet struct {
	sobjectName string
	sobjectID   string
	target      interface{}
}

func (op *opGet) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case op.sobjectID == "":
		return nil, errors.New("missing Sobject id")
	default:
		return NewRequest(http.MethodGet, ctx.SobjectURLWithID(op.sobjectName, op.sobjectID), nil), nil
	}
}

func (op *opGet) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("get operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusOK)
	}
	return json.NewDecoder(resp.Body).Decode(&op.target)
}

/***********************************/
/********** QUERY SOBJECT **********/
/***********************************/

type (
	// OpQuery is a request for quering SObject.
	// Set which Sobject type and fileds to query by Select() and From() is necessary.
	// The reset operations below is optional:
	// 	- OrderDesc(), OrderAsc(), OrderReset(), OrderNullFirst(), OrderNullLast() to control ORDER key.
	//  - Limit() to control LIMIT key
	// See the methods' doc  for more details.
	OpQuery struct {
		sobjectName  string
		selectFileds []string
		whereClauses []whereClause
		order        string
		nullPriority string
		limit        int
		target       interface{}
	}

	whereClause struct {
		field     string
		condition interface{}
	}
)

// IsValid returns true if whereClause's condition is valid.
// In SOQL, condition in where clause can only be number, boolean or string.
func (c *whereClause) IsValid() bool {
	switch c.condition.(type) {
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
		return true
	case bool:
		return true
	case string:
		return true
	default:
		return false
	}
}

// Make request by given request context.
func (op *OpQuery) Make(ctx *RequestCtx) (*Request, error) {
	switch {
	case op.sobjectName == "":
		return nil, errors.New("missing Sobject name")
	case len(op.selectFileds) <= 0:
		return nil, errors.New("missing select fields")
	default:
		return NewRequest(http.MethodGet, ctx.QueryURL(op.makeQueryStatment()), nil), nil
	}
}

// Handle success response from salesforce.
func (op *OpQuery) Handle(resp *http.Response) error {
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("get operator can't handle response with code %d, expect %d", resp.StatusCode, http.StatusOK)
	}
	return json.NewDecoder(resp.Body).Decode(&op.target)
}

// Select defines which columns of SObject will be return in result.
func (op *OpQuery) Select(fields ...string) *OpQuery {
	if op.selectFileds == nil {
		op.selectFileds = make([]string, 0)
	}
	op.selectFileds = append(op.selectFileds, fields...)
	return op
}

// From defines which SObject will be query.
func (op *OpQuery) From(sobjectName string) *OpQuery {
	op.sobjectName = sobjectName
	return op
}

// OrderDesc defines the order column to sort results descendant.
func (op *OpQuery) OrderDesc(field string) *OpQuery {
	op.order = fmt.Sprintf("%s DESC", field)
	return op
}

// OrderAsc defines the order column to sort results ascendant.
func (op *OpQuery) OrderAsc(field string) *OpQuery {
	op.order = fmt.Sprintf("%s ASC", field)
	return op
}

// OrderReset resets the order statment=="".
func (op *OpQuery) OrderReset() *OpQuery {
	op.order = ""
	return op
}

// OrderNullFirst make null null column value first in query results when given order column.
func (op *OpQuery) OrderNullFirst() *OpQuery {
	op.nullPriority = "NULL FIRST"
	return op
}

// OrderNullLast make null column value last in query results when given order column.
func (op *OpQuery) OrderNullLast() *OpQuery {
	op.nullPriority = "NULL LAST"
	return op
}

// Limit defines the max records will be return.
// if n==0, will treat it as a signal to reset limitation to unlimited.
func (op *OpQuery) Limit(n int) *OpQuery {
	op.limit = n
	return op
}

func (op *OpQuery) makeQueryStatment() string {
	baseQuery := fmt.Sprintf("SELECT %s FROM %s", op.makeSelectStatment(), op.sobjectName)
	return strings.Join([]string{
		baseQuery,
		op.makeWhereCluasesStatment(),
		op.makeOrderStatment(),
		op.makeLimitStatment(),
	}, " ")
}

// makeSelectStatment renders statment as below if r.selectFileds has elements:
// SELECT <FIELD1> [,<FIELD2>]...
func (op *OpQuery) makeSelectStatment() string {
	if len(op.selectFileds) == 0 {
		defaultLogger.Print("[QuerySObjectRequest] Missing Select fields")
	}
	return strings.Join(op.selectFileds, ",")
}

// makeWhereCluasesStatment renders statment as below if r.whereClauses has elements:
// WHERE <FIELD1>=<CODITION1> [,<FIELD2>=<CODITION2>]...
func (op *OpQuery) makeWhereCluasesStatment() string {
	var data = make(map[string]interface{})
	for _, clause := range op.whereClauses {
		if !clause.IsValid() {
			defaultLogger.Printf(
				"[QuerySObjectRequest] Found invalid where-clause which should be type of number, boolean or string. QuerySObject=%s, Field=%s, ConditionType=%T",
				op.sobjectName, clause.field, clause.condition,
			)
			continue
		}
		data[clause.field] = clause.condition
	}

	if len(data) == 0 {
		return ""
	}

	var filters = make([]string, 0)
	for field, cond := range data {
		filters = append(filters, fmt.Sprintf("%s=%v", field, cond))
	}
	return fmt.Sprintf("WHERE %s", strings.Join(filters, ","))
}

// makeOrderStatment renders statment as below if r.order!="":
// ORDER BY <FIELD> <DESC|ASC> NULL <FIRST|LAST>
func (op *OpQuery) makeOrderStatment() string {
	if op.order == "" {
		return ""
	}

	var nullPriority = op.nullPriority
	if op.nullPriority == "" {
		nullPriority = "NULL FIRST"
	}
	return fmt.Sprintf("ORDER BY %s %s", op.order, nullPriority)
}

// makeLimitStatment renders statment as below if r.limit>0:
// LIMIT <LIMITATION>
func (op *OpQuery) makeLimitStatment() string {
	if op.limit > 0 {
		return fmt.Sprintf("LIMIT %d", op.limit)
	}
	return ""
}

// NewOpQuery returns a OpQuery instance with given sobjectName.
func NewOpQuery(sobjectName string) *OpQuery {
	return &OpQuery{
		sobjectName: sobjectName,
	}
}
<|endoftext|>


<commit_before>package action

import (
	"context"
	"strings"

	"github.com/gopasspw/gopass/pkg/cui"
	"github.com/urfave/cli"
)

// ConfirmRecipients asks the user to confirm a given set of recipients
func (s *Action) ConfirmRecipients(ctx context.Context, name string, recipients []string) ([]string, error) {
	return cui.ConfirmRecipients(ctx, s.Store.Crypto(ctx, name), name, recipients)
}

type argList []string

func (a argList) Get(n int) string {
	if len(a) > n {
		return a[n]
	}
	return ""
}

func parseArgs(c *cli.Context) (argList, map[string]string) {
	args := make(argList, 0, len(c.Args()))
	kvps := make(map[string]string, len(c.Args()))
OUTER:
	for _, arg := range c.Args() {
		for _, sep := range []string{":", "="} {
			if !strings.Contains(arg, sep) {
				continue
			}
			p := strings.Split(arg, sep)
			if len(p) < 2 {
				args = append(args, arg)
				continue OUTER
			}
			key := p[0]
			kvps[key] = p[1]
			continue OUTER
		}
		args = append(args, arg)
	}
	return args, kvps
}
<commit_msg>Support key values with colons (#1156)<commit_after>package action

import (
	"context"
	"strings"

	"github.com/gopasspw/gopass/pkg/cui"
	"github.com/urfave/cli"
)

// ConfirmRecipients asks the user to confirm a given set of recipients
func (s *Action) ConfirmRecipients(ctx context.Context, name string, recipients []string) ([]string, error) {
	return cui.ConfirmRecipients(ctx, s.Store.Crypto(ctx, name), name, recipients)
}

type argList []string

func (a argList) Get(n int) string {
	if len(a) > n {
		return a[n]
	}
	return ""
}

func parseArgs(c *cli.Context) (argList, map[string]string) {
	args := make(argList, 0, len(c.Args()))
	kvps := make(map[string]string, len(c.Args()))
OUTER:
	for _, arg := range c.Args() {
		for _, sep := range []string{":", "="} {
			if !strings.Contains(arg, sep) {
				continue
			}
			p := strings.Split(arg, sep)
			if len(p) < 2 {
				args = append(args, arg)
				continue OUTER
			}
			key := p[0]
			kvps[key] = strings.Join(p[1:], ":")
			continue OUTER
		}
		args = append(args, arg)
	}
	return args, kvps
}
<|endoftext|>


<commit_before>package gdns

import (
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestHTTPRequest(t *testing.T) {
	HTTPClient = nil
	_, _, err := httpGet(http.MethodGet, "https://baidu.com", nil)
	assert.Error(t, err, "Error due to did not initialize http client")

	HTTPClient = &http.Client{
		Timeout: 2 * time.Second,
	}
	_, statusCode, err := httpGet(http.MethodGet, "https://baidu.com", nil)
	assert.Equalf(t, 200, statusCode, "Except status code 200, but %d get", statusCode)
	assert.NoError(t, err, "Error due to did not initialize http client")
}
<commit_msg>MOD: modify test case<commit_after>package gdns

import (
	"net/http"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestHTTPRequest(t *testing.T) {
	HTTPClient = nil
	_, _, err := httpGet(http.MethodGet, "https://github.com", nil)
	assert.Error(t, err, "Error due to did not initialize http client")

	HTTPClient = &http.Client{
		Timeout: 5 * time.Second,
	}
	_, statusCode, err := httpGet(http.MethodGet, "https://github.com", nil)
	assert.Equalf(t, 200, statusCode, "Except status code 200, but %d get", statusCode)
	assert.NoError(t, err, "Error due to did not initialize http client")
}
<|endoftext|>


<commit_before>package rkeworker

import (
	"context"
	"io"
	"os"
	"reflect"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"github.com/rancher/norman/types/slice"
	"github.com/rancher/rke/services"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/sirupsen/logrus"
)

type NodeConfig struct {
	ClusterName string                `json:"clusterName"`
	Certs       string                `json:"certs"`
	Processes   map[string]v3.Process `json:"processes"`
	Files       []v3.File             `json:"files"`
}

func runProcess(ctx context.Context, name string, p v3.Process, start bool) error {
	c, err := client.NewEnvClient()
	if err != nil {
		return err
	}

	args := filters.NewArgs()
	args.Add("label", "io.cattle.process.name="+name)

	containers, err := c.ContainerList(ctx, types.ContainerListOptions{
		All:     true,
		Filters: args,
	})
	if err != nil {
		return err
	}

	var matchedContainers []types.Container
	for _, container := range containers {
		changed, err := changed(ctx, c, p, container)
		if err != nil {
			return err
		}

		if changed {
			err := remove(ctx, c, container.ID)
			if err != nil {
				return err
			}
		} else {
			matchedContainers = append(matchedContainers, container)
		}
	}

	for i := 1; i < len(matchedContainers); i++ {
		if err := remove(ctx, c, matchedContainers[i].ID); err != nil {
			return err
		}
	}

	if len(matchedContainers) > 0 {
		c.ContainerStart(ctx, matchedContainers[0].ID, types.ContainerStartOptions{})
		return nil
	}

	config, hostConfig, _ := services.GetProcessConfig(p)
	if config.Labels == nil {
		config.Labels = map[string]string{}
	}
	config.Labels["io.cattle.process.name"] = name

	newContainer, err := c.ContainerCreate(ctx, config, hostConfig, nil, name)
	if client.IsErrImageNotFound(err) {
		var output io.ReadCloser
		output, err = c.ImagePull(ctx, config.Image, types.ImagePullOptions{})
		if err != nil {
			return err
		}
		defer output.Close()
		io.Copy(os.Stdout, output)
		newContainer, err = c.ContainerCreate(ctx, config, hostConfig, nil, name)
	}
	if err == nil && start {
		return c.ContainerStart(ctx, newContainer.ID, types.ContainerStartOptions{})
	}
	return err
}

func remove(ctx context.Context, c *client.Client, id string) error {
	return c.ContainerRemove(ctx, id, types.ContainerRemoveOptions{
		Force:         true,
		RemoveVolumes: true,
	})
}

func changed(ctx context.Context, c *client.Client, p v3.Process, container types.Container) (bool, error) {
	inspect, err := c.ContainerInspect(ctx, container.ID)
	if err != nil {
		return false, err
	}

	newProcess := v3.Process{
		Command:     inspect.Config.Entrypoint,
		Args:        inspect.Config.Cmd,
		Env:         inspect.Config.Env,
		Image:       inspect.Config.Image,
		Binds:       inspect.HostConfig.Binds,
		NetworkMode: string(inspect.HostConfig.NetworkMode),
		PidMode:     string(inspect.HostConfig.PidMode),
		Privileged:  inspect.HostConfig.Privileged,
		VolumesFrom: inspect.HostConfig.VolumesFrom,
	}

	if len(p.Command) == 0 {
		p.Command = newProcess.Command
	}
	if len(p.Args) == 0 {
		p.Args = newProcess.Args
	}
	if len(p.Env) == 0 {
		p.Env = newProcess.Env
	}
	if p.NetworkMode == "" {
		p.NetworkMode = newProcess.NetworkMode
	}
	if p.PidMode == "" {
		p.PidMode = newProcess.PidMode
	}

	// Don't detect changes on these fields
	newProcess.Name = p.Name
	newProcess.HealthCheck.URL = p.HealthCheck.URL
	newProcess.RestartPolicy = p.RestartPolicy

	changed := false
	t := reflect.TypeOf(newProcess)
	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)
		if f.Name == "Command" {
			leftMap := sliceToMap(p.Command)
			rightMap := sliceToMap(newProcess.Command)
			if reflect.DeepEqual(leftMap, rightMap) {
				continue
			}
		} else if f.Name == "Env" {
			changed := false
			for _, v := range p.Env {
				if !slice.ContainsString(newProcess.Env, v) {
					changed = true
					break
				}
			}

			if !changed {
				continue
			}
		}

		left := reflect.ValueOf(newProcess).Field(i).Interface()
		right := reflect.ValueOf(p).Field(i).Interface()
		if !reflect.DeepEqual(left, right) {
			logrus.Infof("For process %s, %s has changed from %v to %v", p.Name, f.Name, right, left)
			changed = true
		}
	}

	return changed, nil
}

func sliceToMap(args []string) map[string]bool {
	result := map[string]bool{}
	for _, arg := range args {
		result[arg] = true
	}
	return result
}
<commit_msg>Use registry auth in runProcess<commit_after>package rkeworker

import (
	"context"
	"io"
	"os"
	"reflect"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"github.com/rancher/norman/types/slice"
	"github.com/rancher/rke/services"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/sirupsen/logrus"
)

type NodeConfig struct {
	ClusterName string                `json:"clusterName"`
	Certs       string                `json:"certs"`
	Processes   map[string]v3.Process `json:"processes"`
	Files       []v3.File             `json:"files"`
}

func runProcess(ctx context.Context, name string, p v3.Process, start bool) error {
	c, err := client.NewEnvClient()
	if err != nil {
		return err
	}

	args := filters.NewArgs()
	args.Add("label", "io.cattle.process.name="+name)

	containers, err := c.ContainerList(ctx, types.ContainerListOptions{
		All:     true,
		Filters: args,
	})
	if err != nil {
		return err
	}

	var matchedContainers []types.Container
	for _, container := range containers {
		changed, err := changed(ctx, c, p, container)
		if err != nil {
			return err
		}

		if changed {
			err := remove(ctx, c, container.ID)
			if err != nil {
				return err
			}
		} else {
			matchedContainers = append(matchedContainers, container)
		}
	}

	for i := 1; i < len(matchedContainers); i++ {
		if err := remove(ctx, c, matchedContainers[i].ID); err != nil {
			return err
		}
	}

	if len(matchedContainers) > 0 {
		c.ContainerStart(ctx, matchedContainers[0].ID, types.ContainerStartOptions{})
		return nil
	}

	config, hostConfig, _ := services.GetProcessConfig(p)
	if config.Labels == nil {
		config.Labels = map[string]string{}
	}
	config.Labels["io.cattle.process.name"] = name

	newContainer, err := c.ContainerCreate(ctx, config, hostConfig, nil, name)
	if client.IsErrImageNotFound(err) {
		var output io.ReadCloser
		imagePullOptions := types.ImagePullOptions{}
		if p.ImageRegistryAuthConfig != "" {
			imagePullOptions.RegistryAuth = p.ImageRegistryAuthConfig
			imagePullOptions.PrivilegeFunc = func() (string, error) { return p.ImageRegistryAuthConfig, nil }
		}
		output, err = c.ImagePull(ctx, config.Image, types.ImagePullOptions{})
		if err != nil {
			return err
		}
		defer output.Close()
		io.Copy(os.Stdout, output)
		newContainer, err = c.ContainerCreate(ctx, config, hostConfig, nil, name)
	}
	if err == nil && start {
		return c.ContainerStart(ctx, newContainer.ID, types.ContainerStartOptions{})
	}
	return err
}

func remove(ctx context.Context, c *client.Client, id string) error {
	return c.ContainerRemove(ctx, id, types.ContainerRemoveOptions{
		Force:         true,
		RemoveVolumes: true,
	})
}

func changed(ctx context.Context, c *client.Client, p v3.Process, container types.Container) (bool, error) {
	inspect, err := c.ContainerInspect(ctx, container.ID)
	if err != nil {
		return false, err
	}

	newProcess := v3.Process{
		Command:     inspect.Config.Entrypoint,
		Args:        inspect.Config.Cmd,
		Env:         inspect.Config.Env,
		Image:       inspect.Config.Image,
		Binds:       inspect.HostConfig.Binds,
		NetworkMode: string(inspect.HostConfig.NetworkMode),
		PidMode:     string(inspect.HostConfig.PidMode),
		Privileged:  inspect.HostConfig.Privileged,
		VolumesFrom: inspect.HostConfig.VolumesFrom,
	}

	if len(p.Command) == 0 {
		p.Command = newProcess.Command
	}
	if len(p.Args) == 0 {
		p.Args = newProcess.Args
	}
	if len(p.Env) == 0 {
		p.Env = newProcess.Env
	}
	if p.NetworkMode == "" {
		p.NetworkMode = newProcess.NetworkMode
	}
	if p.PidMode == "" {
		p.PidMode = newProcess.PidMode
	}

	// Don't detect changes on these fields
	newProcess.Name = p.Name
	newProcess.HealthCheck.URL = p.HealthCheck.URL
	newProcess.RestartPolicy = p.RestartPolicy

	changed := false
	t := reflect.TypeOf(newProcess)
	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)
		if f.Name == "Command" {
			leftMap := sliceToMap(p.Command)
			rightMap := sliceToMap(newProcess.Command)
			if reflect.DeepEqual(leftMap, rightMap) {
				continue
			}
		} else if f.Name == "Env" {
			changed := false
			for _, v := range p.Env {
				if !slice.ContainsString(newProcess.Env, v) {
					changed = true
					break
				}
			}

			if !changed {
				continue
			}
		}

		left := reflect.ValueOf(newProcess).Field(i).Interface()
		right := reflect.ValueOf(p).Field(i).Interface()
		if !reflect.DeepEqual(left, right) {
			logrus.Infof("For process %s, %s has changed from %v to %v", p.Name, f.Name, right, left)
			changed = true
		}
	}

	return changed, nil
}

func sliceToMap(args []string) map[string]bool {
	result := map[string]bool{}
	for _, arg := range args {
		result[arg] = true
	}
	return result
}
<|endoftext|>


<commit_before>package sessions

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/crypto"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/labstack/echo"
)

// SessionCookieName: name of the cookie created by cozy
const SessionCookieName = "cozysessid"

const (
	// SessionMaxAge is the maximum duration of the session in seconds
	SessionMaxAge = 7 * 24 * 60 * 60

	// AppCookieMaxAge is the maximum duration of the application cookie in
	// seconds
	AppCookieMaxAge = 24 * 60 * 60
)

var (
	// ErrNoCookie is returned by GetSession if there is no cookie
	ErrNoCookie = errors.New("No session cookie")
	// ErrExpired is returned when the session has expired
	ErrExpired = errors.New("Session expired")
	// ErrInvalidID is returned by GetSession if the cookie contains wrong ID
	ErrInvalidID = errors.New("Session cookie has wrong ID")
)

// A Session is an instance opened in a browser
type Session struct {
	Instance *instance.Instance `json:"-"`
	DocID    string             `json:"_id,omitempty"`
	DocRev   string             `json:"_rev,omitempty"`
	LastSeen time.Time          `json:"last_seen,omitempty"`
}

// DocType implements couchdb.Doc
func (s *Session) DocType() string { return consts.Sessions }

// ID implements couchdb.Doc
func (s *Session) ID() string { return s.DocID }

// SetID implements couchdb.Doc
func (s *Session) SetID(v string) { s.DocID = v }

// Rev implements couchdb.Doc
func (s *Session) Rev() string { return s.DocRev }

// SetRev implements couchdb.Doc
func (s *Session) SetRev(v string) { s.DocRev = v }

// Clone implements couchdb.Doc
func (s *Session) Clone() couchdb.Doc {
	cloned := *s
	if cloned.Instance != nil {
		tmp := *s.Instance
		cloned.Instance = &tmp
	}
	return &cloned
}

// ensure Session implements couchdb.Doc
var _ couchdb.Doc = (*Session)(nil)

// OlderThan checks if a session last seen is older than t from now
func (s *Session) OlderThan(t time.Duration) bool {
	return time.Now().After(s.LastSeen.Add(t))
}

// New creates a session in couchdb for the given instance
func New(i *instance.Instance) (*Session, error) {
	var s = &Session{
		Instance: i,
		LastSeen: time.Now(),
	}
	if err := couchdb.CreateDoc(i, s); err != nil {
		return nil, err
	}
	getCache().Set(i.Domain, s.DocID, s)
	return s, nil
}

// Get fetches the session
func Get(i *instance.Instance, sessionID string) (*Session, error) {
	updateCache := false

	s := getCache().Get(i.Domain, sessionID)
	if s == nil {
		s = &Session{}
		err := couchdb.GetDoc(i, consts.Sessions, sessionID, s)
		if couchdb.IsNotFoundError(err) {
			return nil, ErrInvalidID
		}
		if err != nil {
			return nil, err
		}
		updateCache = true
	}
	s.Instance = i

	// If the session is older than the session max age, it has expired and
	// should be deleted.
	if s.OlderThan(SessionMaxAge * time.Second) {
		err := couchdb.DeleteDoc(i, s)
		if err != nil {
			i.Logger().Warn("[session] Failed to delete expired session:", err)
		}
		getCache().Revoke(i.Domain, s.DocID)
		return nil, ErrExpired
	}

	// If the session is older than half its half-life, update the LastSeen date.
	if s.OlderThan((SessionMaxAge * time.Second) / 2) {
		lastSeen := s.LastSeen
		s.LastSeen = time.Now()
		err := couchdb.UpdateDoc(i, s)
		if err != nil {
			i.Logger().Warn("[session] Failed to update session last seen:", err)
		} else {
			s.LastSeen = lastSeen
			updateCache = true
		}
	}

	if updateCache {
		getCache().Set(i.Domain, s.DocID, s)
	}

	return s, nil
}

// FromCookie retrieves the session from a echo.Context cookies.
func FromCookie(c echo.Context, i *instance.Instance) (*Session, error) {
	cookie, err := c.Cookie(SessionCookieName)
	if err != nil || cookie.Value == "" {
		return nil, ErrNoCookie
	}

	sessionID, err := crypto.DecodeAuthMessage(cookieMACConfig(i, SessionMaxAge),
		[]byte(cookie.Value), nil)
	if err != nil {
		return nil, err
	}

	return Get(i, string(sessionID))
}

// FromAppCookie retrives the session from an application submain cookie.
func FromAppCookie(c echo.Context, i *instance.Instance, slug string) (*Session, error) {
	if config.GetConfig().Subdomains == config.FlatSubdomains {
		cookie, err := c.Cookie(SessionCookieName)
		if err != nil || cookie.Value == "" {
			return nil, ErrNoCookie
		}

		sessionID, err := crypto.DecodeAuthMessage(cookieMACConfig(i, AppCookieMaxAge),
			[]byte(cookie.Value), []byte(slug))
		if err != nil {
			return nil, err
		}

		return Get(i, string(sessionID))
	}
	return FromCookie(c, i)
}

// GetAll returns all the active sessions
func GetAll(inst *instance.Instance) ([]*Session, error) {
	var sessions []*Session
	if err := couchdb.GetAllDocs(inst, consts.Sessions, nil, &sessions); err != nil {
		return nil, err
	}
	return sessions, nil
}

// Delete is a function to delete the session in couchdb,
// and returns a cookie with a negative MaxAge to clear it
func (s *Session) Delete(i *instance.Instance) *http.Cookie {
	err := couchdb.DeleteDoc(i, s)
	getCache().Revoke(i.Domain, s.DocID)
	if err != nil {
		i.Logger().Error("[session] Failed to delete session:", err)
	}
	return &http.Cookie{
		Name:   SessionCookieName,
		Value:  "",
		MaxAge: -1,
		Path:   "/",
		Domain: utils.StripPort("." + i.Domain),
	}
}

// ToCookie returns an http.Cookie for this Session
func (s *Session) ToCookie() (*http.Cookie, error) {
	encoded, err := crypto.EncodeAuthMessage(cookieMACConfig(s.Instance, SessionMaxAge), []byte(s.ID()), nil)
	if err != nil {
		return nil, err
	}

	return &http.Cookie{
		Name:     SessionCookieName,
		Value:    string(encoded),
		MaxAge:   SessionMaxAge,
		Path:     "/",
		Domain:   utils.StripPort("." + s.Instance.Domain),
		Secure:   !s.Instance.Dev,
		HttpOnly: true,
	}, nil
}

// ToAppCookie returns an http.Cookie for this Session on an app subdomain
func (s *Session) ToAppCookie(domain, slug string) (*http.Cookie, error) {
	encoded, err := crypto.EncodeAuthMessage(cookieMACConfig(s.Instance, AppCookieMaxAge), []byte(s.ID()), []byte(slug))
	if err != nil {
		return nil, err
	}

	return &http.Cookie{
		Name:     SessionCookieName,
		Value:    string(encoded),
		MaxAge:   AppCookieMaxAge,
		Path:     "/",
		Domain:   utils.StripPort(domain),
		Secure:   !s.Instance.Dev,
		HttpOnly: true,
	}, nil
}

// DeleteOthers will remove all sessions except the one given in parameter.
func DeleteOthers(i *instance.Instance, selfSessionID string) error {
	var sessions []*Session
	err := couchdb.ForeachDocs(i, consts.Sessions, func(data []byte) error {
		var s Session
		if err := json.Unmarshal(data, &s); err != nil {
			return err
		}
		sessions = append(sessions, &s)
		return nil
	})
	if err != nil {
		return err
	}
	for _, s := range sessions {
		if s.ID() != selfSessionID {
			s.Delete(i)
		}
	}
	return nil
}

// cookieMACConfig returns the options to authenticate the session cookie.
//
// We rely on a MACed cookie value, without additional encryption of the
// message since it should not contain critical private informations and is
// protected by HTTPs (secure cookie).
//
// About MaxLength, for a session of size 100 bytes
//
//       8 bytes time
//   +  32 bytes HMAC-SHA256
//   + 100 bytes session
//   + base64 encoding (4*n/3)
//   < 200 bytes
//
// 256 bytes should be sufficient enough to support any type of session.
//
func cookieMACConfig(i *instance.Instance, maxAge int64) *crypto.MACConfig {
	return &crypto.MACConfig{
		Name:   SessionCookieName,
		Key:    i.SessionSecret,
		MaxAge: maxAge,
		MaxLen: 256,
	}
}
<commit_msg>Fix bad cache invalidation for sessions<commit_after>package sessions

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/crypto"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/labstack/echo"
)

// SessionCookieName: name of the cookie created by cozy
const SessionCookieName = "cozysessid"

const (
	// SessionMaxAge is the maximum duration of the session in seconds
	SessionMaxAge = 7 * 24 * 60 * 60

	// AppCookieMaxAge is the maximum duration of the application cookie in
	// seconds
	AppCookieMaxAge = 24 * 60 * 60
)

var (
	// ErrNoCookie is returned by GetSession if there is no cookie
	ErrNoCookie = errors.New("No session cookie")
	// ErrExpired is returned when the session has expired
	ErrExpired = errors.New("Session expired")
	// ErrInvalidID is returned by GetSession if the cookie contains wrong ID
	ErrInvalidID = errors.New("Session cookie has wrong ID")
)

// A Session is an instance opened in a browser
type Session struct {
	Instance *instance.Instance `json:"-"`
	DocID    string             `json:"_id,omitempty"`
	DocRev   string             `json:"_rev,omitempty"`
	LastSeen time.Time          `json:"last_seen,omitempty"`
}

// DocType implements couchdb.Doc
func (s *Session) DocType() string { return consts.Sessions }

// ID implements couchdb.Doc
func (s *Session) ID() string { return s.DocID }

// SetID implements couchdb.Doc
func (s *Session) SetID(v string) { s.DocID = v }

// Rev implements couchdb.Doc
func (s *Session) Rev() string { return s.DocRev }

// SetRev implements couchdb.Doc
func (s *Session) SetRev(v string) { s.DocRev = v }

// Clone implements couchdb.Doc
func (s *Session) Clone() couchdb.Doc {
	cloned := *s
	if cloned.Instance != nil {
		tmp := *s.Instance
		cloned.Instance = &tmp
	}
	return &cloned
}

// ensure Session implements couchdb.Doc
var _ couchdb.Doc = (*Session)(nil)

// OlderThan checks if a session last seen is older than t from now
func (s *Session) OlderThan(t time.Duration) bool {
	return time.Now().After(s.LastSeen.Add(t))
}

// New creates a session in couchdb for the given instance
func New(i *instance.Instance) (*Session, error) {
	var s = &Session{
		Instance: i,
		LastSeen: time.Now(),
	}
	if err := couchdb.CreateDoc(i, s); err != nil {
		return nil, err
	}
	getCache().Set(i.Domain, s.DocID, s)
	return s, nil
}

// Get fetches the session
func Get(i *instance.Instance, sessionID string) (*Session, error) {
	updateCache := false

	s := getCache().Get(i.Domain, sessionID)
	if s == nil {
		s = &Session{}
		err := couchdb.GetDoc(i, consts.Sessions, sessionID, s)
		if couchdb.IsNotFoundError(err) {
			return nil, ErrInvalidID
		}
		if err != nil {
			return nil, err
		}
		updateCache = true
	}
	s.Instance = i

	// If the session is older than the session max age, it has expired and
	// should be deleted.
	if s.OlderThan(SessionMaxAge * time.Second) {
		err := couchdb.DeleteDoc(i, s)
		if err != nil {
			i.Logger().Warn("[session] Failed to delete expired session:", err)
		}
		getCache().Revoke(i.Domain, s.DocID)
		return nil, ErrExpired
	}

	// If the session is older than half its half-life, update the LastSeen date.
	if s.OlderThan((SessionMaxAge * time.Second) / 2) {
		lastSeen := s.LastSeen
		s.LastSeen = time.Now()
		err := couchdb.UpdateDoc(i, s)
		if err != nil {
			i.Logger().Warn("[session] Failed to update session last seen:", err)
			s.LastSeen = lastSeen
			updateCache = false
		} else {
			updateCache = true
		}
	}

	if updateCache {
		getCache().Set(i.Domain, s.DocID, s)
	}

	return s, nil
}

// FromCookie retrieves the session from a echo.Context cookies.
func FromCookie(c echo.Context, i *instance.Instance) (*Session, error) {
	cookie, err := c.Cookie(SessionCookieName)
	if err != nil || cookie.Value == "" {
		return nil, ErrNoCookie
	}

	sessionID, err := crypto.DecodeAuthMessage(cookieMACConfig(i, SessionMaxAge),
		[]byte(cookie.Value), nil)
	if err != nil {
		return nil, err
	}

	return Get(i, string(sessionID))
}

// FromAppCookie retrives the session from an application submain cookie.
func FromAppCookie(c echo.Context, i *instance.Instance, slug string) (*Session, error) {
	if config.GetConfig().Subdomains == config.FlatSubdomains {
		cookie, err := c.Cookie(SessionCookieName)
		if err != nil || cookie.Value == "" {
			return nil, ErrNoCookie
		}

		sessionID, err := crypto.DecodeAuthMessage(cookieMACConfig(i, AppCookieMaxAge),
			[]byte(cookie.Value), []byte(slug))
		if err != nil {
			return nil, err
		}

		return Get(i, string(sessionID))
	}
	return FromCookie(c, i)
}

// GetAll returns all the active sessions
func GetAll(inst *instance.Instance) ([]*Session, error) {
	var sessions []*Session
	if err := couchdb.GetAllDocs(inst, consts.Sessions, nil, &sessions); err != nil {
		return nil, err
	}
	return sessions, nil
}

// Delete is a function to delete the session in couchdb,
// and returns a cookie with a negative MaxAge to clear it
func (s *Session) Delete(i *instance.Instance) *http.Cookie {
	err := couchdb.DeleteDoc(i, s)
	getCache().Revoke(i.Domain, s.DocID)
	if err != nil {
		i.Logger().Error("[session] Failed to delete session:", err)
	}
	return &http.Cookie{
		Name:   SessionCookieName,
		Value:  "",
		MaxAge: -1,
		Path:   "/",
		Domain: utils.StripPort("." + i.Domain),
	}
}

// ToCookie returns an http.Cookie for this Session
func (s *Session) ToCookie() (*http.Cookie, error) {
	encoded, err := crypto.EncodeAuthMessage(cookieMACConfig(s.Instance, SessionMaxAge), []byte(s.ID()), nil)
	if err != nil {
		return nil, err
	}

	return &http.Cookie{
		Name:     SessionCookieName,
		Value:    string(encoded),
		MaxAge:   SessionMaxAge,
		Path:     "/",
		Domain:   utils.StripPort("." + s.Instance.Domain),
		Secure:   !s.Instance.Dev,
		HttpOnly: true,
	}, nil
}

// ToAppCookie returns an http.Cookie for this Session on an app subdomain
func (s *Session) ToAppCookie(domain, slug string) (*http.Cookie, error) {
	encoded, err := crypto.EncodeAuthMessage(cookieMACConfig(s.Instance, AppCookieMaxAge), []byte(s.ID()), []byte(slug))
	if err != nil {
		return nil, err
	}

	return &http.Cookie{
		Name:     SessionCookieName,
		Value:    string(encoded),
		MaxAge:   AppCookieMaxAge,
		Path:     "/",
		Domain:   utils.StripPort(domain),
		Secure:   !s.Instance.Dev,
		HttpOnly: true,
	}, nil
}

// DeleteOthers will remove all sessions except the one given in parameter.
func DeleteOthers(i *instance.Instance, selfSessionID string) error {
	var sessions []*Session
	err := couchdb.ForeachDocs(i, consts.Sessions, func(data []byte) error {
		var s Session
		if err := json.Unmarshal(data, &s); err != nil {
			return err
		}
		sessions = append(sessions, &s)
		return nil
	})
	if err != nil {
		return err
	}
	for _, s := range sessions {
		if s.ID() != selfSessionID {
			s.Delete(i)
		}
	}
	return nil
}

// cookieMACConfig returns the options to authenticate the session cookie.
//
// We rely on a MACed cookie value, without additional encryption of the
// message since it should not contain critical private informations and is
// protected by HTTPs (secure cookie).
//
// About MaxLength, for a session of size 100 bytes
//
//       8 bytes time
//   +  32 bytes HMAC-SHA256
//   + 100 bytes session
//   + base64 encoding (4*n/3)
//   < 200 bytes
//
// 256 bytes should be sufficient enough to support any type of session.
//
func cookieMACConfig(i *instance.Instance, maxAge int64) *crypto.MACConfig {
	return &crypto.MACConfig{
		Name:   SessionCookieName,
		Key:    i.SessionSecret,
		MaxAge: maxAge,
		MaxLen: 256,
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold Authors

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

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

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

package jib

import (
	"bytes"
	"context"
	"encoding/json"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"

	"github.com/google/go-containerregistry/pkg/name"
	"github.com/karrick/godirwalk"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

const (
	dotDotSlash = ".." + string(filepath.Separator)
)

// PluginType defines the different supported Jib plugins.
type PluginType string

const (
	JibMaven PluginType = "maven"
	JibGradle PluginType = "gradle"
)

// IsKnown checks that the num value is a known value (vs 0 or an unknown value).
func (t PluginType) IsKnown() bool {
	switch t {
	case JibMaven, JibGradle:
		return true
	}
	return false
}

// Name provides a human-oriented label for a plugin type.
func PluginName(t PluginType) string {
	switch t {
	case JibMaven:
		return "Jib Maven Plugin"
	case JibGradle:
		return "Jib Gradle Plugin"
	}
	panic("Unknown Jib Plugin Type: " + string(t))
}

// filesLists contains cached build/input dependencies
type filesLists struct {
	// BuildDefinitions lists paths to build definitions that trigger a call out to Jib to refresh the pathMap, as well as a rebuild, upon changing
	BuildDefinitions []string `json:"build"`

	// Inputs lists paths to build dependencies that trigger a rebuild upon changing
	Inputs []string `json:"inputs"`

	// Results lists paths to files that should be ignored when checking for changes to rebuild
	Results []string `json:"ignore"`

	// BuildFileTimes keeps track of the last modification time of each build file
	BuildFileTimes map[string]time.Time
}

// watchedFiles maps from project name to watched files
var watchedFiles = map[string]filesLists{}

// GetDependencies returns a list of files to watch for changes to rebuild
func GetDependencies(ctx context.Context, workspace string, artifact *latest.JibArtifact) ([]string, error) {
	t, err := DeterminePluginType(workspace, artifact)
	if err != nil {
		return nil, err
	}

	switch t {
	case JibMaven:
		return getDependenciesMaven(ctx, workspace, artifact)
	case JibGradle:
		return getDependenciesGradle(ctx, workspace, artifact)
	default:
		return nil, errors.Errorf("Unable to determine Jib builder type for %s", workspace)
	}
}

// DeterminePluginType tries to determine the Jib plugin type for the given artifact.
func DeterminePluginType(workspace string, artifact *latest.JibArtifact) (PluginType, error) {
	// check if explicitly specified
	if artifact != nil {
		if t := PluginType(artifact.Type); t.IsKnown() {
			return t, nil
		}
	}

	// check for typical gradle files
	for _, gradleFile := range []string{"build.gradle", "build.gradle.kts", "gradle.properties", "settings.gradle", "gradlew", "gradlew.bat", "gradlew.cmd"} {
		if util.IsFile(filepath.Join(workspace, gradleFile)) {
			return JibGradle, nil
		}
	}
	// check for typical maven files; .mvn is a directory used for polyglot maven
	if util.IsFile(filepath.Join(workspace, "pom.xml")) || util.IsDir(filepath.Join(workspace, ".mvn")) {
		return JibMaven, nil
	}
	return "", errors.Errorf("Unable to determine Jib plugin type for %s", workspace)
}

// getDependencies returns a list of files to watch for changes to rebuild
func getDependencies(workspace string, cmd exec.Cmd, projectName string) ([]string, error) {
	var dependencyList []string
	files, ok := watchedFiles[projectName]
	if !ok {
		files = filesLists{}
	}

	if len(files.Inputs) == 0 && len(files.BuildDefinitions) == 0 {
		// Make sure build file modification time map is setup
		if files.BuildFileTimes == nil {
			files.BuildFileTimes = make(map[string]time.Time)
		}

		// Refresh dependency list if empty
		if err := refreshDependencyList(&files, cmd); err != nil {
			return nil, errors.Wrap(err, "initial Jib dependency refresh failed")
		}
	} else if err := walkFiles(workspace, files.BuildDefinitions, files.Results, func(path string, info os.FileInfo) error {
		// Walk build files to check for changes
		if val, ok := files.BuildFileTimes[path]; !ok || info.ModTime() != val {
			return refreshDependencyList(&files, cmd)
		}
		return nil
	}); err != nil {
		return nil, errors.Wrap(err, "failed to walk Jib build files for changes")
	}

	// Walk updated files to build dependency list
	if err := walkFiles(workspace, files.Inputs, files.Results, func(path string, info os.FileInfo) error {
		dependencyList = append(dependencyList, path)
		return nil
	}); err != nil {
		return nil, errors.Wrap(err, "failed to walk Jib input files to build dependency list")
	}
	if err := walkFiles(workspace, files.BuildDefinitions, files.Results, func(path string, info os.FileInfo) error {
		dependencyList = append(dependencyList, path)
		files.BuildFileTimes[path] = info.ModTime()
		return nil
	}); err != nil {
		return nil, errors.Wrap(err, "failed to walk Jib build files to build dependency list")
	}

	// Store updated files list information
	watchedFiles[projectName] = files

	sort.Strings(dependencyList)
	return dependencyList, nil
}

// refreshDependencyList calls out to Jib to update files with the latest list of files/directories to watch.
func refreshDependencyList(files *filesLists, cmd exec.Cmd) error {
	stdout, err := util.RunCmdOut(&cmd)
	if err != nil {
		return errors.Wrap(err, "failed to get Jib dependencies")
	}

	// Search for Jib's output JSON. Jib's Maven/Gradle output takes the following form:
	// ...
	// BEGIN JIB JSON
	// {"build":["/paths","/to","/buildFiles"],"inputs":["/paths","/to","/inputs"],"ignore":["/paths","/to","/ignore"]}
	// ...
	// To parse the output, search for "BEGIN JIB JSON", then unmarshal the next line into the pathMap struct.
	matches := regexp.MustCompile(`BEGIN JIB JSON\r?\n({.*})`).FindSubmatch(stdout)
	if len(matches) == 0 {
		return errors.New("failed to get Jib dependencies")
	}

	line := bytes.Replace(matches[1], []byte(`\`), []byte(`\\`), -1)
	return json.Unmarshal(line, &files)
}

// walkFiles walks through a list of files and directories and performs a callback on each of the files
func walkFiles(workspace string, watchedFiles []string, ignoredFiles []string, callback func(path string, info os.FileInfo) error) error {
	// Skaffold prefers to deal with relative paths. In *practice*, Jib's dependencies
	// are *usually* absolute (relative to the root) and canonical (with all symlinks expanded).
	// But that's not guaranteed, so we try to relativize paths against the workspace as
	// both an absolute path and as a canonicalized workspace.
	workspaceRoots, err := calculateRoots(workspace)
	if err != nil {
		return errors.Wrapf(err, "unable to resolve workspace %s", workspace)
	}

	for _, dep := range watchedFiles {
		if isIgnored(dep, ignoredFiles) {
			continue
		}

		// Resolves directories recursively.
		info, err := os.Stat(dep)
		if err != nil {
			if os.IsNotExist(err) {
				logrus.Debugf("could not stat dependency: %s", err)
				continue // Ignore files that don't exist
			}
			return errors.Wrapf(err, "unable to stat file %s", dep)
		}

		// Process file
		if !info.IsDir() {
			// try to relativize the path: an error indicates that the file cannot
			// be made relative to the roots, and so we just use the full path
			if relative, err := relativize(dep, workspaceRoots...); err == nil {
				dep = relative
			}
			if err := callback(dep, info); err != nil {
				return err
			}
			continue
		}

		// Process directory
		if err = godirwalk.Walk(dep, &godirwalk.Options{
			Unsorted: true,
			Callback: func(path string, _ *godirwalk.Dirent) error {
				if isIgnored(path, ignoredFiles) {
					return filepath.SkipDir
				}
				if info, err := os.Stat(path); err == nil && !info.IsDir() {
					// try to relativize the path: an error indicates that the file cannot
					// be made relative to the roots, and so we just use the full path
					if relative, err := relativize(path, workspaceRoots...); err == nil {
						path = relative
					}
					return callback(path, info)
				}
				return nil
			},
		}); err != nil {
			return errors.Wrap(err, "filepath walk")
		}
	}
	return nil
}

// isIgnored tests a path for whether or not it should be ignored according to a list of ignored files/directories
func isIgnored(path string, ignoredFiles []string) bool {
	for _, ignored := range ignoredFiles {
		if strings.HasPrefix(path, ignored) {
			return true
		}
	}
	return false
}

// calculateRoots returns a list of possible symlink-expanded paths
func calculateRoots(path string) ([]string, error) {
	path, err := filepath.Abs(path)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to resolve %s", path)
	}
	canonical, err := filepath.EvalSymlinks(path)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to canonicalize workspace %s", path)
	}
	if path == canonical {
		return []string{path}, nil
	}
	return []string{canonical, path}, nil
}

// relativize tries to make path relative to one of the given roots
func relativize(path string, roots ...string) (string, error) {
	if !filepath.IsAbs(path) {
		return path, nil
	}
	for _, root := range roots {
		// check that the path can be made relative and is contained (since `filepath.Rel("/a", "/b") => "../b"`)
		if rel, err := filepath.Rel(root, path); err == nil && !strings.HasPrefix(rel, dotDotSlash) {
			return rel, nil
		}
	}
	return "", errors.New("could not relativize path")
}

// isOnInsecureRegistry checks if the given image specifies an insecure registry
func isOnInsecureRegistry(image string, insecureRegistries map[string]bool) (bool, error) {
	ref, err := name.ParseReference(image)
	if err != nil {
		return false, err
	}

	registry := ref.Context().Registry.Name()
	return docker.IsInsecure(registry, insecureRegistries), nil
}
<commit_msg>gofmt<commit_after>/*
Copyright 2019 The Skaffold Authors

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

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

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

package jib

import (
	"bytes"
	"context"
	"encoding/json"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"

	"github.com/google/go-containerregistry/pkg/name"
	"github.com/karrick/godirwalk"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

const (
	dotDotSlash = ".." + string(filepath.Separator)
)

// PluginType defines the different supported Jib plugins.
type PluginType string

const (
	JibMaven  PluginType = "maven"
	JibGradle PluginType = "gradle"
)

// IsKnown checks that the num value is a known value (vs 0 or an unknown value).
func (t PluginType) IsKnown() bool {
	switch t {
	case JibMaven, JibGradle:
		return true
	}
	return false
}

// Name provides a human-oriented label for a plugin type.
func PluginName(t PluginType) string {
	switch t {
	case JibMaven:
		return "Jib Maven Plugin"
	case JibGradle:
		return "Jib Gradle Plugin"
	}
	panic("Unknown Jib Plugin Type: " + string(t))
}

// filesLists contains cached build/input dependencies
type filesLists struct {
	// BuildDefinitions lists paths to build definitions that trigger a call out to Jib to refresh the pathMap, as well as a rebuild, upon changing
	BuildDefinitions []string `json:"build"`

	// Inputs lists paths to build dependencies that trigger a rebuild upon changing
	Inputs []string `json:"inputs"`

	// Results lists paths to files that should be ignored when checking for changes to rebuild
	Results []string `json:"ignore"`

	// BuildFileTimes keeps track of the last modification time of each build file
	BuildFileTimes map[string]time.Time
}

// watchedFiles maps from project name to watched files
var watchedFiles = map[string]filesLists{}

// GetDependencies returns a list of files to watch for changes to rebuild
func GetDependencies(ctx context.Context, workspace string, artifact *latest.JibArtifact) ([]string, error) {
	t, err := DeterminePluginType(workspace, artifact)
	if err != nil {
		return nil, err
	}

	switch t {
	case JibMaven:
		return getDependenciesMaven(ctx, workspace, artifact)
	case JibGradle:
		return getDependenciesGradle(ctx, workspace, artifact)
	default:
		return nil, errors.Errorf("Unable to determine Jib builder type for %s", workspace)
	}
}

// DeterminePluginType tries to determine the Jib plugin type for the given artifact.
func DeterminePluginType(workspace string, artifact *latest.JibArtifact) (PluginType, error) {
	// check if explicitly specified
	if artifact != nil {
		if t := PluginType(artifact.Type); t.IsKnown() {
			return t, nil
		}
	}

	// check for typical gradle files
	for _, gradleFile := range []string{"build.gradle", "build.gradle.kts", "gradle.properties", "settings.gradle", "gradlew", "gradlew.bat", "gradlew.cmd"} {
		if util.IsFile(filepath.Join(workspace, gradleFile)) {
			return JibGradle, nil
		}
	}
	// check for typical maven files; .mvn is a directory used for polyglot maven
	if util.IsFile(filepath.Join(workspace, "pom.xml")) || util.IsDir(filepath.Join(workspace, ".mvn")) {
		return JibMaven, nil
	}
	return "", errors.Errorf("Unable to determine Jib plugin type for %s", workspace)
}

// getDependencies returns a list of files to watch for changes to rebuild
func getDependencies(workspace string, cmd exec.Cmd, projectName string) ([]string, error) {
	var dependencyList []string
	files, ok := watchedFiles[projectName]
	if !ok {
		files = filesLists{}
	}

	if len(files.Inputs) == 0 && len(files.BuildDefinitions) == 0 {
		// Make sure build file modification time map is setup
		if files.BuildFileTimes == nil {
			files.BuildFileTimes = make(map[string]time.Time)
		}

		// Refresh dependency list if empty
		if err := refreshDependencyList(&files, cmd); err != nil {
			return nil, errors.Wrap(err, "initial Jib dependency refresh failed")
		}
	} else if err := walkFiles(workspace, files.BuildDefinitions, files.Results, func(path string, info os.FileInfo) error {
		// Walk build files to check for changes
		if val, ok := files.BuildFileTimes[path]; !ok || info.ModTime() != val {
			return refreshDependencyList(&files, cmd)
		}
		return nil
	}); err != nil {
		return nil, errors.Wrap(err, "failed to walk Jib build files for changes")
	}

	// Walk updated files to build dependency list
	if err := walkFiles(workspace, files.Inputs, files.Results, func(path string, info os.FileInfo) error {
		dependencyList = append(dependencyList, path)
		return nil
	}); err != nil {
		return nil, errors.Wrap(err, "failed to walk Jib input files to build dependency list")
	}
	if err := walkFiles(workspace, files.BuildDefinitions, files.Results, func(path string, info os.FileInfo) error {
		dependencyList = append(dependencyList, path)
		files.BuildFileTimes[path] = info.ModTime()
		return nil
	}); err != nil {
		return nil, errors.Wrap(err, "failed to walk Jib build files to build dependency list")
	}

	// Store updated files list information
	watchedFiles[projectName] = files

	sort.Strings(dependencyList)
	return dependencyList, nil
}

// refreshDependencyList calls out to Jib to update files with the latest list of files/directories to watch.
func refreshDependencyList(files *filesLists, cmd exec.Cmd) error {
	stdout, err := util.RunCmdOut(&cmd)
	if err != nil {
		return errors.Wrap(err, "failed to get Jib dependencies")
	}

	// Search for Jib's output JSON. Jib's Maven/Gradle output takes the following form:
	// ...
	// BEGIN JIB JSON
	// {"build":["/paths","/to","/buildFiles"],"inputs":["/paths","/to","/inputs"],"ignore":["/paths","/to","/ignore"]}
	// ...
	// To parse the output, search for "BEGIN JIB JSON", then unmarshal the next line into the pathMap struct.
	matches := regexp.MustCompile(`BEGIN JIB JSON\r?\n({.*})`).FindSubmatch(stdout)
	if len(matches) == 0 {
		return errors.New("failed to get Jib dependencies")
	}

	line := bytes.Replace(matches[1], []byte(`\`), []byte(`\\`), -1)
	return json.Unmarshal(line, &files)
}

// walkFiles walks through a list of files and directories and performs a callback on each of the files
func walkFiles(workspace string, watchedFiles []string, ignoredFiles []string, callback func(path string, info os.FileInfo) error) error {
	// Skaffold prefers to deal with relative paths. In *practice*, Jib's dependencies
	// are *usually* absolute (relative to the root) and canonical (with all symlinks expanded).
	// But that's not guaranteed, so we try to relativize paths against the workspace as
	// both an absolute path and as a canonicalized workspace.
	workspaceRoots, err := calculateRoots(workspace)
	if err != nil {
		return errors.Wrapf(err, "unable to resolve workspace %s", workspace)
	}

	for _, dep := range watchedFiles {
		if isIgnored(dep, ignoredFiles) {
			continue
		}

		// Resolves directories recursively.
		info, err := os.Stat(dep)
		if err != nil {
			if os.IsNotExist(err) {
				logrus.Debugf("could not stat dependency: %s", err)
				continue // Ignore files that don't exist
			}
			return errors.Wrapf(err, "unable to stat file %s", dep)
		}

		// Process file
		if !info.IsDir() {
			// try to relativize the path: an error indicates that the file cannot
			// be made relative to the roots, and so we just use the full path
			if relative, err := relativize(dep, workspaceRoots...); err == nil {
				dep = relative
			}
			if err := callback(dep, info); err != nil {
				return err
			}
			continue
		}

		// Process directory
		if err = godirwalk.Walk(dep, &godirwalk.Options{
			Unsorted: true,
			Callback: func(path string, _ *godirwalk.Dirent) error {
				if isIgnored(path, ignoredFiles) {
					return filepath.SkipDir
				}
				if info, err := os.Stat(path); err == nil && !info.IsDir() {
					// try to relativize the path: an error indicates that the file cannot
					// be made relative to the roots, and so we just use the full path
					if relative, err := relativize(path, workspaceRoots...); err == nil {
						path = relative
					}
					return callback(path, info)
				}
				return nil
			},
		}); err != nil {
			return errors.Wrap(err, "filepath walk")
		}
	}
	return nil
}

// isIgnored tests a path for whether or not it should be ignored according to a list of ignored files/directories
func isIgnored(path string, ignoredFiles []string) bool {
	for _, ignored := range ignoredFiles {
		if strings.HasPrefix(path, ignored) {
			return true
		}
	}
	return false
}

// calculateRoots returns a list of possible symlink-expanded paths
func calculateRoots(path string) ([]string, error) {
	path, err := filepath.Abs(path)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to resolve %s", path)
	}
	canonical, err := filepath.EvalSymlinks(path)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to canonicalize workspace %s", path)
	}
	if path == canonical {
		return []string{path}, nil
	}
	return []string{canonical, path}, nil
}

// relativize tries to make path relative to one of the given roots
func relativize(path string, roots ...string) (string, error) {
	if !filepath.IsAbs(path) {
		return path, nil
	}
	for _, root := range roots {
		// check that the path can be made relative and is contained (since `filepath.Rel("/a", "/b") => "../b"`)
		if rel, err := filepath.Rel(root, path); err == nil && !strings.HasPrefix(rel, dotDotSlash) {
			return rel, nil
		}
	}
	return "", errors.New("could not relativize path")
}

// isOnInsecureRegistry checks if the given image specifies an insecure registry
func isOnInsecureRegistry(image string, insecureRegistries map[string]bool) (bool, error) {
	ref, err := name.ParseReference(image)
	if err != nil {
		return false, err
	}

	registry := ref.Context().Registry.Name()
	return docker.IsInsecure(registry, insecureRegistries), nil
}
<|endoftext|>


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

package spec

type SelfHostedPolicy struct {
	// BootMemberClientEndpoint specifies a bootstrap member for the cluster.
	// If there is no bootstrap member, a completely new cluster will be created.
	// The boot member will be removed from the cluster once the self-hosted cluster
	// setup successfully.
	BootMemberClientEndpoint string
}
<commit_msg>spec: add json field for BootMemberClientEndpoint<commit_after>// Copyright 2016 The etcd-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package spec

type SelfHostedPolicy struct {
	// BootMemberClientEndpoint specifies a bootstrap member for the cluster.
	// If there is no bootstrap member, a completely new cluster will be created.
	// The boot member will be removed from the cluster once the self-hosted cluster
	// setup successfully.
	BootMemberClientEndpoint string `json:"bootMemberClientEndpoint"`
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package util

import (
	"fmt"
	"os"
	"path"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/mount"
)

const readyFileName = "ready"

// IsReady checks for the existence of a regular file
// called 'ready' in the given directory and returns
// true if that file exists.
func IsReady(dir string) bool {
	readyFile := path.Join(dir, readyFileName)
	s, err := os.Stat(readyFile)
	if err != nil {
		return false
	}

	if !s.Mode().IsRegular() {
		glog.Errorf("ready-file is not a file: %s", readyFile)
		return false
	}

	return true
}

// SetReady creates a file called 'ready' in the given
// directory.  It logs an error if the file cannot be
// created.
func SetReady(dir string) {
	if err := os.MkdirAll(dir, 0750); err != nil && !os.IsExist(err) {
		glog.Errorf("Can't mkdir %s: %v", dir, err)
		return
	}

	readyFile := path.Join(dir, readyFileName)
	file, err := os.Create(readyFile)
	if err != nil {
		glog.Errorf("Can't touch %s: %v", readyFile, err)
		return
	}
	file.Close()
}

// UnmountPath is a common unmount routine that unmounts the given path and
// deletes the remaining directory if successful.
func UnmountPath(mountPath string, mounter mount.Interface) error {
	if pathExists, pathErr := PathExists(mountPath); pathErr != nil {
		return fmt.Errorf("Error checking if path exists: %v", pathErr)
	} else if !pathExists {
		glog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath)
		return nil
	}

	err := mounter.Unmount(mountPath)
	if err == nil {
		// Only delete directory on successful unmount
		glog.V(5).Infof("Unmounted %q. Deleting path.", mountPath)
		return os.Remove(mountPath)
	}

	return err
}

// PathExists returns true if the specified path exists.
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	} else if os.IsNotExist(err) {
		return false, nil
	} else {
		return false, err
	}
}
<commit_msg>Add ismounted check in unmountpath function<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package util

import (
	"fmt"
	"os"
	"path"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/mount"
)

const readyFileName = "ready"

// IsReady checks for the existence of a regular file
// called 'ready' in the given directory and returns
// true if that file exists.
func IsReady(dir string) bool {
	readyFile := path.Join(dir, readyFileName)
	s, err := os.Stat(readyFile)
	if err != nil {
		return false
	}

	if !s.Mode().IsRegular() {
		glog.Errorf("ready-file is not a file: %s", readyFile)
		return false
	}

	return true
}

// SetReady creates a file called 'ready' in the given
// directory.  It logs an error if the file cannot be
// created.
func SetReady(dir string) {
	if err := os.MkdirAll(dir, 0750); err != nil && !os.IsExist(err) {
		glog.Errorf("Can't mkdir %s: %v", dir, err)
		return
	}

	readyFile := path.Join(dir, readyFileName)
	file, err := os.Create(readyFile)
	if err != nil {
		glog.Errorf("Can't touch %s: %v", readyFile, err)
		return
	}
	file.Close()
}

// UnmountPath is a common unmount routine that unmounts the given path and
// deletes the remaining directory if successful.
func UnmountPath(mountPath string, mounter mount.Interface) error {
	if pathExists, pathErr := PathExists(mountPath); pathErr != nil {
		return fmt.Errorf("Error checking if path exists: %v", pathErr)
	} else if !pathExists {
		glog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath)
		return nil
	}

	notMnt, err := mounter.IsLikelyNotMountPoint(mountPath)
	if err != nil {
		return err
	}
	if notMnt {
		glog.Warningf("Warning: %q is not a mountpoint, deleting", mountPath)
		return os.Remove(mountPath)
	}

	// Unmount the mount path
	if err := mounter.Unmount(mountPath); err != nil {
		return err
	}
	notMnt, mntErr := mounter.IsLikelyNotMountPoint(mountPath)
	if mntErr != nil {
		return err
	}
	if notMnt {
		glog.V(4).Info("%q is unmounted, deleting the directory", mountPath)
		return os.Remove(mountPath)
	}
	return nil
}

// PathExists returns true if the specified path exists.
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	} else if os.IsNotExist(err) {
		return false, nil
	} else {
		return false, err
	}
}
<|endoftext|>