<commit_before>package satan

import (
	"log"
	"sync"
	"time"
)

// Satan is the master daemon.
type Satan struct {
	SubscribeFunc SubscribeFunc
	Publisher     Publisher

	daemons  []Daemon
	queue    chan *task
	shutdown chan struct{}
	wg       sync.WaitGroup
	latency  *statistics
}

// Actor is a function that could be executed by daemon workers.
type Actor func()

// SubscribeFunc is a function that is used by daemons to subscribe to messages.
type SubscribeFunc func(consumer, topic string) Streamer

// Streamer is the interface that wraps message consumers. Error handling
// should be provided by the implementation. Feel free to panic.
type Streamer interface {
	Messages() <-chan []byte
	Close()
}

// Publisher is the interface that wraps message publishers. Error handling
// should be provided by the implementation. Feel free to panic.
type Publisher interface {
	Publish(msg []byte)
	Close()
}

type task struct {
	daemon    Daemon
	actor     Actor
	createdAt time.Time
	system    bool
}

const (
	defaultNumWorkers = 10
)

// Summon creates a new instance of Satan.
func Summon() *Satan {
	return &Satan{
		queue:    make(chan *task),
		latency:  newStatistics(),
		shutdown: make(chan struct{}),
	}
}

// AddDaemon adds a new daemon.
func (s *Satan) AddDaemon(d Daemon) {
	base := d.base()
	base.self = d
	base.subscribeFunc = s.SubscribeFunc
	base.publisher = s.Publisher
	base.queue = s.queue
	base.shutdown = make(chan struct{})
	base.stats = newStatistics()

	go d.Startup()
	s.daemons = append(s.daemons, d)
}

// StartDaemons starts all registered daemons.
func (s *Satan) StartDaemons() {
	s.wg.Add(defaultNumWorkers)
	for i := 0; i < defaultNumWorkers; i++ {
		go func(i int) {
			s.runWorker(i)
			s.wg.Done()
		}(i)
	}
}

// StopDaemons stops all running daemons.
func (s *Satan) StopDaemons() {
	for _, d := range s.daemons {
		close(d.base().shutdown)
		d.Shutdown()

		log.Printf("%s daemon performace statistics:\n%s\n",
			d.base(), d.base().stats.snapshot())
	}
	close(s.shutdown)
	s.wg.Wait()
	close(s.queue)

	log.Printf("Task processing latency statistics:\n%s\n", s.latency.snapshot())
}

func (s *Satan) runWorker(i int) {
	log.Printf("Starting worker #%d", i+1)
	defer log.Printf("Worker #%d has stopped", i+1)

	for {
		select {
		case t := <-s.queue:
			dur := time.Now().UnixNano() - t.createdAt.UnixNano()
			s.latency.add(time.Duration(dur))

			// log.Printf("Daemon #%d got some job to do!", i+1)
			s.processTask(t)
		default:
			select {
			case <-s.shutdown:
				return
			default:
			}
		}
	}
}

func (s *Satan) processTask(t *task) {
	defer t.daemon.base().handlePanic()
	start := time.Now()

	t.actor()

	dur := time.Now().UnixNano() - start.UnixNano()
	t.daemon.base().stats.add(time.Duration(dur))
}
<commit_msg>Add a comment that is very useful in reading stack traces<commit_after>package satan

import (
	"log"
	"sync"
	"time"
)

// Satan is the master daemon.
type Satan struct {
	SubscribeFunc SubscribeFunc
	Publisher     Publisher

	daemons  []Daemon
	queue    chan *task
	shutdown chan struct{}
	wg       sync.WaitGroup
	latency  *statistics
}

// Actor is a function that could be executed by daemon workers.
type Actor func()

// SubscribeFunc is a function that is used by daemons to subscribe to messages.
type SubscribeFunc func(consumer, topic string) Streamer

// Streamer is the interface that wraps message consumers. Error handling
// should be provided by the implementation. Feel free to panic.
type Streamer interface {
	Messages() <-chan []byte
	Close()
}

// Publisher is the interface that wraps message publishers. Error handling
// should be provided by the implementation. Feel free to panic.
type Publisher interface {
	Publish(msg []byte)
	Close()
}

type task struct {
	daemon    Daemon
	actor     Actor
	createdAt time.Time
	system    bool
}

const (
	defaultNumWorkers = 10
)

// Summon creates a new instance of Satan.
func Summon() *Satan {
	return &Satan{
		queue:    make(chan *task),
		latency:  newStatistics(),
		shutdown: make(chan struct{}),
	}
}

// AddDaemon adds a new daemon.
func (s *Satan) AddDaemon(d Daemon) {
	base := d.base()
	base.self = d
	base.subscribeFunc = s.SubscribeFunc
	base.publisher = s.Publisher
	base.queue = s.queue
	base.shutdown = make(chan struct{})
	base.stats = newStatistics()

	go d.Startup()
	s.daemons = append(s.daemons, d)
}

// StartDaemons starts all registered daemons.
func (s *Satan) StartDaemons() {
	s.wg.Add(defaultNumWorkers)
	for i := 0; i < defaultNumWorkers; i++ {
		go func(i int) {
			s.runWorker(i)
			s.wg.Done()
		}(i)
	}
}

// StopDaemons stops all running daemons.
func (s *Satan) StopDaemons() {
	for _, d := range s.daemons {
		close(d.base().shutdown)
		d.Shutdown()

		log.Printf("%s daemon performace statistics:\n%s\n",
			d.base(), d.base().stats.snapshot())
	}
	close(s.shutdown)
	s.wg.Wait()
	close(s.queue)

	log.Printf("Task processing latency statistics:\n%s\n", s.latency.snapshot())
}

func (s *Satan) runWorker(i int) {
	log.Printf("Starting worker #%d", i+1)
	defer log.Printf("Worker #%d has stopped", i+1)

	for {
		select {
		case t := <-s.queue:
			dur := time.Now().UnixNano() - t.createdAt.UnixNano()
			s.latency.add(time.Duration(dur))

			// log.Printf("Daemon #%d got some job to do!", i+1)
			s.processTask(t)
		default:
			select {
			case <-s.shutdown:
				return
			default:
			}
		}
	}
}

func (s *Satan) processTask(t *task) {
	defer t.daemon.base().handlePanic()
	start := time.Now()

	t.actor() // <--- THE ACTION HAPPENS HERE

	dur := time.Now().UnixNano() - start.UnixNano()
	t.daemon.base().stats.add(time.Duration(dur))
}
<|endoftext|>


<commit_before>//
//  Synchronized publisher.
//
//  This diverts from the C example by introducing time delays.
//  Without these delays, the subscribers won't catch the END message.
//

package main

import (
	zmq "github.com/pebbe/zmq4"

	"fmt"
	"time"
)

const (
	//  We wait for 10 subscribers
	SUBSCRIBERS_EXPECTED = 10
)

func main() {

	//  Socket to talk to clients
	publisher, _ := zmq.NewSocket(zmq.PUB)
	defer publisher.Close()
	publisher.Bind("tcp://*:5561")

	//  Socket to receive signals
	syncservice, _ := zmq.NewSocket(zmq.REP)
	defer syncservice.Close()
	syncservice.Bind("tcp://*:5562")

	//  Get synchronization from subscribers
	fmt.Println("Waiting for subscribers")
	for subscribers := 0; subscribers < SUBSCRIBERS_EXPECTED; subscribers++ {
		//  - wait for synchronization request
		syncservice.Recv(0)
		//  - send synchronization reply
		syncservice.Send("", 0)
	}
	//  Now broadcast exactly 1M updates followed by END
	fmt.Println("Broadcasting messages")
	for update_nbr := 0; update_nbr < 1000000; update_nbr++ {
		publisher.Send("Rhubarb", 0)
		// subscribers don't get all messages if publisher is too fast
		// a one microsecond pause may still be too short
		time.Sleep(time.Microsecond)
	}

	// a longer pause ensures subscribers are ready to receive this
	time.Sleep(time.Second)
	publisher.Send("END", 0)

	// what's another second?
	time.Sleep(time.Second)
}
<commit_msg>examples/syncpub.go: using high water mark instead of time delay<commit_after>//
//  Synchronized publisher.
//

package main

import (
	zmq "github.com/pebbe/zmq4"

	"fmt"
)

const (
	//  We wait for 10 subscribers
	SUBSCRIBERS_EXPECTED = 10
)

func main() {

	ctx, _ := zmq.NewContext()
	defer ctx.Term()

	//  Socket to talk to clients
	publisher, _ := ctx.NewSocket(zmq.PUB)
	defer publisher.Close()
	publisher.SetSndhwm(1100000)
	publisher.Bind("tcp://*:5561")

	//  Socket to receive signals
	syncservice, _ := ctx.NewSocket(zmq.REP)
	defer syncservice.Close()
	syncservice.Bind("tcp://*:5562")

	//  Get synchronization from subscribers
	fmt.Println("Waiting for subscribers")
	for subscribers := 0; subscribers < SUBSCRIBERS_EXPECTED; subscribers++ {
		//  - wait for synchronization request
		syncservice.Recv(0)
		//  - send synchronization reply
		syncservice.Send("", 0)
	}
	//  Now broadcast exactly 1M updates followed by END
	fmt.Println("Broadcasting messages")
	for update_nbr := 0; update_nbr < 1000000; update_nbr++ {
		publisher.Send("Rhubarb", 0)
	}

	publisher.Send("END", 0)

}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package executor

import (
	"github.com/ngaut/log"
	"github.com/pingcap/tidb/ast"
	"github.com/pingcap/tidb/column"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/infoschema"
	"github.com/pingcap/tidb/optimizer/plan"
	"github.com/pingcap/tidb/parser/opcode"
	"github.com/pingcap/tidb/sessionctx/autocommit"
)

// executorBuilder builds an Executor from a Plan.
// The InfoSchema must be the same one used in InfoBinder.
type executorBuilder struct {
	ctx context.Context
	is  infoschema.InfoSchema
}

func newExecutorBuilder(ctx context.Context, is infoschema.InfoSchema) *executorBuilder {
	return &executorBuilder{
		ctx: ctx,
		is:  is,
	}
}

func (b *executorBuilder) build(p plan.Plan) Executor {
	switch v := p.(type) {
	case *plan.TableScan:
		return b.buildTableScan(v)
	case *plan.IndexScan:
		return b.buildIndexScan(v)
	case *plan.Filter:
		return b.buildFilter(v)
	case *plan.SelectLock:
		return b.buildSelectLock(v)
	case *plan.SelectFields:
		return b.buildSelectFields(v)
	case *plan.Sort:
		return b.buildSort(v)
	case *plan.Limit:
		return b.buildLimit(v)
	}
	log.Fatalf("Unknown Plan %T", p)
	return nil
}

func (b *executorBuilder) buildTableScan(v *plan.TableScan) Executor {
	table, _ := b.is.TableByID(v.Table.ID)
	return &TableScanExec{
		t:      table,
		fields: v.Fields(),
		ctx:    b.ctx,
	}
}

func (b *executorBuilder) buildIndexScan(v *plan.IndexScan) Executor {
	tbl, _ := b.is.TableByID(v.Table.ID)
	var idx *column.IndexedCol
	indices := tbl.Indices()
	for _, val := range indices {
		if val.IndexInfo.Name.L == v.Index.Name.L {
			idx = val
			break
		}
	}
	e := &IndexScanExec{
		tbl:    tbl,
		idx:    idx.X,
		fields: v.Fields(),
		ctx:    b.ctx,
		Desc:   v.Desc,
	}

	for _, col := range tbl.Cols() {
		if col.Name.L == idx.Columns[0].Name.L {
			e.valueType = col.FieldType
		}
	}

	e.Ranges = make([]*IndexRangeExec, len(v.Ranges))
	for i, val := range v.Ranges {
		e.Ranges[i] = b.buildIndexRange(e, val)
	}
	return e
}

func (b *executorBuilder) buildIndexRange(scan *IndexScanExec, v *plan.IndexRange) *IndexRangeExec {
	rang := &IndexRangeExec{
		scan:        scan,
		lowVal:      v.LowVal[0],
		lowExclude:  v.LowExclude,
		highVal:     v.HighVal[0],
		highExclude: v.HighExclude,
	}
	return rang
}

func (b *executorBuilder) joinConditions(conditions []ast.ExprNode) ast.ExprNode {
	if len(conditions) == 1 {
		return conditions[0]
	}
	condition := &ast.BinaryOperationExpr{
		Op: opcode.AndAnd,
		L:  conditions[0],
		R:  b.joinConditions(conditions[1:]),
	}
	return condition
}

func (b *executorBuilder) buildFilter(v *plan.Filter) Executor {
	src := b.build(v.Src())
	e := &FilterExec{
		Src:       src,
		Condition: b.joinConditions(v.Conditions),
		ctx:       b.ctx,
	}
	return e
}

func (b *executorBuilder) buildSelectLock(v *plan.SelectLock) Executor {
	src := b.build(v.Src())
	if autocommit.ShouldAutocommit(b.ctx) {
		// Locking of rows for update using SELECT FOR UPDATE only applies when autocommit
		// is disabled (either by beginning transaction with START TRANSACTION or by setting
		// autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked.
		// See: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
		return src
	}
	e := &SelectLockExec{
		Src:  src,
		Lock: v.Lock,
		ctx:  b.ctx,
	}
	return e
}

func (b *executorBuilder) buildSelectFields(v *plan.SelectFields) Executor {
	src := b.build(v.Src())
	e := &SelectFieldsExec{
		Src:          src,
		ResultFields: v.Fields(),
		ctx:          b.ctx,
	}
	return e
}

func (b *executorBuilder) buildSort(v *plan.Sort) Executor {
	src := b.build(v.Src())
	if v.Bypass && !v.ByItems[0].Desc {
		return src
	}
	e := &SortExec{
		Src:     src,
		ByItems: v.ByItems,
		ctx:     b.ctx,
	}
	return e
}

func (b *executorBuilder) buildLimit(v *plan.Limit) Executor {
	src := b.build(v.Src())
	e := &LimitExec{
		Src:    src,
		Offset: v.Offset,
		Count:  v.Count,
	}
	return e
}
<commit_msg>executor: fix build<commit_after>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package executor

import (
	"github.com/ngaut/log"
	"github.com/pingcap/tidb/ast"
	"github.com/pingcap/tidb/column"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/infoschema"
	"github.com/pingcap/tidb/optimizer/plan"
	"github.com/pingcap/tidb/parser/opcode"
	"github.com/pingcap/tidb/sessionctx/autocommit"
)

// executorBuilder builds an Executor from a Plan.
// The InfoSchema must be the same one used in InfoBinder.
type executorBuilder struct {
	ctx context.Context
	is  infoschema.InfoSchema
}

func newExecutorBuilder(ctx context.Context, is infoschema.InfoSchema) *executorBuilder {
	return &executorBuilder{
		ctx: ctx,
		is:  is,
	}
}

func (b *executorBuilder) build(p plan.Plan) Executor {
	switch v := p.(type) {
	case nil:
		return nil
	case *plan.TableScan:
		return b.buildTableScan(v)
	case *plan.IndexScan:
		return b.buildIndexScan(v)
	case *plan.Filter:
		return b.buildFilter(v)
	case *plan.SelectLock:
		return b.buildSelectLock(v)
	case *plan.SelectFields:
		return b.buildSelectFields(v)
	case *plan.Sort:
		return b.buildSort(v)
	case *plan.Limit:
		return b.buildLimit(v)
	}
	log.Fatalf("Unknown Plan %T", p)
	return nil
}

func (b *executorBuilder) buildTableScan(v *plan.TableScan) Executor {
	table, _ := b.is.TableByID(v.Table.ID)
	return &TableScanExec{
		t:      table,
		fields: v.Fields(),
		ctx:    b.ctx,
	}
}

func (b *executorBuilder) buildIndexScan(v *plan.IndexScan) Executor {
	tbl, _ := b.is.TableByID(v.Table.ID)
	var idx *column.IndexedCol
	indices := tbl.Indices()
	for _, val := range indices {
		if val.IndexInfo.Name.L == v.Index.Name.L {
			idx = val
			break
		}
	}
	e := &IndexScanExec{
		tbl:    tbl,
		idx:    idx.X,
		fields: v.Fields(),
		ctx:    b.ctx,
		Desc:   v.Desc,
	}

	for _, col := range tbl.Cols() {
		if col.Name.L == idx.Columns[0].Name.L {
			e.valueType = col.FieldType
		}
	}

	e.Ranges = make([]*IndexRangeExec, len(v.Ranges))
	for i, val := range v.Ranges {
		e.Ranges[i] = b.buildIndexRange(e, val)
	}
	return e
}

func (b *executorBuilder) buildIndexRange(scan *IndexScanExec, v *plan.IndexRange) *IndexRangeExec {
	rang := &IndexRangeExec{
		scan:        scan,
		lowVal:      v.LowVal[0],
		lowExclude:  v.LowExclude,
		highVal:     v.HighVal[0],
		highExclude: v.HighExclude,
	}
	return rang
}

func (b *executorBuilder) joinConditions(conditions []ast.ExprNode) ast.ExprNode {
	if len(conditions) == 1 {
		return conditions[0]
	}
	condition := &ast.BinaryOperationExpr{
		Op: opcode.AndAnd,
		L:  conditions[0],
		R:  b.joinConditions(conditions[1:]),
	}
	return condition
}

func (b *executorBuilder) buildFilter(v *plan.Filter) Executor {
	src := b.build(v.Src())
	e := &FilterExec{
		Src:       src,
		Condition: b.joinConditions(v.Conditions),
		ctx:       b.ctx,
	}
	return e
}

func (b *executorBuilder) buildSelectLock(v *plan.SelectLock) Executor {
	src := b.build(v.Src())
	if autocommit.ShouldAutocommit(b.ctx) {
		// Locking of rows for update using SELECT FOR UPDATE only applies when autocommit
		// is disabled (either by beginning transaction with START TRANSACTION or by setting
		// autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked.
		// See: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
		return src
	}
	e := &SelectLockExec{
		Src:  src,
		Lock: v.Lock,
		ctx:  b.ctx,
	}
	return e
}

func (b *executorBuilder) buildSelectFields(v *plan.SelectFields) Executor {
	src := b.build(v.Src())
	e := &SelectFieldsExec{
		Src:          src,
		ResultFields: v.Fields(),
		ctx:          b.ctx,
	}
	return e
}

func (b *executorBuilder) buildSort(v *plan.Sort) Executor {
	src := b.build(v.Src())
	if v.Bypass && !v.ByItems[0].Desc {
		return src
	}
	e := &SortExec{
		Src:     src,
		ByItems: v.ByItems,
		ctx:     b.ctx,
	}
	return e
}

func (b *executorBuilder) buildLimit(v *plan.Limit) Executor {
	src := b.build(v.Src())
	e := &LimitExec{
		Src:    src,
		Offset: v.Offset,
		Count:  v.Count,
	}
	return e
}
<|endoftext|>


<commit_before>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"github.com/jacobsa/ogletest/internal"
	"testing"
)

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

// Set up a new test state with empty fields.
func setUpCurrentTest() {
	internal.CurrentTest = internal.NewTestState()
}

type fakeExpectThatMatcher struct {
	desc string
	res  MatchResult
	err  string
}

func (m *fakeExpectThatMatcher) Matches(c interface{}) (MatchResult, string) {
	return m.res, m.err
}

func (m *fakeExpectThatMatcher) Description() string {
	return m.desc
}

func assertEqInt(t *testing.T, e, c int) {
	if e != c {
		t.Fatalf("Expected %d, got %d", e, c)
	}
}

func expectEqUint(t *testing.T, e, c uint) {
	if e != c {
		t.Errorf("Expected %u, got %u", e, c)
	}
}

func expectEqStr(t *testing.T, e, c string) {
	if e != c {
		t.Errorf("Expected %s, got %s", e, c)
	}
}

////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////

func TestNoCurrentTest(t *testing.T) {
	panicked := false

	defer func() {
		if !panicked {
			t.Errorf("Expected panic; got none.")
		}
	}()

	defer func() {
		if r := recover(); r != nil {
			panicked = true
		}
	}()

	internal.CurrentTest = nil
	ExpectThat(17, Equals(17))
}

func TestNoFailure(t *testing.T) {
	setUpCurrentTest()
	matcher := &fakeExpectThatMatcher{"", MATCH_TRUE, ""}
	ExpectThat(17, matcher)

	assertEqInt(t, 0, len(internal.CurrentTest.FailureRecords))
}

func TestMatchFalseWithoutMessages(t *testing.T) {
	setUpCurrentTest()
	matcher := &fakeExpectThatMatcher{"taco", MATCH_FALSE, ""}
	ExpectThat(17, matcher)

	assertEqInt(t, 1, len(internal.CurrentTest.FailureRecords))

	record := internal.CurrentTest.FailureRecords[0]
	expectEqStr(t, "expect_that_test.go", record.FileName)
	expectEqUint(t, 98, record.LineNumber)
	expectEqStr(t, "Expected: taco\nActual:   17", record.GeneratedError)
	expectEqStr(t, "", record.UserError)
}

func TestMatchUndefinedWithoutMessages(t *testing.T) {
}

func TestFailureWithMatcherMessage(t *testing.T) {
}

func TestFailureWithUserMessage(t *testing.T) {
}

func TestAdditionalFailure(t *testing.T) {
}
<commit_msg>TestMatchUndefinedWithoutMessages<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"github.com/jacobsa/ogletest/internal"
	"testing"
)

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

// Set up a new test state with empty fields.
func setUpCurrentTest() {
	internal.CurrentTest = internal.NewTestState()
}

type fakeExpectThatMatcher struct {
	desc string
	res  MatchResult
	err  string
}

func (m *fakeExpectThatMatcher) Matches(c interface{}) (MatchResult, string) {
	return m.res, m.err
}

func (m *fakeExpectThatMatcher) Description() string {
	return m.desc
}

func assertEqInt(t *testing.T, e, c int) {
	if e != c {
		t.Fatalf("Expected %d, got %d", e, c)
	}
}

func expectEqUint(t *testing.T, e, c uint) {
	if e != c {
		t.Errorf("Expected %u, got %u", e, c)
	}
}

func expectEqStr(t *testing.T, e, c string) {
	if e != c {
		t.Errorf("Expected %s, got %s", e, c)
	}
}

////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////

func TestNoCurrentTest(t *testing.T) {
	panicked := false

	defer func() {
		if !panicked {
			t.Errorf("Expected panic; got none.")
		}
	}()

	defer func() {
		if r := recover(); r != nil {
			panicked = true
		}
	}()

	internal.CurrentTest = nil
	ExpectThat(17, Equals(17))
}

func TestNoFailure(t *testing.T) {
	setUpCurrentTest()
	matcher := &fakeExpectThatMatcher{"", MATCH_TRUE, ""}
	ExpectThat(17, matcher)

	assertEqInt(t, 0, len(internal.CurrentTest.FailureRecords))
}

func TestMatchFalseWithoutMessages(t *testing.T) {
	setUpCurrentTest()
	matcher := &fakeExpectThatMatcher{"taco", MATCH_FALSE, ""}
	ExpectThat(17, matcher)

	assertEqInt(t, 1, len(internal.CurrentTest.FailureRecords))

	record := internal.CurrentTest.FailureRecords[0]
	expectEqStr(t, "expect_that_test.go", record.FileName)
	expectEqUint(t, 98, record.LineNumber)
	expectEqStr(t, "Expected: taco\nActual:   17", record.GeneratedError)
	expectEqStr(t, "", record.UserError)
}

func TestMatchUndefinedWithoutMessages(t *testing.T) {
	setUpCurrentTest()
	matcher := &fakeExpectThatMatcher{"taco", MATCH_UNDEFINED, ""}
	ExpectThat(17, matcher)

	assertEqInt(t, 1, len(internal.CurrentTest.FailureRecords))

	record := internal.CurrentTest.FailureRecords[0]
	expectEqStr(t, "expect_that_test.go", record.FileName)
	expectEqUint(t, 112, record.LineNumber)
	expectEqStr(t, "Expected: taco\nActual:   17", record.GeneratedError)
	expectEqStr(t, "", record.UserError)
}

func TestFailureWithMatcherMessage(t *testing.T) {
}

func TestFailureWithUserMessage(t *testing.T) {
}

func TestAdditionalFailure(t *testing.T) {
}
<|endoftext|>


<commit_before>package expr

import (
	"fmt"
	"mime"
	"strings"

	"goa.design/goa/v3/eval"
)

const (
	// DefaultView is the name of the default result type view.
	DefaultView = "default"
)

type (
	// ResultTypeExpr is a user type which describes views used to
	// render responses.
	ResultTypeExpr struct {
		// A result type is a user type
		*UserTypeExpr
		// Identifier is the RFC 6838 result type media type identifier.
		Identifier string
		// ContentType identifies the value written to the response
		// "Content-Type" header. Deprecated.
		ContentType string
		// Views list the supported views indexed by name.
		Views []*ViewExpr
	}

	// ViewExpr defines which fields to render when building a response. The view
	// is an object whose field names must match the names of the parent result
	// type field names. The field definitions are inherited from the parent
	// result type but may be overridden.
	ViewExpr struct {
		// Set of properties included in view
		*AttributeExpr
		// Name of view
		Name string
		// Parent result Type
		Parent *ResultTypeExpr
	}
)

var (
	// ErrorResultIdentifier is the result type identifier used for error
	// responses.
	ErrorResultIdentifier = "application/vnd.goa.error"

	// ErrorResult is the built-in result type for error responses.
	ErrorResult = &ResultTypeExpr{
		UserTypeExpr: &UserTypeExpr{
			AttributeExpr: &AttributeExpr{
				Type:        errorResultType,
				Description: "Error response result type",
				UserExamples: []*ExampleExpr{{
					Summary: "BadRequest",
					Value: Val{
						"name":    "bad_request",
						"id":      "3F1FKVRR",
						"message": "Value of ID must be an integer",
					},
				}},
				Validation: &ValidationExpr{Required: []string{"name", "id", "message", "temporary", "timeout", "fault"}},
			},
			TypeName: "error",
		},
		Identifier: ErrorResultIdentifier,
		Views:      []*ViewExpr{errorResultView},
	}

	errorResultType = &Object{
		{"name", &AttributeExpr{
			Type:         String,
			Description:  "Name is the name of this class of errors.",
			Meta:         MetaExpr{"struct:error:name": nil},
			UserExamples: []*ExampleExpr{{Value: "bad_request"}},
		}},
		{"id", &AttributeExpr{
			Type:         String,
			Description:  "ID is a unique identifier for this particular occurrence of the problem.",
			UserExamples: []*ExampleExpr{{Value: "123abc"}},
		}},
		{"message", &AttributeExpr{
			Type:         String,
			Description:  "Message is a human-readable explanation specific to this occurrence of the problem.",
			UserExamples: []*ExampleExpr{{Value: "parameter 'p' must be an integer"}},
		}},
		{"temporary", &AttributeExpr{
			Type:        Boolean,
			Description: "Is the error temporary?",
		}},
		{"timeout", &AttributeExpr{
			Type:        Boolean,
			Description: "Is the error a timeout?",
		}},
		{"fault", &AttributeExpr{
			Type:        Boolean,
			Description: "Is the error a server-side fault?",
		}},
	}

	errorResultView = &ViewExpr{
		AttributeExpr: &AttributeExpr{Type: errorResultType},
		Name:          "default",
	}
)

// NewResultTypeExpr creates a result type definition but does not
// execute the DSL.
func NewResultTypeExpr(name, identifier string, fn func()) *ResultTypeExpr {
	return &ResultTypeExpr{
		UserTypeExpr: &UserTypeExpr{
			AttributeExpr: &AttributeExpr{Type: &Object{}, DSLFunc: fn},
			TypeName:      name,
			UID:           identifier,
		},
		Identifier: identifier,
	}
}

// CanonicalIdentifier returns the result type identifier sans suffix
// which is what the DSL uses to store and lookup result types.
func CanonicalIdentifier(identifier string) string {
	base, params, err := mime.ParseMediaType(identifier)
	if err != nil {
		return identifier
	}
	id := base
	if i := strings.Index(id, "+"); i != -1 {
		id = id[:i]
	}
	return mime.FormatMediaType(id, params)
}

// Kind implements DataKind.
func (m *ResultTypeExpr) Kind() Kind { return ResultTypeKind }

// Dup creates a deep copy of the result type given a deep copy of its attribute.
func (m *ResultTypeExpr) Dup(att *AttributeExpr) UserType {
	return &ResultTypeExpr{
		UserTypeExpr: m.UserTypeExpr.Dup(att).(*UserTypeExpr),
		Identifier:   m.Identifier,
		Views:        m.Views,
	}
}

// ID returns the identifier of the result type.
func (m *ResultTypeExpr) ID() string {
	return m.Identifier
}

// Name returns the result type name.
func (m *ResultTypeExpr) Name() string { return m.TypeName }

// View returns the view with the given name.
func (m *ResultTypeExpr) View(name string) *ViewExpr {
	for _, v := range m.Views {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// HasMultipleViews returns true if the result type has more than one view.
func (m *ResultTypeExpr) HasMultipleViews() bool {
	return len(m.Views) > 1
}

// ViewHasAttribute returns true if the result type view has the given
// attribute.
func (m *ResultTypeExpr) ViewHasAttribute(view, attr string) bool {
	v := m.View(view)
	if v == nil {
		return false
	}
	return v.AttributeExpr.Find(attr) != nil
}

// Finalize builds the default view if not explicitly defined and finalizes
// the underlying UserTypeExpr.
func (m *ResultTypeExpr) Finalize() {
	if m.View("default") == nil {
		m.ensureDefaultView()
	}
	m.UserTypeExpr.Finalize()
	seen := make(map[string]struct{})
	walkAttribute(m.AttributeExpr, func(_ string, att *AttributeExpr) error {
		if rt, ok := att.Type.(*ResultTypeExpr); ok {
			if _, ok := seen[rt.Identifier]; !ok {
				seen[rt.Identifier] = struct{}{}
				rt.ensureDefaultView()
			}
		}
		return nil
	})
}

// ensureDefaultView builds the default view if not explicitly defined.
func (m *ResultTypeExpr) ensureDefaultView() {
	if m.View("default") == nil {
		att := DupAtt(m.AttributeExpr)
		if arr := AsArray(att.Type); arr != nil {
			att.Type = AsObject(arr.ElemType.Type)
		}
		v := &ViewExpr{
			AttributeExpr: att,
			Name:          "default",
			Parent:        m,
		}
		m.Views = append(m.Views, v)
	}
}

// Project creates a ResultTypeExpr containing the fields defined in the view
// expression of m named after the view argument.
//
// The resulting result type defines a default view. The result type identifier is
// computed by adding a parameter called "view" to the original identifier. The
// value of the "view" parameter is the name of the view.
//
// Project returns an error if the view does not exist for the given result type
// or any result type that makes up its attributes recursively. Note that
// individual attributes may use a different view. In this case Project uses
// that view and returns an error if it isn't defined on the attribute type.
func Project(m *ResultTypeExpr, view string, seen ...map[string]*AttributeExpr) (*ResultTypeExpr, error) {
	_, params, _ := mime.ParseMediaType(m.Identifier)
	if params["view"] == view {
		// nothing to do
		return m, nil
	}
	if _, ok := m.Type.(*Array); ok {
		return projectCollection(m, view, seen...)
	}
	return projectSingle(m, view, seen...)
}

func projectSingle(m *ResultTypeExpr, view string, seen ...map[string]*AttributeExpr) (*ResultTypeExpr, error) {
	v := m.View(view)
	if v == nil {
		return nil, fmt.Errorf("unknown view %#v", view)
	}
	viewObj := v.Type.(*Object)

	// Compute validations - view may not have all fields
	var val *ValidationExpr
	if m.Validation != nil {
		var required []string
		for _, n := range m.Validation.Required {
			if att := viewObj.Attribute(n); att != nil {
				required = append(required, n)
			}
		}
		val = m.Validation.Dup()
		val.Required = required
	}

	// Compute description
	desc := m.Description
	if desc == "" {
		desc = m.TypeName + " result type"
	}
	desc += " (" + view + " view)"

	// Compute type name
	typeName := m.TypeName
	if view != "default" {
		typeName += strings.Title(view)
	}

	var ut *UserTypeExpr
	if len(seen) > 0 {
		s := seen[0]
		if att, ok := s[m.Identifier+"::"+view]; ok {
			if rt, ok2 := att.Type.(*ResultTypeExpr); ok2 {
				ut = &UserTypeExpr{
					AttributeExpr: DupAtt(rt.Attribute()),
					TypeName:      rt.TypeName,
				}
			}
		}
	} else {
		seen = append(seen, make(map[string]*AttributeExpr))
	}
	id := m.projectIdentifier(view)
	if ut == nil {
		ut = &UserTypeExpr{
			AttributeExpr: &AttributeExpr{
				Description: desc,
				Validation:  val,
			},
		}
	}
	ut.TypeName = typeName
	ut.UID = id
	ut.AttributeExpr.Type = Dup(v.Type)
	ut.AttributeExpr.UserExamples = v.UserExamples
	projected := &ResultTypeExpr{
		Identifier:   id,
		UserTypeExpr: ut,
	}
	projected.Views = []*ViewExpr{{
		Name:          "default",
		AttributeExpr: DupAtt(v.AttributeExpr),
		Parent:        projected,
	}}

	projectedObj := projected.Type.(*Object)
	mtObj := AsObject(m.Type)
	for _, nat := range *viewObj {
		if at := mtObj.Attribute(nat.Name); at != nil {
			pat, err := projectRecursive(at, nat, view, seen...)
			if err != nil {
				return nil, err
			}
			projectedObj.Set(nat.Name, pat)
		}
	}
	return projected, nil
}

func projectCollection(m *ResultTypeExpr, view string, seen ...map[string]*AttributeExpr) (*ResultTypeExpr, error) {
	// Project the collection element result type
	e := m.Type.(*Array).ElemType.Type.(*ResultTypeExpr) // validation checked this cast would work
	pe, err2 := Project(e, view, seen...)
	if err2 != nil {
		return nil, fmt.Errorf("collection element: %s", err2)
	}

	// Build the projected collection with the results
	id := m.projectIdentifier(view)
	proj := &ResultTypeExpr{
		Identifier: id,
		UserTypeExpr: &UserTypeExpr{
			AttributeExpr: &AttributeExpr{
				Description:  m.TypeName + " is the result type for an array of " + e.TypeName + " (" + view + " view)",
				Type:         &Array{ElemType: &AttributeExpr{Type: pe}},
				UserExamples: m.UserExamples,
			},
			TypeName: pe.TypeName + "Collection",
			UID:      id,
		},
		Views: []*ViewExpr{{
			AttributeExpr: DupAtt(pe.View("default").AttributeExpr),
			Name:          "default",
			Parent:        pe,
		}},
	}

	// Run the DSL that was created by the CollectionOf function
	if !eval.Execute(proj.DSL(), proj) {
		return nil, eval.Context.Errors
	}

	return proj, nil
}

func projectRecursive(at *AttributeExpr, vat *NamedAttributeExpr, view string, seen ...map[string]*AttributeExpr) (*AttributeExpr, error) {
	s := seen[0]
	ut, isUT := at.Type.(UserType)
	if isUT {
		if att, ok := s[ut.ID()+"::"+view]; ok {
			return att, nil
		}
	}
	at = DupAtt(at)

	if rt, ok := at.Type.(*ResultTypeExpr); ok {
		vatt := vat.Attribute
		var view string
		if len(vatt.Meta["view"]) > 0 {
			view = vatt.Meta["view"][0]
		}
		if view == "" && len(at.Meta["view"]) > 0 {
			view = at.Meta["view"][0]
		}
		if view == "" {
			view = DefaultView
		}
		if isUT {
			s[ut.ID()+"::"+view] = at
		}
		pr, err := Project(rt, view, seen...)
		if err != nil {
			return nil, fmt.Errorf("view %#v on field %#v cannot be computed: %s", view, vat.Name, err)
		}
		at.Type = pr
		return at, nil
	}

	if isUT {
		s[ut.ID()+"::"+view] = at
	}

	if obj := AsObject(at.Type); obj != nil {
		vobj := AsObject(vat.Attribute.Type)
		if vobj == nil {
			return at, nil
		}
		for _, cnat := range *obj {
			var cvnat *NamedAttributeExpr
			for _, nnat := range *vobj {
				if nnat.Name == cnat.Name {
					cvnat = nnat
					break
				}
			}
			if cvnat == nil {
				continue
			}
			pat, err := projectRecursive(cnat.Attribute, cvnat, view, seen...)
			if err != nil {
				return nil, err
			}
			cnat.Attribute = pat
		}
		return at, nil
	}

	if ar := AsArray(at.Type); ar != nil {
		pat, err := projectRecursive(ar.ElemType, vat, view, seen...)
		if err != nil {
			return nil, err
		}
		ar.ElemType = pat
	}

	return at, nil
}

// projectIdentifier computes the projected result type identifier by adding the
// "view" param. We need the projected result type identifier to be different so
// that looking up projected result types from ProjectedResultTypes works
// correctly. It's also good for clients.
func (m *ResultTypeExpr) projectIdentifier(view string) string {
	base, params, err := mime.ParseMediaType(m.Identifier)
	if err != nil {
		base = m.Identifier
	}
	if params == nil {
		params = make(map[string]string)
	}
	params["view"] = view
	return mime.FormatMediaType(base, params)
}

// EvalName returns the generic definition name used in error messages.
func (v *ViewExpr) EvalName() string {
	var prefix, suffix string
	if v.Name != "" {
		prefix = fmt.Sprintf("view %#v", v.Name)
	} else {
		prefix = "unnamed view"
	}
	if v.Parent != nil {
		suffix = fmt.Sprintf(" of %s", v.Parent.EvalName())
	}
	return prefix + suffix
}
<commit_msg>Properly handle projection of tagged attributes (#2943)<commit_after>package expr

import (
	"fmt"
	"mime"
	"strings"

	"goa.design/goa/v3/eval"
)

const (
	// DefaultView is the name of the default result type view.
	DefaultView = "default"
)

type (
	// ResultTypeExpr is a user type which describes views used to
	// render responses.
	ResultTypeExpr struct {
		// A result type is a user type
		*UserTypeExpr
		// Identifier is the RFC 6838 result type media type identifier.
		Identifier string
		// ContentType identifies the value written to the response
		// "Content-Type" header. Deprecated.
		ContentType string
		// Views list the supported views indexed by name.
		Views []*ViewExpr
	}

	// ViewExpr defines which fields to render when building a response. The view
	// is an object whose field names must match the names of the parent result
	// type field names. The field definitions are inherited from the parent
	// result type but may be overridden.
	ViewExpr struct {
		// Set of properties included in view
		*AttributeExpr
		// Name of view
		Name string
		// Parent result Type
		Parent *ResultTypeExpr
	}
)

var (
	// ErrorResultIdentifier is the result type identifier used for error
	// responses.
	ErrorResultIdentifier = "application/vnd.goa.error"

	// ErrorResult is the built-in result type for error responses.
	ErrorResult = &ResultTypeExpr{
		UserTypeExpr: &UserTypeExpr{
			AttributeExpr: &AttributeExpr{
				Type:        errorResultType,
				Description: "Error response result type",
				UserExamples: []*ExampleExpr{{
					Summary: "BadRequest",
					Value: Val{
						"name":    "bad_request",
						"id":      "3F1FKVRR",
						"message": "Value of ID must be an integer",
					},
				}},
				Validation: &ValidationExpr{Required: []string{"name", "id", "message", "temporary", "timeout", "fault"}},
			},
			TypeName: "error",
		},
		Identifier: ErrorResultIdentifier,
		Views:      []*ViewExpr{errorResultView},
	}

	errorResultType = &Object{
		{"name", &AttributeExpr{
			Type:         String,
			Description:  "Name is the name of this class of errors.",
			Meta:         MetaExpr{"struct:error:name": nil},
			UserExamples: []*ExampleExpr{{Value: "bad_request"}},
		}},
		{"id", &AttributeExpr{
			Type:         String,
			Description:  "ID is a unique identifier for this particular occurrence of the problem.",
			UserExamples: []*ExampleExpr{{Value: "123abc"}},
		}},
		{"message", &AttributeExpr{
			Type:         String,
			Description:  "Message is a human-readable explanation specific to this occurrence of the problem.",
			UserExamples: []*ExampleExpr{{Value: "parameter 'p' must be an integer"}},
		}},
		{"temporary", &AttributeExpr{
			Type:        Boolean,
			Description: "Is the error temporary?",
		}},
		{"timeout", &AttributeExpr{
			Type:        Boolean,
			Description: "Is the error a timeout?",
		}},
		{"fault", &AttributeExpr{
			Type:        Boolean,
			Description: "Is the error a server-side fault?",
		}},
	}

	errorResultView = &ViewExpr{
		AttributeExpr: &AttributeExpr{Type: errorResultType},
		Name:          "default",
	}
)

// NewResultTypeExpr creates a result type definition but does not
// execute the DSL.
func NewResultTypeExpr(name, identifier string, fn func()) *ResultTypeExpr {
	return &ResultTypeExpr{
		UserTypeExpr: &UserTypeExpr{
			AttributeExpr: &AttributeExpr{Type: &Object{}, DSLFunc: fn},
			TypeName:      name,
			UID:           identifier,
		},
		Identifier: identifier,
	}
}

// CanonicalIdentifier returns the result type identifier sans suffix
// which is what the DSL uses to store and lookup result types.
func CanonicalIdentifier(identifier string) string {
	base, params, err := mime.ParseMediaType(identifier)
	if err != nil {
		return identifier
	}
	id := base
	if i := strings.Index(id, "+"); i != -1 {
		id = id[:i]
	}
	return mime.FormatMediaType(id, params)
}

// Kind implements DataKind.
func (m *ResultTypeExpr) Kind() Kind { return ResultTypeKind }

// Dup creates a deep copy of the result type given a deep copy of its attribute.
func (m *ResultTypeExpr) Dup(att *AttributeExpr) UserType {
	return &ResultTypeExpr{
		UserTypeExpr: m.UserTypeExpr.Dup(att).(*UserTypeExpr),
		Identifier:   m.Identifier,
		Views:        m.Views,
	}
}

// ID returns the identifier of the result type.
func (m *ResultTypeExpr) ID() string {
	return m.Identifier
}

// Name returns the result type name.
func (m *ResultTypeExpr) Name() string { return m.TypeName }

// View returns the view with the given name.
func (m *ResultTypeExpr) View(name string) *ViewExpr {
	for _, v := range m.Views {
		if v.Name == name {
			return v
		}
	}
	return nil
}

// HasMultipleViews returns true if the result type has more than one view.
func (m *ResultTypeExpr) HasMultipleViews() bool {
	return len(m.Views) > 1
}

// ViewHasAttribute returns true if the result type view has the given
// attribute.
func (m *ResultTypeExpr) ViewHasAttribute(view, attr string) bool {
	v := m.View(view)
	if v == nil {
		return false
	}
	return v.AttributeExpr.Find(attr) != nil
}

// Finalize builds the default view if not explicitly defined and finalizes
// the underlying UserTypeExpr.
func (m *ResultTypeExpr) Finalize() {
	if m.View("default") == nil {
		m.ensureDefaultView()
	}
	m.UserTypeExpr.Finalize()
	seen := make(map[string]struct{})
	walkAttribute(m.AttributeExpr, func(_ string, att *AttributeExpr) error {
		if rt, ok := att.Type.(*ResultTypeExpr); ok {
			if _, ok := seen[rt.Identifier]; !ok {
				seen[rt.Identifier] = struct{}{}
				rt.ensureDefaultView()
			}
		}
		return nil
	})
}

// ensureDefaultView builds the default view if not explicitly defined.
func (m *ResultTypeExpr) ensureDefaultView() {
	if m.View("default") == nil {
		att := DupAtt(m.AttributeExpr)
		if arr := AsArray(att.Type); arr != nil {
			att.Type = AsObject(arr.ElemType.Type)
		}
		v := &ViewExpr{
			AttributeExpr: att,
			Name:          "default",
			Parent:        m,
		}
		m.Views = append(m.Views, v)
	}
}

// Project creates a ResultTypeExpr containing the fields defined in the view
// expression of m named after the view argument.
//
// The resulting result type defines a default view. The result type identifier is
// computed by adding a parameter called "view" to the original identifier. The
// value of the "view" parameter is the name of the view.
//
// Project returns an error if the view does not exist for the given result type
// or any result type that makes up its attributes recursively. Note that
// individual attributes may use a different view. In this case Project uses
// that view and returns an error if it isn't defined on the attribute type.
func Project(m *ResultTypeExpr, view string, seen ...map[string]*AttributeExpr) (*ResultTypeExpr, error) {
	_, params, _ := mime.ParseMediaType(m.Identifier)
	if params["view"] == view {
		// nothing to do
		return m, nil
	}
	if _, ok := m.Type.(*Array); ok {
		return projectCollection(m, view, seen...)
	}
	var s map[string]*AttributeExpr
	if len(seen) > 0 {
		s = seen[0]
	} else {
		s = make(map[string]*AttributeExpr)
	}
	return projectSingle(m, view, s)
}

func projectSingle(m *ResultTypeExpr, view string, seen map[string]*AttributeExpr) (*ResultTypeExpr, error) {
	v := m.View(view)
	if v == nil {
		return nil, fmt.Errorf("unknown view %#v", view)
	}
	viewObj := v.Type.(*Object)

	// Compute validations - view may not have all fields
	var val *ValidationExpr
	if m.Validation != nil {
		var required []string
		for _, n := range m.Validation.Required {
			if att := viewObj.Attribute(n); att != nil {
				required = append(required, n)
			}
		}
		val = m.Validation.Dup()
		val.Required = required
	}

	// Compute description
	desc := m.Description
	if desc == "" {
		desc = m.TypeName + " result type"
	}
	desc += " (" + view + " view)"

	// Compute type name
	typeName := m.TypeName
	if view != "default" {
		typeName += strings.Title(view)
	}

	var ut *UserTypeExpr
	if att, ok := seen[hash(m.Attribute(), view)]; ok {
		if rt, ok2 := att.Type.(*ResultTypeExpr); ok2 {
			ut = &UserTypeExpr{
				AttributeExpr: DupAtt(rt.Attribute()),
				TypeName:      rt.TypeName,
			}
		}
	}
	id := m.projectIdentifier(view)
	if ut == nil {
		ut = &UserTypeExpr{
			AttributeExpr: &AttributeExpr{
				Description: desc,
				Validation:  val,
			},
		}
	}
	ut.TypeName = typeName
	ut.UID = id
	ut.AttributeExpr.Type = Dup(v.Type)
	ut.AttributeExpr.UserExamples = v.UserExamples
	projected := &ResultTypeExpr{
		Identifier:   id,
		UserTypeExpr: ut,
	}
	projected.Views = []*ViewExpr{{
		Name:          "default",
		AttributeExpr: DupAtt(v.AttributeExpr),
		Parent:        projected,
	}}

	projectedObj := projected.Type.(*Object)
	mtObj := AsObject(m.Type)
	for _, nat := range *viewObj {
		if at := mtObj.Attribute(nat.Name); at != nil {
			pat, err := projectRecursive(at, nat, view, seen)
			if err != nil {
				return nil, err
			}
			projectedObj.Set(nat.Name, pat)
		}
	}
	return projected, nil
}

func projectCollection(m *ResultTypeExpr, view string, seen ...map[string]*AttributeExpr) (*ResultTypeExpr, error) {
	// Project the collection element result type
	e := m.Type.(*Array).ElemType.Type.(*ResultTypeExpr) // validation checked this cast would work
	pe, err2 := Project(e, view, seen...)
	if err2 != nil {
		return nil, fmt.Errorf("collection element: %s", err2)
	}

	// Build the projected collection with the results
	id := m.projectIdentifier(view)
	proj := &ResultTypeExpr{
		Identifier: id,
		UserTypeExpr: &UserTypeExpr{
			AttributeExpr: &AttributeExpr{
				Description:  m.TypeName + " is the result type for an array of " + e.TypeName + " (" + view + " view)",
				Type:         &Array{ElemType: &AttributeExpr{Type: pe}},
				UserExamples: m.UserExamples,
			},
			TypeName: pe.TypeName + "Collection",
			UID:      id,
		},
		Views: []*ViewExpr{{
			AttributeExpr: DupAtt(pe.View("default").AttributeExpr),
			Name:          "default",
			Parent:        pe,
		}},
	}

	// Run the DSL that was created by the CollectionOf function
	if !eval.Execute(proj.DSL(), proj) {
		return nil, eval.Context.Errors
	}

	return proj, nil
}

func projectRecursive(at *AttributeExpr, vat *NamedAttributeExpr, view string, seen map[string]*AttributeExpr) (*AttributeExpr, error) {
	if att, ok := seen[hash(at, view)]; ok {
		return att, nil
	}
	at = DupAtt(at)

	if rt, ok := at.Type.(*ResultTypeExpr); ok {
		vatt := vat.Attribute
		var view string
		if len(vatt.Meta["view"]) > 0 {
			view = vatt.Meta["view"][0]
		}
		if view == "" && len(at.Meta["view"]) > 0 {
			view = at.Meta["view"][0]
		}
		if view == "" {
			view = DefaultView
		}
		seen[hash(at, view)] = at
		pr, err := Project(rt, view, seen)
		if err != nil {
			return nil, fmt.Errorf("view %#v on field %#v cannot be computed: %s", view, vat.Name, err)
		}
		at.Type = pr
		return at, nil
	}

	seen[hash(at, view)] = at

	if obj := AsObject(at.Type); obj != nil {
		vobj := AsObject(vat.Attribute.Type)
		if vobj == nil {
			return at, nil
		}
		for _, cnat := range *obj {
			var cvnat *NamedAttributeExpr
			for _, nnat := range *vobj {
				if nnat.Name == cnat.Name {
					cvnat = nnat
					break
				}
			}
			if cvnat == nil {
				continue
			}
			pat, err := projectRecursive(cnat.Attribute, cvnat, view, seen)
			if err != nil {
				return nil, err
			}
			cnat.Attribute = pat
		}
		return at, nil
	}

	if ar := AsArray(at.Type); ar != nil {
		pat, err := projectRecursive(ar.ElemType, vat, view, seen)
		if err != nil {
			return nil, err
		}
		ar.ElemType = pat
	}

	return at, nil
}

// projectIdentifier computes the projected result type identifier by adding the
// "view" param. We need the projected result type identifier to be different so
// that looking up projected result types from ProjectedResultTypes works
// correctly. It's also good for clients.
func (m *ResultTypeExpr) projectIdentifier(view string) string {
	base, params, err := mime.ParseMediaType(m.Identifier)
	if err != nil {
		base = m.Identifier
	}
	if params == nil {
		params = make(map[string]string)
	}
	params["view"] = view
	return mime.FormatMediaType(base, params)
}

// EvalName returns the generic definition name used in error messages.
func (v *ViewExpr) EvalName() string {
	var prefix, suffix string
	if v.Name != "" {
		prefix = fmt.Sprintf("view %#v", v.Name)
	} else {
		prefix = "unnamed view"
	}
	if v.Parent != nil {
		suffix = fmt.Sprintf(" of %s", v.Parent.EvalName())
	}
	return prefix + suffix
}

// hash computes a hash for an attribute and a view that returns the same value
// for two attributes and views that produce the same projected type.
func hash(att *AttributeExpr, view string) string {
	sep := "!"
	ut, ok := att.Type.(UserType)
	if !ok {
		return att.Type.Name() + sep + view
	}
	// elems contain the elements that affect how types are projected:
	// - the type identifier
	// - the view
	// - the type attribute tags (if any)
	elems := []string{ut.ID(), view}

	add := func(att *AttributeExpr) {
		for k, v := range att.Meta {
			elems = append(elems, k)
			elems = append(elems, v...)
		}
	}

	add(att)
	if obj := AsObject(ut); obj != nil {
		for _, nat := range *obj {
			add(nat.Attribute)
		}
	}
	n := len(sep) * (len(elems) - 1)
	for i := 0; i < len(elems); i++ {
		n += len(elems[i])
	}

	var b strings.Builder
	b.Grow(n)
	b.WriteString(elems[0])
	for _, s := range elems[1:] {
		b.WriteString(sep)
		b.WriteString(s)
	}
	return b.String()
}
<|endoftext|>


<commit_before>package main

import (
    "log"
    "net"
    "time"
    "github.com/docker/libchan/unix"
)

type Sched struct {
    new_worker chan *Worker
    ask_worker chan *Worker
    die_worker chan *Worker
    started bool
    worker_count int
    timer *time.Timer
}

func NewSched() *Sched {
    sched = new(Sched)
    sched.started = false
    sched.new_worker = make(chan *Worker, 1)
    sched.ask_worker = make(chan *Worker, 1)
    sched.die_worker = make(chan *Worker, 1)
    sched.worker_count = 0
    sched.timer = time.NewTimer(1 * time.Hour)
    return sched
}

func (sched *Sched) Start() {
    sched.started = true
    go sched.run()
}

func (sched *Sched) Notify() {
    sched.timer.Reset(time.Millisecond)
}

func (sched *Sched) run() {
    var worker *Worker
    for {
        select {
        case worker = <-sched.new_worker:
            sched.worker_count += 1
            log.Printf("worker_count: %d\n", sched.worker_count)
            go worker.HandeNewConnection()
            break
        case worker =<-sched.ask_worker:
            job, err := NextSchedJob(0, 0)
            if err != nil {
                go worker.HandleNoJob()
            }
            now := int(time.Now().Unix())
            if job[0].SchedAt < now {
                sched.SubmitJob(worker, job[0].JobId)
            } else {
                sched.timer.Reset(time.Second * time.Duration(job[0].SchedAt - now))
                <-sched.timer.C
                now := int(time.Now().Unix())
                if job[0].SchedAt <= now {
                    sched.SubmitJob(worker, job[0].JobId)
                } else {
                    go worker.HandleWaitForJob()
                }
            }
            break
        case worker =<-sched.die_worker:
            log.Printf("%v close\n", worker)
            sched.worker_count -= 1
            log.Printf("worker_count: %d\n", sched.worker_count)
            worker.Close()
            break
        case <-sched.timer.C:
            go worker.HandleDo("haha")
        }
    }
    sched.started = false
}

func (sched *Sched) NewConnectioin(conn net.Conn) {
    uconn := conn.(*net.UnixConn)
    worker := NewWorker(sched, &unix.UnixConn{UnixConn: uconn})
    sched.new_worker <- worker
    sched.Notify()
}

func (sched *Sched) Done(job string) {
    return
}

func (sched *Sched) SubmitJob(worker *Worker, job string) {
    delay := RandomDelay()
    SchedLater(job, delay)
    go worker.HandleDo(job)
}

func (sched *Sched) Fail(job string) {
    return
}

func (sched *Sched) Close() {
}
<commit_msg>do submit job on a goruntime<commit_after>package main

import (
    "log"
    "net"
    "time"
    "container/list"
    "github.com/docker/libchan/unix"
)

type Sched struct {
    new_worker chan *Worker
    ask_worker chan *Worker
    die_worker chan *Worker
    started bool
    worker_count int
    timer *time.Timer
    queue *list.List
}

func NewSched() *Sched {
    sched = new(Sched)
    sched.started = false
    sched.new_worker = make(chan *Worker, 1)
    sched.ask_worker = make(chan *Worker, 1)
    sched.die_worker = make(chan *Worker, 1)
    sched.worker_count = 0
    sched.timer = time.NewTimer(1 * time.Hour)
    sched.queue = list.New()
    return sched
}

func (sched *Sched) Start() {
    sched.started = true
    go sched.run()
    go sched.handle()
}

func (sched *Sched) Notify() {
    sched.timer.Reset(time.Millisecond)
}

func (sched *Sched) run() {
    var worker *Worker
    for {
        select {
        case worker = <-sched.new_worker:
            sched.worker_count += 1
            log.Printf("worker_count: %d\n", sched.worker_count)
            go worker.HandeNewConnection()
            break
        case worker =<-sched.ask_worker:
            sched.queue.PushBack(worker)
            sched.Notify()
            break
        case worker =<-sched.die_worker:
            log.Printf("%v close\n", worker)
            sched.worker_count -= 1
            log.Printf("worker_count: %d\n", sched.worker_count)
            sched.removeQueue(worker)
            worker.Close()
            break
        }
    }
    sched.started = false
}

func (sched *Sched) NewConnectioin(conn net.Conn) {
    uconn := conn.(*net.UnixConn)
    worker := NewWorker(sched, &unix.UnixConn{UnixConn: uconn})
    sched.new_worker <- worker
    sched.Notify()
}

func (sched *Sched) Done(job string) {
    return
}

func (sched *Sched) SubmitJob(worker *Worker, job string) {
    delay := RandomDelay()
    SchedLater(job, delay)
    go worker.HandleDo(job)
}

func (sched *Sched) handle() {
    var current time.Time
    var timestamp int
    for {
        for e := sched.queue.Front(); e != nil; e = e.Next() {
            worker := e.Value.(*Worker)
            job, err := NextSchedJob(0, 0)
            if err != nil {
                sched.queue.Remove(e)
                go worker.HandleNoJob()
            }
            timestamp = int(time.Now().Unix())
            if job[0].SchedAt < timestamp {
                sched.queue.Remove(e)
                sched.SubmitJob(worker, job[0].JobId)
            } else {
                sched.timer.Reset(time.Second * time.Duration(job[0].SchedAt - timestamp))
                current =<-sched.timer.C
                timestamp = int(current.Unix())
                if job[0].SchedAt <= timestamp {
                    sched.queue.Remove(e)
                    sched.SubmitJob(worker, job[0].JobId)
                }
            }
        }
        if sched.queue.Len() == 0 {
            current =<-sched.timer.C
        }
    }
}

func (sched *Sched) Fail(job string) {
    return
}

func (sched *Sched) removeQueue(worker *Worker) {
    for e := sched.queue.Front(); e != nil; e = e.Next() {
        if e.Value.(*Worker) == worker {
            sched.queue.Remove(e)
        }
    }
}

func (sched *Sched) Close() {
}
<|endoftext|>


<commit_before>package miner

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/persist"
	siasync "github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

var (
	errNilCS     = errors.New("miner cannot use a nil consensus set")
	errNilTpool  = errors.New("miner cannot use a nil transaction pool")
	errNilWallet = errors.New("miner cannot use a nil wallet")

	// HeaderMemory is the number of previous calls to 'header'
	// that are remembered. Additionally, 'header' will only poll for a
	// new block every 'headerMemory / blockMemory' times it is
	// called. This reduces the amount of memory used, but comes at the cost of
	// not always having the most recent transactions.
	HeaderMemory = func() int {
		if build.Release == "dev" {
			return 500
		}
		if build.Release == "standard" {
			return 10000
		}
		if build.Release == "testing" {
			return 50
		}
		panic("unrecognized build.Release")
	}()

	// BlockMemory is the maximum number of blocks the miner will store
	// Blocks take up to 2 megabytes of memory, which is why this number is
	// limited.
	BlockMemory = func() int {
		if build.Release == "dev" {
			return 10
		}
		if build.Release == "standard" {
			return 50
		}
		if build.Release == "testing" {
			return 5
		}
		panic("unrecognized build.Release")
	}()

	// MaxSourceBlockAge is the maximum amount of time that is allowed to
	// elapse between generating source blocks.
	MaxSourceBlockAge = func() time.Duration {
		if build.Release == "dev" {
			return 5 * time.Second
		}
		if build.Release == "standard" {
			return 30 * time.Second
		}
		if build.Release == "testing" {
			return 1 * time.Second
		}
		panic("unrecognized build.Release")
	}()
)

// Miner struct contains all variables the miner needs
// in order to create and submit blocks.
type Miner struct {
	// Module dependencies.
	cs     modules.ConsensusSet
	tpool  modules.TransactionPool
	wallet modules.Wallet

	// BlockManager variables. Becaues blocks are large, one block is used to
	// make many headers which can be used by miners. Headers include an
	// arbitrary data transaction (appended to the block) to make the merkle
	// roots unique (preventing miners from doing redundant work). Every N
	// requests or M seconds, a new block is used to create headers.
	//
	// Only 'blocksMemory' blocks are kept in memory at a time, which
	// keeps ram usage reasonable. Miners may request many headers in parallel,
	// and thus may be working on different blocks. When they submit the solved
	// header to the block manager, the rest of the block needs to be found in
	// a lookup.
	blockMem        map[types.BlockHeader]*types.Block             // Mappings from headers to the blocks they are derived from.
	arbDataMem      map[types.BlockHeader][crypto.EntropySize]byte // Mappings from the headers to their unique arb data.
	headerMem       []types.BlockHeader                            // A circular list of headers that have been given out from the api recently.
	sourceBlock     *types.Block                                   // The block from which new headers for mining are created.
	sourceBlockTime time.Time                                      // How long headers have been using the same block (different from 'recent block').
	memProgress     int                                            // The index of the most recent header used in headerMem.

	// CPUMiner variables.
	miningOn bool  // indicates if the miner is supposed to be running
	mining   bool  // indicates if the miner is actually running
	hashRate int64 // indicates hashes per second

	// Utils
	log        *persist.Logger
	mu         sync.RWMutex
	persist    persistence
	persistDir string
	// tg signals the Miner's goroutines to shut down and blocks until all
	// goroutines have exited before returning from Close().
	tg siasync.ThreadGroup
}

// startupRescan will rescan the blockchain in the event that the miner
// persistance layer has become desynchronized from the consensus persistance
// layer. This might happen if a user replaces any of the folders with backups
// or deletes any of the folders.
func (m *Miner) startupRescan() error {
	// Reset all of the variables that have relevance to the consensus set. The
	// operations are wrapped by an anonymous function so that the locking can
	// be handled using a defer statement.
	err := func() error {
		m.mu.Lock()
		defer m.mu.Unlock()

		m.log.Println("Performing a miner rescan.")
		m.persist.RecentChange = modules.ConsensusChangeBeginning
		m.persist.Height = 0
		m.persist.Target = types.Target{}
		return m.save()
	}()
	if err != nil {
		return err
	}

	// Subscribe to the consensus set. This is a blocking call that will not
	// return until the miner has fully caught up to the current block.
	err = m.cs.ConsensusSetSubscribe(m, modules.ConsensusChangeBeginning)
	if err != nil {
		return err
	}
	m.tg.OnStop(func() {
		m.cs.Unsubscribe(m)
	})
	return nil
}

// New returns a ready-to-go miner that is not mining.
func New(cs modules.ConsensusSet, tpool modules.TransactionPool, w modules.Wallet, persistDir string) (*Miner, error) {
	// Create the miner and its dependencies.
	if cs == nil {
		return nil, errNilCS
	}
	if tpool == nil {
		return nil, errNilTpool
	}
	if w == nil {
		return nil, errNilWallet
	}

	// Assemble the miner. The miner is assembled without an address because
	// the wallet is likely not unlocked yet. The miner will grab an address
	// after the miner is unlocked (this must be coded manually for each
	// function that potentially requires the miner to have an address.
	m := &Miner{
		cs:     cs,
		tpool:  tpool,
		wallet: w,

		blockMem:   make(map[types.BlockHeader]*types.Block),
		arbDataMem: make(map[types.BlockHeader][crypto.EntropySize]byte),
		headerMem:  make([]types.BlockHeader, HeaderMemory),

		persistDir: persistDir,
	}

	err := m.initPersist()
	if err != nil {
		return nil, errors.New("miner persistence startup failed: " + err.Error())
	}

	err = m.cs.ConsensusSetSubscribe(m, m.persist.RecentChange)
	if err == modules.ErrInvalidConsensusChangeID {
		// Perform a rescan of the consensus set if the change id is not found.
		// The id will only be not found if there has been desynchronization
		// between the miner and the consensus package.
		err = m.startupRescan()
		if err != nil {
			return nil, errors.New("miner startup failed - rescanning failed: " + err.Error())
		}
	} else if err != nil {
		return nil, errors.New("miner subscription failed: " + err.Error())
	}
	m.tg.OnStop(func() {
		m.cs.Unsubscribe(m)
	})

	m.tpool.TransactionPoolSubscribe(m)
	m.tg.OnStop(func() {
		m.tpool.Unsubscribe(m)
	})

	// Save after synchronizing with consensus
	err = m.save()
	if err != nil {
		return nil, errors.New("miner could not save during startup: " + err.Error())
	}

	return m, nil
}

// Close terminates all ongoing processes involving the miner, enabling garbage
// collection.
func (m *Miner) Close() error {
	if err := m.tg.Stop(); err != nil {
		return err
	}

	m.mu.Lock()
	defer m.mu.Unlock()

	m.cs.Unsubscribe(m)

	var errs []error
	if err := m.saveSync(); err != nil {
		errs = append(errs, fmt.Errorf("save failed: %v", err))
	}
	if err := m.log.Close(); err != nil {
		errs = append(errs, fmt.Errorf("log.Close failed: %v", err))
	}
	return build.JoinErrors(errs, "; ")
}

// checkAddress checks that the miner has an address, fetching an address from
// the wallet if not.
func (m *Miner) checkAddress() error {
	if m.persist.Address != (types.UnlockHash{}) {
		return nil
	}
	uc, err := m.wallet.NextAddress()
	if err != nil {
		return err
	}
	m.persist.Address = uc.UnlockHash()
	return nil
}

// BlocksMined returns the number of good blocks and stale blocks that have
// been mined by the miner.
func (m *Miner) BlocksMined() (goodBlocks, staleBlocks int) {
	if err := m.tg.Add(); err != nil {
		build.Critical(err)
	}
	defer m.tg.Done()

	m.mu.Lock()
	defer m.mu.Unlock()

	for _, blockID := range m.persist.BlocksFound {
		if m.cs.InCurrentPath(blockID) {
			goodBlocks++
		} else {
			staleBlocks++
		}
	}
	return
}
<commit_msg>demonstrate use of build.Select in miner<commit_after>package miner

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/persist"
	siasync "github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
)

var (
	errNilCS     = errors.New("miner cannot use a nil consensus set")
	errNilTpool  = errors.New("miner cannot use a nil transaction pool")
	errNilWallet = errors.New("miner cannot use a nil wallet")

	// HeaderMemory is the number of previous calls to 'header'
	// that are remembered. Additionally, 'header' will only poll for a
	// new block every 'headerMemory / blockMemory' times it is
	// called. This reduces the amount of memory used, but comes at the cost of
	// not always having the most recent transactions.
	HeaderMemory = build.Select(build.Var{
		Standard: 10000,
		Dev:      500,
		Testing:  50,
	}).(int)

	// BlockMemory is the maximum number of blocks the miner will store
	// Blocks take up to 2 megabytes of memory, which is why this number is
	// limited.
	BlockMemory = build.Select(build.Var{
		Standard: 50,
		Dev:      10,
		Testing:  5,
	}).(int)

	// MaxSourceBlockAge is the maximum amount of time that is allowed to
	// elapse between generating source blocks.
	MaxSourceBlockAge = build.Select(build.Var{
		Standard: 30 * time.Second,
		Dev:      5 * time.Second,
		Testing:  1 * time.Second,
	}).(time.Duration)
)

// Miner struct contains all variables the miner needs
// in order to create and submit blocks.
type Miner struct {
	// Module dependencies.
	cs     modules.ConsensusSet
	tpool  modules.TransactionPool
	wallet modules.Wallet

	// BlockManager variables. Becaues blocks are large, one block is used to
	// make many headers which can be used by miners. Headers include an
	// arbitrary data transaction (appended to the block) to make the merkle
	// roots unique (preventing miners from doing redundant work). Every N
	// requests or M seconds, a new block is used to create headers.
	//
	// Only 'blocksMemory' blocks are kept in memory at a time, which
	// keeps ram usage reasonable. Miners may request many headers in parallel,
	// and thus may be working on different blocks. When they submit the solved
	// header to the block manager, the rest of the block needs to be found in
	// a lookup.
	blockMem        map[types.BlockHeader]*types.Block             // Mappings from headers to the blocks they are derived from.
	arbDataMem      map[types.BlockHeader][crypto.EntropySize]byte // Mappings from the headers to their unique arb data.
	headerMem       []types.BlockHeader                            // A circular list of headers that have been given out from the api recently.
	sourceBlock     *types.Block                                   // The block from which new headers for mining are created.
	sourceBlockTime time.Time                                      // How long headers have been using the same block (different from 'recent block').
	memProgress     int                                            // The index of the most recent header used in headerMem.

	// CPUMiner variables.
	miningOn bool  // indicates if the miner is supposed to be running
	mining   bool  // indicates if the miner is actually running
	hashRate int64 // indicates hashes per second

	// Utils
	log        *persist.Logger
	mu         sync.RWMutex
	persist    persistence
	persistDir string
	// tg signals the Miner's goroutines to shut down and blocks until all
	// goroutines have exited before returning from Close().
	tg siasync.ThreadGroup
}

// startupRescan will rescan the blockchain in the event that the miner
// persistance layer has become desynchronized from the consensus persistance
// layer. This might happen if a user replaces any of the folders with backups
// or deletes any of the folders.
func (m *Miner) startupRescan() error {
	// Reset all of the variables that have relevance to the consensus set. The
	// operations are wrapped by an anonymous function so that the locking can
	// be handled using a defer statement.
	err := func() error {
		m.mu.Lock()
		defer m.mu.Unlock()

		m.log.Println("Performing a miner rescan.")
		m.persist.RecentChange = modules.ConsensusChangeBeginning
		m.persist.Height = 0
		m.persist.Target = types.Target{}
		return m.save()
	}()
	if err != nil {
		return err
	}

	// Subscribe to the consensus set. This is a blocking call that will not
	// return until the miner has fully caught up to the current block.
	err = m.cs.ConsensusSetSubscribe(m, modules.ConsensusChangeBeginning)
	if err != nil {
		return err
	}
	m.tg.OnStop(func() {
		m.cs.Unsubscribe(m)
	})
	return nil
}

// New returns a ready-to-go miner that is not mining.
func New(cs modules.ConsensusSet, tpool modules.TransactionPool, w modules.Wallet, persistDir string) (*Miner, error) {
	// Create the miner and its dependencies.
	if cs == nil {
		return nil, errNilCS
	}
	if tpool == nil {
		return nil, errNilTpool
	}
	if w == nil {
		return nil, errNilWallet
	}

	// Assemble the miner. The miner is assembled without an address because
	// the wallet is likely not unlocked yet. The miner will grab an address
	// after the miner is unlocked (this must be coded manually for each
	// function that potentially requires the miner to have an address.
	m := &Miner{
		cs:     cs,
		tpool:  tpool,
		wallet: w,

		blockMem:   make(map[types.BlockHeader]*types.Block),
		arbDataMem: make(map[types.BlockHeader][crypto.EntropySize]byte),
		headerMem:  make([]types.BlockHeader, HeaderMemory),

		persistDir: persistDir,
	}

	err := m.initPersist()
	if err != nil {
		return nil, errors.New("miner persistence startup failed: " + err.Error())
	}

	err = m.cs.ConsensusSetSubscribe(m, m.persist.RecentChange)
	if err == modules.ErrInvalidConsensusChangeID {
		// Perform a rescan of the consensus set if the change id is not found.
		// The id will only be not found if there has been desynchronization
		// between the miner and the consensus package.
		err = m.startupRescan()
		if err != nil {
			return nil, errors.New("miner startup failed - rescanning failed: " + err.Error())
		}
	} else if err != nil {
		return nil, errors.New("miner subscription failed: " + err.Error())
	}
	m.tg.OnStop(func() {
		m.cs.Unsubscribe(m)
	})

	m.tpool.TransactionPoolSubscribe(m)
	m.tg.OnStop(func() {
		m.tpool.Unsubscribe(m)
	})

	// Save after synchronizing with consensus
	err = m.save()
	if err != nil {
		return nil, errors.New("miner could not save during startup: " + err.Error())
	}

	return m, nil
}

// Close terminates all ongoing processes involving the miner, enabling garbage
// collection.
func (m *Miner) Close() error {
	if err := m.tg.Stop(); err != nil {
		return err
	}

	m.mu.Lock()
	defer m.mu.Unlock()

	m.cs.Unsubscribe(m)

	var errs []error
	if err := m.saveSync(); err != nil {
		errs = append(errs, fmt.Errorf("save failed: %v", err))
	}
	if err := m.log.Close(); err != nil {
		errs = append(errs, fmt.Errorf("log.Close failed: %v", err))
	}
	return build.JoinErrors(errs, "; ")
}

// checkAddress checks that the miner has an address, fetching an address from
// the wallet if not.
func (m *Miner) checkAddress() error {
	if m.persist.Address != (types.UnlockHash{}) {
		return nil
	}
	uc, err := m.wallet.NextAddress()
	if err != nil {
		return err
	}
	m.persist.Address = uc.UnlockHash()
	return nil
}

// BlocksMined returns the number of good blocks and stale blocks that have
// been mined by the miner.
func (m *Miner) BlocksMined() (goodBlocks, staleBlocks int) {
	if err := m.tg.Add(); err != nil {
		build.Critical(err)
	}
	defer m.tg.Done()

	m.mu.Lock()
	defer m.mu.Unlock()

	for _, blockID := range m.persist.BlocksFound {
		if m.cs.InCurrentPath(blockID) {
			goodBlocks++
		} else {
			staleBlocks++
		}
	}
	return
}
<|endoftext|>


<commit_before>package goss

import (
	"bytes"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/aelsabbahy/goss/outputs"
	"github.com/aelsabbahy/goss/system"
	"github.com/fatih/color"
	"github.com/patrickmn/go-cache"
	"github.com/urfave/cli"
)

func Serve(c *cli.Context) {
	endpoint := c.String("endpoint")
	color.NoColor = true
	cache := cache.New(c.Duration("cache"), 30*time.Second)

	health := healthHandler{
		gossConfig:    getGossConfig(c),
		sys:           system.New(c),
		outputer:      getOutputer(c),
		cache:         cache,
		gossMu:        &sync.Mutex{},
		maxConcurrent: c.Int("max-concurrent"),
	}
	if c.String("format") == "json" {
		health.contentType = "application/json"
	}
	http.Handle(endpoint, health)
	listenAddr := c.String("listen-addr")
	log.Printf("Starting to listen on: %s", listenAddr)
	log.Fatal(http.ListenAndServe(c.String("listen-addr"), nil))
}

type res struct {
	exitCode int
	b        bytes.Buffer
}
type healthHandler struct {
	c             *cli.Context
	gossConfig    GossConfig
	sys           *system.System
	outputer      outputs.Outputer
	cache         *cache.Cache
	gossMu        *sync.Mutex
	contentType   string
	maxConcurrent int
}

func (h healthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("%v: requesting health probe", r.RemoteAddr)
	var resp res
	tmp, found := h.cache.Get("res")
	if found {
		resp = tmp.(res)
	} else {
		h.gossMu.Lock()
		defer h.gossMu.Unlock()
		tmp, found := h.cache.Get("res")
		if found {
			resp = tmp.(res)
		} else {
			log.Printf("%v: Stale cache, running tests", r.RemoteAddr)
			iStartTime := time.Now()
			out := validate(h.sys, h.gossConfig, h.maxConcurrent)
			var b bytes.Buffer
			exitCode := h.outputer.Output(&b, out, iStartTime)
			resp = res{exitCode: exitCode, b: b}
			h.cache.Set("res", resp, cache.DefaultExpiration)
		}
	}
	if h.contentType != "" {
		w.Header().Set("Content-Type", h.contentType)
	}
	if resp.exitCode == 0 {
		resp.b.WriteTo(w)
	} else {
		w.WriteHeader(http.StatusServiceUnavailable)
		resp.b.WriteTo(w)
	}
}
<commit_msg>Reset cache between `goss serve` runs (#180)<commit_after>package goss

import (
	"bytes"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/aelsabbahy/goss/outputs"
	"github.com/aelsabbahy/goss/system"
	"github.com/fatih/color"
	"github.com/patrickmn/go-cache"
	"github.com/urfave/cli"
)

func Serve(c *cli.Context) {
	endpoint := c.String("endpoint")
	color.NoColor = true
	cache := cache.New(c.Duration("cache"), 30*time.Second)

	health := healthHandler{
		c:             c,
		gossConfig:    getGossConfig(c),
		sys:           system.New(c),
		outputer:      getOutputer(c),
		cache:         cache,
		gossMu:        &sync.Mutex{},
		maxConcurrent: c.Int("max-concurrent"),
	}
	if c.String("format") == "json" {
		health.contentType = "application/json"
	}
	http.Handle(endpoint, health)
	listenAddr := c.String("listen-addr")
	log.Printf("Starting to listen on: %s", listenAddr)
	log.Fatal(http.ListenAndServe(c.String("listen-addr"), nil))
}

type res struct {
	exitCode int
	b        bytes.Buffer
}
type healthHandler struct {
	c             *cli.Context
	gossConfig    GossConfig
	sys           *system.System
	outputer      outputs.Outputer
	cache         *cache.Cache
	gossMu        *sync.Mutex
	contentType   string
	maxConcurrent int
}

func (h healthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("%v: requesting health probe", r.RemoteAddr)
	var resp res
	tmp, found := h.cache.Get("res")
	if found {
		resp = tmp.(res)
	} else {
		h.gossMu.Lock()
		defer h.gossMu.Unlock()
		tmp, found := h.cache.Get("res")
		if found {
			resp = tmp.(res)
		} else {
			h.sys = system.New(h.c)
			log.Printf("%v: Stale cache, running tests", r.RemoteAddr)
			iStartTime := time.Now()
			out := validate(h.sys, h.gossConfig, h.maxConcurrent)
			var b bytes.Buffer
			exitCode := h.outputer.Output(&b, out, iStartTime)
			resp = res{exitCode: exitCode, b: b}
			h.cache.Set("res", resp, cache.DefaultExpiration)
		}
	}
	if h.contentType != "" {
		w.Header().Set("Content-Type", h.contentType)
	}
	if resp.exitCode == 0 {
		resp.b.WriteTo(w)
	} else {
		w.WriteHeader(http.StatusServiceUnavailable)
		resp.b.WriteTo(w)
	}
}
<|endoftext|>


<commit_before>package kami

import (
	"flag"
	"log"
	"net/http"

	"github.com/zenazn/goji/bind"
	"github.com/zenazn/goji/graceful"
)

func init() {
	bind.WithFlag()
}

// Serve starts kami with reasonable defaults.
// It works (exactly) like Goji, looking for Einhorn, the bind flag, GOJI_BIND...
func Serve() {
	if !flag.Parsed() {
		flag.Parse()
	}

	// Install our handler at the root of the standard net/http default mux.
	// This allows packages like expvar to continue working as expected.
	http.Handle("/", Handler())

	listener := bind.Default()
	log.Println("Starting kami on", listener.Addr())

	graceful.HandleSignals()
	bind.Ready()
	graceful.PreHook(func() { log.Printf("kami received signal, gracefully stopping") })
	graceful.PostHook(func() { log.Printf("kami stopped") })

	err := graceful.Serve(listener, http.DefaultServeMux)

	if err != nil {
		log.Fatal(err)
	}

	graceful.Wait()
}
<commit_msg>ignore build on appengine<commit_after>// +build !appengine

package kami

import (
	"flag"
	"log"
	"net/http"

	"github.com/zenazn/goji/bind"
	"github.com/zenazn/goji/graceful"
)

func init() {
	bind.WithFlag()
}

// Serve starts kami with reasonable defaults.
// It works (exactly) like Goji, looking for Einhorn, the bind flag, GOJI_BIND...
func Serve() {
	if !flag.Parsed() {
		flag.Parse()
	}

	// Install our handler at the root of the standard net/http default mux.
	// This allows packages like expvar to continue working as expected.
	http.Handle("/", Handler())

	listener := bind.Default()
	log.Println("Starting kami on", listener.Addr())

	graceful.HandleSignals()
	bind.Ready()
	graceful.PreHook(func() { log.Printf("kami received signal, gracefully stopping") })
	graceful.PostHook(func() { log.Printf("kami stopped") })

	err := graceful.Serve(listener, http.DefaultServeMux)

	if err != nil {
		log.Fatal(err)
	}

	graceful.Wait()
}
<|endoftext|>


<commit_before>// Copyright 2016-2017, Cyrill @ Schumacher.fm and the CaddyESI Contributors
//
// 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 caddyesi

import (
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/SchumacherFM/caddyesi/esicache"
	"github.com/SchumacherFM/caddyesi/esitag"
	"github.com/SchumacherFM/caddyesi/helper"
	"github.com/corestoreio/errors"
	"github.com/corestoreio/log"
	"github.com/corestoreio/log/zapw"
	"github.com/dustin/go-humanize"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/uber-go/zap"
)

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

// PluginSetup used internally by Caddy to set up this middleware
func PluginSetup(c *caddy.Controller) error {
	pcs, err := configEsiParse(c)
	if err != nil {
		return errors.Wrap(err, "[caddyesi] Failed to parse configuration")
	}

	cfg := httpserver.GetConfig(c)

	mw := &Middleware{
		Root:        cfg.Root,
		FileSys:     http.Dir(cfg.Root),
		PathConfigs: pcs,
	}

	cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
		mw.Next = next
		return mw
	})

	c.OnShutdown(func() error {
		return errors.Wrap(esitag.CloseAllResourceHandler(), "[caddyesi] OnShutdown")
	})
	c.OnRestart(func() error {
		// really necessary? investigate later
		for _, pc := range pcs {
			pc.purgeESICache()
		}
		return errors.Wrap(esitag.CloseAllResourceHandler(), "[caddyesi] OnRestart")
	})

	return nil
}

func configEsiParse(c *caddy.Controller) (PathConfigs, error) {
	pcs := make(PathConfigs, 0, 2)

	for c.Next() {
		pc := NewPathConfig()

		// Get the path scope
		args := c.RemainingArgs()
		switch len(args) {
		case 0:
			pc.Scope = "/"
		case 1:
			pc.Scope = args[0]
		default:
			return nil, c.ArgErr()
		}

		// Load any other configuration parameters
		for c.NextBlock() {
			if err := configLoadParams(c, pc); err != nil {
				return nil, errors.Wrap(err, "[caddyesi] Failed to load params")
			}
		}
		if err := setupLogger(pc); err != nil {
			return nil, errors.Wrap(err, "[caddyesi] Failed to setup Logger")
		}

		if pc.MaxBodySize == 0 {
			pc.MaxBodySize = DefaultMaxBodySize
		}
		if pc.Timeout == 0 {
			pc.Timeout = DefaultTimeOut
		}
		if len(pc.OnError) == 0 {
			pc.OnError = []byte(DefaultOnError)
		}

		pcs = append(pcs, pc)
	}
	return pcs, nil
}

// mocked out for testing
var osStdErr io.Writer = os.Stderr
var osStdOut io.Writer = os.Stdout

func setupLogger(pc *PathConfig) error {
	pc.Log = log.BlackHole{}

	var lvl zap.Level = -5000
	switch pc.LogLevel {
	case "debug":
		lvl = zap.DebugLevel
	case "info":
		lvl = zap.InfoLevel
	case "fatal":
		lvl = zap.FatalLevel
	}

	if lvl == -5000 {
		// logging disabled
		return nil
	}

	var w io.Writer
	switch pc.LogFile {
	case "stderr":
		w = osStdErr
	case "stdout":
		w = osStdOut
	case "":
		// logging disabled
		return nil
	default:
		var err error
		w, err = os.OpenFile(pc.LogFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
		// maybe handle file close on server restart or shutdown
		if err != nil {
			return errors.NewFatalf("[caddyesi] Failed to open file %q with error: %s", pc.LogFile, err)
		}
	}

	pc.Log = zapw.Wrap{
		Level: lvl,
		Zap: zap.New(
			zap.NewJSONEncoder(zap.TimeFormatter(func(t time.Time) zap.Field {
				return zap.String("ts", t.Format(time.RFC3339Nano))
			})),
			zap.Output(zap.AddSync(w)),
			lvl,
		),
	}

	return nil
}

func configLoadParams(c *caddy.Controller, pc *PathConfig) error {
	switch key := c.Val(); key {

	case "timeout":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] timeout: %s", c.ArgErr())
		}
		d, err := time.ParseDuration(c.Val())
		if err != nil {
			return errors.NewNotValidf("[caddyesi] Invalid duration in timeout configuration: %q Error: %s", c.Val(), err)
		}
		pc.Timeout = d

	case "ttl":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] ttl: %s", c.ArgErr())
		}
		d, err := time.ParseDuration(c.Val())
		if err != nil {
			return errors.NewNotValidf("[caddyesi] Invalid duration in ttl configuration: %q Error: %s", c.Val(), err)
		}
		pc.TTL = d

	case "max_body_size":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] max_body_size: %s", c.ArgErr())
		}
		d, err := humanize.ParseBytes(c.Val())
		if err != nil {
			return errors.NewNotValidf("[caddyesi] Invalid max body size value configuration: %q Error: %s", c.Val(), err)
		}
		pc.MaxBodySize = d

	case "cache":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] cache: %s", c.ArgErr())
		}

		if err := esicache.MainRegistry.Register(pc.Scope, c.Val()); err != nil {
			return errors.Wrapf(err, "[caddyesi] esicache.MainRegistry.Register Key %q with URL: %q", key, c.Val())
		}

	case "page_id_source":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] page_id_source: %s", c.ArgErr())
		}
		pc.PageIDSource = helper.CommaListToSlice(c.Val())

	case "allowed_methods":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] allowed_methods: %s", c.ArgErr())
		}
		pc.AllowedMethods = helper.CommaListToSlice(strings.ToUpper(c.Val()))
	case "cmd_header_name":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] cmd_header_name: %s", c.ArgErr())
		}
		pc.CmdHeaderName = http.CanonicalHeaderKey(c.Val())
	case "on_error":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] allowed_methods: %s", c.ArgErr())
		}
		if err := pc.parseOnError(c.Val()); err != nil {
			return errors.Wrap(err, "[caddyesi] PathConfig.parseOnError")
		}
	case "log_file":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] log_file: %s", c.ArgErr())
		}
		pc.LogFile = c.Val()
	case "log_level":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] log_level: %s", c.ArgErr())
		}
		pc.LogLevel = strings.ToLower(c.Val())
	case "resources":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] resources: %s", c.ArgErr())
		}
		// c.Val() contains the file name or raw-content ;-)
		items, err := UnmarshalResourceItems(c.Val())
		if err != nil {
			return errors.Wrapf(err, "[caddyesi] Failed to unmarshal resource config %q", c.Val())
		}
		for _, item := range items {
			f, err := esitag.NewResourceHandler(esitag.NewResourceOptions(item.URL, item.Alias, item.Query))
			if err != nil {
				// may disclose passwords which are stored in the URL
				return errors.Wrapf(err, "[caddyesi] esikv Service init failed for URL %q in file %q", item.URL, c.Val())
			}
			esitag.RegisterResourceHandler(item.Alias, f)
		}
	default:
		c.NextArg()
		return errors.NewNotSupportedf("[caddyesi] Key %q with value %q not supported", key, c.Val())
	}

	return nil
}
<commit_msg>caddyesi: a backend package to register resources<commit_after>// Copyright 2016-2017, Cyrill @ Schumacher.fm and the CaddyESI Contributors
//
// 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 caddyesi

import (
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/SchumacherFM/caddyesi/esicache"
	"github.com/SchumacherFM/caddyesi/esitag"
	_ "github.com/SchumacherFM/caddyesi/esitag/backend" // Let them register depending on the build tag
	"github.com/SchumacherFM/caddyesi/helper"
	"github.com/corestoreio/errors"
	"github.com/corestoreio/log"
	"github.com/corestoreio/log/zapw"
	"github.com/dustin/go-humanize"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/uber-go/zap"
)

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

// PluginSetup used internally by Caddy to set up this middleware
func PluginSetup(c *caddy.Controller) error {
	pcs, err := configEsiParse(c)
	if err != nil {
		return errors.Wrap(err, "[caddyesi] Failed to parse configuration")
	}

	cfg := httpserver.GetConfig(c)

	mw := &Middleware{
		Root:        cfg.Root,
		FileSys:     http.Dir(cfg.Root),
		PathConfigs: pcs,
	}

	cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
		mw.Next = next
		return mw
	})

	c.OnShutdown(func() error {
		return errors.Wrap(esitag.CloseAllResourceHandler(), "[caddyesi] OnShutdown")
	})
	c.OnRestart(func() error {
		// really necessary? investigate later
		for _, pc := range pcs {
			pc.purgeESICache()
		}
		return errors.Wrap(esitag.CloseAllResourceHandler(), "[caddyesi] OnRestart")
	})

	return nil
}

func configEsiParse(c *caddy.Controller) (PathConfigs, error) {
	pcs := make(PathConfigs, 0, 2)

	for c.Next() {
		pc := NewPathConfig()

		// Get the path scope
		args := c.RemainingArgs()
		switch len(args) {
		case 0:
			pc.Scope = "/"
		case 1:
			pc.Scope = args[0]
		default:
			return nil, c.ArgErr()
		}

		// Load any other configuration parameters
		for c.NextBlock() {
			if err := configLoadParams(c, pc); err != nil {
				return nil, errors.Wrap(err, "[caddyesi] Failed to load params")
			}
		}
		if err := setupLogger(pc); err != nil {
			return nil, errors.Wrap(err, "[caddyesi] Failed to setup Logger")
		}

		if pc.MaxBodySize == 0 {
			pc.MaxBodySize = DefaultMaxBodySize
		}
		if pc.Timeout == 0 {
			pc.Timeout = DefaultTimeOut
		}
		if len(pc.OnError) == 0 {
			pc.OnError = []byte(DefaultOnError)
		}

		pcs = append(pcs, pc)
	}
	return pcs, nil
}

// mocked out for testing
var osStdErr io.Writer = os.Stderr
var osStdOut io.Writer = os.Stdout

func setupLogger(pc *PathConfig) error {
	pc.Log = log.BlackHole{}

	var lvl zap.Level = -5000
	switch pc.LogLevel {
	case "debug":
		lvl = zap.DebugLevel
	case "info":
		lvl = zap.InfoLevel
	case "fatal":
		lvl = zap.FatalLevel
	}

	if lvl == -5000 {
		// logging disabled
		return nil
	}

	var w io.Writer
	switch pc.LogFile {
	case "stderr":
		w = osStdErr
	case "stdout":
		w = osStdOut
	case "":
		// logging disabled
		return nil
	default:
		var err error
		w, err = os.OpenFile(pc.LogFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
		// maybe handle file close on server restart or shutdown
		if err != nil {
			return errors.NewFatalf("[caddyesi] Failed to open file %q with error: %s", pc.LogFile, err)
		}
	}

	pc.Log = zapw.Wrap{
		Level: lvl,
		Zap: zap.New(
			zap.NewJSONEncoder(zap.TimeFormatter(func(t time.Time) zap.Field {
				return zap.String("ts", t.Format(time.RFC3339Nano))
			})),
			zap.Output(zap.AddSync(w)),
			lvl,
		),
	}

	return nil
}

func configLoadParams(c *caddy.Controller, pc *PathConfig) error {
	switch key := c.Val(); key {

	case "timeout":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] timeout: %s", c.ArgErr())
		}
		d, err := time.ParseDuration(c.Val())
		if err != nil {
			return errors.NewNotValidf("[caddyesi] Invalid duration in timeout configuration: %q Error: %s", c.Val(), err)
		}
		pc.Timeout = d

	case "ttl":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] ttl: %s", c.ArgErr())
		}
		d, err := time.ParseDuration(c.Val())
		if err != nil {
			return errors.NewNotValidf("[caddyesi] Invalid duration in ttl configuration: %q Error: %s", c.Val(), err)
		}
		pc.TTL = d

	case "max_body_size":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] max_body_size: %s", c.ArgErr())
		}
		d, err := humanize.ParseBytes(c.Val())
		if err != nil {
			return errors.NewNotValidf("[caddyesi] Invalid max body size value configuration: %q Error: %s", c.Val(), err)
		}
		pc.MaxBodySize = d

	case "cache":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] cache: %s", c.ArgErr())
		}

		if err := esicache.MainRegistry.Register(pc.Scope, c.Val()); err != nil {
			return errors.Wrapf(err, "[caddyesi] esicache.MainRegistry.Register Key %q with URL: %q", key, c.Val())
		}

	case "page_id_source":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] page_id_source: %s", c.ArgErr())
		}
		pc.PageIDSource = helper.CommaListToSlice(c.Val())

	case "allowed_methods":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] allowed_methods: %s", c.ArgErr())
		}
		pc.AllowedMethods = helper.CommaListToSlice(strings.ToUpper(c.Val()))
	case "cmd_header_name":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] cmd_header_name: %s", c.ArgErr())
		}
		pc.CmdHeaderName = http.CanonicalHeaderKey(c.Val())
	case "on_error":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] allowed_methods: %s", c.ArgErr())
		}
		if err := pc.parseOnError(c.Val()); err != nil {
			return errors.Wrap(err, "[caddyesi] PathConfig.parseOnError")
		}
	case "log_file":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] log_file: %s", c.ArgErr())
		}
		pc.LogFile = c.Val()
	case "log_level":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] log_level: %s", c.ArgErr())
		}
		pc.LogLevel = strings.ToLower(c.Val())
	case "resources":
		if !c.NextArg() {
			return errors.NewNotValidf("[caddyesi] resources: %s", c.ArgErr())
		}
		// c.Val() contains the file name or raw-content ;-)
		items, err := UnmarshalResourceItems(c.Val())
		if err != nil {
			return errors.Wrapf(err, "[caddyesi] Failed to unmarshal resource config %q", c.Val())
		}
		for _, item := range items {
			f, err := esitag.NewResourceHandler(esitag.NewResourceOptions(item.URL, item.Alias, item.Query))
			if err != nil {
				// may disclose passwords which are stored in the URL
				return errors.Wrapf(err, "[caddyesi] esikv Service init failed for URL %q in file %q", item.URL, c.Val())
			}
			esitag.RegisterResourceHandler(item.Alias, f)
		}
	default:
		c.NextArg()
		return errors.NewNotSupportedf("[caddyesi] Key %q with value %q not supported", key, c.Val())
	}

	return nil
}
<|endoftext|>