<commit_before>package gorm

import (
	"fmt"
)

type callback struct {
	creates    []*func(scope *Scope)
	updates    []*func(scope *Scope)
	deletes    []*func(scope *Scope)
	queries    []*func(scope *Scope)
	processors []*callbackProcessor
}

type callbackProcessor struct {
	name      string
	before    string
	after     string
	replace   bool
	remove    bool
	typ       string
	processor *func(scope *Scope)
	callback  *callback
}

func (c *callback) addProcessor(typ string) *callbackProcessor {
	cp := &callbackProcessor{typ: typ, callback: c}
	c.processors = append(c.processors, cp)
	return cp
}

func (c *callback) clone() *callback {
	return &callback{processors: c.processors}
}

func (c *callback) Create() *callbackProcessor {
	return c.addProcessor("create")
}

func (c *callback) Update() *callbackProcessor {
	return c.addProcessor("update")
}

func (c *callback) Delete() *callbackProcessor {
	return c.addProcessor("delete")
}

func (c *callback) Query() *callbackProcessor {
	return c.addProcessor("query")
}

func (cp *callbackProcessor) Before(name string) *callbackProcessor {
	cp.before = name
	return cp
}

func (cp *callbackProcessor) After(name string) *callbackProcessor {
	cp.after = name
	return cp
}

func (cp *callbackProcessor) Register(name string, fc func(scope *Scope)) {
	cp.name = name
	cp.processor = &fc
	cp.callback.sort()
}

func (cp *callbackProcessor) Remove(name string) {
	fmt.Printf("[info] removing callback `%v` from %v\n", name, fileWithLineNum())
	cp.name = name
	cp.remove = true
	cp.callback.sort()
}

func (cp *callbackProcessor) Replace(name string, fc func(scope *Scope)) {
	fmt.Printf("[info] replacing callback `%v` from %v\n", name, fileWithLineNum())
	cp.name = name
	cp.processor = &fc
	cp.replace = true
	cp.callback.sort()
}

func getRIndex(strs []string, str string) int {
	for i := len(strs) - 1; i >= 0; i-- {
		if strs[i] == str {
			return i
		}
	}
	return -1
}

func sortProcessors(cps []*callbackProcessor) []*func(scope *Scope) {
	var sortCallbackProcessor func(c *callbackProcessor)
	var names, sortedNames = []string{}, []string{}

	for _, cp := range cps {
		if index := getRIndex(names, cp.name); index > -1 {
			if !cp.replace && !cp.remove {
				fmt.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
			}
		}
		names = append(names, cp.name)
	}

	sortCallbackProcessor = func(c *callbackProcessor) {
		if getRIndex(sortedNames, c.name) > -1 {
			return
		}

		if len(c.before) > 0 {
			if index := getRIndex(sortedNames, c.before); index > -1 {
				sortedNames = append(sortedNames[:index], append([]string{c.name}, sortedNames[index:]...)...)
			} else if index := getRIndex(names, c.before); index > -1 {
				sortedNames = append(sortedNames, c.name)
				sortCallbackProcessor(cps[index])
			} else {
				sortedNames = append(sortedNames, c.name)
			}
		}

		if len(c.after) > 0 {
			if index := getRIndex(sortedNames, c.after); index > -1 {
				sortedNames = append(sortedNames[:index+1], append([]string{c.name}, sortedNames[index+1:]...)...)
			} else if index := getRIndex(names, c.after); index > -1 {
				cp := cps[index]
				if len(cp.before) == 0 {
					cp.before = c.name
				}
				sortCallbackProcessor(cp)
			} else {
				sortedNames = append(sortedNames, c.name)
			}
		}

		if getRIndex(sortedNames, c.name) == -1 {
			sortedNames = append(sortedNames, c.name)
		}
	}

	for _, cp := range cps {
		sortCallbackProcessor(cp)
	}

	var funcs = []*func(scope *Scope){}
	var sortedFuncs = []*func(scope *Scope){}
	for _, name := range sortedNames {
		index := getRIndex(names, name)
		if !cps[index].remove {
			sortedFuncs = append(sortedFuncs, cps[index].processor)
		}
	}

	for _, cp := range cps {
		if sindex := getRIndex(sortedNames, cp.name); sindex == -1 {
			if !cp.remove {
				funcs = append(funcs, cp.processor)
			}
		}
	}

	return append(sortedFuncs, funcs...)
}

func (c *callback) sort() {
	creates, updates, deletes, queries := []*callbackProcessor{}, []*callbackProcessor{}, []*callbackProcessor{}, []*callbackProcessor{}

	for _, processor := range c.processors {
		switch processor.typ {
		case "create":
			creates = append(creates, processor)
		case "update":
			updates = append(updates, processor)
		case "delete":
			deletes = append(deletes, processor)
		case "query":
			queries = append(queries, processor)
		}
	}

	c.creates = sortProcessors(creates)
	c.updates = sortProcessors(updates)
	c.deletes = sortProcessors(deletes)
	c.queries = sortProcessors(queries)
}

var DefaultCallback = &callback{processors: []*callbackProcessor{}}
<commit_msg>Fix clone callback<commit_after>package gorm

import (
	"fmt"
)

type callback struct {
	creates    []*func(scope *Scope)
	updates    []*func(scope *Scope)
	deletes    []*func(scope *Scope)
	queries    []*func(scope *Scope)
	processors []*callbackProcessor
}

type callbackProcessor struct {
	name      string
	before    string
	after     string
	replace   bool
	remove    bool
	typ       string
	processor *func(scope *Scope)
	callback  *callback
}

func (c *callback) addProcessor(typ string) *callbackProcessor {
	cp := &callbackProcessor{typ: typ, callback: c}
	c.processors = append(c.processors, cp)
	return cp
}

func (c *callback) clone() *callback {
	return &callback{
		creates:    c.creates,
		updates:    c.updates,
		deletes:    c.deletes,
		queries:    c.queries,
		processors: c.processors,
	}
}

func (c *callback) Create() *callbackProcessor {
	return c.addProcessor("create")
}

func (c *callback) Update() *callbackProcessor {
	return c.addProcessor("update")
}

func (c *callback) Delete() *callbackProcessor {
	return c.addProcessor("delete")
}

func (c *callback) Query() *callbackProcessor {
	return c.addProcessor("query")
}

func (cp *callbackProcessor) Before(name string) *callbackProcessor {
	cp.before = name
	return cp
}

func (cp *callbackProcessor) After(name string) *callbackProcessor {
	cp.after = name
	return cp
}

func (cp *callbackProcessor) Register(name string, fc func(scope *Scope)) {
	cp.name = name
	cp.processor = &fc
	cp.callback.sort()
}

func (cp *callbackProcessor) Remove(name string) {
	fmt.Printf("[info] removing callback `%v` from %v\n", name, fileWithLineNum())
	cp.name = name
	cp.remove = true
	cp.callback.sort()
}

func (cp *callbackProcessor) Replace(name string, fc func(scope *Scope)) {
	fmt.Printf("[info] replacing callback `%v` from %v\n", name, fileWithLineNum())
	cp.name = name
	cp.processor = &fc
	cp.replace = true
	cp.callback.sort()
}

func getRIndex(strs []string, str string) int {
	for i := len(strs) - 1; i >= 0; i-- {
		if strs[i] == str {
			return i
		}
	}
	return -1
}

func sortProcessors(cps []*callbackProcessor) []*func(scope *Scope) {
	var sortCallbackProcessor func(c *callbackProcessor)
	var names, sortedNames = []string{}, []string{}

	for _, cp := range cps {
		if index := getRIndex(names, cp.name); index > -1 {
			if !cp.replace && !cp.remove {
				fmt.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
			}
		}
		names = append(names, cp.name)
	}

	sortCallbackProcessor = func(c *callbackProcessor) {
		if getRIndex(sortedNames, c.name) > -1 {
			return
		}

		if len(c.before) > 0 {
			if index := getRIndex(sortedNames, c.before); index > -1 {
				sortedNames = append(sortedNames[:index], append([]string{c.name}, sortedNames[index:]...)...)
			} else if index := getRIndex(names, c.before); index > -1 {
				sortedNames = append(sortedNames, c.name)
				sortCallbackProcessor(cps[index])
			} else {
				sortedNames = append(sortedNames, c.name)
			}
		}

		if len(c.after) > 0 {
			if index := getRIndex(sortedNames, c.after); index > -1 {
				sortedNames = append(sortedNames[:index+1], append([]string{c.name}, sortedNames[index+1:]...)...)
			} else if index := getRIndex(names, c.after); index > -1 {
				cp := cps[index]
				if len(cp.before) == 0 {
					cp.before = c.name
				}
				sortCallbackProcessor(cp)
			} else {
				sortedNames = append(sortedNames, c.name)
			}
		}

		if getRIndex(sortedNames, c.name) == -1 {
			sortedNames = append(sortedNames, c.name)
		}
	}

	for _, cp := range cps {
		sortCallbackProcessor(cp)
	}

	var funcs = []*func(scope *Scope){}
	var sortedFuncs = []*func(scope *Scope){}
	for _, name := range sortedNames {
		index := getRIndex(names, name)
		if !cps[index].remove {
			sortedFuncs = append(sortedFuncs, cps[index].processor)
		}
	}

	for _, cp := range cps {
		if sindex := getRIndex(sortedNames, cp.name); sindex == -1 {
			if !cp.remove {
				funcs = append(funcs, cp.processor)
			}
		}
	}

	return append(sortedFuncs, funcs...)
}

func (c *callback) sort() {
	creates, updates, deletes, queries := []*callbackProcessor{}, []*callbackProcessor{}, []*callbackProcessor{}, []*callbackProcessor{}

	for _, processor := range c.processors {
		switch processor.typ {
		case "create":
			creates = append(creates, processor)
		case "update":
			updates = append(updates, processor)
		case "delete":
			deletes = append(deletes, processor)
		case "query":
			queries = append(queries, processor)
		}
	}

	c.creates = sortProcessors(creates)
	c.updates = sortProcessors(updates)
	c.deletes = sortProcessors(deletes)
	c.queries = sortProcessors(queries)
}

var DefaultCallback = &callback{processors: []*callbackProcessor{}}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess Authors.

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

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

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

package schemamanager

import (
	"strings"
	"testing"
	"time"

	"context"

	"vitess.io/vitess/go/vt/logutil"
	"vitess.io/vitess/go/vt/mysqlctl/tmutils"
	tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata"
	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
	"vitess.io/vitess/go/vt/schema"
	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/topo/memorytopo"
	"vitess.io/vitess/go/vt/wrangler"

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

var (
	testWaitReplicasTimeout = 10 * time.Second
)

func TestTabletExecutorOpen(t *testing.T) {
	executor := newFakeExecutor(t)
	ctx := context.Background()

	if err := executor.Open(ctx, "test_keyspace"); err != nil {
		t.Fatalf("executor.Open should succeed")
	}

	defer executor.Close()

	if err := executor.Open(ctx, "test_keyspace"); err != nil {
		t.Fatalf("open an opened executor should also succeed")
	}
}

func TestTabletExecutorOpenWithEmptyMasterAlias(t *testing.T) {
	ctx := context.Background()
	ts := memorytopo.NewServer("test_cell")
	wr := wrangler.New(logutil.NewConsoleLogger(), ts, newFakeTabletManagerClient())
	tablet := &topodatapb.Tablet{
		Alias: &topodatapb.TabletAlias{
			Cell: "test_cell",
			Uid:  1,
		},
		Keyspace: "test_keyspace",
		Shard:    "0",
		Type:     topodatapb.TabletType_REPLICA,
	}
	// This will create the Keyspace, Shard and Tablet record.
	// Since this is a replica tablet, the Shard will have no master.
	if err := wr.InitTablet(ctx, tablet, false /*allowMasterOverride*/, true /*createShardAndKeyspace*/, false /*allowUpdate*/); err != nil {
		t.Fatalf("InitTablet failed: %v", err)
	}
	executor := NewTabletExecutor("TestTabletExecutorOpenWithEmptyMasterAlias", wr, testWaitReplicasTimeout)
	if err := executor.Open(ctx, "test_keyspace"); err == nil || !strings.Contains(err.Error(), "does not have a master") {
		t.Fatalf("executor.Open() = '%v', want error", err)
	}
	executor.Close()
}

func TestTabletExecutorValidate(t *testing.T) {
	fakeTmc := newFakeTabletManagerClient()

	fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{
		DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */",
		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
			{
				Name:   "test_table",
				Schema: "table schema",
				Type:   tmutils.TableBaseTable,
			},
			{
				Name:     "test_table_03",
				Schema:   "table schema",
				Type:     tmutils.TableBaseTable,
				RowCount: 200000,
			},
			{
				Name:     "test_table_04",
				Schema:   "table schema",
				Type:     tmutils.TableBaseTable,
				RowCount: 3000000,
			},
		},
	})

	wr := wrangler.New(logutil.NewConsoleLogger(), newFakeTopo(t), fakeTmc)
	executor := NewTabletExecutor("TestTabletExecutorValidate", wr, testWaitReplicasTimeout)
	ctx := context.Background()

	sqls := []string{
		"ALTER TABLE test_table ADD COLUMN new_id bigint(20)",
		"CREATE TABLE test_table_02 (pk int)",
		"ALTER DATABASE db_name DEFAULT CHARACTER SET = utf8mb4",
		"ALTER SCHEMA db_name CHARACTER SET = utf8mb4",
	}

	if err := executor.Validate(ctx, sqls); err == nil {
		t.Fatalf("validate should fail because executor is closed")
	}

	executor.Open(ctx, "test_keyspace")
	defer executor.Close()

	// schema changes with DMLs should fail
	if err := executor.Validate(ctx, []string{
		"INSERT INTO test_table VALUES(1)"}); err == nil {
		t.Fatalf("schema changes are for DDLs")
	}

	// validates valid ddls
	if err := executor.Validate(ctx, sqls); err != nil {
		t.Fatalf("executor.Validate should succeed, but got error: %v", err)
	}

	// alter a table with more than 100,000 rows
	if err := executor.Validate(ctx, []string{
		"ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)",
	}); err == nil {
		t.Fatalf("executor.Validate should fail, alter a table more than 100,000 rows")
	}

	if err := executor.Validate(ctx, []string{
		"TRUNCATE TABLE test_table_04",
	}); err != nil {
		t.Fatalf("executor.Validate should succeed, drop a table with more than 2,000,000 rows is allowed")
	}

	if err := executor.Validate(ctx, []string{
		"DROP TABLE test_table_04",
	}); err != nil {
		t.Fatalf("executor.Validate should succeed, drop a table with more than 2,000,000 rows is allowed")
	}

	executor.AllowBigSchemaChange()
	// alter a table with more than 100,000 rows
	if err := executor.Validate(ctx, []string{
		"ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)",
	}); err != nil {
		t.Fatalf("executor.Validate should succeed, big schema change is disabled")
	}

	executor.DisallowBigSchemaChange()
	if err := executor.Validate(ctx, []string{
		"ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)",
	}); err == nil {
		t.Fatalf("executor.Validate should fail, alter a table more than 100,000 rows")
	}
}

func TestTabletExecutorDML(t *testing.T) {
	fakeTmc := newFakeTabletManagerClient()

	fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{
		DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */",
		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
			{
				Name:   "test_table",
				Schema: "table schema",
				Type:   tmutils.TableBaseTable,
			},
			{
				Name:     "test_table_03",
				Schema:   "table schema",
				Type:     tmutils.TableBaseTable,
				RowCount: 200000,
			},
			{
				Name:     "test_table_04",
				Schema:   "table schema",
				Type:     tmutils.TableBaseTable,
				RowCount: 3000000,
			},
		},
	})

	wr := wrangler.New(logutil.NewConsoleLogger(), newFakeTopo(t), fakeTmc)
	executor := NewTabletExecutor("TestTabletExecutorDML", wr, testWaitReplicasTimeout)
	ctx := context.Background()

	executor.Open(ctx, "unsharded_keyspace")
	defer executor.Close()

	// schema changes with DMLs should fail
	if err := executor.Validate(ctx, []string{
		"INSERT INTO test_table VALUES(1)"}); err != nil {
		t.Fatalf("executor.Validate should succeed, for DML to unsharded keyspace")
	}
}

func TestTabletExecutorExecute(t *testing.T) {
	executor := newFakeExecutor(t)
	ctx := context.Background()

	sqls := []string{"DROP TABLE unknown_table"}

	result := executor.Execute(ctx, sqls)
	if result.ExecutorErr == "" {
		t.Fatalf("execute should fail, call execute.Open first")
	}
}

func TestIsOnlineSchemaDDL(t *testing.T) {
	tt := []struct {
		query       string
		ddlStrategy string
		isOnlineDDL bool
		strategy    schema.DDLStrategy
		options     string
	}{
		{
			query:       "CREATE TABLE t(id int)",
			isOnlineDDL: false,
		},
		{
			query:       "CREATE TABLE t(id int)",
			ddlStrategy: "gh-ost",
			isOnlineDDL: true,
			strategy:    schema.DDLStrategyGhost,
		},
		{
			query:       "ALTER TABLE t ADD COLUMN i INT",
			ddlStrategy: "",
			isOnlineDDL: false,
		},
		{
			query:       "ALTER TABLE t ADD COLUMN i INT",
			ddlStrategy: "gh-ost",
			isOnlineDDL: true,
			strategy:    schema.DDLStrategyGhost,
		},
		{
			query:       "ALTER TABLE t ADD COLUMN i INT",
			ddlStrategy: "gh-ost --max-load=Threads_running=100",
			isOnlineDDL: true,
			strategy:    schema.DDLStrategyGhost,
			options:     "--max-load=Threads_running=100",
		},
		{
			query:       "TRUNCATE TABLE t",
			ddlStrategy: "gh-ost",
			isOnlineDDL: false,
		},
		{
			query:       "RENAME TABLE t to t2",
			ddlStrategy: "gh-ost",
			isOnlineDDL: false,
		},
	}

	for _, ts := range tt {
		e := &TabletExecutor{}
		err := e.SetDDLStrategy(ts.ddlStrategy)
		assert.NoError(t, err)

		stmt, err := sqlparser.Parse(ts.query)
		assert.NoError(t, err)

		ddlStmt, ok := stmt.(sqlparser.DDLStatement)
		assert.True(t, ok)

		isOnlineDDL, strategy, options := e.isOnlineSchemaDDL(ddlStmt)
		assert.Equal(t, ts.isOnlineDDL, isOnlineDDL)
		if isOnlineDDL {
			assert.Equal(t, ts.strategy, strategy)
			assert.Equal(t, ts.options, options)
		}
	}
}
<commit_msg>testing for DDLStrategyOnline<commit_after>/*
Copyright 2019 The Vitess Authors.

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

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

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

package schemamanager

import (
	"strings"
	"testing"
	"time"

	"context"

	"vitess.io/vitess/go/vt/logutil"
	"vitess.io/vitess/go/vt/mysqlctl/tmutils"
	tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata"
	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
	"vitess.io/vitess/go/vt/schema"
	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/topo/memorytopo"
	"vitess.io/vitess/go/vt/wrangler"

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

var (
	testWaitReplicasTimeout = 10 * time.Second
)

func TestTabletExecutorOpen(t *testing.T) {
	executor := newFakeExecutor(t)
	ctx := context.Background()

	if err := executor.Open(ctx, "test_keyspace"); err != nil {
		t.Fatalf("executor.Open should succeed")
	}

	defer executor.Close()

	if err := executor.Open(ctx, "test_keyspace"); err != nil {
		t.Fatalf("open an opened executor should also succeed")
	}
}

func TestTabletExecutorOpenWithEmptyMasterAlias(t *testing.T) {
	ctx := context.Background()
	ts := memorytopo.NewServer("test_cell")
	wr := wrangler.New(logutil.NewConsoleLogger(), ts, newFakeTabletManagerClient())
	tablet := &topodatapb.Tablet{
		Alias: &topodatapb.TabletAlias{
			Cell: "test_cell",
			Uid:  1,
		},
		Keyspace: "test_keyspace",
		Shard:    "0",
		Type:     topodatapb.TabletType_REPLICA,
	}
	// This will create the Keyspace, Shard and Tablet record.
	// Since this is a replica tablet, the Shard will have no master.
	if err := wr.InitTablet(ctx, tablet, false /*allowMasterOverride*/, true /*createShardAndKeyspace*/, false /*allowUpdate*/); err != nil {
		t.Fatalf("InitTablet failed: %v", err)
	}
	executor := NewTabletExecutor("TestTabletExecutorOpenWithEmptyMasterAlias", wr, testWaitReplicasTimeout)
	if err := executor.Open(ctx, "test_keyspace"); err == nil || !strings.Contains(err.Error(), "does not have a master") {
		t.Fatalf("executor.Open() = '%v', want error", err)
	}
	executor.Close()
}

func TestTabletExecutorValidate(t *testing.T) {
	fakeTmc := newFakeTabletManagerClient()

	fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{
		DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */",
		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
			{
				Name:   "test_table",
				Schema: "table schema",
				Type:   tmutils.TableBaseTable,
			},
			{
				Name:     "test_table_03",
				Schema:   "table schema",
				Type:     tmutils.TableBaseTable,
				RowCount: 200000,
			},
			{
				Name:     "test_table_04",
				Schema:   "table schema",
				Type:     tmutils.TableBaseTable,
				RowCount: 3000000,
			},
		},
	})

	wr := wrangler.New(logutil.NewConsoleLogger(), newFakeTopo(t), fakeTmc)
	executor := NewTabletExecutor("TestTabletExecutorValidate", wr, testWaitReplicasTimeout)
	ctx := context.Background()

	sqls := []string{
		"ALTER TABLE test_table ADD COLUMN new_id bigint(20)",
		"CREATE TABLE test_table_02 (pk int)",
		"ALTER DATABASE db_name DEFAULT CHARACTER SET = utf8mb4",
		"ALTER SCHEMA db_name CHARACTER SET = utf8mb4",
	}

	if err := executor.Validate(ctx, sqls); err == nil {
		t.Fatalf("validate should fail because executor is closed")
	}

	executor.Open(ctx, "test_keyspace")
	defer executor.Close()

	// schema changes with DMLs should fail
	if err := executor.Validate(ctx, []string{
		"INSERT INTO test_table VALUES(1)"}); err == nil {
		t.Fatalf("schema changes are for DDLs")
	}

	// validates valid ddls
	if err := executor.Validate(ctx, sqls); err != nil {
		t.Fatalf("executor.Validate should succeed, but got error: %v", err)
	}

	// alter a table with more than 100,000 rows
	if err := executor.Validate(ctx, []string{
		"ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)",
	}); err == nil {
		t.Fatalf("executor.Validate should fail, alter a table more than 100,000 rows")
	}

	if err := executor.Validate(ctx, []string{
		"TRUNCATE TABLE test_table_04",
	}); err != nil {
		t.Fatalf("executor.Validate should succeed, drop a table with more than 2,000,000 rows is allowed")
	}

	if err := executor.Validate(ctx, []string{
		"DROP TABLE test_table_04",
	}); err != nil {
		t.Fatalf("executor.Validate should succeed, drop a table with more than 2,000,000 rows is allowed")
	}

	executor.AllowBigSchemaChange()
	// alter a table with more than 100,000 rows
	if err := executor.Validate(ctx, []string{
		"ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)",
	}); err != nil {
		t.Fatalf("executor.Validate should succeed, big schema change is disabled")
	}

	executor.DisallowBigSchemaChange()
	if err := executor.Validate(ctx, []string{
		"ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)",
	}); err == nil {
		t.Fatalf("executor.Validate should fail, alter a table more than 100,000 rows")
	}
}

func TestTabletExecutorDML(t *testing.T) {
	fakeTmc := newFakeTabletManagerClient()

	fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{
		DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */",
		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
			{
				Name:   "test_table",
				Schema: "table schema",
				Type:   tmutils.TableBaseTable,
			},
			{
				Name:     "test_table_03",
				Schema:   "table schema",
				Type:     tmutils.TableBaseTable,
				RowCount: 200000,
			},
			{
				Name:     "test_table_04",
				Schema:   "table schema",
				Type:     tmutils.TableBaseTable,
				RowCount: 3000000,
			},
		},
	})

	wr := wrangler.New(logutil.NewConsoleLogger(), newFakeTopo(t), fakeTmc)
	executor := NewTabletExecutor("TestTabletExecutorDML", wr, testWaitReplicasTimeout)
	ctx := context.Background()

	executor.Open(ctx, "unsharded_keyspace")
	defer executor.Close()

	// schema changes with DMLs should fail
	if err := executor.Validate(ctx, []string{
		"INSERT INTO test_table VALUES(1)"}); err != nil {
		t.Fatalf("executor.Validate should succeed, for DML to unsharded keyspace")
	}
}

func TestTabletExecutorExecute(t *testing.T) {
	executor := newFakeExecutor(t)
	ctx := context.Background()

	sqls := []string{"DROP TABLE unknown_table"}

	result := executor.Execute(ctx, sqls)
	if result.ExecutorErr == "" {
		t.Fatalf("execute should fail, call execute.Open first")
	}
}

func TestIsOnlineSchemaDDL(t *testing.T) {
	tt := []struct {
		query       string
		ddlStrategy string
		isOnlineDDL bool
		strategy    schema.DDLStrategy
		options     string
	}{
		{
			query:       "CREATE TABLE t(id int)",
			isOnlineDDL: false,
		},
		{
			query:       "CREATE TABLE t(id int)",
			ddlStrategy: "gh-ost",
			isOnlineDDL: true,
			strategy:    schema.DDLStrategyGhost,
		},
		{
			query:       "ALTER TABLE t ADD COLUMN i INT",
			ddlStrategy: "online",
			isOnlineDDL: true,
			strategy:    schema.DDLStrategyOnline,
		},
		{
			query:       "ALTER TABLE t ADD COLUMN i INT",
			ddlStrategy: "",
			isOnlineDDL: false,
		},
		{
			query:       "ALTER TABLE t ADD COLUMN i INT",
			ddlStrategy: "gh-ost",
			isOnlineDDL: true,
			strategy:    schema.DDLStrategyGhost,
		},
		{
			query:       "ALTER TABLE t ADD COLUMN i INT",
			ddlStrategy: "gh-ost --max-load=Threads_running=100",
			isOnlineDDL: true,
			strategy:    schema.DDLStrategyGhost,
			options:     "--max-load=Threads_running=100",
		},
		{
			query:       "TRUNCATE TABLE t",
			ddlStrategy: "online",
			isOnlineDDL: false,
		},
		{
			query:       "TRUNCATE TABLE t",
			ddlStrategy: "gh-ost",
			isOnlineDDL: false,
		},
		{
			query:       "RENAME TABLE t to t2",
			ddlStrategy: "gh-ost",
			isOnlineDDL: false,
		},
	}

	for _, ts := range tt {
		e := &TabletExecutor{}
		err := e.SetDDLStrategy(ts.ddlStrategy)
		assert.NoError(t, err)

		stmt, err := sqlparser.Parse(ts.query)
		assert.NoError(t, err)

		ddlStmt, ok := stmt.(sqlparser.DDLStatement)
		assert.True(t, ok)

		isOnlineDDL, strategy, options := e.isOnlineSchemaDDL(ddlStmt)
		assert.Equal(t, ts.isOnlineDDL, isOnlineDDL)
		if isOnlineDDL {
			assert.Equal(t, ts.strategy, strategy)
			assert.Equal(t, ts.options, options)
		}
	}
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

func TestAccRedisInstance_update(t *testing.T) {
	t.Parallel()

	name := acctest.RandomWithPrefix("tf-test")

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckRedisInstanceDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccRedisInstance_update(name),
			},
			{
				ResourceName:      "google_redis_instance.test",
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccRedisInstance_update2(name),
			},
			{
				ResourceName:      "google_redis_instance.test",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccRedisInstance_regionFromLocation(t *testing.T) {
	t.Parallel()

	name := acctest.RandomWithPrefix("tf-test")

	// Pick a zone that isn't in the provider-specified region so we know we
	// didn't fall back to that one.
	region := "us-west1"
	zone := "us-west1-b"
	if getTestRegionFromEnv() == "us-west1" {
		region = "us-central1"
		zone = "us-central1-a"
	}

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckRedisInstanceDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccRedisInstance_regionFromLocation(name, zone),
				Check:  resource.TestCheckResourceAttr("google_redis_instance.test", "region", region),
			},
			{
				ResourceName:      "google_redis_instance.test",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccRedisInstance_update(name string) string {
	return fmt.Sprintf(`
resource "google_redis_instance" "test" {
  name           = "%s"
  display_name   = "pre-update"
  memory_size_gb = 1
  region         = "us-central1"

  labels = {
    my_key    = "my_val"
    other_key = "other_val"
  }

  redis_configs = {
    maxmemory-policy       = "allkeys-lru"
    notify-keyspace-events = "KEA"
  }
}
`, name)
}

func testAccRedisInstance_update2(name string) string {
	return fmt.Sprintf(`
resource "google_redis_instance" "test" {
  name           = "%s"
  display_name   = "post-update"
  memory_size_gb = 1

  labels = {
    my_key    = "my_val"
    other_key = "new_val"
  }

  redis_configs = {
    maxmemory-policy       = "noeviction"
    notify-keyspace-events = ""
  }
}
`, name)
}

func testAccRedisInstance_regionFromLocation(name, zone string) string {
	return fmt.Sprintf(`
resource "google_redis_instance" "test" {
  name           = "%s"
  memory_size_gb = 1
  location_id    = "%s"
}
`, name, zone)
}
<commit_msg>Use non-exhausted zone (#1567)<commit_after>package google

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

func TestAccRedisInstance_update(t *testing.T) {
	t.Parallel()

	name := acctest.RandomWithPrefix("tf-test")

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckRedisInstanceDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccRedisInstance_update(name),
			},
			{
				ResourceName:      "google_redis_instance.test",
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccRedisInstance_update2(name),
			},
			{
				ResourceName:      "google_redis_instance.test",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccRedisInstance_regionFromLocation(t *testing.T) {
	t.Parallel()

	name := acctest.RandomWithPrefix("tf-test")

	// Pick a zone that isn't in the provider-specified region so we know we
	// didn't fall back to that one.
	region := "us-west1"
	zone := "us-west1-a"
	if getTestRegionFromEnv() == "us-west1" {
		region = "us-central1"
		zone = "us-central1-a"
	}

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckRedisInstanceDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccRedisInstance_regionFromLocation(name, zone),
				Check:  resource.TestCheckResourceAttr("google_redis_instance.test", "region", region),
			},
			{
				ResourceName:      "google_redis_instance.test",
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccRedisInstance_update(name string) string {
	return fmt.Sprintf(`
resource "google_redis_instance" "test" {
  name           = "%s"
  display_name   = "pre-update"
  memory_size_gb = 1
  region         = "us-central1"

  labels = {
    my_key    = "my_val"
    other_key = "other_val"
  }

  redis_configs = {
    maxmemory-policy       = "allkeys-lru"
    notify-keyspace-events = "KEA"
  }
}
`, name)
}

func testAccRedisInstance_update2(name string) string {
	return fmt.Sprintf(`
resource "google_redis_instance" "test" {
  name           = "%s"
  display_name   = "post-update"
  memory_size_gb = 1

  labels = {
    my_key    = "my_val"
    other_key = "new_val"
  }

  redis_configs = {
    maxmemory-policy       = "noeviction"
    notify-keyspace-events = ""
  }
}
`, name)
}

func testAccRedisInstance_regionFromLocation(name, zone string) string {
	return fmt.Sprintf(`
resource "google_redis_instance" "test" {
  name           = "%s"
  memory_size_gb = 1
  location_id    = "%s"
}
`, name, zone)
}
<|endoftext|>


<commit_before>package engine

import (
	"context"
	"fmt"
	"sync"
	"time"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagerctx"

	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/atc/creds"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/exec"
	"github.com/concourse/concourse/atc/metric"
)

//go:generate counterfeiter . Engine

type Engine interface {
	NewBuild(db.Build) Runnable
	NewCheck(db.Check) Runnable
	ReleaseAll(lager.Logger)
}

//go:generate counterfeiter . Runnable

type Runnable interface {
	Run(logger lager.Logger)
}

//go:generate counterfeiter . StepBuilder

type StepBuilder interface {
	BuildStep(lager.Logger, db.Build) (exec.Step, error)
	CheckStep(lager.Logger, db.Check) (exec.Step, error)

	BuildStepErrored(lager.Logger, db.Build, error)
}

func NewEngine(builder StepBuilder) Engine {
	return &engine{
		builder:       builder,
		release:       make(chan bool),
		trackedStates: new(sync.Map),
		waitGroup:     new(sync.WaitGroup),
	}
}

type engine struct {
	builder       StepBuilder
	release       chan bool
	trackedStates *sync.Map
	waitGroup     *sync.WaitGroup
}

func (engine *engine) ReleaseAll(logger lager.Logger) {
	logger.Info("calling-release-on-builds")

	close(engine.release)

	logger.Info("waiting-on-builds")

	engine.waitGroup.Wait()

	logger.Info("finished-waiting-on-builds")
}

func (engine *engine) NewBuild(build db.Build) Runnable {

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

	return NewBuild(
		ctx,
		cancel,
		build,
		engine.builder,
		engine.release,
		engine.trackedStates,
		engine.waitGroup,
	)
}

func (engine *engine) NewCheck(check db.Check) Runnable {

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

	return NewCheck(
		ctx,
		cancel,
		check,
		engine.builder,
		engine.release,
		engine.trackedStates,
		engine.waitGroup,
	)
}

func NewBuild(
	ctx context.Context,
	cancel func(),
	build db.Build,
	builder StepBuilder,
	release chan bool,
	trackedStates *sync.Map,
	waitGroup *sync.WaitGroup,
) Runnable {
	return &engineBuild{
		ctx:    ctx,
		cancel: cancel,

		build:   build,
		builder: builder,

		release:       release,
		trackedStates: trackedStates,
		waitGroup:     waitGroup,
	}
}

type engineBuild struct {
	ctx    context.Context
	cancel func()

	build   db.Build
	builder StepBuilder

	release       chan bool
	trackedStates *sync.Map
	waitGroup     *sync.WaitGroup

	pipelineCredMgrs []creds.Manager
}

func (b *engineBuild) Run(logger lager.Logger) {
	b.waitGroup.Add(1)
	defer b.waitGroup.Done()

	logger = logger.WithData(lager.Data{
		"build":    b.build.ID(),
		"pipeline": b.build.PipelineName(),
		"job":      b.build.JobName(),
	})

	lock, acquired, err := b.build.AcquireTrackingLock(logger, time.Minute)
	if err != nil {
		logger.Error("failed-to-get-lock", err)
		return
	}

	if !acquired {
		logger.Debug("build-already-tracked")
		return
	}

	defer lock.Release()

	found, err := b.build.Reload()
	if err != nil {
		logger.Error("failed-to-load-build-from-db", err)
		return
	}

	if !found {
		logger.Info("build-not-found")
		return
	}

	if !b.build.IsRunning() {
		logger.Info("build-already-finished")
		return
	}

	notifier, err := b.build.AbortNotifier()
	if err != nil {
		logger.Error("failed-to-listen-for-aborts", err)
		return
	}

	defer notifier.Close()

	step, err := b.builder.BuildStep(logger, b.build)
	if err != nil {
		logger.Error("failed-to-build-step", err)

		// Fails the build if BuildStep returned error. Because some unrecoverable error,
		// like pipeline var_source is wrong, will cause a build to never start
		// to run.
		b.builder.BuildStepErrored(logger, b.build, err)
		b.finish(logger.Session("finish"), err, false)

		return
	}
	b.trackStarted(logger)
	defer b.trackFinished(logger)

	logger.Info("running")

	state := b.runState()
	defer b.clearRunState()

	noleak := make(chan bool)
	defer close(noleak)

	go func() {
		select {
		case <-noleak:
		case <-notifier.Notify():
			logger.Info("aborting")
			b.cancel()
		}
	}()

	done := make(chan error)
	go func() {
		ctx := lagerctx.NewContext(b.ctx, logger)
		done <- step.Run(ctx, state)
	}()

	select {
	case <-b.release:
		logger.Info("releasing")

	case err = <-done:
		logger.Debug("engine-build-done")
		b.finish(logger.Session("finish"), err, step.Succeeded())
	}
}

func (b *engineBuild) finish(logger lager.Logger, err error, succeeded bool) {
	if err == context.Canceled {
		b.saveStatus(logger, atc.StatusAborted)
		logger.Info("aborted")

	} else if err != nil {
		b.saveStatus(logger, atc.StatusErrored)
		logger.Info("errored", lager.Data{"error": err.Error()})

	} else if succeeded {
		b.saveStatus(logger, atc.StatusSucceeded)
		logger.Info("succeeded")

	} else {
		b.saveStatus(logger, atc.StatusFailed)
		logger.Info("failed")
	}
}

func (b *engineBuild) saveStatus(logger lager.Logger, status atc.BuildStatus) {
	if err := b.build.Finish(db.BuildStatus(status)); err != nil {
		logger.Error("failed-to-finish-build", err)
	}
}

func (b *engineBuild) trackStarted(logger lager.Logger) {
	metric.BuildStarted{
		PipelineName: b.build.PipelineName(),
		JobName:      b.build.JobName(),
		BuildName:    b.build.Name(),
		BuildID:      b.build.ID(),
		TeamName:     b.build.TeamName(),
	}.Emit(logger)
}

func (b *engineBuild) trackFinished(logger lager.Logger) {
	found, err := b.build.Reload()
	if err != nil {
		logger.Error("failed-to-load-build-from-db", err)
		return
	}

	if !found {
		logger.Info("build-removed")
		return
	}

	if !b.build.IsRunning() {
		metric.BuildFinished{
			PipelineName:  b.build.PipelineName(),
			JobName:       b.build.JobName(),
			BuildName:     b.build.Name(),
			BuildID:       b.build.ID(),
			BuildStatus:   b.build.Status(),
			BuildDuration: b.build.EndTime().Sub(b.build.StartTime()),
			TeamName:      b.build.TeamName(),
		}.Emit(logger)
	}
}

func (b *engineBuild) runState() exec.RunState {
	id := fmt.Sprintf("build:%v", b.build.ID())
	existingState, _ := b.trackedStates.LoadOrStore(id, exec.NewRunState())
	return existingState.(exec.RunState)
}

func (b *engineBuild) clearRunState() {
	id := fmt.Sprintf("build:%v", b.build.ID())
	b.trackedStates.Delete(id)
}

func NewCheck(
	ctx context.Context,
	cancel func(),
	check db.Check,
	builder StepBuilder,
	release chan bool,
	trackedStates *sync.Map,
	waitGroup *sync.WaitGroup,
) Runnable {
	return &engineCheck{
		ctx:    ctx,
		cancel: cancel,

		check:   check,
		builder: builder,

		release:       release,
		trackedStates: trackedStates,
		waitGroup:     waitGroup,
	}
}

type engineCheck struct {
	ctx    context.Context
	cancel func()

	check   db.Check
	builder StepBuilder

	release       chan bool
	trackedStates *sync.Map
	waitGroup     *sync.WaitGroup
}

func (c *engineCheck) Run(logger lager.Logger) {
	c.waitGroup.Add(1)
	defer c.waitGroup.Done()

	logger = logger.WithData(lager.Data{
		"check": c.check.ID(),
	})

	lock, acquired, err := c.check.AcquireTrackingLock(logger)
	if err != nil {
		logger.Error("failed-to-get-lock", err)
		return
	}

	if !acquired {
		logger.Debug("check-already-tracked")
		return
	}

	defer lock.Release()

	err = c.check.Start()
	if err != nil {
		logger.Error("failed-to-start-check", err)
		return
	}

	c.trackStarted(logger)
	defer c.trackFinished(logger)

	step, err := c.builder.CheckStep(logger, c.check)
	if err != nil {
		logger.Error("failed-to-create-check-step", err)
		c.check.FinishWithError(fmt.Errorf("create check step: %w", err))
		return
	}

	logger.Info("running")

	state := c.runState()
	defer c.clearRunState()

	done := make(chan error)
	go func() {
		ctx := lagerctx.NewContext(c.ctx, logger)
		done <- step.Run(ctx, state)
	}()

	select {
	case <-c.release:
		logger.Info("releasing")

	case err = <-done:
		if err != nil {
			logger.Info("errored", lager.Data{"error": err.Error()})
			c.check.FinishWithError(fmt.Errorf("run check step: %w", err))
		} else {
			logger.Info("succeeded")
			if err = c.check.Finish(); err != nil {
				logger.Error("failed-to-finish-check", err)
			}
		}
	}
}

func (c *engineCheck) runState() exec.RunState {
	id := fmt.Sprintf("check:%v", c.check.ID())
	existingState, _ := c.trackedStates.LoadOrStore(id, exec.NewRunState())
	return existingState.(exec.RunState)
}

func (c *engineCheck) clearRunState() {
	id := fmt.Sprintf("check:%v", c.check.ID())
	c.trackedStates.Delete(id)
}

func (c *engineCheck) trackStarted(logger lager.Logger) {
	metric.CheckStarted{
		CheckName:             c.check.Plan().Check.Name,
		ResourceConfigScopeID: c.check.ResourceConfigScopeID(),
		CheckStatus:           c.check.Status(),
		CheckPendingDuration:  c.check.StartTime().Sub(c.check.CreateTime()),
	}.Emit(logger)
}

func (c *engineCheck) trackFinished(logger lager.Logger) {
	metric.CheckFinished{
		CheckName:             c.check.Plan().Check.Name,
		ResourceConfigScopeID: c.check.ResourceConfigScopeID(),
		CheckStatus:           c.check.Status(),
		CheckDuration:         c.check.EndTime().Sub(c.check.StartTime()),
	}.Emit(logger)
}
<commit_msg>atc: add tracepoint to engine build<commit_after>package engine

import (
	"context"
	"fmt"
	"sync"
	"time"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagerctx"
	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/atc/creds"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/exec"
	"github.com/concourse/concourse/atc/metric"
	"github.com/concourse/concourse/tracing"
)

//go:generate counterfeiter . Engine

type Engine interface {
	NewBuild(db.Build) Runnable
	NewCheck(db.Check) Runnable
	ReleaseAll(lager.Logger)
}

//go:generate counterfeiter . Runnable

type Runnable interface {
	Run(logger lager.Logger)
}

//go:generate counterfeiter . StepBuilder

type StepBuilder interface {
	BuildStep(lager.Logger, db.Build) (exec.Step, error)
	CheckStep(lager.Logger, db.Check) (exec.Step, error)

	BuildStepErrored(lager.Logger, db.Build, error)
}

func NewEngine(builder StepBuilder) Engine {
	return &engine{
		builder:       builder,
		release:       make(chan bool),
		trackedStates: new(sync.Map),
		waitGroup:     new(sync.WaitGroup),
	}
}

type engine struct {
	builder       StepBuilder
	release       chan bool
	trackedStates *sync.Map
	waitGroup     *sync.WaitGroup
}

func (engine *engine) ReleaseAll(logger lager.Logger) {
	logger.Info("calling-release-on-builds")

	close(engine.release)

	logger.Info("waiting-on-builds")

	engine.waitGroup.Wait()

	logger.Info("finished-waiting-on-builds")
}

func (engine *engine) NewBuild(build db.Build) Runnable {

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

	return NewBuild(
		ctx,
		cancel,
		build,
		engine.builder,
		engine.release,
		engine.trackedStates,
		engine.waitGroup,
	)
}

func (engine *engine) NewCheck(check db.Check) Runnable {

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

	return NewCheck(
		ctx,
		cancel,
		check,
		engine.builder,
		engine.release,
		engine.trackedStates,
		engine.waitGroup,
	)
}

func NewBuild(
	ctx context.Context,
	cancel func(),
	build db.Build,
	builder StepBuilder,
	release chan bool,
	trackedStates *sync.Map,
	waitGroup *sync.WaitGroup,
) Runnable {
	return &engineBuild{
		ctx:    ctx,
		cancel: cancel,

		build:   build,
		builder: builder,

		release:       release,
		trackedStates: trackedStates,
		waitGroup:     waitGroup,
	}
}

type engineBuild struct {
	ctx    context.Context
	cancel func()

	build   db.Build
	builder StepBuilder

	release       chan bool
	trackedStates *sync.Map
	waitGroup     *sync.WaitGroup

	pipelineCredMgrs []creds.Manager
}

func (b *engineBuild) Run(logger lager.Logger) {
	b.waitGroup.Add(1)
	defer b.waitGroup.Done()

	logger = logger.WithData(lager.Data{
		"build":    b.build.ID(),
		"pipeline": b.build.PipelineName(),
		"job":      b.build.JobName(),
	})

	lock, acquired, err := b.build.AcquireTrackingLock(logger, time.Minute)
	if err != nil {
		logger.Error("failed-to-get-lock", err)
		return
	}

	if !acquired {
		logger.Debug("build-already-tracked")
		return
	}

	defer lock.Release()

	found, err := b.build.Reload()
	if err != nil {
		logger.Error("failed-to-load-build-from-db", err)
		return
	}

	if !found {
		logger.Info("build-not-found")
		return
	}

	if !b.build.IsRunning() {
		logger.Info("build-already-finished")
		return
	}

	notifier, err := b.build.AbortNotifier()
	if err != nil {
		logger.Error("failed-to-listen-for-aborts", err)
		return
	}

	defer notifier.Close()

	ctx, span := tracing.StartSpan(b.ctx, "build", tracing.Attrs{
		"team":     b.build.TeamName(),
		"pipeline": b.build.PipelineName(),
		"job":      b.build.JobName(),
		"build":    b.build.Name(),
	})
	defer span.End()

	step, err := b.builder.BuildStep(logger, b.build)
	if err != nil {
		logger.Error("failed-to-build-step", err)

		// Fails the build if BuildStep returned error. Because some unrecoverable error,
		// like pipeline var_source is wrong, will cause a build to never start
		// to run.
		b.builder.BuildStepErrored(logger, b.build, err)
		b.finish(logger.Session("finish"), err, false)

		return
	}
	b.trackStarted(logger)
	defer b.trackFinished(logger)

	logger.Info("running")

	state := b.runState()
	defer b.clearRunState()

	noleak := make(chan bool)
	defer close(noleak)

	go func() {
		select {
		case <-noleak:
		case <-notifier.Notify():
			logger.Info("aborting")
			b.cancel()
		}
	}()

	done := make(chan error)
	go func() {
		ctx = lagerctx.NewContext(ctx, logger)
		done <- step.Run(ctx, state)
	}()

	select {
	case <-b.release:
		logger.Info("releasing")

	case err = <-done:
		logger.Debug("engine-build-done")
		b.finish(logger.Session("finish"), err, step.Succeeded())
	}
}

func (b *engineBuild) finish(logger lager.Logger, err error, succeeded bool) {
	if err == context.Canceled {
		b.saveStatus(logger, atc.StatusAborted)
		logger.Info("aborted")

	} else if err != nil {
		b.saveStatus(logger, atc.StatusErrored)
		logger.Info("errored", lager.Data{"error": err.Error()})

	} else if succeeded {
		b.saveStatus(logger, atc.StatusSucceeded)
		logger.Info("succeeded")

	} else {
		b.saveStatus(logger, atc.StatusFailed)
		logger.Info("failed")
	}
}

func (b *engineBuild) saveStatus(logger lager.Logger, status atc.BuildStatus) {
	if err := b.build.Finish(db.BuildStatus(status)); err != nil {
		logger.Error("failed-to-finish-build", err)
	}
}

func (b *engineBuild) trackStarted(logger lager.Logger) {
	metric.BuildStarted{
		PipelineName: b.build.PipelineName(),
		JobName:      b.build.JobName(),
		BuildName:    b.build.Name(),
		BuildID:      b.build.ID(),
		TeamName:     b.build.TeamName(),
	}.Emit(logger)
}

func (b *engineBuild) trackFinished(logger lager.Logger) {
	found, err := b.build.Reload()
	if err != nil {
		logger.Error("failed-to-load-build-from-db", err)
		return
	}

	if !found {
		logger.Info("build-removed")
		return
	}

	if !b.build.IsRunning() {
		metric.BuildFinished{
			PipelineName:  b.build.PipelineName(),
			JobName:       b.build.JobName(),
			BuildName:     b.build.Name(),
			BuildID:       b.build.ID(),
			BuildStatus:   b.build.Status(),
			BuildDuration: b.build.EndTime().Sub(b.build.StartTime()),
			TeamName:      b.build.TeamName(),
		}.Emit(logger)
	}
}

func (b *engineBuild) runState() exec.RunState {
	id := fmt.Sprintf("build:%v", b.build.ID())
	existingState, _ := b.trackedStates.LoadOrStore(id, exec.NewRunState())
	return existingState.(exec.RunState)
}

func (b *engineBuild) clearRunState() {
	id := fmt.Sprintf("build:%v", b.build.ID())
	b.trackedStates.Delete(id)
}

func NewCheck(
	ctx context.Context,
	cancel func(),
	check db.Check,
	builder StepBuilder,
	release chan bool,
	trackedStates *sync.Map,
	waitGroup *sync.WaitGroup,
) Runnable {
	return &engineCheck{
		ctx:    ctx,
		cancel: cancel,

		check:   check,
		builder: builder,

		release:       release,
		trackedStates: trackedStates,
		waitGroup:     waitGroup,
	}
}

type engineCheck struct {
	ctx    context.Context
	cancel func()

	check   db.Check
	builder StepBuilder

	release       chan bool
	trackedStates *sync.Map
	waitGroup     *sync.WaitGroup
}

func (c *engineCheck) Run(logger lager.Logger) {
	c.waitGroup.Add(1)
	defer c.waitGroup.Done()

	logger = logger.WithData(lager.Data{
		"check": c.check.ID(),
	})

	lock, acquired, err := c.check.AcquireTrackingLock(logger)
	if err != nil {
		logger.Error("failed-to-get-lock", err)
		return
	}

	if !acquired {
		logger.Debug("check-already-tracked")
		return
	}

	defer lock.Release()

	err = c.check.Start()
	if err != nil {
		logger.Error("failed-to-start-check", err)
		return
	}

	c.trackStarted(logger)
	defer c.trackFinished(logger)

	step, err := c.builder.CheckStep(logger, c.check)
	if err != nil {
		logger.Error("failed-to-create-check-step", err)
		c.check.FinishWithError(fmt.Errorf("create check step: %w", err))
		return
	}

	logger.Info("running")

	state := c.runState()
	defer c.clearRunState()

	done := make(chan error)
	go func() {
		ctx := lagerctx.NewContext(c.ctx, logger)
		done <- step.Run(ctx, state)
	}()

	select {
	case <-c.release:
		logger.Info("releasing")

	case err = <-done:
		if err != nil {
			logger.Info("errored", lager.Data{"error": err.Error()})
			c.check.FinishWithError(fmt.Errorf("run check step: %w", err))
		} else {
			logger.Info("succeeded")
			if err = c.check.Finish(); err != nil {
				logger.Error("failed-to-finish-check", err)
			}
		}
	}
}

func (c *engineCheck) runState() exec.RunState {
	id := fmt.Sprintf("check:%v", c.check.ID())
	existingState, _ := c.trackedStates.LoadOrStore(id, exec.NewRunState())
	return existingState.(exec.RunState)
}

func (c *engineCheck) clearRunState() {
	id := fmt.Sprintf("check:%v", c.check.ID())
	c.trackedStates.Delete(id)
}

func (c *engineCheck) trackStarted(logger lager.Logger) {
	metric.CheckStarted{
		CheckName:             c.check.Plan().Check.Name,
		ResourceConfigScopeID: c.check.ResourceConfigScopeID(),
		CheckStatus:           c.check.Status(),
		CheckPendingDuration:  c.check.StartTime().Sub(c.check.CreateTime()),
	}.Emit(logger)
}

func (c *engineCheck) trackFinished(logger lager.Logger) {
	metric.CheckFinished{
		CheckName:             c.check.Plan().Check.Name,
		ResourceConfigScopeID: c.check.ResourceConfigScopeID(),
		CheckStatus:           c.check.Status(),
		CheckDuration:         c.check.EndTime().Sub(c.check.StartTime()),
	}.Emit(logger)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"math/rand"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type animeList []anime

type anime struct {
	ID         string    `bson:"id"`
	Name       string    `bson:"name"`
	Href       string    `bson:"href"`
	Episode    int       `bson:"ep"`
	Subs       []string  `bson:"subs"`
	LastUpdate time.Time `bson:"lastUpdate"`
	Show       bool      `bson:"show"`
}

//LIMIT is a time constant for 15 Days
const LIMIT = 15 * 24 * time.Hour

//Gets every anime in animeList db and returns it as AnimeList type
func getAnimeList() (result animeList) {
	defer panicRecovery()

	err := DBanimeList.Find(nil).Sort("lastUpdate").All(&result)
	if err != nil {
		panic(fmt.Sprintf("Error querying MongoDB in function: %s - %s", "getAnimeList", err))
	}

	return
}

//Get every anime this user.id is subscribed to
func getAnimeListForUser(userID string) (result animeList) {
	defer panicRecovery()

	err := DBanimeList.Find(bson.M{"subs": userID}).All(&result)
	if err != nil {
		panic(fmt.Sprintf("Error querying MongoDB in function: %s - %s", "getAnimeListForUser", err))
	}

	return
}

// maintainAnimeList db maintanance function
// changes the 'show' database field of all entries over LIMIT days old to false
// tries to update the 'href' database field for entries that don't have one yet
// TODO: Potentially merge this with rssReader
func maintainAnimeList() {
	// Number of hidden and/or updated entries
	hidden, updated := 0, 0
	newAnimeList := getAnimeList()
	now := time.Now()

	for _, a := range newAnimeList {
		if len(a.Href) < 5 {
			updated += a.GetHref() //TODO: Limit HS scraping to maximum 1 per maintanance, instead of 1 per empty href per maintanance
		}
		if now.Sub(a.LastUpdate) > LIMIT {
			a.Remove()
			hidden++
		}
	}
	log.Printf("AUTO-MAINTANANCE: animeList updated! (hidden: %d | updated: %d)\n",
		hidden, updated)
}

// Insert inserts a new anime entry to db
// generates a unique ID if it isn't present in the object yet
func (a *anime) Insert() {
	if a.ID == "" {
		a.GenID()
	}
	a.LastUpdate = time.Now()
	a.Show = true
	DBanimeList.Insert(a)
}

//Remove anime from db by Anime.Name or Anime.Id
func (a anime) Remove() (success int) {
	success = 0
	if a.Name != "" {
		DBanimeList.Remove(bson.M{"name": a.Name})
		success = 1
	} else if a.ID != "" {
		DBanimeList.Remove(bson.M{"id": a.ID})
		success = 1
	}
	return
}

// UpdateEp updates the db entry with the new episode number
func (a *anime) UpdateEp() {
	updateQuery := bson.M{
		"$set": bson.M{
			"ep":         a.Episode,
			"lastUpdate": time.Now(),
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"name": a.Name}).Apply(change, a)
}

//Adds new sub Name to the db entry of Anime.Id
func (a *anime) AddSub(sub string) {
	updateQuery := bson.M{
		"$addToSet": bson.M{
			"subs": sub,
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"id": a.ID}).Apply(change, a)
}

//Removes the sub from the db entry of Anime.Id
func (a *anime) RemoveSub(sub string) {
	updateQuery := bson.M{
		"$pull": bson.M{
			"subs": sub,
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"id": a.ID}).Apply(change, a)
}

// GenID generates a unique 3char alphanumeric ID
// not case sensitive
func (a *anime) GenID() {
	var id, byteList string
	byteList = "0123456789abcdefghijklmnopqrstuvwxyz"
	for i := 0; i < 3; i++ {
		id += string(byteList[rand.Intn(len(byteList))])
	}

	if n, _ := DBanimeList.Find(bson.M{"id": id}).Count(); n == 0 {
		a.ID = id
	} else {
		a.GenID()
	}
}

//Gets href for Anime.Name
func (a *anime) GetHref() (success int) {
	success = 0
	//NOTE: Cloudflare scraping not needed for now
	//scrapper := "http://scraper-422.rhcloud.com/?href="
	target := "http://horriblesubs.info/current-season/"

	doc, err := goquery.NewDocument( /*scrapper + */ target)
	if err != nil {
		log.Println(err)
	} else {
		doc.Find(".ind-show.linkful").Each(func(i int, s *goquery.Selection) {
			name, _ := s.Find("a").Attr("title")
			url, _ := s.Find("a").Attr("href")
			if strings.ToLower(name) == strings.ToLower(a.Name) {
				newHref := fmt.Sprintf("http://horriblesubs.info%s", url)
				updateQuery := bson.M{
					"$set": bson.M{
						"href":       newHref,
						"lastUpdate": time.Now(),
					},
				}
				DBanimeList.Update(bson.M{"name": a.Name}, updateQuery)
				success = 1
			}
		})
	}
	return
}

// Exists checks if there is already an entry in db
// with the same id OR the same name
// Returns true if it already exists
func (a anime) Exists() bool {
	query := bson.M{
		"$or": []interface{}{
			bson.M{"id": a.ID},
			bson.M{"name": a.Name},
		},
	}
	if n, _ := DBanimeList.Find(query).Count(); n == 0 {
		return false
	}
	return true
}

// NewEpisode checks if episode # already exists in db
// Returns true if episode in db is outdated and
// needs to be updated and false if db is already
// up to date
func (a anime) NewEpisode() bool {
	query := bson.M{
		"name": a.Name,
		"ep": bson.M{
			"$lt": a.Episode,
		},
	}

	if n, _ := DBanimeList.Find(query).Count(); n == 0 {
		return false
	}
	return true
}

//returns length of AnimeList
//used for sort interface
func (a animeList) Len() int {
	return len(a)
}

//Checks if index i should sort before index j
//used for sort interface
func (a animeList) Less(i, j int) bool {
	if len(a[i].Subs) > len(a[j].Subs) {
		return true
	}
	return false
}

//Swaps the values of i and j indexes
//used for sort interface
func (a animeList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
<commit_msg>changed anime method Remove() to Hide()<commit_after>package main

import (
	"fmt"
	"log"
	"math/rand"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type animeList []anime

type anime struct {
	ID         string    `bson:"id"`
	Name       string    `bson:"name"`
	Href       string    `bson:"href"`
	Episode    int       `bson:"ep"`
	Subs       []string  `bson:"subs"`
	LastUpdate time.Time `bson:"lastUpdate"`
	Show       bool      `bson:"show"`
}

//LIMIT is a time constant for 15 Days
const LIMIT = 15 * 24 * time.Hour

//Gets every anime in animeList db and returns it as AnimeList type
func getAnimeList() (result animeList) {
	defer panicRecovery()

	err := DBanimeList.Find(nil).Sort("lastUpdate").All(&result)
	if err != nil {
		panic(fmt.Sprintf("Error querying MongoDB in function: %s - %s", "getAnimeList", err))
	}

	return
}

//Get every anime this user.id is subscribed to
func getAnimeListForUser(userID string) (result animeList) {
	defer panicRecovery()

	err := DBanimeList.Find(bson.M{"subs": userID}).All(&result)
	if err != nil {
		panic(fmt.Sprintf("Error querying MongoDB in function: %s - %s", "getAnimeListForUser", err))
	}

	return
}

// maintainAnimeList db maintanance function
// changes the 'show' database field of all entries over LIMIT days old to false
// tries to update the 'href' database field for entries that don't have one yet
// TODO: Potentially merge this with rssReader
func maintainAnimeList() {
	// Number of hidden and/or updated entries
	hidden, updated := 0, 0
	newAnimeList := getAnimeList()
	now := time.Now()

	for _, a := range newAnimeList {
		if len(a.Href) < 5 {
			updated += a.GetHref() //TODO: Limit HS scraping to maximum 1 per maintanance, instead of 1 per empty href per maintanance
		}
		if now.Sub(a.LastUpdate) > LIMIT {
			hidden += a.Hide()
		}
	}
	log.Printf("AUTO-MAINTANANCE: animeList updated! (hidden: %d | updated: %d)\n",
		hidden, updated)
}

// Insert inserts a new anime entry to db
// generates a unique ID if it isn't present in the object yet
func (a *anime) Insert() {
	if a.ID == "" {
		a.GenID()
	}
	a.LastUpdate = time.Now()
	a.Show = true
	DBanimeList.Insert(a)
}

// Hide changes database field 'show' for this anime to false
func (a anime) Hide() (success int) {
	defer panicRecovery()

	success = 0

	if a.Name != "" {
		err := DBanimeList.Update(bson.M{"name": a.Name}, bson.M{"show": false})

		if err != nil {
			panic(fmt.Sprintf("Error updating MongoDB document in anime method: %s - %s", "Hide", err))
		}

		success = 1
	} else if a.ID != "" {
		err := DBanimeList.Update(bson.M{"id": a.ID}, bson.M{"show": false})

		if err != nil {
			panic(fmt.Sprintf("Error updating MongoDB document in anime method: %s - %s", "Hide", err))
		}

		success = 1
	}

	return
}

// UpdateEp updates the db entry with the new episode number
func (a *anime) UpdateEp() {
	updateQuery := bson.M{
		"$set": bson.M{
			"ep":         a.Episode,
			"lastUpdate": time.Now(),
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"name": a.Name}).Apply(change, a)
}

//Adds new sub Name to the db entry of Anime.Id
func (a *anime) AddSub(sub string) {
	updateQuery := bson.M{
		"$addToSet": bson.M{
			"subs": sub,
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"id": a.ID}).Apply(change, a)
}

//Removes the sub from the db entry of Anime.Id
func (a *anime) RemoveSub(sub string) {
	updateQuery := bson.M{
		"$pull": bson.M{
			"subs": sub,
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"id": a.ID}).Apply(change, a)
}

// GenID generates a unique 3char alphanumeric ID
// not case sensitive
func (a *anime) GenID() {
	var id, byteList string
	byteList = "0123456789abcdefghijklmnopqrstuvwxyz"
	for i := 0; i < 3; i++ {
		id += string(byteList[rand.Intn(len(byteList))])
	}

	if n, _ := DBanimeList.Find(bson.M{"id": id}).Count(); n == 0 {
		a.ID = id
	} else {
		a.GenID()
	}
}

//Gets href for Anime.Name
func (a *anime) GetHref() (success int) {
	success = 0
	//NOTE: Cloudflare scraping not needed for now
	//scrapper := "http://scraper-422.rhcloud.com/?href="
	target := "http://horriblesubs.info/current-season/"

	doc, err := goquery.NewDocument( /*scrapper + */ target)
	if err != nil {
		log.Println(err)
	} else {
		doc.Find(".ind-show.linkful").Each(func(i int, s *goquery.Selection) {
			name, _ := s.Find("a").Attr("title")
			url, _ := s.Find("a").Attr("href")
			if strings.ToLower(name) == strings.ToLower(a.Name) {
				newHref := fmt.Sprintf("http://horriblesubs.info%s", url)
				updateQuery := bson.M{
					"$set": bson.M{
						"href":       newHref,
						"lastUpdate": time.Now(),
					},
				}
				DBanimeList.Update(bson.M{"name": a.Name}, updateQuery)
				success = 1
			}
		})
	}
	return
}

// Exists checks if there is already an entry in db
// with the same id OR the same name
// Returns true if it already exists
func (a anime) Exists() bool {
	query := bson.M{
		"$or": []interface{}{
			bson.M{"id": a.ID},
			bson.M{"name": a.Name},
		},
	}
	if n, _ := DBanimeList.Find(query).Count(); n == 0 {
		return false
	}
	return true
}

// NewEpisode checks if episode # already exists in db
// Returns true if episode in db is outdated and
// needs to be updated and false if db is already
// up to date
func (a anime) NewEpisode() bool {
	query := bson.M{
		"name": a.Name,
		"ep": bson.M{
			"$lt": a.Episode,
		},
	}

	if n, _ := DBanimeList.Find(query).Count(); n == 0 {
		return false
	}
	return true
}

//returns length of AnimeList
//used for sort interface
func (a animeList) Len() int {
	return len(a)
}

//Checks if index i should sort before index j
//used for sort interface
func (a animeList) Less(i, j int) bool {
	if len(a[i].Subs) > len(a[j].Subs) {
		return true
	}
	return false
}

//Swaps the values of i and j indexes
//used for sort interface
func (a animeList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
<|endoftext|>


<commit_before>package schema

import "testing"

// To be in par with the python library.
func TestDecodeString_URIMustRequireScheme(t *testing.T) {
	if _, err := decodeString(stringURI, "google.com", Constraints{}); err == nil {
		t.Errorf("want:err got:nil")
	}
}

func TestDecodeString_InvalidUUIDVersion(t *testing.T) {
	// This is a uuid3: namespace DNS and python.org.
	if _, err := decodeString(stringUUID, "6fa459ea-ee8a-3ca4-894e-db77e160355e", Constraints{}); err == nil {
		t.Errorf("want:err got:nil")
	}
}

func TestDecodeString_Success(t *testing.T) {
	t.Run("ValidMinLength", func(t *testing.T) {
		var data = []struct {
			Desc        string
			Value       string
			Format      string
			constraints Constraints
		}{
			{"URI", "http://google.com", stringURI, Constraints{MinLength: 5}},
			{"Email", "foo@bar.com", stringEmail, Constraints{MinLength: 3}},
			{"UUID", "C56A4180-65AA-42EC-A945-5FD21DEC0538", stringUUID, Constraints{MinLength: 36}},
		}
		for _, d := range data {
			v, err := decodeString(d.Format, d.Value, d.constraints)
			if err != nil {
				t.Errorf("want:nil got:%q", err)
			}
			if v != d.Value {
				t.Errorf("want:%s got:%s", d.Value, v)
			}
		}
	})

	t.Run("Error", func(t *testing.T) {
		var data = []struct {
			Desc        string
			Value       string
			Format      string
			constraints Constraints
		}{
			{"InvalidMinLengthUUID", "C56A4180-65AA-42EC-A945-5FD21DEC0538", stringUUID, Constraints{MinLength: 40}},
		}
		for _, d := range data {
			_, err := decodeString(d.Format, d.Value, d.constraints)
			if err == nil {
				t.Fatalf("err want:nil got:%q", err)
			}
		}
	})

}
<commit_msg>Revert "Modify string_test"<commit_after>package schema

import "testing"

// To be in par with the python library.
func TestDecodeString_URIMustRequireScheme(t *testing.T) {
	if _, err := decodeString(stringURI, "google.com", Constraints{}); err == nil {
		t.Errorf("want:err got:nil")
	}
}

func TestDecodeString_InvalidUUIDVersion(t *testing.T) {
	// This is a uuid3: namespace DNS and python.org.
	if _, err := decodeString(stringUUID, "6fa459ea-ee8a-3ca4-894e-db77e160355e", Constraints{}); err == nil {
		t.Errorf("want:err got:nil")
	}
}

func TestDecodeString_Success(t *testing.T) {
	var data = []struct {
		Desc        string
		Value       string
		Format      string
		constraints Constraints
	}{
		{"URI", "http://google.com", stringURI, Constraints{}},
		{"Email", "foo@bar.com", stringEmail, Constraints{}},
		{"UUID", "C56A4180-65AA-42EC-A945-5FD21DEC0538", stringUUID, Constraints{MinLength: 36}},
	}
	for _, d := range data {
		v, err := decodeString(d.Format, d.Value, d.constraints)
		if err != nil {
			t.Errorf("want:nil got:%q", err)
		}
		if v != d.Value {
			t.Errorf("want:%s got:%s", d.Value, v)
		}
	}
}
<|endoftext|>


<commit_before>package watcher

import (
	"log"
	"os/exec"
	"syscall"

	"github.com/fatih/color"
)

// Build listens watch events from Watcher and sends messages to Runner
// when new changes are built.
func Build(w *Watcher, r *Runner, p *Params) {
	for {
		w.Wait()

		run := p.Get("run")
		if run == "" {
			run = "."
		}

		color.Cyan("Building %s...\n", run)

		cmd, err := runCommand("go", "build", "-o", getBinaryName(), run)
		if err != nil {
			log.Fatalf("Could not run 'go build' command: %s", err)
			continue
		}

		if err := cmd.Wait(); err != nil {
			if err := interpretError(err); err != nil {
				log.Fatal("An error occurred while building")
			}

			color.Red("A build error occurred. Please update your code...")

			continue
		}

		// when binary is successfully updated, kill the old running process
		r.Kill()

		// and start the new process
		r.Run()
	}
}

// interpretError checks the error, and returns nil if it is
// an exit code 2 error. Otherwise error is returned as it is.
// when a compilation error occurres, it returns with code 2.
func interpretError(err error) error {
	exiterr, ok := err.(*exec.ExitError)
	if !ok {
		return err
	}

	status, ok := exiterr.Sys().(syscall.WaitStatus)
	if !ok {
		return err
	}

	if status.ExitStatus() == 2 {
		return nil
	}

	return err
}
<commit_msg>colorize build messages<commit_after>package watcher

import (
	"log"
	"os/exec"
	"syscall"

	"github.com/fatih/color"
)

// Build listens watch events from Watcher and sends messages to Runner
// when new changes are built.
func Build(w *Watcher, r *Runner, p *Params) {
	for {
		w.Wait()

		run := p.Get("run")
		if run == "" {
			run = "."
		}

		color.Cyan("Building %s...\n", run)

		cmd, err := runCommand("go", "build", "-o", getBinaryName(), run)
		if err != nil {
			log.Fatalf("Could not run 'go build' command: %s", err)
			continue
		}

		if err := cmd.Wait(); err != nil {
			if err := interpretError(err); err != nil {
				color.Red("An error occurred while building: %s", err)
			} else {
				color.Red("A build error occurred. Please update your code...")
			}

			continue
		}

		// when binary is successfully updated, kill the old running process
		r.Kill()

		// and start the new process
		r.Run()
	}
}

// interpretError checks the error, and returns nil if it is
// an exit code 2 error. Otherwise error is returned as it is.
// when a compilation error occurres, it returns with code 2.
func interpretError(err error) error {
	exiterr, ok := err.(*exec.ExitError)
	if !ok {
		return err
	}

	status, ok := exiterr.Sys().(syscall.WaitStatus)
	if !ok {
		return err
	}

	if status.ExitStatus() == 2 {
		return nil
	}

	return err
}
<|endoftext|>


<commit_before><commit_msg>[fix] http link detection<commit_after><|endoftext|>


<commit_before>package metafora

type Balancer interface {
	// Init is called once and only once with an interface for use by CanClaim
	// and Balance to use the state of the Consumer.
	Init(ConsumerState)

	// CanClaim should return true if the consumer should accept a task. No new
	// tasks will be claimed while CanClaim is called.
	CanClaim(taskID string) bool

	// Balance should return the list of Task IDs that should be released. No new
	// tasks will be claimed during balancing.
	Balance() (release []string)
}

type DumbBalancer struct{}

func (*DumbBalancer) Init(ConsumerState)   {}
func (*DumbBalancer) CanClaim(string) bool { return true }
func (*DumbBalancer) Balance() []string    { return nil }
<commit_msg>Improve balancer docs<commit_after>package metafora

type Balancer interface {
	// Init is called once and only once with an interface for use by CanClaim
	// and Balance to use the state of the Consumer.
	Init(ConsumerState)

	// CanClaim should return true if the consumer should accept a task. No new
	// tasks will be claimed while CanClaim is called.
	CanClaim(taskID string) bool

	// Balance should return the list of Task IDs that should be released. No new
	// tasks will be claimed during balancing. The criteria used to determine
	// which tasks should be released is left up to the implementation.
	Balance() (release []string)
}

// DumbBalancer is the simplest possible balancer implementation which simply
// accepts all tasks.
type DumbBalancer struct{}

// Init does nothing.
func (*DumbBalancer) Init(ConsumerState) {}

// CanClaim always returns true.
func (*DumbBalancer) CanClaim(string) bool { return true }

// Balance never returns any tasks to balance.
func (*DumbBalancer) Balance() []string { return nil }
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/sedalu/sqrl"
	"io"
	"net/http"
)

// hello world, the web server
func HelloServer(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Content-Type", "text/html")
	io.WriteString(w, "hello, world!\n")
	io.WriteString(w, "\n")
}

func main() {
	http.HandleFunc("/sqrl", HelloServer)

	http.Handle("/qr.png", sqrl.QRHandler("sqrl"))

	err := http.ListenAndServe(":8080", nil)

	if err != nil {
		fmt.Println(err)
	}
}<commit_msg>Added sample implementation of the AuthHandler using my sqrl package<commit_after>package main

import (
	"fmt"
//	"github.com/sedalu/sqrl"
	"github.com/kalaspuffar/sqrl"
	"io"
	"net/http"
)

// hello world, the web server
func HelloServer(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Content-Type", "text/html")
	io.WriteString(w, "hello, world!\n")
	io.WriteString(w, "\n")
}

func main() {
	http.HandleFunc("/hello", HelloServer)
	server := sqrl.NewServer()
	http.Handle("/qr.png", server.QRHandler("sqrl"))
	http.Handle("/sqrl", server.AuthHandler())

	err := http.ListenAndServe(":8080", nil)

	if err != nil {
		fmt.Println(err)
	}
}<|endoftext|>


<commit_before>package mpb_test

import (
	"bytes"
	"testing"
	"time"
	"unicode/utf8"

	"github.com/vbauerster/mpb"
)

func TestBarSetWidth(t *testing.T) {
	var buf bytes.Buffer
	p := mpb.New().SetOut(&buf)
	// overwrite default width 80
	customWidth := 60
	bar := p.AddBar(100).SetWidth(customWidth).
		TrimLeftSpace().TrimRightSpace()
	for i := 0; i < 100; i++ {
		bar.Incr(1)
	}
	p.Stop()

	gotWidth := len(buf.Bytes())
	if gotWidth != customWidth+1 { // +1 for new line
		t.Errorf("Expected width: %d, got: %d\n", customWidth, gotWidth)
	}
}

func TestBarSetInvalidWidth(t *testing.T) {
	var buf bytes.Buffer
	p := mpb.New().SetOut(&buf)
	bar := p.AddBar(100).SetWidth(1).
		TrimLeftSpace().TrimRightSpace()
	for i := 0; i < 100; i++ {
		bar.Incr(1)
	}
	p.Stop()

	wantWidth := 80
	gotWidth := len(buf.Bytes())
	if gotWidth != wantWidth+1 { // +1 for new line
		t.Errorf("Expected width: %d, got: %d\n", wantWidth, gotWidth)
	}
}

func TestBarFormat(t *testing.T) {
	var buf bytes.Buffer
	cancel := make(chan struct{})
	p := mpb.New().WithCancel(cancel).SetOut(&buf)
	customFormat := "(#>_)"
	bar := p.AddBar(100).Format(customFormat).
		TrimLeftSpace().TrimRightSpace()

	go func() {
		for i := 0; i < 100; i++ {
			time.Sleep(10 * time.Millisecond)
			bar.Incr(1)
		}
	}()

	time.Sleep(250 * time.Millisecond)
	close(cancel)
	p.Stop()

	bytes := buf.Bytes()
	_, size := utf8.DecodeLastRune(bytes)
	bytes = bytes[:len(bytes)-size] // removing new line

	seen := make(map[rune]bool)
	for _, r := range string(bytes) {
		if !seen[r] {
			seen[r] = true
		}
	}
	for _, r := range customFormat {
		if !seen[r] {
			t.Errorf("Rune %#U not found in bar\n", r)
		}
	}
}

func TestBarInProgress(t *testing.T) {
	var buf bytes.Buffer
	cancel := make(chan struct{})
	p := mpb.New().WithCancel(cancel).SetOut(&buf)
	bar := p.AddBar(100).TrimLeftSpace().TrimRightSpace()

	stopped := make(chan struct{})

	go func() {
		defer close(stopped)
		for bar.InProgress() {
			time.Sleep(10 * time.Millisecond)
			bar.Incr(1)
		}
	}()

	time.Sleep(250 * time.Millisecond)
	close(cancel)
	p.Stop()

	select {
	case <-stopped:
	case <-time.After(300 * time.Millisecond):
		t.Error("bar.InProgress returns true after cancel")
	}
}
<commit_msg>TestBarInvalidFormat<commit_after>package mpb_test

import (
	"bytes"
	"testing"
	"time"
	"unicode/utf8"

	"github.com/vbauerster/mpb"
)

func TestBarSetWidth(t *testing.T) {
	var buf bytes.Buffer
	p := mpb.New().SetOut(&buf)
	// overwrite default width 80
	customWidth := 60
	bar := p.AddBar(100).SetWidth(customWidth).
		TrimLeftSpace().TrimRightSpace()
	for i := 0; i < 100; i++ {
		bar.Incr(1)
	}
	p.Stop()

	gotWidth := len(buf.Bytes())
	if gotWidth != customWidth+1 { // +1 for new line
		t.Errorf("Expected width: %d, got: %d\n", customWidth, gotWidth)
	}
}

func TestBarSetInvalidWidth(t *testing.T) {
	var buf bytes.Buffer
	p := mpb.New().SetOut(&buf)
	bar := p.AddBar(100).SetWidth(1).
		TrimLeftSpace().TrimRightSpace()
	for i := 0; i < 100; i++ {
		bar.Incr(1)
	}
	p.Stop()

	wantWidth := 80
	gotWidth := len(buf.Bytes())
	if gotWidth != wantWidth+1 { // +1 for new line
		t.Errorf("Expected width: %d, got: %d\n", wantWidth, gotWidth)
	}
}

func TestBarFormat(t *testing.T) {
	var buf bytes.Buffer
	cancel := make(chan struct{})
	p := mpb.New().WithCancel(cancel).SetOut(&buf)
	customFormat := "(#>_)"
	bar := p.AddBar(100).Format(customFormat).
		TrimLeftSpace().TrimRightSpace()

	go func() {
		for i := 0; i < 100; i++ {
			time.Sleep(10 * time.Millisecond)
			bar.Incr(1)
		}
	}()

	time.Sleep(250 * time.Millisecond)
	close(cancel)
	p.Stop()

	// removing new line
	bytes := removeLastRune(buf.Bytes())

	seen := make(map[rune]bool)
	for _, r := range string(bytes) {
		if !seen[r] {
			seen[r] = true
		}
	}
	for _, r := range customFormat {
		if !seen[r] {
			t.Errorf("Rune %#U not found in bar\n", r)
		}
	}
}

func TestBarInvalidFormat(t *testing.T) {
	var buf bytes.Buffer
	customWidth := 60
	p := mpb.New().SetWidth(customWidth).SetOut(&buf)
	customFormat := "(#>=_)"
	bar := p.AddBar(100).Format(customFormat).
		TrimLeftSpace().TrimRightSpace()

	for i := 0; i < 100; i++ {
		time.Sleep(10 * time.Millisecond)
		bar.Incr(1)
	}

	p.Stop()

	bytes := removeLastRune(buf.Bytes())
	got := string(bytes[len(bytes)-customWidth:])
	want := "[==========================================================]"
	if got != want {
		t.Errorf("Expected format: %s, got %s\n", want, got)
	}
}

func TestBarInProgress(t *testing.T) {
	var buf bytes.Buffer
	cancel := make(chan struct{})
	p := mpb.New().WithCancel(cancel).SetOut(&buf)
	bar := p.AddBar(100).TrimLeftSpace().TrimRightSpace()

	stopped := make(chan struct{})

	go func() {
		defer close(stopped)
		for bar.InProgress() {
			time.Sleep(10 * time.Millisecond)
			bar.Incr(1)
		}
	}()

	time.Sleep(250 * time.Millisecond)
	close(cancel)
	p.Stop()

	select {
	case <-stopped:
	case <-time.After(300 * time.Millisecond):
		t.Error("bar.InProgress returns true after cancel")
	}
}

func removeLastRune(bytes []byte) []byte {
	_, size := utf8.DecodeLastRune(bytes)
	return bytes[:len(bytes)-size]
}
<|endoftext|>


<commit_before>package portal

import "time"
import "log"

import "github.com/chris-wood/spud/stack"
import "github.com/chris-wood/spud/util/random"
import "github.com/chris-wood/spud/stack/component/tunnel"
import "github.com/chris-wood/spud/codec"
import "github.com/chris-wood/spud/messages"
import "github.com/chris-wood/spud/messages/name"
import "github.com/chris-wood/spud/messages/kex"
import "github.com/chris-wood/spud/messages/interest"
import "github.com/chris-wood/spud/messages/content"

// XXX: wrap this in the crypto box
import "golang.org/x/crypto/nacl/box"

const connectString string = "CONNECT"

type SecurePortal struct {
	apiStack stack.Stack
	macKey   []byte
	encKey   []byte
}

func NewSecurePortal(s stack.Stack) SecurePortal {
	api := SecurePortal{
		apiStack: s,
	}

	return api
}

func (n SecurePortal) Connect(prefix *name.Name) {
	randomSuffix, _ := random.GenerateRandomString(16)
	bareHelloName, _ := prefix.AppendComponent(connectString)
	bareHelloName, _ = bareHelloName.AppendComponent(randomSuffix)

	// Send the bare hello
	log.Println("Sending the hello")
	bareHello := kex.KEXHello()
	bareHelloRequest := interest.CreateWithName(bareHelloName)
	bareHelloRequest.AddContainer(bareHello)
	n.apiStack.Push(messages.Package(bareHelloRequest))

	// Wait for the response, and use it to build the full hello
	replyWrapper := n.apiStack.Pop()
	reply := replyWrapper.InnerMessage()
	log.Println("Got the REJECT")

	reject, err := reply.GetContainer(codec.T_KEX)
	if err != nil {
		log.Println("Error: no KEX container in the REJECT content object")
		return
	}
	hello := kex.KEXFullHello(bareHello, reject.(*kex.KEX))

	randomSuffix, _ = random.GenerateRandomString(16)
	helloName, _ := prefix.AppendComponent(connectString)
	helloName, _ = prefix.AppendComponent(randomSuffix)

	helloRequest := interest.CreateWithName(helloName)
	helloRequest.AddContainer(hello)
	n.apiStack.Push(messages.Package(helloRequest))

	// Wait for the response to complete the KEX
	replyWrapper = n.apiStack.Pop()
	reply = replyWrapper.InnerMessage()

	accept, err := reply.GetContainer(codec.T_KEX)
	if err != nil {
		log.Println("Error: no KEX container in the ACCEPT content object")
		return
	}
	acceptKEX := accept.(*kex.KEX)

	var sharedKey [32]byte
	var peerPublic [32]byte
	var privateKey [32]byte
	copy(peerPublic[:], acceptKEX.GetPublicKeyShare())
	copy(privateKey[:], hello.GetPrivateKeyShare())
	box.Precompute(&sharedKey, &peerPublic, &privateKey)

	log.Println("Consumer key: ", sharedKey)

	log.Println("Adding a consumer tunnel.")
	session := tunnel.NewSession(sharedKey[:], acceptKEX.GetSessionID())
	n.apiStack.AddSession(session, prefix)
	log.Println("Done.")

	time.Sleep(100 * time.Millisecond)
}

func (n SecurePortal) Get(request *messages.MessageWrapper, timeout time.Duration) (*messages.MessageWrapper, error) {
	signalChannel := make(chan *messages.MessageWrapper, 1)
	n.apiStack.Get(request, func(msg *messages.MessageWrapper) {
		signalChannel <- msg
	})

	var response *messages.MessageWrapper
	select {
	case data := <-signalChannel:
		return data, nil
	case <-time.After(timeout):
		return response, PortalError{0, "Timeout"}
	}
}

func (n SecurePortal) GetAsync(request *messages.MessageWrapper, callback ResponseMessageCallback) {
	n.apiStack.Get(request, func(msg *messages.MessageWrapper) {
		callback(msg)
	})
}

func (p SecurePortal) GetAsyncWithTimeout(request *messages.MessageWrapper, timeout time.Duration, callback ResponseMessageCallback) {
	signalChannel := make(chan *messages.MessageWrapper, 1)
	p.apiStack.Get(request, func(msg *messages.MessageWrapper) {
		signalChannel <- msg
	})

	select {
	case data := <-signalChannel:
		callback(data)
	case <-time.After(timeout):
		p.apiStack.Cancel(request)
	}
}

func (n SecurePortal) Serve(prefix *name.Name, callback RequestMessageCallback) {
	if prefix == nil {
		return
	}

	established := false
	for {
		requestWrapper := n.apiStack.Pop()

		if established {
			log.Println("Handling a request", requestWrapper.Name())
			response := callback(requestWrapper)
			if response != nil {
				n.apiStack.Push(response)
			} else {
				log.Println("Failed to generate a response")
			}
		} else {
			request := requestWrapper.InnerMessage()
			if !prefix.IsPrefix(request.Name()) {
				break
			}

			kexTLV, _ := request.GetContainer(codec.T_KEX)
			kexContainer := kexTLV.(*kex.KEX)

			switch kexContainer.GetMessageType() {
			case codec.T_KEX_BAREHELLO:
				log.Println("Got the BARE HELLO")
				reject := kex.KEXHelloReject(kexContainer, n.macKey)
				rejectResponse := content.CreateWithName(request.Name())
				rejectResponse.AddContainer(reject)
				n.apiStack.Push(messages.Package(rejectResponse))
				break

			case codec.T_KEX_HELLO:
				log.Println("Got the HELLO")
				accept, err := kex.KEXHelloAccept(kexContainer, n.macKey, n.encKey)
				if err != nil {
					log.Println(err)
					break
				}

				acceptResponse := content.CreateWithName(request.Name())
				acceptResponse.AddContainer(accept)
				n.apiStack.Push(messages.Package(acceptResponse))

				// XXX: go to the KDF step

				var sharedKey [32]byte
				var peerPublic [32]byte
				var privateKey [32]byte
				copy(peerPublic[:], kexContainer.GetPublicKeyShare())
				copy(privateKey[:], accept.GetPrivateKeyShare())
				box.Precompute(&sharedKey, &peerPublic, &privateKey)

				log.Println("Producer key:", sharedKey)

				// Create and start the session
				// session := esic.NewESIC(n.apiStack, sharedKey[:], accept.GetSessionID())
				// callback(session)
				time.Sleep(100 * time.Millisecond)

				log.Println("Adding a tunnel session")
				session := tunnel.NewSession(sharedKey[:], accept.GetSessionID())
				n.apiStack.AddSession(session, prefix)
				established = true
				log.Println("Done.")

				break

			case codec.T_KEX_REJECT:
			case codec.T_KEX_ACCEPT:
				log.Println("Got an invalid message...")
				// invalid message type to be received here...
				break
			}
		}
	}
}

func (p SecurePortal) Produce(data *messages.MessageWrapper) {
	p.apiStack.Push(data)
}
<commit_msg>Minor cleanup<commit_after>package portal

import "time"
import "log"

import "github.com/chris-wood/spud/stack"
import "github.com/chris-wood/spud/util/random"
import "github.com/chris-wood/spud/stack/component/tunnel"
import "github.com/chris-wood/spud/codec"
import "github.com/chris-wood/spud/messages"
import "github.com/chris-wood/spud/messages/name"
import "github.com/chris-wood/spud/messages/kex"
import "github.com/chris-wood/spud/messages/interest"
import "github.com/chris-wood/spud/messages/content"

// XXX: wrap this in the crypto box
import (
	"golang.org/x/crypto/nacl/box"
	"encoding/hex"
)

const connectString string = "CONNECT"

type SecurePortal struct {
	apiStack stack.Stack
	macKey   []byte
	encKey   []byte
}

func NewSecurePortal(s stack.Stack) SecurePortal {
	api := SecurePortal{
		apiStack: s,
	}

	return api
}

func (n SecurePortal) Connect(prefix *name.Name) {
	randomSuffix, _ := random.GenerateRandomString(16)
	bareHelloName, _ := prefix.AppendComponent(connectString)
	bareHelloName, _ = bareHelloName.AppendComponent(randomSuffix)

	// Send the bare hello
	log.Println("Sending the hello")
	bareHello := kex.KEXHello()
	bareHelloRequest := interest.CreateWithName(bareHelloName)
	bareHelloRequest.AddContainer(bareHello)
	n.apiStack.Push(messages.Package(bareHelloRequest))

	// Wait for the response, and use it to build the full hello
	replyWrapper := n.apiStack.Pop()
	reply := replyWrapper.InnerMessage()
	log.Println("Got the REJECT")

	reject, err := reply.GetContainer(codec.T_KEX)
	if err != nil {
		log.Println("Error: no KEX container in the REJECT content object")
		return
	}
	hello := kex.KEXFullHello(bareHello, reject.(*kex.KEX))

	randomSuffix, _ = random.GenerateRandomString(16)
	helloName, _ := prefix.AppendComponent(connectString)
	helloName, _ = prefix.AppendComponent(randomSuffix)

	helloRequest := interest.CreateWithName(helloName)
	helloRequest.AddContainer(hello)
	n.apiStack.Push(messages.Package(helloRequest))

	// Wait for the response to complete the KEX
	replyWrapper = n.apiStack.Pop()
	reply = replyWrapper.InnerMessage()

	accept, err := reply.GetContainer(codec.T_KEX)
	if err != nil {
		log.Println("Error: no KEX container in the ACCEPT content object")
		return
	}
	acceptKEX := accept.(*kex.KEX)

	var sharedKey [32]byte
	var peerPublic [32]byte
	var privateKey [32]byte
	copy(peerPublic[:], acceptKEX.GetPublicKeyShare())
	copy(privateKey[:], hello.GetPrivateKeyShare())
	box.Precompute(&sharedKey, &peerPublic, &privateKey)

	log.Println("Consumer key: ", hex.EncodeToString(sharedKey[:]))

	session := tunnel.NewSession(sharedKey[:], acceptKEX.GetSessionID())
	n.apiStack.AddSession(session, prefix)
}

func (n SecurePortal) Get(request *messages.MessageWrapper, timeout time.Duration) (*messages.MessageWrapper, error) {
	signalChannel := make(chan *messages.MessageWrapper, 1)
	n.apiStack.Get(request, func(msg *messages.MessageWrapper) {
		signalChannel <- msg
	})

	var response *messages.MessageWrapper
	select {
	case data := <-signalChannel:
		return data, nil
	case <-time.After(timeout):
		return response, PortalError{0, "Timeout"}
	}
}

func (n SecurePortal) GetAsync(request *messages.MessageWrapper, callback ResponseMessageCallback) {
	n.apiStack.Get(request, func(msg *messages.MessageWrapper) {
		callback(msg)
	})
}

func (p SecurePortal) GetAsyncWithTimeout(request *messages.MessageWrapper, timeout time.Duration, callback ResponseMessageCallback) {
	signalChannel := make(chan *messages.MessageWrapper, 1)
	p.apiStack.Get(request, func(msg *messages.MessageWrapper) {
		signalChannel <- msg
	})

	select {
	case data := <-signalChannel:
		callback(data)
	case <-time.After(timeout):
		p.apiStack.Cancel(request)
	}
}

func (n SecurePortal) Serve(prefix *name.Name, callback RequestMessageCallback) {
	if prefix == nil {
		return
	}

	established := false
	for {
		requestWrapper := n.apiStack.Pop()

		// TODO(cawood): check the session ID to ensure that we have a live session
		if established {
			log.Println("Handling a request", requestWrapper.Name())
			response := callback(requestWrapper)
			if response != nil {
				n.apiStack.Push(response)
			} else {
				log.Println("Failed to generate a response")
			}
		} else {
			request := requestWrapper.InnerMessage()
			if !prefix.IsPrefix(request.Name()) {
				break
			}

			kexTLV, _ := request.GetContainer(codec.T_KEX)
			kexContainer := kexTLV.(*kex.KEX)

			switch kexContainer.GetMessageType() {
			case codec.T_KEX_BAREHELLO:
				log.Println("Got the BARE HELLO")
				reject := kex.KEXHelloReject(kexContainer, n.macKey)
				rejectResponse := content.CreateWithName(request.Name())
				rejectResponse.AddContainer(reject)
				n.apiStack.Push(messages.Package(rejectResponse))
				break

			case codec.T_KEX_HELLO:
				log.Println("Got the HELLO")
				accept, err := kex.KEXHelloAccept(kexContainer, n.macKey, n.encKey)
				if err != nil {
					log.Println(err)
					break
				}

				acceptResponse := content.CreateWithName(request.Name())
				acceptResponse.AddContainer(accept)
				n.apiStack.Push(messages.Package(acceptResponse))

				// XXX: go to the KDF step

				var sharedKey [32]byte
				var peerPublic [32]byte
				var privateKey [32]byte
				copy(peerPublic[:], kexContainer.GetPublicKeyShare())
				copy(privateKey[:], accept.GetPrivateKeyShare())
				box.Precompute(&sharedKey, &peerPublic, &privateKey)

				log.Println("Producer key:", hex.EncodeToString(sharedKey[:]))

				// Create and start the session
				time.Sleep(50 * time.Millisecond)
				session := tunnel.NewSession(sharedKey[:], accept.GetSessionID())
				n.apiStack.AddSession(session, prefix)
				established = true

				break

			case codec.T_KEX_REJECT:
			case codec.T_KEX_ACCEPT:
				log.Println("Got an invalid message...")
				// invalid message type to be received here...
				break
			}
		}
	}
}

func (p SecurePortal) Produce(data *messages.MessageWrapper) {
	p.apiStack.Push(data)
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/compute/v1"
)

func resourceComputeProjectMetadata() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeProjectMetadataCreate,
		Read:   resourceComputeProjectMetadataRead,
		Update: resourceComputeProjectMetadataUpdate,
		Delete: resourceComputeProjectMetadataDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		SchemaVersion: 0,

		Schema: map[string]*schema.Schema{
			"metadata": &schema.Schema{
				Type:     schema.TypeMap,
				Required: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"project": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
		},
	}
}

func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	projectID, err := getProject(d, config)
	if err != nil {
		return err
	}

	createMD := func() error {
		// Load project service
		log.Printf("[DEBUG] Loading project service: %s", projectID)
		project, err := config.clientCompute.Projects.Get(projectID).Do()
		if err != nil {
			return fmt.Errorf("Error loading project '%s': %s", projectID, err)
		}

		md := project.CommonInstanceMetadata

		newMDMap := d.Get("metadata").(map[string]interface{})
		// Ensure that we aren't overwriting entries that already exist
		for _, kv := range md.Items {
			if _, ok := newMDMap[kv.Key]; ok {
				return fmt.Errorf("Error, key '%s' already exists in project '%s'", kv.Key, projectID)
			}
		}

		// Append new metadata to existing metadata
		for key, val := range newMDMap {
			v := val.(string)
			md.Items = append(md.Items, &compute.MetadataItems{
				Key:   key,
				Value: &v,
			})
		}

		op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()

		if err != nil {
			return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
		}

		log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)

		return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
	}

	err = MetadataRetryWrapper(createMD)
	if err != nil {
		return err
	}

	return resourceComputeProjectMetadataRead(d, meta)
}

func resourceComputeProjectMetadataRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	if d.Id() == "" {
		projectID, err := getProject(d, config)
		if err != nil {
			return err
		}
		d.SetId(projectID)
	}

	// Load project service
	log.Printf("[DEBUG] Loading project service: %s", d.Id())
	project, err := config.clientCompute.Projects.Get(d.Id()).Do()
	if err != nil {
		return handleNotFoundError(err, d, fmt.Sprintf("Project metadata for project %q", d.Id()))
	}

	md := flattenMetadata(project.CommonInstanceMetadata)
	existingMetadata := d.Get("metadata").(map[string]interface{})
	// Remove all keys not explicitly mentioned in the terraform config
	for k := range md {
		if _, ok := existingMetadata[k]; !ok {
			delete(md, k)
		}
	}

	if err = d.Set("metadata", md); err != nil {
		return fmt.Errorf("Error setting metadata: %s", err)
	}

	d.Set("project", d.Id())
	d.SetId(d.Id())
	return nil
}

func resourceComputeProjectMetadataUpdate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	projectID, err := getProject(d, config)
	if err != nil {
		return err
	}

	if d.HasChange("metadata") {
		o, n := d.GetChange("metadata")

		updateMD := func() error {
			// Load project service
			log.Printf("[DEBUG] Loading project service: %s", projectID)
			project, err := config.clientCompute.Projects.Get(projectID).Do()
			if err != nil {
				return fmt.Errorf("Error loading project '%s': %s", projectID, err)
			}

			md := project.CommonInstanceMetadata

			MetadataUpdate(o.(map[string]interface{}), n.(map[string]interface{}), md)

			op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()

			if err != nil {
				return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
			}

			log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)

			// Optimistic locking requires the fingerprint received to match
			// the fingerprint we send the server, if there is a mismatch then we
			// are working on old data, and must retry
			return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
		}

		err := MetadataRetryWrapper(updateMD)
		if err != nil {
			return err
		}

		return resourceComputeProjectMetadataRead(d, meta)
	}

	return nil
}

func resourceComputeProjectMetadataDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	projectID, err := getProject(d, config)
	if err != nil {
		return err
	}

	// Load project service
	log.Printf("[DEBUG] Loading project service: %s", projectID)
	project, err := config.clientCompute.Projects.Get(projectID).Do()
	if err != nil {
		return fmt.Errorf("Error loading project '%s': %s", projectID, err)
	}

	md := project.CommonInstanceMetadata

	// Remove all items
	md.Items = nil

	op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()

	if err != nil {
		return fmt.Errorf("Error removing metadata from project %s: %s", projectID, err)
	}

	log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)

	err = computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
	if err != nil {
		return err
	}

	return resourceComputeProjectMetadataRead(d, meta)
}
<commit_msg>Compute project metadata reads were failing on import.<commit_after>package google

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/compute/v1"
)

func resourceComputeProjectMetadata() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeProjectMetadataCreate,
		Read:   resourceComputeProjectMetadataRead,
		Update: resourceComputeProjectMetadataUpdate,
		Delete: resourceComputeProjectMetadataDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		SchemaVersion: 0,

		Schema: map[string]*schema.Schema{
			"metadata": &schema.Schema{
				Type:     schema.TypeMap,
				Required: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"project": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
		},
	}
}

func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	projectID, err := getProject(d, config)
	if err != nil {
		return err
	}

	createMD := func() error {
		// Load project service
		log.Printf("[DEBUG] Loading project service: %s", projectID)
		project, err := config.clientCompute.Projects.Get(projectID).Do()
		if err != nil {
			return fmt.Errorf("Error loading project '%s': %s", projectID, err)
		}

		md := project.CommonInstanceMetadata

		newMDMap := d.Get("metadata").(map[string]interface{})
		// Ensure that we aren't overwriting entries that already exist
		for _, kv := range md.Items {
			if _, ok := newMDMap[kv.Key]; ok {
				return fmt.Errorf("Error, key '%s' already exists in project '%s'", kv.Key, projectID)
			}
		}

		// Append new metadata to existing metadata
		for key, val := range newMDMap {
			v := val.(string)
			md.Items = append(md.Items, &compute.MetadataItems{
				Key:   key,
				Value: &v,
			})
		}

		op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()

		if err != nil {
			return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
		}

		log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)

		return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
	}

	err = MetadataRetryWrapper(createMD)
	if err != nil {
		return err
	}

	return resourceComputeProjectMetadataRead(d, meta)
}

func resourceComputeProjectMetadataRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	if d.Id() == "" {
		projectID, err := getProject(d, config)
		if err != nil {
			return err
		}
		d.SetId(projectID)
	}

	// Load project service
	log.Printf("[DEBUG] Loading project service: %s", d.Id())
	project, err := config.clientCompute.Projects.Get(d.Id()).Do()
	if err != nil {
		return handleNotFoundError(err, d, fmt.Sprintf("Project metadata for project %q", d.Id()))
	}

	md := flattenMetadata(project.CommonInstanceMetadata)
	existingMetadata := d.Get("metadata").(map[string]interface{})
	// Remove all keys not explicitly mentioned in the terraform config
	// unless you're doing an import.
	if len(existingMetadata) > 0 {
		for k := range md {
			if _, ok := existingMetadata[k]; !ok {
				delete(md, k)
			}
		}
	}

	if err = d.Set("metadata", md); err != nil {
		return fmt.Errorf("Error setting metadata: %s", err)
	}

	d.Set("project", project.Name)
	d.SetId(project.Name)
	return nil
}

func resourceComputeProjectMetadataUpdate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	projectID, err := getProject(d, config)
	if err != nil {
		return err
	}

	if d.HasChange("metadata") {
		o, n := d.GetChange("metadata")

		updateMD := func() error {
			// Load project service
			log.Printf("[DEBUG] Loading project service: %s", projectID)
			project, err := config.clientCompute.Projects.Get(projectID).Do()
			if err != nil {
				return fmt.Errorf("Error loading project '%s': %s", projectID, err)
			}

			md := project.CommonInstanceMetadata

			MetadataUpdate(o.(map[string]interface{}), n.(map[string]interface{}), md)

			op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()

			if err != nil {
				return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err)
			}

			log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)

			// Optimistic locking requires the fingerprint received to match
			// the fingerprint we send the server, if there is a mismatch then we
			// are working on old data, and must retry
			return computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
		}

		err := MetadataRetryWrapper(updateMD)
		if err != nil {
			return err
		}

		return resourceComputeProjectMetadataRead(d, meta)
	}

	return nil
}

func resourceComputeProjectMetadataDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	projectID, err := getProject(d, config)
	if err != nil {
		return err
	}

	// Load project service
	log.Printf("[DEBUG] Loading project service: %s", projectID)
	project, err := config.clientCompute.Projects.Get(projectID).Do()
	if err != nil {
		return fmt.Errorf("Error loading project '%s': %s", projectID, err)
	}

	md := project.CommonInstanceMetadata

	// Remove all items
	md.Items = nil

	op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(projectID, md).Do()

	if err != nil {
		return fmt.Errorf("Error removing metadata from project %s: %s", projectID, err)
	}

	log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)

	err = computeOperationWait(config.clientCompute, op, project.Name, "SetCommonMetadata")
	if err != nil {
		return err
	}

	return resourceComputeProjectMetadataRead(d, meta)
}
<|endoftext|>