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

package lease

import (
	"fmt"

	"golang.org/x/net/context"
)

// Create a read proxy consisting of the contents defined by the supplied
// refreshers concatenated. See NewReadProxy for more.
//
// If rl is non-nil, it will be used as the first temporary copy of the
// contents, and must match the concatenation of the content returned by the
// refreshers.
func NewMultiReadProxy(
	fl FileLeaser,
	refreshers []Refresher,
	rl ReadLease) (rp ReadProxy) {
	// Create one wrapped read proxy per refresher.
	var wrappedProxies []readProxyAndOffset
	var size int64
	for _, r := range refreshers {
		wrapped := NewReadProxy(fl, r, nil)
		wrappedProxies = append(wrappedProxies, readProxyAndOffset{size, wrapped})
		size += wrapped.Size()
	}

	rp = &multiReadProxy{
		size:  size,
		rps:   wrappedProxies,
		lease: rl,
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////

type multiReadProxy struct {
	// The size of the proxied content.
	size int64

	// The wrapped read proxies, indexed by their logical starting offset.
	//
	// INVARIANT: If len(rps) != 0, rps[0].off == 0
	// INVARIANT: For each x, x.rp.Size() >= 0
	// INVARIANT: For each i>0, rps[i].off == rps[i-i].off + rps[i-i].rp.Size()
	// INVARIANT: size is the sum over the wrapped proxy sizes.
	rps []readProxyAndOffset

	// A read lease for the entire contents. May be nil.
	//
	// INVARIANT: If lease != nil, size == lease.Size()
	lease ReadLease

	destroyed bool
}

func (mrp *multiReadProxy) Size() (size int64) {
	size = mrp.size
	return
}

func (mrp *multiReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	panic("TODO")
}

func (mrp *multiReadProxy) Upgrade(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	panic("TODO")
}

func (mrp *multiReadProxy) Destroy() {
	// Destroy all of the wrapped proxies.
	for _, entry := range mrp.rps {
		entry.rp.Destroy()
	}

	// Destroy the lease for the entire contents, if any.
	if mrp.lease != nil {
		mrp.lease.Revoke()
	}

	// Crash early if called again.
	mrp.rps = nil
	mrp.lease = nil
}

func (mrp *multiReadProxy) CheckInvariants() {
	// INVARIANT: If len(rps) != 0, rps[0].off == 0
	if len(mrp.rps) != 0 && mrp.rps[0].off != 0 {
		panic(fmt.Sprintf("Unexpected starting point: %v", mrp.rps[0].off))
	}

	// INVARIANT: For each x, x.rp.Size() >= 0
	for _, x := range mrp.rps {
		if x.rp.Size() < 0 {
			panic(fmt.Sprintf("Negative size: %v", x.rp.Size()))
		}
	}

	// INVARIANT: For each i>0, rps[i].off == rps[i-i].off + rps[i-i].rp.Size()
	for i := range mrp.rps {
		if i > 0 && !(mrp.rps[i].off == mrp.rps[i-1].off+mrp.rps[i-1].rp.Size()) {
			panic("Offsets are not indexed correctly.")
		}
	}

	// INVARIANT: size is the sum over the wrapped proxy sizes.
	var sum int64
	for _, wrapped := range mrp.rps {
		sum += wrapped.rp.Size()
	}

	if sum != mrp.size {
		panic(fmt.Sprintf("Size mismatch: %v vs. %v", sum, mrp.size))
	}

	// INVARIANT: If lease != nil, size == lease.Size()
	if mrp.lease != nil && mrp.size != mrp.lease.Size() {
		panic(fmt.Sprintf("Size mismatch: %v vs. %v", mrp.size, mrp.lease.Size()))
	}
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type readProxyAndOffset struct {
	off int64
	rp  ReadProxy
}

// Return the index within mrp.rps of the first read proxy whose logical offset
// is greater than off. If there is none, return len(mrp.rps).
func (mrp *multiReadProxy) upperBound(off int64) (index int) {
	panic("TODO")
}
<commit_msg>Handle some special cases.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"fmt"
	"io"

	"golang.org/x/net/context"
)

// Create a read proxy consisting of the contents defined by the supplied
// refreshers concatenated. See NewReadProxy for more.
//
// If rl is non-nil, it will be used as the first temporary copy of the
// contents, and must match the concatenation of the content returned by the
// refreshers.
func NewMultiReadProxy(
	fl FileLeaser,
	refreshers []Refresher,
	rl ReadLease) (rp ReadProxy) {
	// Create one wrapped read proxy per refresher.
	var wrappedProxies []readProxyAndOffset
	var size int64
	for _, r := range refreshers {
		wrapped := NewReadProxy(fl, r, nil)
		wrappedProxies = append(wrappedProxies, readProxyAndOffset{size, wrapped})
		size += wrapped.Size()
	}

	rp = &multiReadProxy{
		size:  size,
		rps:   wrappedProxies,
		lease: rl,
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////

type multiReadProxy struct {
	// The size of the proxied content.
	size int64

	// The wrapped read proxies, indexed by their logical starting offset.
	//
	// INVARIANT: If len(rps) != 0, rps[0].off == 0
	// INVARIANT: For each x, x.rp.Size() >= 0
	// INVARIANT: For each i>0, rps[i].off == rps[i-i].off + rps[i-i].rp.Size()
	// INVARIANT: size is the sum over the wrapped proxy sizes.
	rps []readProxyAndOffset

	// A read lease for the entire contents. May be nil.
	//
	// INVARIANT: If lease != nil, size == lease.Size()
	lease ReadLease

	destroyed bool
}

func (mrp *multiReadProxy) Size() (size int64) {
	size = mrp.size
	return
}

func (mrp *multiReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	// Special case: we don't support negative offsets, silly user.
	if off < 0 {
		err = fmt.Errorf("Invalid offset: %v", off)
		return
	}

	// Special case: offsets at or beyond the end of our content can never yield
	// any content, and the io.ReaderAt spec allows us to return EOF. Knock them
	// out here so we know off is in range when we start below.
	if off >= mrp.Size() {
		err = io.EOF
		return
	}

	panic("TODO")
}

func (mrp *multiReadProxy) Upgrade(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	panic("TODO")
}

func (mrp *multiReadProxy) Destroy() {
	// Destroy all of the wrapped proxies.
	for _, entry := range mrp.rps {
		entry.rp.Destroy()
	}

	// Destroy the lease for the entire contents, if any.
	if mrp.lease != nil {
		mrp.lease.Revoke()
	}

	// Crash early if called again.
	mrp.rps = nil
	mrp.lease = nil
}

func (mrp *multiReadProxy) CheckInvariants() {
	// INVARIANT: If len(rps) != 0, rps[0].off == 0
	if len(mrp.rps) != 0 && mrp.rps[0].off != 0 {
		panic(fmt.Sprintf("Unexpected starting point: %v", mrp.rps[0].off))
	}

	// INVARIANT: For each x, x.rp.Size() >= 0
	for _, x := range mrp.rps {
		if x.rp.Size() < 0 {
			panic(fmt.Sprintf("Negative size: %v", x.rp.Size()))
		}
	}

	// INVARIANT: For each i>0, rps[i].off == rps[i-i].off + rps[i-i].rp.Size()
	for i := range mrp.rps {
		if i > 0 && !(mrp.rps[i].off == mrp.rps[i-1].off+mrp.rps[i-1].rp.Size()) {
			panic("Offsets are not indexed correctly.")
		}
	}

	// INVARIANT: size is the sum over the wrapped proxy sizes.
	var sum int64
	for _, wrapped := range mrp.rps {
		sum += wrapped.rp.Size()
	}

	if sum != mrp.size {
		panic(fmt.Sprintf("Size mismatch: %v vs. %v", sum, mrp.size))
	}

	// INVARIANT: If lease != nil, size == lease.Size()
	if mrp.lease != nil && mrp.size != mrp.lease.Size() {
		panic(fmt.Sprintf("Size mismatch: %v vs. %v", mrp.size, mrp.lease.Size()))
	}
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type readProxyAndOffset struct {
	off int64
	rp  ReadProxy
}

// Return the index within mrp.rps of the first read proxy whose logical offset
// is greater than off. If there is none, return len(mrp.rps).
func (mrp *multiReadProxy) upperBound(off int64) (index int) {
	panic("TODO")
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/codegangsta/cli"
)

const (
	EnvProd = "prod"
	EnvDev  = "dev"
)

var DefaultDevStack = []rest.Middleware{

	// logging
	&rest.AccessLogApacheMiddleware{},
	&rest.TimerMiddleware{},
	&rest.RecorderMiddleware{},

	// catches the panic errors that occur with stack trace
	&rest.RecoverMiddleware{
		EnableResponseStackTrace: true,
	},

	// json pretty print
	&rest.JsonIndentMiddleware{},

	// verifies the request Content-Type header
	// The expected Content-Type is 'application/json'
	// if the content is non-null
	&rest.ContentTypeCheckerMiddleware{},
}

var DefaultProdStack = []rest.Middleware{

	// logging
	&rest.AccessLogJsonMiddleware{
		// No prefix or other fields, entire output is JSON encoded and could break it.
		Logger: log.New(os.Stdout, "", 0),
	},
	&rest.TimerMiddleware{},
	&rest.RecorderMiddleware{},

	// catches the panic errorsx
	&rest.RecoverMiddleware{},

	// response compression
	&rest.GzipMiddleware{},

	// verifies the request Content-Type header
	// The expected Content-Type is 'application/json'
	// if the content is non-null
	&rest.ContentTypeCheckerMiddleware{},
}

func InstallMiddleware(c *cli.Context, api *rest.Api) {

	env := c.String(EnvFlag)

	switch env {
	case EnvProd:
		api.Use(DefaultProdStack...)
	case EnvDev:
		api.Use(DefaultDevStack...)
	default:
		log.Fatal(InvalidValueError(EnvFlag, env))
	}

	api.Use(&rest.CorsMiddleware{
		RejectNonCorsRequests: false,
		// Accept all requests
		// Should be tested with some list
		OriginValidator: func(origin string, request *rest.Request) bool {
			return true
		},
	})
}
<commit_msg>Configure CORS<commit_after>package main

import (
	"log"
	"os"

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/codegangsta/cli"
	"github.com/mendersoftware/artifacts/handlers"
)

const (
	EnvProd = "prod"
	EnvDev  = "dev"
)

var DefaultDevStack = []rest.Middleware{

	// logging
	&rest.AccessLogApacheMiddleware{},
	&rest.TimerMiddleware{},
	&rest.RecorderMiddleware{},

	// catches the panic errors that occur with stack trace
	&rest.RecoverMiddleware{
		EnableResponseStackTrace: true,
	},

	// json pretty print
	&rest.JsonIndentMiddleware{},

	// verifies the request Content-Type header
	// The expected Content-Type is 'application/json'
	// if the content is non-null
	&rest.ContentTypeCheckerMiddleware{},
}

var DefaultProdStack = []rest.Middleware{

	// logging
	&rest.AccessLogJsonMiddleware{
		// No prefix or other fields, entire output is JSON encoded and could break it.
		Logger: log.New(os.Stdout, "", 0),
	},
	&rest.TimerMiddleware{},
	&rest.RecorderMiddleware{},

	// catches the panic errorsx
	&rest.RecoverMiddleware{},

	// response compression
	&rest.GzipMiddleware{},

	// verifies the request Content-Type header
	// The expected Content-Type is 'application/json'
	// if the content is non-null
	&rest.ContentTypeCheckerMiddleware{},
}

func InstallMiddleware(c *cli.Context, api *rest.Api) {

	env := c.String(EnvFlag)

	switch env {
	case EnvProd:
		api.Use(DefaultProdStack...)
	case EnvDev:
		api.Use(DefaultDevStack...)
	default:
		log.Fatal(InvalidValueError(EnvFlag, env))
	}

	api.Use(&rest.CorsMiddleware{
		RejectNonCorsRequests: false,

		// Should be tested with some list
		OriginValidator: func(origin string, request *rest.Request) bool {
			// Accept all requests
			return true
		},

		// Preflight request cache lenght
		AccessControlMaxAge: 60,

		// Allow authentication requests
		AccessControlAllowCredentials: true,

		// Allowed headers
		AllowedMethods: []string{handlers.HttpMethodGet, handlers.HttpMethodPost, handlers.HttpMethodPut, handlers.HttpMethodDelete, handlers.HttpMethodOptions},

		// Allowed heardes
		AllowedHeaders: []string{"Accept", "Content-type", "Origin", "Authorization", "Accept-Encoding", "Access-Control-Request-Headers", "Access-Control-Request-Method"},
	})
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"reflect"
	"strings"
	"testing"
)

func TestParseAST(t *testing.T) {
	tests := []struct {
		in   string
		want prog
	}{
		{
			in:   "",
			want: prog{},
		},
		{
			in:   "# comment",
			want: prog{},
		},
		{
			in: "foo",
			want: prog{stmts: []node{
				command{args: []string{"foo"}},
			}},
		},
		{
			in:   "# comment\nfoo",
			want: prog{stmts: []node{
				command{args: []string{"foo"}},
			}},
		},
		{
			in: "foo arg1 arg2",
			want: prog{stmts: []node{
				command{args: []string{"foo", "arg1", "arg2"}},
			}},
		},
		{
			in: "( foo; )",
			want: prog{stmts: []node{
				subshell{stmts: []node{
					command{args: []string{"foo"}},
				}},
			}},
		},
		{
			in: "{ foo; }",
			want: prog{stmts: []node{
				block{stmts: []node{
					command{args: []string{"foo"}},
				}},
			}},
		},
		{
			in: "if foo; then bar; fi",
			want: prog{stmts: []node{
				ifStmt{
					cond: command{args: []string{"foo"}},
					thenStmts: []node{
						command{args: []string{"bar"}},
					},
				},
			}},
		},
		{
			in: "if foo; then bar; else pass; fi",
			want: prog{stmts: []node{
				ifStmt{
					cond: command{args: []string{"foo"}},
					thenStmts: []node{
						command{args: []string{"bar"}},
					},
					elseStmts: []node{
						command{args: []string{"pass"}},
					},
				},
			}},
		},
		{
			in: "while foo; do bar; done",
			want: prog{stmts: []node{
				whileStmt{
					cond: command{args: []string{"foo"}},
					doStmts: []node{
						command{args: []string{"bar"}},
					},
				},
			}},
		},
	}
	for _, c := range tests {
		r := strings.NewReader(c.in)
		got, err := parse(r, "")
		if err != nil {
			t.Fatalf("Unexpected error in %q: %v", c.in, err)
		}
		if !reflect.DeepEqual(got, c.want) {
			t.Fatalf("AST mismatch in %q\nwant: %#v\ngot:  %#v",
				c.in, c.want, got)
		}
	}
}
<commit_msg>Test strings in the AST tests<commit_after>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"reflect"
	"strings"
	"testing"
)

func TestParseAST(t *testing.T) {
	tests := []struct {
		in   string
		want prog
	}{
		{
			in:   "",
			want: prog{},
		},
		{
			in:   "# comment",
			want: prog{},
		},
		{
			in: "foo",
			want: prog{stmts: []node{
				command{args: []string{"foo"}},
			}},
		},
		{
			in: "# comment\nfoo",
			want: prog{stmts: []node{
				command{args: []string{"foo"}},
			}},
		},
		{
			in: "foo arg1 arg2",
			want: prog{stmts: []node{
				command{args: []string{"foo", "arg1", "arg2"}},
			}},
		},
		{
			in: "( foo; )",
			want: prog{stmts: []node{
				subshell{stmts: []node{
					command{args: []string{"foo"}},
				}},
			}},
		},
		{
			in: "{ foo; }",
			want: prog{stmts: []node{
				block{stmts: []node{
					command{args: []string{"foo"}},
				}},
			}},
		},
		{
			in: "if foo; then bar; fi",
			want: prog{stmts: []node{
				ifStmt{
					cond: command{args: []string{"foo"}},
					thenStmts: []node{
						command{args: []string{"bar"}},
					},
				},
			}},
		},
		{
			in: "if foo; then bar; else pass; fi",
			want: prog{stmts: []node{
				ifStmt{
					cond: command{args: []string{"foo"}},
					thenStmts: []node{
						command{args: []string{"bar"}},
					},
					elseStmts: []node{
						command{args: []string{"pass"}},
					},
				},
			}},
		},
		{
			in: "while foo; do bar; done",
			want: prog{stmts: []node{
				whileStmt{
					cond: command{args: []string{"foo"}},
					doStmts: []node{
						command{args: []string{"bar"}},
					},
				},
			}},
		},
		{
			in: "echo ' ' \"foo bar\"",
			want: prog{stmts: []node{
				command{args: []string{"echo", "' '", "\"foo bar\""}},
			}},
		},
	}
	for _, c := range tests {
		r := strings.NewReader(c.in)
		got, err := parse(r, "")
		if err != nil {
			t.Fatalf("Unexpected error in %q: %v", c.in, err)
		}
		if !reflect.DeepEqual(got, c.want) {
			t.Fatalf("AST mismatch in %q\nwant: %#v\ngot:  %#v",
				c.in, c.want, got)
		}
	}
}
<|endoftext|>


<commit_before>package nsq

import (
	"io"
	"log"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/pkg/errors"
)

type Consumer struct {
	// Communication channels of the consumer.
	msgs  chan Message  // messages read from the connections
	close chan struct{} // closed when the consumer is shutdown
	done  chan struct{} // closed when the consumer is not running anymore

	// Immutable state of the consumer.
	topic        string
	channel      string
	address      string
	lookup       []string
	maxInFlight  int
	identify     Identify
	dialTimeout  time.Duration
	readTimeout  time.Duration
	writeTimeout time.Duration

	// Shared state of the consumer.
	mtx   sync.Mutex
	join  sync.WaitGroup
	conns map[string](chan<- Command)
}

type ConsumerConfig struct {
	Topic        string
	Channel      string
	Address      string
	Lookup       []string
	MaxInFlight  int
	Identify     Identify
	DialTimeout  time.Duration
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
}

func StartConsumer(config ConsumerConfig) (c *Consumer, err error) {
	if len(config.Topic) == 0 {
		err = errors.New("creating a new consumer requires a non-empty topic")
		return
	}

	if len(config.Channel) == 0 {
		err = errors.New("creating a new consumer requires a non-empty channel")
		return
	}

	if config.MaxInFlight == 0 {
		config.MaxInFlight = DefaultMaxInFlight
	}

	if config.DialTimeout == 0 {
		config.DialTimeout = DefaultDialTimeout
	}

	if config.ReadTimeout == 0 {
		config.ReadTimeout = DefaultReadTimeout
	}

	if config.WriteTimeout == 0 {
		config.WriteTimeout = DefaultWriteTimeout
	}

	c = &Consumer{
		msgs:  make(chan Message, config.MaxInFlight),
		close: make(chan struct{}),
		done:  make(chan struct{}),

		topic:        config.Topic,
		channel:      config.Channel,
		address:      config.Address,
		lookup:       append([]string{}, config.Lookup...),
		maxInFlight:  config.MaxInFlight,
		identify:     setIdentifyDefaults(config.Identify),
		dialTimeout:  config.DialTimeout,
		readTimeout:  config.ReadTimeout,
		writeTimeout: config.WriteTimeout,

		conns: make(map[string](chan<- Command)),
	}

	go c.run()
	return
}

func (c *Consumer) Close() (err error) {
	err = c.Shutdown()
	c.stop()
	c.wait()
	return
}

func (c *Consumer) Shutdown() (err error) {
	c.mtx.Lock()

	for _, cmdChan := range c.conns {
		cmdChan <- Cls{}
	}

	c.mtx.Unlock()
	return
}

func (c *Consumer) Messages() <-chan Message {
	return c.msgs
}

func (c *Consumer) stop() {
	defer func() { recover() }() // allow the method to be called more than once
	close(c.close)
}

func (c *Consumer) wait() {
	<-c.done
}

func (c *Consumer) run() {
	ticker := time.NewTicker(15 * time.Second)
	defer ticker.Stop()
	defer close(c.done)

	for {
		select {
		case <-ticker.C:
			if err := c.pulse(); err != nil {
				log.Print(err)
			}

		case <-c.close:
			close(c.msgs)
			c.join.Done()
			return
		}
	}
}

func (c *Consumer) pulse() (err error) {
	var nodes []string

	if len(c.lookup) == 0 {
		nodes = []string{c.address}
	} else {
		var res LookupTopicResponse

		if res, err = LookupTopic(c.topic, c.lookup...); err != nil {
			return
		}

		for _, p := range res.Producers {
			nodes = append(nodes, net.JoinHostPort(p.RemoteAddress, strconv.Itoa(p.TcpPort)))
		}
	}

	c.mtx.Lock()

	for _, addr := range nodes {
		if _, exists := c.conns[addr]; !exists {
			c.openConn(addr)
		}
	}

	c.mtx.Unlock()
	return
}

func (c *Consumer) openConn(addr string) {
	cmdChan := make(chan Command, c.maxInFlight)
	c.mtx.Lock()
	c.conns[addr] = cmdChan
	c.mtx.Unlock()
	c.join.Add(1)
	go c.runConn(addr, cmdChan)
	return
}

func (c *Consumer) closeConn(addr string) {
	c.mtx.Lock()
	cmdChan := c.conns[addr]
	delete(c.conns, addr)
	c.mtx.Unlock()
	c.join.Done()
	close(cmdChan)
}

func (c *Consumer) runConn(addr string, cmdChan chan Command) {
	defer c.closeConn(addr)

	var conn *Conn
	var err error
	var rdy int

	if conn, err = DialTimeout(addr, c.dialTimeout); err != nil {
		log.Printf("connecting to %s: %s", addr, err)
		return
	}

	c.join.Add(1)
	go c.writeConn(conn, cmdChan)

	identified := false
	cmdChan <- c.identify

	defer func() { recover() }() // catch the panic that happens when pushing a message after the consumer was shutdown

	for {
		var frame Frame
		var err error

		if rdy == 0 {
			rdy = c.approximateRdyCount()
			cmdChan <- Rdy{Count: rdy}
		}

		if err = conn.SetReadDeadline(time.Now().Add(c.readTimeout)); err != nil {
			log.Print(err)
			return
		}

		if frame, err = conn.ReadFrame(); err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				log.Print(err)
			}
			return
		}

		switch f := frame.(type) {
		case Message:
			f.cmdChan = cmdChan
			c.msgs <- f
			rdy--

		case Response:
			switch f {
			case OK:
				if !identified {
					identified = true
					continue
				}

			case Heartbeat:
				cmdChan <- Nop{}
				continue

			case CloseWait:
				return
			}

			log.Printf("closing connection after receiving an unexpected response from %s: %s", conn.RemoteAddr(), f)
			return

		case Error:
			log.Printf("closing connection after receiving an error from %s: %s", conn.RemoteAddr(), f)
			return

		default:
			log.Printf("closing connection after receiving an unsupported frame from %s: %s", conn.RemoteAddr(), f.FrameType())
			return
		}
	}
}

func (c *Consumer) writeConn(conn *Conn, cmdChan <-chan Command) {
	defer c.join.Done()
	defer conn.Close()

	for cmd := range cmdChan {
		if err := c.writeConnCommand(conn, cmd); err != nil {
			log.Print(err)
			return
		}
	}
}

func (c *Consumer) writeConnCommand(conn *Conn, cmd Command) (err error) {
	if err = conn.SetWriteDeadline(time.Now().Add(c.writeTimeout)); err != nil {
		return
	}
	err = conn.WriteCommand(cmd)
	return
}

func (c *Consumer) approximateRdyCount() (count int) {
	c.mtx.Lock()
	conns := len(c.conns)
	c.mtx.Unlock()

	if conns == 0 {
		count = 1
	} else {
		count = c.maxInFlight / conns
	}

	if count < 1 {
		count = 1
	}

	return
}
<commit_msg>simplify consumer cleanup logic<commit_after>package nsq

import (
	"io"
	"log"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/pkg/errors"
)

type Consumer struct {
	// Communication channels of the consumer.
	msgs chan Message  // messages read from the connections
	done chan struct{} // closed when the consumer is shutdown

	// Immutable state of the consumer.
	topic        string
	channel      string
	address      string
	lookup       []string
	maxInFlight  int
	identify     Identify
	dialTimeout  time.Duration
	readTimeout  time.Duration
	writeTimeout time.Duration

	// Shared state of the consumer.
	mtx   sync.Mutex
	join  sync.WaitGroup
	conns map[string](chan<- Command)
}

type ConsumerConfig struct {
	Topic        string
	Channel      string
	Address      string
	Lookup       []string
	MaxInFlight  int
	Identify     Identify
	DialTimeout  time.Duration
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
}

func StartConsumer(config ConsumerConfig) (c *Consumer, err error) {
	if len(config.Topic) == 0 {
		err = errors.New("creating a new consumer requires a non-empty topic")
		return
	}

	if len(config.Channel) == 0 {
		err = errors.New("creating a new consumer requires a non-empty channel")
		return
	}

	if config.MaxInFlight == 0 {
		config.MaxInFlight = DefaultMaxInFlight
	}

	if config.DialTimeout == 0 {
		config.DialTimeout = DefaultDialTimeout
	}

	if config.ReadTimeout == 0 {
		config.ReadTimeout = DefaultReadTimeout
	}

	if config.WriteTimeout == 0 {
		config.WriteTimeout = DefaultWriteTimeout
	}

	c = &Consumer{
		msgs: make(chan Message, config.MaxInFlight),
		done: make(chan struct{}),

		topic:        config.Topic,
		channel:      config.Channel,
		address:      config.Address,
		lookup:       append([]string{}, config.Lookup...),
		maxInFlight:  config.MaxInFlight,
		identify:     setIdentifyDefaults(config.Identify),
		dialTimeout:  config.DialTimeout,
		readTimeout:  config.ReadTimeout,
		writeTimeout: config.WriteTimeout,

		conns: make(map[string](chan<- Command)),
	}

	go c.run()
	return
}

func (c *Consumer) Stop() {
	defer func() { recover() }() // allow the method to be called more than once
	close(c.done)
}

func (c *Consumer) Messages() <-chan Message {
	return c.msgs
}

func (c *Consumer) run() {
	ticker := time.NewTicker(15 * time.Second)
	defer ticker.Stop()
	defer close(c.msgs)

	for {
		select {
		case <-ticker.C:
			if err := c.pulse(); err != nil {
				log.Print(err)
			}

		case <-c.done:
			c.close()
			c.join.Wait()
			return
		}
	}
}

func (c *Consumer) pulse() (err error) {
	var nodes []string

	if len(c.lookup) == 0 {
		nodes = []string{c.address}
	} else {
		var res LookupTopicResponse

		if res, err = LookupTopic(c.topic, c.lookup...); err != nil {
			return
		}

		for _, p := range res.Producers {
			nodes = append(nodes, net.JoinHostPort(p.RemoteAddress, strconv.Itoa(p.TcpPort)))
		}
	}

	c.mtx.Lock()

	for _, addr := range nodes {
		if _, exists := c.conns[addr]; !exists {
			c.openConn(addr)
		}
	}

	c.mtx.Unlock()
	return
}

func (c *Consumer) close() {
	c.mtx.Lock()

	for _, cmdChan := range c.conns {
		cmdChan <- Cls{}
	}

	c.mtx.Unlock()
	return
}

func (c *Consumer) openConn(addr string) {
	cmdChan := make(chan Command, c.maxInFlight)
	c.mtx.Lock()
	c.conns[addr] = cmdChan
	c.mtx.Unlock()
	c.join.Add(1)
	go c.runConn(addr, cmdChan)
	return
}

func (c *Consumer) closeConn(addr string) {
	c.mtx.Lock()
	cmdChan := c.conns[addr]
	delete(c.conns, addr)
	c.mtx.Unlock()
	c.join.Done()
	close(cmdChan)
}

func (c *Consumer) runConn(addr string, cmdChan chan Command) {
	defer c.closeConn(addr)

	var conn *Conn
	var err error
	var rdy int

	if conn, err = DialTimeout(addr, c.dialTimeout); err != nil {
		log.Printf("failed to connect to %s: %s", addr, err)
		return
	}

	c.join.Add(1)
	go c.writeConn(conn, cmdChan)

	cmdChan <- c.identify
	cmdChan <- Sub{Topic: c.topic, Channel: c.channel}

	for {
		var frame Frame
		var err error

		select {
		default:
		case <-c.done:
			return
		}

		if rdy == 0 {
			rdy = c.approximateRdyCount()
			cmdChan <- Rdy{Count: rdy}
		}

		if err = conn.SetReadDeadline(time.Now().Add(c.readTimeout)); err != nil {
			log.Print(err)
			return
		}

		if frame, err = conn.ReadFrame(); err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				log.Print(err)
			}
			return
		}

		switch f := frame.(type) {
		case Message:
			f.cmdChan = cmdChan
			c.msgs <- f
			rdy--

		case Response:
			switch f {
			case OK:
				continue

			case Heartbeat:
				cmdChan <- Nop{}
				continue

			case CloseWait:
				return
			}

			log.Printf("closing connection after receiving an unexpected response from %s: %s", conn.RemoteAddr(), f)
			return

		case Error:
			log.Printf("closing connection after receiving an error from %s: %s", conn.RemoteAddr(), f)
			return

		default:
			log.Printf("closing connection after receiving an unsupported frame from %s: %s", conn.RemoteAddr(), f.FrameType())
			return
		}
	}
}

func (c *Consumer) writeConn(conn *Conn, cmdChan <-chan Command) {
	defer c.join.Done()
	defer conn.Close()

	for cmd := range cmdChan {
		if err := c.writeConnCommand(conn, cmd); err != nil {
			log.Print(err)
			return
		}
	}
}

func (c *Consumer) writeConnCommand(conn *Conn, cmd Command) (err error) {
	if err = conn.SetWriteDeadline(time.Now().Add(c.writeTimeout)); err != nil {
		return
	}
	err = conn.WriteCommand(cmd)
	return
}

func (c *Consumer) approximateRdyCount() (count int) {
	c.mtx.Lock()
	conns := len(c.conns)
	c.mtx.Unlock()

	if conns == 0 {
		count = 1
	} else {
		count = c.maxInFlight / conns
	}

	if count < 1 {
		count = 1
	}

	return
}
<|endoftext|>


<commit_before>package humanizeurl

import (
	"testing"
)

func TestHumanize(t *testing.T) {

	var tests = map[string]string{
		"http://sekimura.org":      "sekimura.org",
		" http://sekimura.org":     "sekimura.org",
		"https://sekimura.org":     "sekimura.org",
		"http://www.sekimura.org":  "sekimura.org",
		"www.sekimura.org":         "sekimura.org",
		"http://sekimura.org/":     "sekimura.org",
		"sekimura.org/":            "sekimura.org",
		"http://sekimura.org/foo/": "sekimura.org/foo",

		"file:///Users/sindresorhus/dev/humanize-url/": "file:///Users/sindresorhus/dev/humanize-url",
	}

	for orig, expected := range tests {
		actual, err := humanize(orig)
		if err != nil {
			t.Error("humanize failed", err, expected)
		}
		if actual != expected {
			t.Error("humanize did not match expeted value", actual, expected)
		}
	}
}
<commit_msg>chore: tweaking test data with anon struct<commit_after>package humanizeurl

import (
	"testing"
)

func TestHumanize(t *testing.T) {

	tests := []struct {
		value    string
		expected string
	}{
		{"http://sekimura.org", "sekimura.org"},
		{" http://sekimura.org", "sekimura.org"},
		{"https://sekimura.org", "sekimura.org"},
		{"http://www.sekimura.org", "sekimura.org"},
		{"www.sekimura.org", "sekimura.org"},
		{"http://sekimura.org/", "sekimura.org"},
		{"sekimura.org/", "sekimura.org"},
		{"http://sekimura.org/foo/", "sekimura.org/foo"},
		{"file:///Users/sindresorhus/dev/humanize-url/", "file:///Users/sindresorhus/dev/humanize-url"},
	}

	for i := range tests {
		actual, err := humanize(tests[i].value)
		if err != nil {
			t.Error("humanize failed", err)
		}
		if actual != tests[i].expected {
			t.Error("humanize did not match expected value", actual, tests[i].expected)
		}
	}
}
<|endoftext|>


<commit_before>package myaws

import (
	"fmt"

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

// SSMParameterLsOptions customize the behavior of the ParameterGet command.
type SSMParameterLsOptions struct {
	Name string
}

// SSMParameterLs get values from SSM parameter store with KMS decryption.
func (client *Client) SSMParameterLs(options SSMParameterLsOptions) error {
	filter := &ssm.ParametersFilter{
		Key: aws.String("Name"),
		Values: []*string{
			aws.String(options.Name),
		},
	}
	filters := []*ssm.ParametersFilter{filter}

	params := &ssm.DescribeParametersInput{
		Filters: filters,
	}

	response, err := client.SSM.DescribeParameters(params)
	if err != nil {
		return errors.Wrap(err, "DescribeParameters failed:")
	}

	for _, parameter := range response.Parameters {
		fmt.Fprintln(client.stdout, *parameter.Name)
	}

	return nil
}
<commit_msg>Fix bug of ssm parameter ls when name is empty<commit_after>package myaws

import (
	"fmt"

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

// SSMParameterLsOptions customize the behavior of the ParameterGet command.
type SSMParameterLsOptions struct {
	Name string
}

// SSMParameterLs get values from SSM parameter store with KMS decryption.
func (client *Client) SSMParameterLs(options SSMParameterLsOptions) error {
	var filter *ssm.ParametersFilter
	if len(options.Name) > 0 {
		filter = &ssm.ParametersFilter{
			Key: aws.String("Name"),
			Values: []*string{
				aws.String(options.Name),
			},
		}
	}
	filters := []*ssm.ParametersFilter{filter}

	params := &ssm.DescribeParametersInput{
		Filters: filters,
	}

	response, err := client.SSM.DescribeParameters(params)
	if err != nil {
		return errors.Wrap(err, "DescribeParameters failed:")
	}

	for _, parameter := range response.Parameters {
		fmt.Fprintln(client.stdout, *parameter.Name)
	}

	return nil
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"strings"

	shlex "github.com/flynn/go-shlex"
	"github.com/moul/advanced-ssh-config/vendor/github.com/Sirupsen/logrus"
	"github.com/moul/advanced-ssh-config/vendor/github.com/codegangsta/cli"

	"github.com/moul/advanced-ssh-config/pkg/config"
)

func cmdProxy(c *cli.Context) {
	if len(c.Args()) < 1 {
		logrus.Fatalf("assh: \"proxy\" requires 1 argument. See 'assh proxy --help'.")
	}

	host, port, err := configGetHostPort(c.Args()[0], c.Int("port"))
	if err != nil {
		logrus.Fatalf("Cannot get host '%s': %v", c.Args()[0], err)
	}

	err = proxyGo(host, port)
	//err = proxyCommand("nc -v -w 180 -G 5 {host} {port}", host, port)
	if err != nil {
		logrus.Fatalf("Proxy error: %v", err)
	}
}

func configGetHostPort(dest string, portFlag int) (string, uint, error) {
	conf, err := config.Open()
	if err != nil {
		return "", 0, err
	}

	// Get host configuration
	host := conf.GetHostSafe(dest)

	// Dial
	var port uint
	if portFlag > 0 {
		port = uint(portFlag)
	} else {
		port = host.Port
	}

	return host.Host, port, nil
}

func proxyCommand(command string, host string, port uint) error {
	command = strings.Replace(command, "{host}", host, -1)
	command = strings.Replace(command, "{port}", fmt.Sprintf("%d", port), -1)
	args, err := shlex.Split(command)
	if err != nil {
		return err
	}
	spawn := exec.Command(args[0], args[1:]...)
	spawn.Stdout = os.Stdout
	spawn.Stdin = os.Stdin
	spawn.Stderr = os.Stderr
	return spawn.Run()
}

func proxyGo(host string, port uint) error {
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		return err
	}

	defer conn.Close()

	logrus.Debugf("Connected to %s:%d\n", host, port)

	// Create Stdio pipes
	c1 := readAndWrite(conn, os.Stdout)
	c2 := readAndWrite(os.Stdin, conn)

	select {
	case err = <-c1:
	case err = <-c2:
	}
	if err != nil {
		return err
	}

	return nil
}

func readAndWrite(r io.Reader, w io.Writer) <-chan error {
	// Fixme: add an error channel
	buf := make([]byte, 1024)
	c := make(chan error)

	go func() {
		for {
			// Read
			n, err := r.Read(buf)
			if err != nil {
				if err != io.EOF {
					c <- err
				}
				break
			}

			// Write
			_, err = w.Write(buf[0:n])
			if err != nil {
				c <- err
			}
		}
		c <- nil
	}()
	return c
}
<commit_msg>Refactored cmdProxy<commit_after>package commands

import (
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"strings"

	shlex "github.com/flynn/go-shlex"
	"github.com/moul/advanced-ssh-config/vendor/github.com/Sirupsen/logrus"
	"github.com/moul/advanced-ssh-config/vendor/github.com/codegangsta/cli"

	"github.com/moul/advanced-ssh-config/pkg/config"
)

func cmdProxy(c *cli.Context) {
	if len(c.Args()) < 1 {
		logrus.Fatalf("assh: \"proxy\" requires 1 argument. See 'assh proxy --help'.")
	}

	host, err := computeHost(c.Args()[0], c.Int("port"))
	if err != nil {
		logrus.Fatalf("Cannot get host '%s': %v", c.Args()[0], err)
	}

	err = proxy(host)
	if err != nil {
		logrus.Fatalf("Proxy error: %v", err)
	}
}

func computeHost(dest string, portOverride int) (*config.Host, error) {
	conf, err := config.Open()
	if err != nil {
		return nil, err
	}

	host := conf.GetHostSafe(dest)
	if portOverride > 0 {
		host.Port = uint(portOverride)
	}

	return host, nil
}

func proxy(host *config.Host) error {
	// FIXME: gateways
	// FIXME: proxyCommand(host, "nc -v -w 180 -G 5 {host} {port}")
	return proxyGo(host)
}

func proxyCommand(host *config.Host, command string) error {
	command = strings.Replace(command, "{host}", host.Host, -1)
	command = strings.Replace(command, "{port}", fmt.Sprintf("%d", host.Port), -1)
	args, err := shlex.Split(command)
	if err != nil {
		return err
	}
	spawn := exec.Command(args[0], args[1:]...)
	spawn.Stdout = os.Stdout
	spawn.Stdin = os.Stdin
	spawn.Stderr = os.Stderr
	return spawn.Run()
}

func proxyGo(host *config.Host) error {
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host.Host, host.Port))
	if err != nil {
		return err
	}

	defer conn.Close()

	logrus.Debugf("Connected to %s:%d\n", host.Host, host.Port)

	// Create Stdio pipes
	c1 := readAndWrite(conn, os.Stdout)
	c2 := readAndWrite(os.Stdin, conn)

	select {
	case err = <-c1:
	case err = <-c2:
	}
	if err != nil {
		return err
	}

	return nil
}

func readAndWrite(r io.Reader, w io.Writer) <-chan error {
	// Fixme: add an error channel
	buf := make([]byte, 1024)
	c := make(chan error)

	go func() {
		for {
			// Read
			n, err := r.Read(buf)
			if err != nil {
				if err != io.EOF {
					c <- err
				}
				break
			}

			// Write
			_, err = w.Write(buf[0:n])
			if err != nil {
				c <- err
			}
		}
		c <- nil
	}()
	return c
}
<|endoftext|>


<commit_before>// Package log implements request (access) logging middleware.
package log

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"sync"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

func init() {
	caddy.RegisterPlugin("log", caddy.Plugin{
		ServerType: "http",
		Action:     setup,
	})
}

// Logger is a basic request logging middleware.
type Logger struct {
	Next      httpserver.Handler
	Rules     []*Rule
	ErrorFunc func(http.ResponseWriter, *http.Request, int) // failover error handler
}

func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range l.Rules {
		if httpserver.Path(r.URL.Path).Matches(rule.PathScope) {
			// Record the response
			responseRecorder := httpserver.NewResponseRecorder(w)

			// Attach the Replacer we'll use so that other middlewares can
			// set their own placeholders if they want to.
			rep := httpserver.NewReplacer(r, responseRecorder, CommonLogEmptyValue)
			responseRecorder.Replacer = rep

			// Bon voyage, request!
			status, err := l.Next.ServeHTTP(responseRecorder, r)

			if status >= 400 {
				// There was an error up the chain, but no response has been written yet.
				// The error must be handled here so the log entry will record the response size.
				if l.ErrorFunc != nil {
					l.ErrorFunc(responseRecorder, r, status)
				} else {
					// Default failover error handler
					responseRecorder.WriteHeader(status)
					fmt.Fprintf(responseRecorder, "%d %s", status, http.StatusText(status))
				}
				status = 0
			}

			// Write log entries
			for _, e := range rule.Entries {
				e.fileMu.RLock()
				e.Log.Println(rep.Replace(e.Format))
				e.fileMu.RUnlock()
			}

			return status, err
		}
	}
	return l.Next.ServeHTTP(w, r)
}

// Entry represents a log entry under a path scope
type Entry struct {
	OutputFile string
	Format     string
	Log        *log.Logger
	Roller     *httpserver.LogRoller
	file       *os.File      // if logging to a file that needs to be closed
	fileMu     *sync.RWMutex // files can't be safely read/written in one goroutine and closed in another (issue #1371)
}

// Rule configures the logging middleware.
type Rule struct {
	PathScope string
	Entries   []*Entry
}

const (
	// DefaultLogFilename is the default log filename.
	DefaultLogFilename = "access.log"
	// CommonLogFormat is the common log format.
	CommonLogFormat = `{remote} ` + CommonLogEmptyValue + ` [{when}] "{method} {uri} {proto}" {status} {size}`
	// CommonLogEmptyValue is the common empty log value.
	CommonLogEmptyValue = "-"
	// CombinedLogFormat is the combined log format.
	CombinedLogFormat = CommonLogFormat + ` "{>Referer}" "{>User-Agent}"`
	// DefaultLogFormat is the default log format.
	DefaultLogFormat = CommonLogFormat
)
<commit_msg>Added additional - to common log file format (#1399)<commit_after>// Package log implements request (access) logging middleware.
package log

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"sync"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

func init() {
	caddy.RegisterPlugin("log", caddy.Plugin{
		ServerType: "http",
		Action:     setup,
	})
}

// Logger is a basic request logging middleware.
type Logger struct {
	Next      httpserver.Handler
	Rules     []*Rule
	ErrorFunc func(http.ResponseWriter, *http.Request, int) // failover error handler
}

func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	for _, rule := range l.Rules {
		if httpserver.Path(r.URL.Path).Matches(rule.PathScope) {
			// Record the response
			responseRecorder := httpserver.NewResponseRecorder(w)

			// Attach the Replacer we'll use so that other middlewares can
			// set their own placeholders if they want to.
			rep := httpserver.NewReplacer(r, responseRecorder, CommonLogEmptyValue)
			responseRecorder.Replacer = rep

			// Bon voyage, request!
			status, err := l.Next.ServeHTTP(responseRecorder, r)

			if status >= 400 {
				// There was an error up the chain, but no response has been written yet.
				// The error must be handled here so the log entry will record the response size.
				if l.ErrorFunc != nil {
					l.ErrorFunc(responseRecorder, r, status)
				} else {
					// Default failover error handler
					responseRecorder.WriteHeader(status)
					fmt.Fprintf(responseRecorder, "%d %s", status, http.StatusText(status))
				}
				status = 0
			}

			// Write log entries
			for _, e := range rule.Entries {
				e.fileMu.RLock()
				e.Log.Println(rep.Replace(e.Format))
				e.fileMu.RUnlock()
			}

			return status, err
		}
	}
	return l.Next.ServeHTTP(w, r)
}

// Entry represents a log entry under a path scope
type Entry struct {
	OutputFile string
	Format     string
	Log        *log.Logger
	Roller     *httpserver.LogRoller
	file       *os.File      // if logging to a file that needs to be closed
	fileMu     *sync.RWMutex // files can't be safely read/written in one goroutine and closed in another (issue #1371)
}

// Rule configures the logging middleware.
type Rule struct {
	PathScope string
	Entries   []*Entry
}

const (
	// DefaultLogFilename is the default log filename.
	DefaultLogFilename = "access.log"
	// CommonLogFormat is the common log format.
	CommonLogFormat = `{remote} ` + CommonLogEmptyValue + " " + CommonLogEmptyValue + ` [{when}] "{method} {uri} {proto}" {status} {size}`
	// CommonLogEmptyValue is the common empty log value.
	CommonLogEmptyValue = "-"
	// CombinedLogFormat is the combined log format.
	CombinedLogFormat = CommonLogFormat + ` "{>Referer}" "{>User-Agent}"`
	// DefaultLogFormat is the default log format.
	DefaultLogFormat = CommonLogFormat
)
<|endoftext|>


<commit_before>package network

import (
	"fmt"
	"net"
	"os"
	"strings"

	"github.com/pkg/errors"

	lxd "github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
	"github.com/lxc/lxd/shared/validate"
)

// Info represents information about a network driver.
type Info struct {
	Projects           bool // Indicates if driver can be used in network enabled projects.
	NodeSpecificConfig bool // Whether driver has cluster node specific config as a prerequisite for creation.
}

// common represents a generic LXD network.
type common struct {
	logger      logger.Logger
	state       *state.State
	id          int64
	project     string
	name        string
	description string
	config      map[string]string
	status      string
	managed     bool
	nodes       map[int64]db.NetworkNode
}

// init initialise internal variables.
func (n *common) init(state *state.State, id int64, projectName string, netInfo *api.Network, netNodes map[int64]db.NetworkNode) {
	n.logger = logging.AddContext(logger.Log, log.Ctx{"project": projectName, "driver": netInfo.Type, "network": netInfo.Name})
	n.id = id
	n.project = projectName
	n.name = netInfo.Name
	n.config = netInfo.Config
	n.state = state
	n.description = netInfo.Description
	n.status = netInfo.Status
	n.managed = netInfo.Managed
	n.nodes = netNodes
}

// FillConfig fills requested config with any default values, by default this is a no-op.
func (n *common) FillConfig(config map[string]string) error {
	return nil
}

// validationRules returns a map of config rules common to all drivers.
func (n *common) validationRules() map[string]func(string) error {
	return map[string]func(string) error{}
}

// validate a network config against common rules and optional driver specific rules.
func (n *common) validate(config map[string]string, driverRules map[string]func(value string) error) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := n.validationRules()

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(config[k])
		if err != nil {
			return errors.Wrapf(err, "Invalid value for network %q option %q", n.name, k)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if strings.HasPrefix(k, "user.") {
			continue
		}

		return fmt.Errorf("Invalid option for network %q option %q", n.name, k)
	}

	return nil
}

// ValidateName validates network name.
func (n *common) ValidateName(name string) error {
	err := validate.IsURLSegmentSafe(name)
	if err != nil {
		return err
	}

	if strings.Contains(name, ":") {
		return fmt.Errorf("Cannot contain %q", ":")
	}

	return nil
}

// ID returns the network ID.
func (n *common) ID() int64 {
	return n.id
}

// Name returns the network name.
func (n *common) Name() string {
	return n.name
}

// Project returns the network project.
func (n *common) Project() string {
	return n.project
}

// Description returns the network description.
func (n *common) Description() string {
	return n.description
}

// Status returns the network status.
func (n *common) Status() string {
	return n.status
}

// LocalStatus returns network status of the local cluster member.
func (n *common) LocalStatus() string {
	node, exists := n.nodes[n.state.Cluster.GetNodeID()]
	if !exists {
		return api.NetworkStatusUnknown
	}

	return db.NetworkStateToAPIStatus(node.State)
}

// Config returns the network config.
func (n *common) Config() map[string]string {
	return n.config
}

func (n *common) IsManaged() bool {
	return n.managed
}

// Config returns the common network driver info.
func (n *common) Info() Info {
	return Info{
		Projects:           false,
		NodeSpecificConfig: true,
	}
}

// IsUsed returns whether the network is used by any instances or profiles.
func (n *common) IsUsed() (bool, error) {
	usedBy, err := UsedBy(n.state, n.project, n.name, true)
	if err != nil {
		return false, err
	}

	return len(usedBy) > 0, nil
}

// DHCPv4Subnet returns nil always.
func (n *common) DHCPv4Subnet() *net.IPNet {
	return nil
}

// DHCPv6Subnet returns nil always.
func (n *common) DHCPv6Subnet() *net.IPNet {
	return nil
}

// DHCPv4Ranges returns a parsed set of DHCPv4 ranges for this network.
func (n *common) DHCPv4Ranges() []shared.IPRange {
	dhcpRanges := make([]shared.IPRange, 0)
	if n.config["ipv4.dhcp.ranges"] != "" {
		for _, r := range strings.Split(n.config["ipv4.dhcp.ranges"], ",") {
			parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
			if len(parts) == 2 {
				startIP := net.ParseIP(parts[0])
				endIP := net.ParseIP(parts[1])
				dhcpRanges = append(dhcpRanges, shared.IPRange{
					Start: startIP.To4(),
					End:   endIP.To4(),
				})
			}
		}
	}

	return dhcpRanges
}

// DHCPv6Ranges returns a parsed set of DHCPv6 ranges for this network.
func (n *common) DHCPv6Ranges() []shared.IPRange {
	dhcpRanges := make([]shared.IPRange, 0)
	if n.config["ipv6.dhcp.ranges"] != "" {
		for _, r := range strings.Split(n.config["ipv6.dhcp.ranges"], ",") {
			parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
			if len(parts) == 2 {
				startIP := net.ParseIP(parts[0])
				endIP := net.ParseIP(parts[1])
				dhcpRanges = append(dhcpRanges, shared.IPRange{
					Start: startIP.To16(),
					End:   endIP.To16(),
				})
			}
		}
	}

	return dhcpRanges
}

// update the internal config variables, and if not cluster notification, notifies all nodes and updates database.
func (n *common) update(applyNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	// Update internal config before database has been updated (so that if update is a notification we apply
	// the config being supplied and not that in the database).
	n.description = applyNetwork.Description
	n.config = applyNetwork.Config

	// If this update isn't coming via a cluster notification itself, then notify all nodes of change and then
	// update the database.
	if clientType != request.ClientTypeNotifier {
		if targetNode == "" {
			// Notify all other nodes to update the network if no target specified.
			notifier, err := cluster.NewNotifier(n.state, n.state.Endpoints.NetworkCert(), n.state.ServerCert(), cluster.NotifyAll)
			if err != nil {
				return err
			}

			sendNetwork := applyNetwork
			sendNetwork.Config = make(map[string]string)
			for k, v := range applyNetwork.Config {
				// Don't forward node specific keys (these will be merged in on recipient node).
				if shared.StringInSlice(k, db.NodeSpecificNetworkConfig) {
					continue
				}

				sendNetwork.Config[k] = v
			}

			err = notifier(func(client lxd.InstanceServer) error {
				return client.UseProject(n.project).UpdateNetwork(n.name, sendNetwork, "")
			})
			if err != nil {
				return err
			}
		}

		// Update the database.
		err := n.state.Cluster.UpdateNetwork(n.project, n.name, applyNetwork.Description, applyNetwork.Config)
		if err != nil {
			return err
		}

	}

	return nil
}

// configChanged compares supplied new config with existing config. Returns a boolean indicating if differences in
// the config or description were found (and the database record needs updating), and a list of non-user config
// keys that have changed, and a copy of the current internal network config that can be used to revert if needed.
func (n *common) configChanged(newNetwork api.NetworkPut) (bool, []string, api.NetworkPut, error) {
	// Backup the current state.
	oldNetwork := api.NetworkPut{
		Description: n.description,
		Config:      map[string]string{},
	}

	err := shared.DeepCopy(&n.config, &oldNetwork.Config)
	if err != nil {
		return false, nil, oldNetwork, err
	}

	// Diff the configurations.
	changedKeys := []string{}
	dbUpdateNeeded := false

	if newNetwork.Description != n.description {
		dbUpdateNeeded = true
	}

	for k, v := range oldNetwork.Config {
		if v != newNetwork.Config[k] {
			dbUpdateNeeded = true

			// Add non-user changed key to list of changed keys.
			if !strings.HasPrefix(k, "user.") && !shared.StringInSlice(k, changedKeys) {
				changedKeys = append(changedKeys, k)
			}
		}
	}

	for k, v := range newNetwork.Config {
		if v != oldNetwork.Config[k] {
			dbUpdateNeeded = true

			// Add non-user changed key to list of changed keys.
			if !strings.HasPrefix(k, "user.") && !shared.StringInSlice(k, changedKeys) {
				changedKeys = append(changedKeys, k)
			}
		}
	}

	return dbUpdateNeeded, changedKeys, oldNetwork, nil
}

// rename the network directory, update database record and update internal variables.
func (n *common) rename(newName string) error {
	// Clear new directory if exists.
	if shared.PathExists(shared.VarPath("networks", newName)) {
		os.RemoveAll(shared.VarPath("networks", newName))
	}

	// Rename directory to new name.
	if shared.PathExists(shared.VarPath("networks", n.name)) {
		err := os.Rename(shared.VarPath("networks", n.name), shared.VarPath("networks", newName))
		if err != nil {
			return err
		}
	}

	// Rename the database entry.
	err := n.state.Cluster.RenameNetwork(n.project, n.name, newName)
	if err != nil {
		return err
	}

	// Reinitialise internal name variable and logger context with new name.
	n.name = newName

	return nil
}

// delete the network from the database if clusterNotification is false.
func (n *common) delete(clientType request.ClientType) error {
	// Cleanup storage.
	if shared.PathExists(shared.VarPath("networks", n.name)) {
		os.RemoveAll(shared.VarPath("networks", n.name))
	}

	return nil
}

// Create is a no-op.
func (n *common) Create(clientType request.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})
	return nil
}

// HandleHeartbeat is a no-op.
func (n *common) HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error {
	return nil
}

// notifyDependentNetworks allows any dependent networks to apply changes to themselves when this network changes.
func (n *common) notifyDependentNetworks(changedKeys []string) {
	if n.Project() != project.Default {
		return // Only networks in the default project can be used as dependent networks.
	}

	// Get a list of projects.
	var err error
	var projectNames []string

	err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		projectNames, err = tx.GetProjectNames()
		return err
	})
	if err != nil {
		n.logger.Error("Failed to load projects", log.Ctx{"err": err})
		return
	}

	for _, projectName := range projectNames {
		// Get a list of managed networks in project.
		depNets, err := n.state.Cluster.GetCreatedNetworks(projectName)
		if err != nil {
			n.logger.Error("Failed to load networks in project", log.Ctx{"project": projectName, "err": err})
			continue // Continue to next project.
		}

		for _, depName := range depNets {
			depNet, err := LoadByName(n.state, projectName, depName)
			if err != nil {
				n.logger.Error("Failed to load dependent network", log.Ctx{"project": projectName, "dependentNetwork": depName, "err": err})
				continue // Continue to next network.
			}

			if depNet.Config()["network"] != n.Name() {
				continue // Skip network, as does not depend on our network.
			}

			err = depNet.handleDependencyChange(n.Name(), n.Config(), changedKeys)
			if err != nil {
				n.logger.Error("Failed notifying dependent network", log.Ctx{"project": projectName, "dependentNetwork": depName, "err": err})
				continue // Continue to next network.
			}
		}
	}
}

// handleDependencyChange is a placeholder for networks that don't need to handle changes from dependent networks.
func (n *common) handleDependencyChange(netName string, netConfig map[string]string, changedKeys []string) error {
	return nil
}
<commit_msg>lxd/network/driver/common: Updates validate to use shared.IsUserConfig<commit_after>package network

import (
	"fmt"
	"net"
	"os"
	"strings"

	"github.com/pkg/errors"

	lxd "github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
	"github.com/lxc/lxd/shared/validate"
)

// Info represents information about a network driver.
type Info struct {
	Projects           bool // Indicates if driver can be used in network enabled projects.
	NodeSpecificConfig bool // Whether driver has cluster node specific config as a prerequisite for creation.
}

// common represents a generic LXD network.
type common struct {
	logger      logger.Logger
	state       *state.State
	id          int64
	project     string
	name        string
	description string
	config      map[string]string
	status      string
	managed     bool
	nodes       map[int64]db.NetworkNode
}

// init initialise internal variables.
func (n *common) init(state *state.State, id int64, projectName string, netInfo *api.Network, netNodes map[int64]db.NetworkNode) {
	n.logger = logging.AddContext(logger.Log, log.Ctx{"project": projectName, "driver": netInfo.Type, "network": netInfo.Name})
	n.id = id
	n.project = projectName
	n.name = netInfo.Name
	n.config = netInfo.Config
	n.state = state
	n.description = netInfo.Description
	n.status = netInfo.Status
	n.managed = netInfo.Managed
	n.nodes = netNodes
}

// FillConfig fills requested config with any default values, by default this is a no-op.
func (n *common) FillConfig(config map[string]string) error {
	return nil
}

// validationRules returns a map of config rules common to all drivers.
func (n *common) validationRules() map[string]func(string) error {
	return map[string]func(string) error{}
}

// validate a network config against common rules and optional driver specific rules.
func (n *common) validate(config map[string]string, driverRules map[string]func(value string) error) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := n.validationRules()

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(config[k])
		if err != nil {
			return errors.Wrapf(err, "Invalid value for network %q option %q", n.name, k)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if shared.IsUserConfig(k) {
			continue
		}

		return fmt.Errorf("Invalid option for network %q option %q", n.name, k)
	}

	return nil
}

// ValidateName validates network name.
func (n *common) ValidateName(name string) error {
	err := validate.IsURLSegmentSafe(name)
	if err != nil {
		return err
	}

	if strings.Contains(name, ":") {
		return fmt.Errorf("Cannot contain %q", ":")
	}

	return nil
}

// ID returns the network ID.
func (n *common) ID() int64 {
	return n.id
}

// Name returns the network name.
func (n *common) Name() string {
	return n.name
}

// Project returns the network project.
func (n *common) Project() string {
	return n.project
}

// Description returns the network description.
func (n *common) Description() string {
	return n.description
}

// Status returns the network status.
func (n *common) Status() string {
	return n.status
}

// LocalStatus returns network status of the local cluster member.
func (n *common) LocalStatus() string {
	node, exists := n.nodes[n.state.Cluster.GetNodeID()]
	if !exists {
		return api.NetworkStatusUnknown
	}

	return db.NetworkStateToAPIStatus(node.State)
}

// Config returns the network config.
func (n *common) Config() map[string]string {
	return n.config
}

func (n *common) IsManaged() bool {
	return n.managed
}

// Config returns the common network driver info.
func (n *common) Info() Info {
	return Info{
		Projects:           false,
		NodeSpecificConfig: true,
	}
}

// IsUsed returns whether the network is used by any instances or profiles.
func (n *common) IsUsed() (bool, error) {
	usedBy, err := UsedBy(n.state, n.project, n.name, true)
	if err != nil {
		return false, err
	}

	return len(usedBy) > 0, nil
}

// DHCPv4Subnet returns nil always.
func (n *common) DHCPv4Subnet() *net.IPNet {
	return nil
}

// DHCPv6Subnet returns nil always.
func (n *common) DHCPv6Subnet() *net.IPNet {
	return nil
}

// DHCPv4Ranges returns a parsed set of DHCPv4 ranges for this network.
func (n *common) DHCPv4Ranges() []shared.IPRange {
	dhcpRanges := make([]shared.IPRange, 0)
	if n.config["ipv4.dhcp.ranges"] != "" {
		for _, r := range strings.Split(n.config["ipv4.dhcp.ranges"], ",") {
			parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
			if len(parts) == 2 {
				startIP := net.ParseIP(parts[0])
				endIP := net.ParseIP(parts[1])
				dhcpRanges = append(dhcpRanges, shared.IPRange{
					Start: startIP.To4(),
					End:   endIP.To4(),
				})
			}
		}
	}

	return dhcpRanges
}

// DHCPv6Ranges returns a parsed set of DHCPv6 ranges for this network.
func (n *common) DHCPv6Ranges() []shared.IPRange {
	dhcpRanges := make([]shared.IPRange, 0)
	if n.config["ipv6.dhcp.ranges"] != "" {
		for _, r := range strings.Split(n.config["ipv6.dhcp.ranges"], ",") {
			parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
			if len(parts) == 2 {
				startIP := net.ParseIP(parts[0])
				endIP := net.ParseIP(parts[1])
				dhcpRanges = append(dhcpRanges, shared.IPRange{
					Start: startIP.To16(),
					End:   endIP.To16(),
				})
			}
		}
	}

	return dhcpRanges
}

// update the internal config variables, and if not cluster notification, notifies all nodes and updates database.
func (n *common) update(applyNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	// Update internal config before database has been updated (so that if update is a notification we apply
	// the config being supplied and not that in the database).
	n.description = applyNetwork.Description
	n.config = applyNetwork.Config

	// If this update isn't coming via a cluster notification itself, then notify all nodes of change and then
	// update the database.
	if clientType != request.ClientTypeNotifier {
		if targetNode == "" {
			// Notify all other nodes to update the network if no target specified.
			notifier, err := cluster.NewNotifier(n.state, n.state.Endpoints.NetworkCert(), n.state.ServerCert(), cluster.NotifyAll)
			if err != nil {
				return err
			}

			sendNetwork := applyNetwork
			sendNetwork.Config = make(map[string]string)
			for k, v := range applyNetwork.Config {
				// Don't forward node specific keys (these will be merged in on recipient node).
				if shared.StringInSlice(k, db.NodeSpecificNetworkConfig) {
					continue
				}

				sendNetwork.Config[k] = v
			}

			err = notifier(func(client lxd.InstanceServer) error {
				return client.UseProject(n.project).UpdateNetwork(n.name, sendNetwork, "")
			})
			if err != nil {
				return err
			}
		}

		// Update the database.
		err := n.state.Cluster.UpdateNetwork(n.project, n.name, applyNetwork.Description, applyNetwork.Config)
		if err != nil {
			return err
		}

	}

	return nil
}

// configChanged compares supplied new config with existing config. Returns a boolean indicating if differences in
// the config or description were found (and the database record needs updating), and a list of non-user config
// keys that have changed, and a copy of the current internal network config that can be used to revert if needed.
func (n *common) configChanged(newNetwork api.NetworkPut) (bool, []string, api.NetworkPut, error) {
	// Backup the current state.
	oldNetwork := api.NetworkPut{
		Description: n.description,
		Config:      map[string]string{},
	}

	err := shared.DeepCopy(&n.config, &oldNetwork.Config)
	if err != nil {
		return false, nil, oldNetwork, err
	}

	// Diff the configurations.
	changedKeys := []string{}
	dbUpdateNeeded := false

	if newNetwork.Description != n.description {
		dbUpdateNeeded = true
	}

	for k, v := range oldNetwork.Config {
		if v != newNetwork.Config[k] {
			dbUpdateNeeded = true

			// Add non-user changed key to list of changed keys.
			if !strings.HasPrefix(k, "user.") && !shared.StringInSlice(k, changedKeys) {
				changedKeys = append(changedKeys, k)
			}
		}
	}

	for k, v := range newNetwork.Config {
		if v != oldNetwork.Config[k] {
			dbUpdateNeeded = true

			// Add non-user changed key to list of changed keys.
			if !strings.HasPrefix(k, "user.") && !shared.StringInSlice(k, changedKeys) {
				changedKeys = append(changedKeys, k)
			}
		}
	}

	return dbUpdateNeeded, changedKeys, oldNetwork, nil
}

// rename the network directory, update database record and update internal variables.
func (n *common) rename(newName string) error {
	// Clear new directory if exists.
	if shared.PathExists(shared.VarPath("networks", newName)) {
		os.RemoveAll(shared.VarPath("networks", newName))
	}

	// Rename directory to new name.
	if shared.PathExists(shared.VarPath("networks", n.name)) {
		err := os.Rename(shared.VarPath("networks", n.name), shared.VarPath("networks", newName))
		if err != nil {
			return err
		}
	}

	// Rename the database entry.
	err := n.state.Cluster.RenameNetwork(n.project, n.name, newName)
	if err != nil {
		return err
	}

	// Reinitialise internal name variable and logger context with new name.
	n.name = newName

	return nil
}

// delete the network from the database if clusterNotification is false.
func (n *common) delete(clientType request.ClientType) error {
	// Cleanup storage.
	if shared.PathExists(shared.VarPath("networks", n.name)) {
		os.RemoveAll(shared.VarPath("networks", n.name))
	}

	return nil
}

// Create is a no-op.
func (n *common) Create(clientType request.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})
	return nil
}

// HandleHeartbeat is a no-op.
func (n *common) HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error {
	return nil
}

// notifyDependentNetworks allows any dependent networks to apply changes to themselves when this network changes.
func (n *common) notifyDependentNetworks(changedKeys []string) {
	if n.Project() != project.Default {
		return // Only networks in the default project can be used as dependent networks.
	}

	// Get a list of projects.
	var err error
	var projectNames []string

	err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		projectNames, err = tx.GetProjectNames()
		return err
	})
	if err != nil {
		n.logger.Error("Failed to load projects", log.Ctx{"err": err})
		return
	}

	for _, projectName := range projectNames {
		// Get a list of managed networks in project.
		depNets, err := n.state.Cluster.GetCreatedNetworks(projectName)
		if err != nil {
			n.logger.Error("Failed to load networks in project", log.Ctx{"project": projectName, "err": err})
			continue // Continue to next project.
		}

		for _, depName := range depNets {
			depNet, err := LoadByName(n.state, projectName, depName)
			if err != nil {
				n.logger.Error("Failed to load dependent network", log.Ctx{"project": projectName, "dependentNetwork": depName, "err": err})
				continue // Continue to next network.
			}

			if depNet.Config()["network"] != n.Name() {
				continue // Skip network, as does not depend on our network.
			}

			err = depNet.handleDependencyChange(n.Name(), n.Config(), changedKeys)
			if err != nil {
				n.logger.Error("Failed notifying dependent network", log.Ctx{"project": projectName, "dependentNetwork": depName, "err": err})
				continue // Continue to next network.
			}
		}
	}
}

// handleDependencyChange is a placeholder for networks that don't need to handle changes from dependent networks.
func (n *common) handleDependencyChange(netName string, netConfig map[string]string, changedKeys []string) error {
	return nil
}
<|endoftext|>


<commit_before>package database

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/fatih/structs"
	"github.com/hashicorp/vault/builtin/logical/database/dbs"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func pathResetConnection(b *databaseBackend) *framework.Path {
	return &framework.Path{
		Pattern: fmt.Sprintf("reset/%s", framework.GenericNameRegex("name")),
		Fields: map[string]*framework.FieldSchema{
			"name": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Name of this DB type",
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathConnectionReset,
		},

		HelpSynopsis:    pathConfigConnectionHelpSyn,
		HelpDescription: pathConfigConnectionHelpDesc,
	}
}

func (b *databaseBackend) pathConnectionReset(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	name := data.Get("name").(string)
	if name == "" {
		return nil, errors.New("No database name set")
	}

	// Grab the mutex lock
	b.Lock()
	defer b.Unlock()

	entry, err := req.Storage.Get(fmt.Sprintf("dbs/%s", name))
	if err != nil {
		return nil, fmt.Errorf("failed to read connection configuration")
	}
	if entry == nil {
		return nil, nil
	}

	var config dbs.DatabaseConfig
	if err := entry.DecodeJSON(&config); err != nil {
		return nil, err
	}

	db, ok := b.connections[name]
	if !ok {
		return logical.ErrorResponse("Can not change type of existing connection."), nil
	}

	db.Close()

	factory := config.GetFactory()

	db, err = factory(&config, b.System(), b.logger)
	if err != nil {
		return logical.ErrorResponse(fmt.Sprintf("Error creating database object: %s", err)), nil
	}

	err = db.Initialize(config.ConnectionDetails)
	if err != nil {
		return logical.ErrorResponse(fmt.Sprintf("Error creating database object: %s", err)), nil
	}

	b.connections[name] = db

	return nil, nil
}

// pathConfigureBuiltinConnection returns a configured framework.Path setup to
// operate on builtin databases.
func pathConfigureBuiltinConnection(b *databaseBackend) *framework.Path {
	return buildConfigConnectionPath("dbs/%s", b.connectionWriteHandler(dbs.BuiltinFactory), b.connectionReadHandler(), b.connectionDeleteHandler())
}

// pathConfigurePluginConnection returns a configured framework.Path setup to
// operate on plugins.
func pathConfigurePluginConnection(b *databaseBackend) *framework.Path {
	return buildConfigConnectionPath("dbs/plugin/%s", b.connectionWriteHandler(dbs.PluginFactory), b.connectionReadHandler(), b.connectionDeleteHandler())
}

// buildConfigConnectionPath reutns a configured framework.Path using the passed
// in operation functions to complete the request. Used to distinguish calls
// between builtin and plugin databases.
func buildConfigConnectionPath(path string, updateOp, readOp, deleteOp framework.OperationFunc) *framework.Path {
	return &framework.Path{
		Pattern: fmt.Sprintf(path, framework.GenericNameRegex("name")),
		Fields: map[string]*framework.FieldSchema{
			"name": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Name of this DB type",
			},

			"connection_type": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "DB type (e.g. postgres)",
			},

			"verify_connection": &framework.FieldSchema{
				Type:        framework.TypeBool,
				Default:     true,
				Description: `If set, connection_url is verified by actually connecting to the database`,
			},

			"max_open_connections": &framework.FieldSchema{
				Type: framework.TypeInt,
				Description: `Maximum number of open connections to the database;
a zero uses the default value of two and a
negative value means unlimited`,
			},

			"max_idle_connections": &framework.FieldSchema{
				Type: framework.TypeInt,
				Description: `Maximum number of idle connections to the database;
a zero uses the value of max_open_connections
and a negative value disables idle connections.
If larger than max_open_connections it will be
reduced to the same size.`,
			},

			"max_connection_lifetime": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "0s",
				Description: `Maximum amount of time a connection may be reused;
				a zero or negative value reuses connections forever.`,
			},

			"plugin_command": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `Maximum amount of time a connection may be reused;
				a zero or negative value reuses connections forever.`,
			},

			"plugin_checksum": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `Maximum amount of time a connection may be reused;
				a zero or negative value reuses connections forever.`,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: updateOp,
			logical.ReadOperation:   readOp,
			logical.DeleteOperation: deleteOp,
		},

		HelpSynopsis:    pathConfigConnectionHelpSyn,
		HelpDescription: pathConfigConnectionHelpDesc,
	}
}

// pathConnectionRead reads out the connection configuration
func (b *databaseBackend) connectionReadHandler() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		name := data.Get("name").(string)

		entry, err := req.Storage.Get(fmt.Sprintf("dbs/%s", name))
		if err != nil {
			return nil, fmt.Errorf("failed to read connection configuration")
		}
		if entry == nil {
			return nil, nil
		}

		var config dbs.DatabaseConfig
		if err := entry.DecodeJSON(&config); err != nil {
			return nil, err
		}
		return &logical.Response{
			Data: structs.New(config).Map(),
		}, nil
	}
}

// connectionDeleteHandler deletes the connection configuration
func (b *databaseBackend) connectionDeleteHandler() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		name := data.Get("name").(string)
		if name == "" {
			return logical.ErrorResponse("Empty name attribute given"), nil
		}

		err := req.Storage.Delete(fmt.Sprintf("dbs/%s", name))
		if err != nil {
			return nil, fmt.Errorf("failed to delete connection configuration")
		}

		b.Lock()
		defer b.Unlock()

		if _, ok := b.connections[name]; ok {
			err = b.connections[name].Close()
			if err != nil {
				return nil, err
			}
		}

		delete(b.connections, name)

		return nil, nil
	}
}

// connectionWriteHandler returns a handler function for creating and updating
// both builtin and plugin database types.
func (b *databaseBackend) connectionWriteHandler(factory dbs.Factory) framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		connType := data.Get("connection_type").(string)
		if connType == "" {
			return logical.ErrorResponse("connection_type not set"), nil
		}

		maxOpenConns := data.Get("max_open_connections").(int)
		if maxOpenConns == 0 {
			maxOpenConns = 2
		}

		maxIdleConns := data.Get("max_idle_connections").(int)
		if maxIdleConns == 0 {
			maxIdleConns = maxOpenConns
		}
		if maxIdleConns > maxOpenConns {
			maxIdleConns = maxOpenConns
		}

		maxConnLifetimeRaw := data.Get("max_connection_lifetime").(string)
		maxConnLifetime, err := time.ParseDuration(maxConnLifetimeRaw)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf(
				"Invalid max_connection_lifetime: %s", err)), nil
		}

		config := &dbs.DatabaseConfig{
			DatabaseType:          connType,
			ConnectionDetails:     data.Raw,
			MaxOpenConnections:    maxOpenConns,
			MaxIdleConnections:    maxIdleConns,
			MaxConnectionLifetime: maxConnLifetime,
			PluginCommand:         data.Get("plugin_command").(string),
			PluginChecksum:        data.Get("plugin_checksum").(string),
		}

		name := data.Get("name").(string)
		if name == "" {
			return logical.ErrorResponse("Empty name attribute given"), nil
		}

		verifyConnection := data.Get("verify_connection").(bool)

		// Grab the mutex lock
		b.Lock()
		defer b.Unlock()

		db, err := factory(config, b.System(), b.logger)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf("Error creating database object: %s", err)), nil
		}

		err = db.Initialize(config.ConnectionDetails)
		if err != nil {
			if !strings.Contains(err.Error(), "Error Initializing Connection") {
				return logical.ErrorResponse(fmt.Sprintf("Error creating database object: %s", err)), nil
			}

			if verifyConnection {
				return logical.ErrorResponse(err.Error()), nil
			}
		}

		if _, ok := b.connections[name]; ok {
			// Don't update connection until the reset api is hit, close for
			// now.
			err = db.Close()
			if err != nil {
				return nil, err
			}

			// Don't allow the connection type to change
			if b.connections[name].Type() != connType {
				return logical.ErrorResponse("Can not change type of existing connection."), nil
			}
		} else {
			// Save the new connection
			b.connections[name] = db
		}

		// Store it
		entry, err := logical.StorageEntryJSON(fmt.Sprintf("dbs/%s", name), config)
		if err != nil {
			return nil, err
		}
		if err := req.Storage.Put(entry); err != nil {
			return nil, err
		}

		// Reset the DB connection
		resp := &logical.Response{}
		resp.AddWarning("Read access to this endpoint should be controlled via ACLs as it will return the connection string or URL as it is, including passwords, if any.")

		return resp, nil
	}
}

const pathConfigConnectionHelpSyn = `
Configure the connection string to talk to PostgreSQL.
`

const pathConfigConnectionHelpDesc = `
This path configures the connection string used to connect to PostgreSQL.
The value of the string can be a URL, or a PG style string in the
format of "user=foo host=bar" etc.

The URL looks like:
"postgresql://user:pass@host:port/dbname"

When configuring the connection string, the backend will verify its validity.
`
<commit_msg>Fix for checking types of database on update<commit_after>package database

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/fatih/structs"
	"github.com/hashicorp/vault/builtin/logical/database/dbs"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func pathResetConnection(b *databaseBackend) *framework.Path {
	return &framework.Path{
		Pattern: fmt.Sprintf("reset/%s", framework.GenericNameRegex("name")),
		Fields: map[string]*framework.FieldSchema{
			"name": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Name of this DB type",
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathConnectionReset,
		},

		HelpSynopsis:    pathConfigConnectionHelpSyn,
		HelpDescription: pathConfigConnectionHelpDesc,
	}
}

func (b *databaseBackend) pathConnectionReset(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	name := data.Get("name").(string)
	if name == "" {
		return nil, errors.New("No database name set")
	}

	// Grab the mutex lock
	b.Lock()
	defer b.Unlock()

	entry, err := req.Storage.Get(fmt.Sprintf("dbs/%s", name))
	if err != nil {
		return nil, fmt.Errorf("failed to read connection configuration")
	}
	if entry == nil {
		return nil, nil
	}

	var config dbs.DatabaseConfig
	if err := entry.DecodeJSON(&config); err != nil {
		return nil, err
	}

	db, ok := b.connections[name]
	if !ok {
		return logical.ErrorResponse("Can not change type of existing connection."), nil
	}

	db.Close()

	factory := config.GetFactory()

	db, err = factory(&config, b.System(), b.logger)
	if err != nil {
		return logical.ErrorResponse(fmt.Sprintf("Error creating database object: %s", err)), nil
	}

	err = db.Initialize(config.ConnectionDetails)
	if err != nil {
		return logical.ErrorResponse(fmt.Sprintf("Error creating database object: %s", err)), nil
	}

	b.connections[name] = db

	return nil, nil
}

// pathConfigureBuiltinConnection returns a configured framework.Path setup to
// operate on builtin databases.
func pathConfigureBuiltinConnection(b *databaseBackend) *framework.Path {
	return buildConfigConnectionPath("dbs/%s", b.connectionWriteHandler(dbs.BuiltinFactory), b.connectionReadHandler(), b.connectionDeleteHandler())
}

// pathConfigurePluginConnection returns a configured framework.Path setup to
// operate on plugins.
func pathConfigurePluginConnection(b *databaseBackend) *framework.Path {
	return buildConfigConnectionPath("dbs/plugin/%s", b.connectionWriteHandler(dbs.PluginFactory), b.connectionReadHandler(), b.connectionDeleteHandler())
}

// buildConfigConnectionPath reutns a configured framework.Path using the passed
// in operation functions to complete the request. Used to distinguish calls
// between builtin and plugin databases.
func buildConfigConnectionPath(path string, updateOp, readOp, deleteOp framework.OperationFunc) *framework.Path {
	return &framework.Path{
		Pattern: fmt.Sprintf(path, framework.GenericNameRegex("name")),
		Fields: map[string]*framework.FieldSchema{
			"name": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Name of this DB type",
			},

			"connection_type": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "DB type (e.g. postgres)",
			},

			"verify_connection": &framework.FieldSchema{
				Type:        framework.TypeBool,
				Default:     true,
				Description: `If set, connection_url is verified by actually connecting to the database`,
			},

			"max_open_connections": &framework.FieldSchema{
				Type: framework.TypeInt,
				Description: `Maximum number of open connections to the database;
a zero uses the default value of two and a
negative value means unlimited`,
			},

			"max_idle_connections": &framework.FieldSchema{
				Type: framework.TypeInt,
				Description: `Maximum number of idle connections to the database;
a zero uses the value of max_open_connections
and a negative value disables idle connections.
If larger than max_open_connections it will be
reduced to the same size.`,
			},

			"max_connection_lifetime": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "0s",
				Description: `Maximum amount of time a connection may be reused;
				a zero or negative value reuses connections forever.`,
			},

			"plugin_command": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `Maximum amount of time a connection may be reused;
				a zero or negative value reuses connections forever.`,
			},

			"plugin_checksum": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `Maximum amount of time a connection may be reused;
				a zero or negative value reuses connections forever.`,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: updateOp,
			logical.ReadOperation:   readOp,
			logical.DeleteOperation: deleteOp,
		},

		HelpSynopsis:    pathConfigConnectionHelpSyn,
		HelpDescription: pathConfigConnectionHelpDesc,
	}
}

// pathConnectionRead reads out the connection configuration
func (b *databaseBackend) connectionReadHandler() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		name := data.Get("name").(string)

		entry, err := req.Storage.Get(fmt.Sprintf("dbs/%s", name))
		if err != nil {
			return nil, fmt.Errorf("failed to read connection configuration")
		}
		if entry == nil {
			return nil, nil
		}

		var config dbs.DatabaseConfig
		if err := entry.DecodeJSON(&config); err != nil {
			return nil, err
		}
		return &logical.Response{
			Data: structs.New(config).Map(),
		}, nil
	}
}

// connectionDeleteHandler deletes the connection configuration
func (b *databaseBackend) connectionDeleteHandler() framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		name := data.Get("name").(string)
		if name == "" {
			return logical.ErrorResponse("Empty name attribute given"), nil
		}

		err := req.Storage.Delete(fmt.Sprintf("dbs/%s", name))
		if err != nil {
			return nil, fmt.Errorf("failed to delete connection configuration")
		}

		b.Lock()
		defer b.Unlock()

		if _, ok := b.connections[name]; ok {
			err = b.connections[name].Close()
			if err != nil {
				return nil, err
			}
		}

		delete(b.connections, name)

		return nil, nil
	}
}

// connectionWriteHandler returns a handler function for creating and updating
// both builtin and plugin database types.
func (b *databaseBackend) connectionWriteHandler(factory dbs.Factory) framework.OperationFunc {
	return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
		connType := data.Get("connection_type").(string)
		if connType == "" {
			return logical.ErrorResponse("connection_type not set"), nil
		}

		maxOpenConns := data.Get("max_open_connections").(int)
		if maxOpenConns == 0 {
			maxOpenConns = 2
		}

		maxIdleConns := data.Get("max_idle_connections").(int)
		if maxIdleConns == 0 {
			maxIdleConns = maxOpenConns
		}
		if maxIdleConns > maxOpenConns {
			maxIdleConns = maxOpenConns
		}

		maxConnLifetimeRaw := data.Get("max_connection_lifetime").(string)
		maxConnLifetime, err := time.ParseDuration(maxConnLifetimeRaw)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf(
				"Invalid max_connection_lifetime: %s", err)), nil
		}

		config := &dbs.DatabaseConfig{
			DatabaseType:          connType,
			ConnectionDetails:     data.Raw,
			MaxOpenConnections:    maxOpenConns,
			MaxIdleConnections:    maxIdleConns,
			MaxConnectionLifetime: maxConnLifetime,
			PluginCommand:         data.Get("plugin_command").(string),
			PluginChecksum:        data.Get("plugin_checksum").(string),
		}

		name := data.Get("name").(string)
		if name == "" {
			return logical.ErrorResponse("Empty name attribute given"), nil
		}

		verifyConnection := data.Get("verify_connection").(bool)

		// Grab the mutex lock
		b.Lock()
		defer b.Unlock()

		db, err := factory(config, b.System(), b.logger)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf("Error creating database object: %s", err)), nil
		}

		err = db.Initialize(config.ConnectionDetails)
		if err != nil {
			if !strings.Contains(err.Error(), "Error Initializing Connection") {
				return logical.ErrorResponse(fmt.Sprintf("Error creating database object: %s", err)), nil
			}

			if verifyConnection {
				return logical.ErrorResponse(err.Error()), nil
			}
		}

		if _, ok := b.connections[name]; ok {
			newType := db.Type()

			// Don't update connection until the reset api is hit, close for
			// now.
			err = db.Close()
			if err != nil {
				return nil, err
			}

			// Don't allow the connection type to change
			if b.connections[name].Type() != newType {
				return logical.ErrorResponse("Can not change type of existing connection."), nil
			}
		} else {
			// Save the new connection
			b.connections[name] = db
		}

		// Store it
		entry, err := logical.StorageEntryJSON(fmt.Sprintf("dbs/%s", name), config)
		if err != nil {
			return nil, err
		}
		if err := req.Storage.Put(entry); err != nil {
			return nil, err
		}

		// Reset the DB connection
		resp := &logical.Response{}
		resp.AddWarning("Read access to this endpoint should be controlled via ACLs as it will return the connection string or URL as it is, including passwords, if any.")

		return resp, nil
	}
}

const pathConfigConnectionHelpSyn = `
Configure the connection string to talk to PostgreSQL.
`

const pathConfigConnectionHelpDesc = `
This path configures the connection string used to connect to PostgreSQL.
The value of the string can be a URL, or a PG style string in the
format of "user=foo host=bar" etc.

The URL looks like:
"postgresql://user:pass@host:port/dbname"

When configuring the connection string, the backend will verify its validity.
`
<|endoftext|>


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

package lease

import (
	"fmt"
	"io"
	"sort"

	"golang.org/x/net/context"
)

// Create a read proxy consisting of the contents defined by the supplied
// refreshers concatenated. See NewReadProxy for more.
//
// If rl is non-nil, it will be used as the first temporary copy of the
// contents, and must match the concatenation of the content returned by the
// refreshers.
func NewMultiReadProxy(
	fl FileLeaser,
	refreshers []Refresher,
	rl ReadLease) (rp ReadProxy) {
	// Create one wrapped read proxy per refresher.
	var wrappedProxies []readProxyAndOffset
	var size int64
	for _, r := range refreshers {
		wrapped := NewReadProxy(fl, r, nil)
		wrappedProxies = append(wrappedProxies, readProxyAndOffset{size, wrapped})
		size += wrapped.Size()
	}

	rp = &multiReadProxy{
		size:  size,
		rps:   wrappedProxies,
		lease: rl,
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////

type multiReadProxy struct {
	// The size of the proxied content.
	size int64

	// The wrapped read proxies, indexed by their logical starting offset.
	//
	// INVARIANT: If len(rps) != 0, rps[0].off == 0
	// INVARIANT: For each x, x.rp.Size() >= 0
	// INVARIANT: For each i>0, rps[i].off == rps[i-i].off + rps[i-i].rp.Size()
	// INVARIANT: size is the sum over the wrapped proxy sizes.
	rps []readProxyAndOffset

	// A read lease for the entire contents. May be nil.
	//
	// INVARIANT: If lease != nil, size == lease.Size()
	lease ReadLease

	destroyed bool
}

func (mrp *multiReadProxy) Size() (size int64) {
	size = mrp.size
	return
}

func (mrp *multiReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	// Special case: we don't support negative offsets, silly user.
	if off < 0 {
		err = fmt.Errorf("Invalid offset: %v", off)
		return
	}

	// Special case: offsets at or beyond the end of our content can never yield
	// any content, and the io.ReaderAt spec allows us to return EOF. Knock them
	// out here so we know off is in range when we start below.
	if off >= mrp.Size() {
		err = io.EOF
		return
	}

	// The read proxy that contains off is the *last* read proxy whose start
	// offset is less than or equal to off. Find the first that is greater and
	// move back one.
	//
	// Because we handled the special cases above, this must be in range.
	wrappedIndex := mrp.upperBound(off) - 1

	if wrappedIndex < 0 || wrappedIndex >= len(mrp.rps) {
		panic(fmt.Sprintf("Unexpected index: %v", wrappedIndex))
	}

	// Keep going until we've got nothing left to do.
	for len(p) > 0 {
		// Have we run out of wrapped read proxies?
		if wrappedIndex == len(mrp.rps) {
			err = io.EOF
			return
		}

		// Read from the wrapped proxy, accumulating into our total before checking
		// for a read error.
		wrappedN, wrappedErr := mrp.readFromOne(ctx, index, p, off)
		n += wrappedN
		if wrappedErr != nil {
			err = wrappedErr
			return
		}

		// readFromOne guarantees to either fill our buffer or exhaust the wrapped
		// proxy. So advance the buffer, the offset, and the wrapped proxy index
		// and go again.
		p = p[wrappedN:]
		off += int64(wrappedN)
		wrappedIndex++
	}

	return
}

func (mrp *multiReadProxy) Upgrade(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	panic("TODO")
}

func (mrp *multiReadProxy) Destroy() {
	// Destroy all of the wrapped proxies.
	for _, entry := range mrp.rps {
		entry.rp.Destroy()
	}

	// Destroy the lease for the entire contents, if any.
	if mrp.lease != nil {
		mrp.lease.Revoke()
	}

	// Crash early if called again.
	mrp.rps = nil
	mrp.lease = nil
}

func (mrp *multiReadProxy) CheckInvariants() {
	// INVARIANT: If len(rps) != 0, rps[0].off == 0
	if len(mrp.rps) != 0 && mrp.rps[0].off != 0 {
		panic(fmt.Sprintf("Unexpected starting point: %v", mrp.rps[0].off))
	}

	// INVARIANT: For each x, x.rp.Size() >= 0
	for _, x := range mrp.rps {
		if x.rp.Size() < 0 {
			panic(fmt.Sprintf("Negative size: %v", x.rp.Size()))
		}
	}

	// INVARIANT: For each i>0, rps[i].off == rps[i-i].off + rps[i-i].rp.Size()
	for i := range mrp.rps {
		if i > 0 && !(mrp.rps[i].off == mrp.rps[i-1].off+mrp.rps[i-1].rp.Size()) {
			panic("Offsets are not indexed correctly.")
		}
	}

	// INVARIANT: size is the sum over the wrapped proxy sizes.
	var sum int64
	for _, wrapped := range mrp.rps {
		sum += wrapped.rp.Size()
	}

	if sum != mrp.size {
		panic(fmt.Sprintf("Size mismatch: %v vs. %v", sum, mrp.size))
	}

	// INVARIANT: If lease != nil, size == lease.Size()
	if mrp.lease != nil && mrp.size != mrp.lease.Size() {
		panic(fmt.Sprintf("Size mismatch: %v vs. %v", mrp.size, mrp.lease.Size()))
	}
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type readProxyAndOffset struct {
	off int64
	rp  ReadProxy
}

// Return the index within mrp.rps of the first read proxy whose logical offset
// is greater than off. If there is none, return len(mrp.rps).
func (mrp *multiReadProxy) upperBound(off int64) (index int) {
	pred := func(i int) bool {
		return mrp.rps[i].off > off
	}

	return sort.Search(len(mrp.rps), pred)
}

// Serve a read from the wrapped proxy at the given index within our array of
// wrapped proxies. The offset is relative to the start of the multiReadProxy,
// not the wrapped proxy.
//
// Guarantees:
//
//  *  If err == nil, either n == len(p) or n == wrapped.Size().
//  *  Never returns err == io.EOF.
//
// REQUIRES: index < len(mrp.rps)
// REQUIRES: mrp.rps[i].off <= off < mrp.rps[i].off + wrapped.Size()
func (mrp *multiReadProxy) readFromOne(
	ctx context.Context,
	index int,
	p []byte,
	off int64) (n int, err error)
<commit_msg>Fixed a build error.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"fmt"
	"io"
	"sort"

	"golang.org/x/net/context"
)

// Create a read proxy consisting of the contents defined by the supplied
// refreshers concatenated. See NewReadProxy for more.
//
// If rl is non-nil, it will be used as the first temporary copy of the
// contents, and must match the concatenation of the content returned by the
// refreshers.
func NewMultiReadProxy(
	fl FileLeaser,
	refreshers []Refresher,
	rl ReadLease) (rp ReadProxy) {
	// Create one wrapped read proxy per refresher.
	var wrappedProxies []readProxyAndOffset
	var size int64
	for _, r := range refreshers {
		wrapped := NewReadProxy(fl, r, nil)
		wrappedProxies = append(wrappedProxies, readProxyAndOffset{size, wrapped})
		size += wrapped.Size()
	}

	rp = &multiReadProxy{
		size:  size,
		rps:   wrappedProxies,
		lease: rl,
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////

type multiReadProxy struct {
	// The size of the proxied content.
	size int64

	// The wrapped read proxies, indexed by their logical starting offset.
	//
	// INVARIANT: If len(rps) != 0, rps[0].off == 0
	// INVARIANT: For each x, x.rp.Size() >= 0
	// INVARIANT: For each i>0, rps[i].off == rps[i-i].off + rps[i-i].rp.Size()
	// INVARIANT: size is the sum over the wrapped proxy sizes.
	rps []readProxyAndOffset

	// A read lease for the entire contents. May be nil.
	//
	// INVARIANT: If lease != nil, size == lease.Size()
	lease ReadLease

	destroyed bool
}

func (mrp *multiReadProxy) Size() (size int64) {
	size = mrp.size
	return
}

func (mrp *multiReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	// Special case: we don't support negative offsets, silly user.
	if off < 0 {
		err = fmt.Errorf("Invalid offset: %v", off)
		return
	}

	// Special case: offsets at or beyond the end of our content can never yield
	// any content, and the io.ReaderAt spec allows us to return EOF. Knock them
	// out here so we know off is in range when we start below.
	if off >= mrp.Size() {
		err = io.EOF
		return
	}

	// The read proxy that contains off is the *last* read proxy whose start
	// offset is less than or equal to off. Find the first that is greater and
	// move back one.
	//
	// Because we handled the special cases above, this must be in range.
	wrappedIndex := mrp.upperBound(off) - 1

	if wrappedIndex < 0 || wrappedIndex >= len(mrp.rps) {
		panic(fmt.Sprintf("Unexpected index: %v", wrappedIndex))
	}

	// Keep going until we've got nothing left to do.
	for len(p) > 0 {
		// Have we run out of wrapped read proxies?
		if wrappedIndex == len(mrp.rps) {
			err = io.EOF
			return
		}

		// Read from the wrapped proxy, accumulating into our total before checking
		// for a read error.
		wrappedN, wrappedErr := mrp.readFromOne(ctx, wrappedIndex, p, off)
		n += wrappedN
		if wrappedErr != nil {
			err = wrappedErr
			return
		}

		// readFromOne guarantees to either fill our buffer or exhaust the wrapped
		// proxy. So advance the buffer, the offset, and the wrapped proxy index
		// and go again.
		p = p[wrappedN:]
		off += int64(wrappedN)
		wrappedIndex++
	}

	return
}

func (mrp *multiReadProxy) Upgrade(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	panic("TODO")
}

func (mrp *multiReadProxy) Destroy() {
	// Destroy all of the wrapped proxies.
	for _, entry := range mrp.rps {
		entry.rp.Destroy()
	}

	// Destroy the lease for the entire contents, if any.
	if mrp.lease != nil {
		mrp.lease.Revoke()
	}

	// Crash early if called again.
	mrp.rps = nil
	mrp.lease = nil
}

func (mrp *multiReadProxy) CheckInvariants() {
	// INVARIANT: If len(rps) != 0, rps[0].off == 0
	if len(mrp.rps) != 0 && mrp.rps[0].off != 0 {
		panic(fmt.Sprintf("Unexpected starting point: %v", mrp.rps[0].off))
	}

	// INVARIANT: For each x, x.rp.Size() >= 0
	for _, x := range mrp.rps {
		if x.rp.Size() < 0 {
			panic(fmt.Sprintf("Negative size: %v", x.rp.Size()))
		}
	}

	// INVARIANT: For each i>0, rps[i].off == rps[i-i].off + rps[i-i].rp.Size()
	for i := range mrp.rps {
		if i > 0 && !(mrp.rps[i].off == mrp.rps[i-1].off+mrp.rps[i-1].rp.Size()) {
			panic("Offsets are not indexed correctly.")
		}
	}

	// INVARIANT: size is the sum over the wrapped proxy sizes.
	var sum int64
	for _, wrapped := range mrp.rps {
		sum += wrapped.rp.Size()
	}

	if sum != mrp.size {
		panic(fmt.Sprintf("Size mismatch: %v vs. %v", sum, mrp.size))
	}

	// INVARIANT: If lease != nil, size == lease.Size()
	if mrp.lease != nil && mrp.size != mrp.lease.Size() {
		panic(fmt.Sprintf("Size mismatch: %v vs. %v", mrp.size, mrp.lease.Size()))
	}
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type readProxyAndOffset struct {
	off int64
	rp  ReadProxy
}

// Return the index within mrp.rps of the first read proxy whose logical offset
// is greater than off. If there is none, return len(mrp.rps).
func (mrp *multiReadProxy) upperBound(off int64) (index int) {
	pred := func(i int) bool {
		return mrp.rps[i].off > off
	}

	return sort.Search(len(mrp.rps), pred)
}

// Serve a read from the wrapped proxy at the given index within our array of
// wrapped proxies. The offset is relative to the start of the multiReadProxy,
// not the wrapped proxy.
//
// Guarantees:
//
//  *  If err == nil, either n == len(p) or n == wrapped.Size().
//  *  Never returns err == io.EOF.
//
// REQUIRES: index < len(mrp.rps)
// REQUIRES: mrp.rps[i].off <= off < mrp.rps[i].off + wrapped.Size()
func (mrp *multiReadProxy) readFromOne(
	ctx context.Context,
	index int,
	p []byte,
	off int64) (n int, err error)
<|endoftext|>