<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var (
	isup             = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	build            = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	up               = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	push             = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	pushup           = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.")
	down             = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	test             = flag.Bool("test", false, "Run Ginkgo tests.")
	testArgs         = flag.String("test_args", "", "Space-separated list of arguments to pass to Ginkgo test runner.")
	root             = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
	verbose          = flag.Bool("v", false, "If true, print all command output.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")

	ctlCmd = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)")
)

const (
	minNodeCount = 2
)

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

type TestResult struct {
	Pass int
	Fail int
}

type ResultsByTest map[string]TestResult

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()

	if *isup {
		status := 1
		if IsUp() {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		// The build-release script needs stdin to ask the user whether
		// it's OK to download the docker image.
		cmd := exec.Command(path.Join(*root, "hack/e2e-internal/build-release.sh"))
		cmd.Stdin = os.Stdin
		if !finishRunning("build-release", cmd) {
			log.Fatal("Error building. Aborting.")
		}
	}

	os.Setenv("KUBECTL", *root+`/cluster/kubectl.sh`+kubectlArgs())

	if *pushup {
		if IsUp() {
			log.Printf("e2e cluster is up, pushing.")
			*up = false
			*push = true
		} else {
			log.Printf("e2e cluster is down, creating.")
			*up = true
			*push = false
		}
	}
	if *up {
		if !Up() {
			log.Fatal("Error starting e2e cluster. Aborting.")
		}
	} else if *push {
		if !finishRunning("push", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-push.sh"))) {
			log.Fatal("Error pushing e2e cluster. Aborting.")
		}
	}

	success := true
	switch {
	case *ctlCmd != "":
		ctlArgs := strings.Fields(*ctlCmd)
		os.Setenv("KUBE_CONFIG_FILE", "config-test.sh")
		success = finishRunning("'kubectl "+*ctlCmd+"'", exec.Command(path.Join(*root, "cluster/kubectl.sh"), ctlArgs...))
	case *test:
		success = Test()
	}

	if *down {
		TearDown()
	}

	if !success {
		os.Exit(1)
	}
}

func TearDown() bool {
	return finishRunning("teardown", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-down.sh")))
}

// Up brings an e2e cluster up, recreating it if one is already running.
func Up() bool {
	if IsUp() {
		log.Printf("e2e cluster already running; will teardown")
		if res := TearDown(); !res {
			return false
		}
	}
	return finishRunning("up", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-up.sh")))
}

// Ensure that the cluster is large engough to run the e2e tests.
func ValidateClusterSize() {
	// Check that there are at least minNodeCount nodes running
	cmd := exec.Command(path.Join(*root, "hack/e2e-internal/e2e-cluster-size.sh"))
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	stdout, err := cmd.Output()
	if err != nil {
		log.Fatalf("Could not get nodes to validate cluster size (%s)", err)
	}

	numNodes, err := strconv.Atoi(strings.TrimSpace(string(stdout)))
	if err != nil {
		log.Fatalf("Could not count number of nodes to validate cluster size (%s)", err)
	}

	if numNodes < minNodeCount {
		log.Fatalf("Cluster size (%d) is too small to run e2e tests.  %d Nodes are required.", numNodes, minNodeCount)
	}
}

// Is the e2e cluster up?
func IsUp() bool {
	return finishRunning("get status", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-status.sh")))
}

func Test() bool {
	if !IsUp() {
		log.Fatal("Testing requested, but e2e cluster not up!")
	}

	ValidateClusterSize()

	return finishRunning("Ginkgo tests", exec.Command(filepath.Join(*root, "hack/ginkgo-e2e.sh"), strings.Fields(*testArgs)...))
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		return false
	}
	return true
}

// returns either "", or a list of args intended for appending with the
// kubectl command (beginning with a space).
func kubectlArgs() string {
	if *checkVersionSkew {
		return " --match-server-version"
	}
	return ""
}
<commit_msg>Add a flag that lets e2e tests be run against single-node clusters.<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var (
	isup             = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	build            = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	up               = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	push             = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	pushup           = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.")
	down             = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	test             = flag.Bool("test", false, "Run Ginkgo tests.")
	testArgs         = flag.String("test_args", "", "Space-separated list of arguments to pass to Ginkgo test runner.")
	root             = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
	verbose          = flag.Bool("v", false, "If true, print all command output.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")
	checkNodeCount = flag.Bool("check_node_count", true, ""+
		"By default, verify that the cluster has at least two nodes."+
		"You can explicitly set to false if you're, e.g., testing single-node clusters "+
		"for which the node count is supposed to be one.")

	ctlCmd = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)")
)

const (
	minNodeCount = 2
)

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

type TestResult struct {
	Pass int
	Fail int
}

type ResultsByTest map[string]TestResult

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()

	if *isup {
		status := 1
		if IsUp() {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		// The build-release script needs stdin to ask the user whether
		// it's OK to download the docker image.
		cmd := exec.Command(path.Join(*root, "hack/e2e-internal/build-release.sh"))
		cmd.Stdin = os.Stdin
		if !finishRunning("build-release", cmd) {
			log.Fatal("Error building. Aborting.")
		}
	}

	os.Setenv("KUBECTL", *root+`/cluster/kubectl.sh`+kubectlArgs())

	if *pushup {
		if IsUp() {
			log.Printf("e2e cluster is up, pushing.")
			*up = false
			*push = true
		} else {
			log.Printf("e2e cluster is down, creating.")
			*up = true
			*push = false
		}
	}
	if *up {
		if !Up() {
			log.Fatal("Error starting e2e cluster. Aborting.")
		}
	} else if *push {
		if !finishRunning("push", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-push.sh"))) {
			log.Fatal("Error pushing e2e cluster. Aborting.")
		}
	}

	success := true
	switch {
	case *ctlCmd != "":
		ctlArgs := strings.Fields(*ctlCmd)
		os.Setenv("KUBE_CONFIG_FILE", "config-test.sh")
		success = finishRunning("'kubectl "+*ctlCmd+"'", exec.Command(path.Join(*root, "cluster/kubectl.sh"), ctlArgs...))
	case *test:
		success = Test()
	}

	if *down {
		TearDown()
	}

	if !success {
		os.Exit(1)
	}
}

func TearDown() bool {
	return finishRunning("teardown", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-down.sh")))
}

// Up brings an e2e cluster up, recreating it if one is already running.
func Up() bool {
	if IsUp() {
		log.Printf("e2e cluster already running; will teardown")
		if res := TearDown(); !res {
			return false
		}
	}
	return finishRunning("up", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-up.sh")))
}

// Ensure that the cluster is large engough to run the e2e tests.
func ValidateClusterSize() {
	// Check that there are at least minNodeCount nodes running
	cmd := exec.Command(path.Join(*root, "hack/e2e-internal/e2e-cluster-size.sh"))
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	stdout, err := cmd.Output()
	if err != nil {
		log.Fatalf("Could not get nodes to validate cluster size (%s)", err)
	}

	numNodes, err := strconv.Atoi(strings.TrimSpace(string(stdout)))
	if err != nil {
		log.Fatalf("Could not count number of nodes to validate cluster size (%s)", err)
	}

	if numNodes < minNodeCount {
		log.Fatalf("Cluster size (%d) is too small to run e2e tests.  %d Nodes are required.", numNodes, minNodeCount)
	}
}

// Is the e2e cluster up?
func IsUp() bool {
	return finishRunning("get status", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-status.sh")))
}

func Test() bool {
	if !IsUp() {
		log.Fatal("Testing requested, but e2e cluster not up!")
	}

	if *checkNodeCount {
		ValidateClusterSize()
	}

	return finishRunning("Ginkgo tests", exec.Command(filepath.Join(*root, "hack/ginkgo-e2e.sh"), strings.Fields(*testArgs)...))
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		return false
	}
	return true
}

// returns either "", or a list of args intended for appending with the
// kubectl command (beginning with a space).
func kubectlArgs() string {
	if *checkVersionSkew {
		return " --match-server-version"
	}
	return ""
}
<|endoftext|>


<commit_before>package indexer

import (
	"io"
	"sync"
	"testing"
	"time"

	"github.com/ava-labs/avalanchego/database/memdb"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/snow/engine/common"
	"github.com/ava-labs/avalanchego/snow/triggers"
	"github.com/ava-labs/avalanchego/utils"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/stretchr/testify/assert"
)

type apiServerMock struct {
	timesCalled int
}

func (a *apiServerMock) AddRoute(_ *common.HTTPHandler, _ *sync.RWMutex, _, _ string, _ io.Writer) error {
	a.timesCalled++
	return nil
}

// Test that the indexer works handles initially indexed chains properly
func TestIndexInitialChains(t *testing.T) {
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})
	chain1ID, chain2ID := ids.GenerateTestID(), ids.GenerateTestID()
	initiallyIndexed := ids.Set{}
	initiallyIndexed.Add(chain1ID, chain2ID)
	chain1Ctx, chain2Ctx := snow.DefaultContextTest(), snow.DefaultContextTest()
	chain1Ctx.ChainID = chain1ID
	chain2Ctx.ChainID = chain2ID
	config := Config{
		IndexingEnabled:        true,
		AllowIncompleteIndex:   false,
		Log:                    logging.NoLog{},
		Name:                   "test",
		DB:                     memdb.New(),
		EventDispatcher:        ed,
		InitiallyIndexedChains: initiallyIndexed,
		ChainLookupF:           func(string) (ids.ID, error) { return ids.ID{}, nil },
		APIServer:              &apiServerMock{},
	}

	// Create indexer and make sure its state is right
	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)
	assert.Equal(config.Name, idxr.name)
	assert.NotNil(idxr.log)
	assert.NotNil(idxr.db)
	assert.NotNil(idxr.indexedChains)
	assert.False(idxr.allowIncompleteIndex)
	assert.NotNil(idxr.chainLookup)
	assert.NotNil(idxr.codec)
	assert.NotNil(idxr.chainToIndex)
	assert.Equal(1, config.APIServer.(*apiServerMock).timesCalled)
	indexedChains := idxr.GetIndexedChains()
	assert.Len(indexedChains, 2)
	assert.True(
		indexedChains[0] == chain1ID && indexedChains[1] == chain2ID ||
			indexedChains[1] == chain1ID && indexedChains[0] == chain2ID)

	// Accept a transaction on chain1 and on chain2
	container1ID, container1Bytes := ids.GenerateTestID(), utils.RandomBytes(32)
	container2ID, container2Bytes := ids.GenerateTestID(), utils.RandomBytes(32)

	type test struct {
		chainCtx       *snow.Context
		containerID    ids.ID
		containerBytes []byte
	}

	tests := []test{
		{
			chain1Ctx,
			container1ID,
			container1Bytes,
		},
		{
			chain2Ctx,
			container2ID,
			container2Bytes,
		},
	}

	for _, test := range tests {
		now := time.Now()
		idxr.clock.Set(now)
		expectedContainer := Container{
			ID:        test.containerID,
			Bytes:     test.containerBytes,
			Timestamp: now.Unix(),
		}

		// Accept a container
		ed.Accept(test.chainCtx, test.containerID, test.containerBytes)

		// Verify GetLastAccepted is right
		gotLastAccepted, err := idxr.GetLastAccepted(test.chainCtx.ChainID)
		assert.NoError(err)
		assert.Equal(expectedContainer, gotLastAccepted)

		// Verify GetContainerByID is right
		container, err := idxr.GetContainerByID(test.chainCtx.ChainID, test.containerID)
		assert.NoError(err)
		assert.Equal(expectedContainer, container)

		// Verify GetIndex is right
		index, err := idxr.GetIndex(test.chainCtx.ChainID, test.containerID)
		assert.NoError(err)
		assert.EqualValues(0, index)

		// Verify GetContainerByIndex is right
		container, err = idxr.GetContainerByIndex(test.chainCtx.ChainID, 0)
		assert.NoError(err)
		assert.Equal(expectedContainer, container)

		// Verify GetContainerRange is right
		containers, err := idxr.GetContainerRange(test.chainCtx.ChainID, 0, 1)
		assert.NoError(err)
		assert.Len(containers, 1)
		assert.Equal(expectedContainer, containers[0])
	}
	assert.NoError(idxr.Close())
}

// Test that chains added to indexer with IndexChain work
func TestIndexChain(t *testing.T) {
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})
	config := Config{
		IndexingEnabled:        true,
		AllowIncompleteIndex:   false,
		Log:                    logging.NoLog{},
		Name:                   "test",
		DB:                     memdb.New(),
		EventDispatcher:        ed,
		InitiallyIndexedChains: ids.Set{}, // No chains indexed at start
		ChainLookupF:           func(string) (ids.ID, error) { return ids.ID{}, nil },
		APIServer:              &apiServerMock{},
	}

	// Create indexer and make sure its state is right
	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)
	assert.Equal(config.Name, idxr.name)
	assert.NotNil(idxr.log)
	assert.NotNil(idxr.db)
	assert.NotNil(idxr.indexedChains)
	assert.False(idxr.allowIncompleteIndex)
	assert.NotNil(idxr.chainLookup)
	assert.NotNil(idxr.codec)
	assert.NotNil(idxr.chainToIndex)
	assert.Equal(1, config.APIServer.(*apiServerMock).timesCalled)
	assert.Len(idxr.GetIndexedChains(), 0)

	ctx := snow.DefaultContextTest()
	ctx.ChainID = ids.GenerateTestID()
	err = idxr.IndexChain(ctx.ChainID)
	assert.Error(err)                // Should error because incomplete indices not allowed
	idxr.allowIncompleteIndex = true // Allow incomplete index
	err = idxr.IndexChain(ctx.ChainID)
	assert.NoError(err) // Should succeed now

	containerID, containerBytes := ids.GenerateTestID(), utils.RandomBytes(32)
	now := time.Now()
	idxr.clock.Set(now)
	expectedContainer := Container{
		ID:        containerID,
		Bytes:     containerBytes,
		Timestamp: now.Unix(),
	}

	// Accept a container
	ed.Accept(ctx, containerID, containerBytes)

	// Verify GetLastAccepted is right
	gotLastAccepted, err := idxr.GetLastAccepted(ctx.ChainID)
	assert.NoError(err)
	assert.Equal(expectedContainer, gotLastAccepted)

	// Verify GetContainerByID is right
	container, err := idxr.GetContainerByID(ctx.ChainID, containerID)
	assert.NoError(err)
	assert.Equal(expectedContainer, container)

	// Verify GetIndex is right
	index, err := idxr.GetIndex(ctx.ChainID, containerID)
	assert.NoError(err)
	assert.EqualValues(0, index)

	// Verify GetContainerByIndex is right
	container, err = idxr.GetContainerByIndex(ctx.ChainID, 0)
	assert.NoError(err)
	assert.Equal(expectedContainer, container)

	// Verify GetContainerRange is right
	containers, err := idxr.GetContainerRange(ctx.ChainID, 0, 1)
	assert.NoError(err)
	assert.Len(containers, 1)
	assert.Equal(expectedContainer, containers[0])

	assert.NoError(idxr.Close())
}

// Test method CloseIndex
func TestCloseIndex(t *testing.T) {
	// Setup
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})
	chain1ID := ids.GenerateTestID()
	initiallyIndexed := ids.Set{}
	initiallyIndexed.Add(chain1ID)
	chain1Ctx := snow.DefaultContextTest()
	chain1Ctx.ChainID = chain1ID
	config := Config{
		IndexingEnabled:        true,
		AllowIncompleteIndex:   false,
		Log:                    logging.NoLog{},
		Name:                   "test",
		DB:                     memdb.New(),
		EventDispatcher:        ed,
		InitiallyIndexedChains: initiallyIndexed,
		ChainLookupF:           func(string) (ids.ID, error) { return ids.ID{}, nil },
		APIServer:              &apiServerMock{},
	}

	// Create indexer and make sure its state is right
	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)
	assert.Equal(config.Name, idxr.name)
	assert.NotNil(idxr.log)
	assert.NotNil(idxr.db)
	assert.NotNil(idxr.indexedChains)
	assert.False(idxr.allowIncompleteIndex)
	assert.NotNil(idxr.chainLookup)
	assert.NotNil(idxr.codec)
	assert.NotNil(idxr.chainToIndex)
	assert.Equal(1, config.APIServer.(*apiServerMock).timesCalled)
	assert.Len(idxr.GetIndexedChains(), 1)

	// Stop indexing a non-existent chain (shouldn't do anything)
	err = idxr.CloseIndex(ids.GenerateTestID())
	assert.NoError(err)
	assert.Len(idxr.GetIndexedChains(), 1)

	// Stop indexing a chain
	err = idxr.CloseIndex(chain1ID)
	assert.NoError(err)
	assert.Len(idxr.GetIndexedChains(), 0)

	// Shouldn't be able to get things from this index anymore
	_, err = idxr.GetLastAccepted(chain1ID)
	assert.Error(err)
}

// Test that indexer doesn't allow an incomplete index
// unless that is allowed in the config
func TestIncompleteIndexStartup(t *testing.T) {
	// Setup
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})
	chain1ID := ids.GenerateTestID()
	initiallyIndexed := ids.Set{}
	initiallyIndexed.Add(chain1ID)
	chain1Ctx := snow.DefaultContextTest()
	chain1Ctx.ChainID = chain1ID
	db := memdb.New()
	defer db.Close()
	dbCopy := versiondb.New(db) // Will be written to [db]
	config := Config{
		IndexingEnabled:        true,
		AllowIncompleteIndex:   false,
		Log:                    logging.NoLog{},
		Name:                   "test",
		DB:                     dbCopy,
		EventDispatcher:        ed,
		InitiallyIndexedChains: initiallyIndexed,
		ChainLookupF:           func(string) (ids.ID, error) { return ids.ID{}, nil },
		APIServer:              &apiServerMock{},
	}

	// Create indexer with incomplete index disallowed
	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)

	// Close the indexer after copying its contents to [db]
	assert.NoError(dbCopy.Commit())
	assert.NoError(idxr.Close())

	// Re-open the indexer. Should be allowed since we never ran without indexing chain1.
	dbCopy = versiondb.New(db) // Because [dbCopy] was closed when indexer closed
	config.DB = dbCopy
	idxrIntf, err = NewIndexer(config)
	assert.NoError(err)
	idxr, ok = idxrIntf.(*indexer)
	assert.True(ok)
	// Close the indexer again
	assert.NoError(dbCopy.Commit())
	assert.NoError(idxr.Close())

	// Re-open the indexer with indexing disabled.
	dbCopy = versiondb.New(db) // Because [dbCopy] was closed when indexer closed
	config.DB = dbCopy
	config.IndexingEnabled = false
	_, err = NewIndexer(config)
	assert.NoError(dbCopy.Commit())
	assert.Error(err) // Should error because running would cause incomplete index

	config.AllowIncompleteIndex = true // allow incomplete index
	idxrIntf, err = NewIndexer(config)
	assert.NoError(err)
	assert.NoError(dbCopy.Commit())
	assert.NoError(idxrIntf.Close()) // close the indexer

	dbCopy = versiondb.New(db) // Because [dbCopy] was closed when indexer closed
	config.DB = dbCopy
	config.AllowIncompleteIndex = false
	config.IndexingEnabled = true
	_, err = NewIndexer(config)
	assert.NoError(dbCopy.Commit())
	assert.Error(err) // Should error because we have an incomplete index
}

// Test that indexer doesn't allow an incomplete index
// unless that is allowed in the config
func TestIncompleteIndexNewChain(t *testing.T) {
	// Setup
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})

	db := memdb.New()
	defer db.Close()
	dbCopy := versiondb.New(db) // Will be written to [db]
	config := Config{
		IndexingEnabled:        true,
		AllowIncompleteIndex:   false,
		Log:                    logging.NoLog{},
		Name:                   "test",
		DB:                     dbCopy,
		EventDispatcher:        ed,
		InitiallyIndexedChains: nil, // no initially indexed chains
		ChainLookupF:           func(string) (ids.ID, error) { return ids.ID{}, nil },
		APIServer:              &apiServerMock{},
	}

	// Create indexer with incomplete index disallowed
	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)

	// Should error because indexing new chain would cause incomplete index
	err = idxr.IndexChain(ids.GenerateTestID())
	assert.Error(err)
	assert.NoError(idxr.Close())

	// Allow incomplete index
	dbCopy = versiondb.New(db)
	config.DB = dbCopy
	config.AllowIncompleteIndex = true
	idxrIntf, err = NewIndexer(config)
	assert.NoError(err)
	idxr, ok = idxrIntf.(*indexer)
	assert.True(ok)

	err = idxr.IndexChain(ids.GenerateTestID()) // Should allow incomplete index
	assert.NoError(err)
	assert.NoError(idxr.Close())
}
<commit_msg>fix test<commit_after>package indexer

import (
	"io"
	"sync"
	"testing"

	"github.com/ava-labs/avalanchego/database/memdb"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/snow/engine/common"
	"github.com/ava-labs/avalanchego/snow/triggers"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/stretchr/testify/assert"
)

type apiServerMock struct {
	timesCalled int
}

func (a *apiServerMock) AddRoute(_ *common.HTTPHandler, _ *sync.RWMutex, _, _ string, _ io.Writer) error {
	a.timesCalled++
	return nil
}

// Test that newIndexer sets fields correctly
func TestNewIndexer(t *testing.T) {
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})
	config := Config{
		IndexingEnabled:      true,
		AllowIncompleteIndex: true,
		Log:                  logging.NoLog{},
		Name:                 "test",
		DB:                   memdb.New(),
		ConsensusDispatcher:  ed,
		DecisionDispatcher:   ed,
		APIServer:            &apiServerMock{},
		ShutdownF:            func() {},
	}

	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)
	assert.Equal("test", idxr.name)
	assert.NotNil(idxr.codec)
	assert.NotNil(idxr.log)
	assert.NotNil(idxr.db)
	assert.False(idxr.closed)
	assert.NotNil(idxr.routeAdder)
	assert.True(idxr.indexingEnabled)
	assert.True(idxr.allowIncompleteIndex)
	assert.NotNil(idxr.blockIndices)
	assert.NotNil(idxr.txIndices)
	assert.NotNil(idxr.vtxIndices)
	assert.NotNil(idxr.consensusDispatcher)
	assert.NotNil(idxr.decisionDispatcher)
	assert.NotNil(idxr.shutdownF)
	assert.False(idxr.hasRunBefore)
}

// Test that [hasRunBefore] is set correctly
func TestMarkHasRun(t *testing.T) {
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})
	baseDB := memdb.New()
	db := versiondb.New(baseDB)
	config := Config{
		IndexingEnabled:      true,
		AllowIncompleteIndex: true,
		Log:                  logging.NoLog{},
		Name:                 "test",
		DB:                   db,
		ConsensusDispatcher:  ed,
		DecisionDispatcher:   ed,
		APIServer:            &apiServerMock{},
		ShutdownF:            func() {},
	}

	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	assert.NoError(db.Commit())
	assert.NoError(idxrIntf.Close())

	config.DB = versiondb.New(baseDB)
	idxrIntf, err = NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)
	assert.True(idxr.hasRunBefore)
}

/*

// Test that chains added to indexer with IndexChain work
func TestIndexChain(t *testing.T) {
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})
	config := Config{
		IndexingEnabled:        true,
		AllowIncompleteIndex:   false,
		Log:                    logging.NoLog{},
		Name:                   "test",
		DB:                     memdb.New(),
		ConsensusDispatcher:        ed,
		DecisionsDispatcher: ed,
		InitiallyIndexedChains: ids.Set{}, // No chains indexed at start
		APIServer:              &apiServerMock{},
	}

	// Create indexer and make sure its state is right
	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)
	assert.Equal(config.Name, idxr.name)
	assert.NotNil(idxr.log)
	assert.NotNil(idxr.db)
	assert.NotNil(idxr.indexedChains)
	assert.False(idxr.allowIncompleteIndex)
	assert.NotNil(idxr.chainLookup)
	assert.NotNil(idxr.codec)
	assert.NotNil(idxr.chainToIndex)
	assert.Equal(1, config.APIServer.(*apiServerMock).timesCalled)
	assert.Len(idxr.GetIndexedChains(), 0)

	ctx := snow.DefaultContextTest()
	ctx.ChainID = ids.GenerateTestID()
	err = idxr.IndexChain(ctx.ChainID)
	assert.Error(err)                // Should error because incomplete indices not allowed
	idxr.allowIncompleteIndex = true // Allow incomplete index
	err = idxr.IndexChain(ctx.ChainID)
	assert.NoError(err) // Should succeed now

	containerID, containerBytes := ids.GenerateTestID(), utils.RandomBytes(32)
	now := time.Now()
	idxr.clock.Set(now)
	expectedContainer := Container{
		ID:        containerID,
		Bytes:     containerBytes,
		Timestamp: now.Unix(),
	}

	// Accept a container
	ed.Accept(ctx, containerID, containerBytes)

	// Verify GetLastAccepted is right
	gotLastAccepted, err := idxr.GetLastAccepted(ctx.ChainID)
	assert.NoError(err)
	assert.Equal(expectedContainer, gotLastAccepted)

	// Verify GetContainerByID is right
	container, err := idxr.GetContainerByID(ctx.ChainID, containerID)
	assert.NoError(err)
	assert.Equal(expectedContainer, container)

	// Verify GetIndex is right
	index, err := idxr.GetIndex(ctx.ChainID, containerID)
	assert.NoError(err)
	assert.EqualValues(0, index)

	// Verify GetContainerByIndex is right
	container, err = idxr.GetContainerByIndex(ctx.ChainID, 0)
	assert.NoError(err)
	assert.Equal(expectedContainer, container)

	// Verify GetContainerRange is right
	containers, err := idxr.GetContainerRange(ctx.ChainID, 0, 1)
	assert.NoError(err)
	assert.Len(containers, 1)
	assert.Equal(expectedContainer, containers[0])

	assert.NoError(idxr.Close())
}

// Test method CloseIndex
func TestCloseIndex(t *testing.T) {
	// Setup
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})
	chain1ID := ids.GenerateTestID()
	initiallyIndexed := ids.Set{}
	initiallyIndexed.Add(chain1ID)
	chain1Ctx := snow.DefaultContextTest()
	chain1Ctx.ChainID = chain1ID
	config := Config{
		IndexingEnabled:        true,
		AllowIncompleteIndex:   false,
		Log:                    logging.NoLog{},
		Name:                   "test",
		DB:                     memdb.New(),
		ConsensusDispatcher:        ed,
		DecisionsDispatcher: ed,
		APIServer:              &apiServerMock{},
	}

	// Create indexer and make sure its state is right
	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)
	assert.Equal(config.Name, idxr.name)
	assert.NotNil(idxr.log)
	assert.NotNil(idxr.db)
	assert.NotNil(idxr.indexedChains)
	assert.False(idxr.allowIncompleteIndex)
	assert.NotNil(idxr.chainLookup)
	assert.NotNil(idxr.codec)
	assert.NotNil(idxr.chainToIndex)
	assert.Equal(1, config.APIServer.(*apiServerMock).timesCalled)
	assert.Len(idxr.GetIndexedChains(), 1)

	// Stop indexing a non-existent chain (shouldn't do anything)
	err = idxr.CloseIndex(ids.GenerateTestID())
	assert.NoError(err)
	assert.Len(idxr.GetIndexedChains(), 1)

	// Stop indexing a chain
	err = idxr.CloseIndex(chain1ID)
	assert.NoError(err)
	assert.Len(idxr.GetIndexedChains(), 0)

	// Shouldn't be able to get things from this index anymore
	_, err = idxr.GetLastAccepted(chain1ID)
	assert.Error(err)
}

// Test that indexer doesn't allow an incomplete index
// unless that is allowed in the config
func TestIncompleteIndexStartup(t *testing.T) {
	// Setup
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})
	chain1ID := ids.GenerateTestID()
	initiallyIndexed := ids.Set{}
	initiallyIndexed.Add(chain1ID)
	chain1Ctx := snow.DefaultContextTest()
	chain1Ctx.ChainID = chain1ID
	db := memdb.New()
	defer db.Close()
	dbCopy := versiondb.New(db) // Will be written to [db]
	config := Config{
		IndexingEnabled:        true,
		AllowIncompleteIndex:   false,
		Log:                    logging.NoLog{},
		Name:                   "test",
		DB:                     dbCopy,
		ConsensusDispatcher:        ed,
		DecisionsDispatcher: ed,
		APIServer:              &apiServerMock{},
	}

	// Create indexer with incomplete index disallowed
	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)

	// Close the indexer after copying its contents to [db]
	assert.NoError(dbCopy.Commit())
	assert.NoError(idxr.Close())

	// Re-open the indexer. Should be allowed since we never ran without indexing chain1.
	dbCopy = versiondb.New(db) // Because [dbCopy] was closed when indexer closed
	config.DB = dbCopy
	idxrIntf, err = NewIndexer(config)
	assert.NoError(err)
	idxr, ok = idxrIntf.(*indexer)
	assert.True(ok)
	// Close the indexer again
	assert.NoError(dbCopy.Commit())
	assert.NoError(idxr.Close())

	// Re-open the indexer with indexing disabled.
	dbCopy = versiondb.New(db) // Because [dbCopy] was closed when indexer closed
	config.DB = dbCopy
	config.IndexingEnabled = false
	_, err = NewIndexer(config)
	assert.NoError(dbCopy.Commit())
	assert.Error(err) // Should error because running would cause incomplete index

	config.AllowIncompleteIndex = true // allow incomplete index
	idxrIntf, err = NewIndexer(config)
	assert.NoError(err)
	assert.NoError(dbCopy.Commit())
	assert.NoError(idxrIntf.Close()) // close the indexer

	dbCopy = versiondb.New(db) // Because [dbCopy] was closed when indexer closed
	config.DB = dbCopy
	config.AllowIncompleteIndex = false
	config.IndexingEnabled = true
	_, err = NewIndexer(config)
	assert.NoError(dbCopy.Commit())
	assert.Error(err) // Should error because we have an incomplete index
}

// Test that indexer doesn't allow an incomplete index
// unless that is allowed in the config
func TestIncompleteIndexNewChain(t *testing.T) {
	// Setup
	assert := assert.New(t)
	ed := &triggers.EventDispatcher{}
	ed.Initialize(logging.NoLog{})

	db := memdb.New()
	defer db.Close()
	dbCopy := versiondb.New(db) // Will be written to [db]
	config := Config{
		IndexingEnabled:        true,
		AllowIncompleteIndex:   false,
		Log:                    logging.NoLog{},
		Name:                   "test",
		DB:                     dbCopy,
		ConsensusDispatcher:        ed,
		DecisionsDispatcher: ed,
		InitiallyIndexedChains: nil, // no initially indexed chains
		APIServer:              &apiServerMock{},
	}

	// Create indexer with incomplete index disallowed
	idxrIntf, err := NewIndexer(config)
	assert.NoError(err)
	idxr, ok := idxrIntf.(*indexer)
	assert.True(ok)

	// Should error because indexing new chain would cause incomplete index
	err = idxr.IndexChain(ids.GenerateTestID())
	assert.Error(err)
	assert.NoError(idxr.Close())

	// Allow incomplete index
	dbCopy = versiondb.New(db)
	config.DB = dbCopy
	config.AllowIncompleteIndex = true
	idxrIntf, err = NewIndexer(config)
	assert.NoError(err)
	idxr, ok = idxrIntf.(*indexer)
	assert.True(ok)

	err = idxr.IndexChain(ids.GenerateTestID()) // Should allow incomplete index
	assert.NoError(err)
	assert.NoError(idxr.Close())
}
*/
<|endoftext|>


<commit_before>package gospel

import "testing"

func TestDescribe(t *testing.T) {
	Describe(t, "gospel.Expectation#ToEqual", func() {
		Context("wIth 1 & 1", func() {
			It("compares integers by ==", func() {
				Expect(1).ToEqual(2)
				Expect(1).ToEqual(1)
			})
		})
		Context("wIth `1` & `1`", func() {
			It("compares strings by ==", func() {
				Expect("1").ToEqual("1")
			})
		})
	})

	Describe(t, "gospel.Expectation#ToNotEqual", func() {
		Context("wIth 1 & 2", func() {
			It("compares integers by !=", func() {
				Expect(1).ToNotEqual(2)
			})
		})
		Context("wIth `1` & `2`", func() {
			It("compares strings by !=", func() {
				Expect("1").ToNotEqual("2")
			})
		})
	})

	Describe(t, "gospel.Expectation#ToExist", func() {
		Context("wIth 1", func() {
			It("checks existence by non-equivalence wIth nil", func() {
				Expect(1).ToExist()
			})
		})
		Context("wIth `1`", func() {
			It("checks existence by non-equivalence wIth nil", func() {
				Expect("1").ToExist()
			})
		})
	})

	Describe(t, "gospel.Expectation#ToNotExist", func() {
		Context("wIth nil", func() {
			It("checks existence by equivalence wIth nil", func() {
				Expect(nil).ToNotExist()
			})
		})
	})
}
<commit_msg>Fix typo<commit_after>package gospel

import "testing"

func TestDescribe(t *testing.T) {
	Describe(t, "gospel.Expectation#ToEqual", func() {
		Context("with 1 & 1", func() {
			It("compares integers by ==", func() {
				Expect(1).ToEqual(2)
				Expect(1).ToEqual(1)
			})
		})
		Context("with `1` & `1`", func() {
			It("compares strings by ==", func() {
				Expect("1").ToEqual("1")
			})
		})
	})

	Describe(t, "gospel.Expectation#ToNotEqual", func() {
		Context("with 1 & 2", func() {
			It("compares integers by !=", func() {
				Expect(1).ToNotEqual(2)
			})
		})
		Context("with `1` & `2`", func() {
			It("compares strings by !=", func() {
				Expect("1").ToNotEqual("2")
			})
		})
	})

	Describe(t, "gospel.Expectation#ToExist", func() {
		Context("with 1", func() {
			It("checks existence by non-equivalence with nil", func() {
				Expect(1).ToExist()
			})
		})
		Context("with `1`", func() {
			It("checks existence by non-equivalence with nil", func() {
				Expect("1").ToExist()
			})
		})
	})

	Describe(t, "gospel.Expectation#ToNotExist", func() {
		Context("with nil", func() {
			It("checks existence by equivalence with nil", func() {
				Expect(nil).ToNotExist()
			})
		})
	})
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"hash/fnv"
	"math/rand"
	"net/http"
	"sync"
	"time"
)

func requestID(next http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		ctx := newContextWithRequestID(r.Context(), r)
		next.ServeHTTP(rw, r.WithContext(ctx))
	})
}

func newContextWithRequestID(ctx context.Context, r *http.Request) context.Context {
	reqID := r.Header.Get("X-Request-ID")
	if reqID == "" {
		reqID = generateRandomID()
	}

	return context.WithValue(ctx, requestIDKey, reqID)
}

func serviceTime(next http.Handler) http.Handler {
	record := func(r *http.Request, duration time.Duration) {
		// TODO(jabley): send data to a metrics gathering service
	}

	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		start := time.Now()
		defer record(r, time.Now().Sub(start))
		next.ServeHTTP(rw, r)
	})
}

func instrument(next http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		ctx := newInstrumentedContext(r.Context())
		next.ServeHTTP(rw, r.WithContext(ctx))
	})
}

func newInstrumentedContext(ctx context.Context) context.Context {
	// TODO(jabley): add metrics gathering objects to the request context.
	return ctx
}

func mainHandler(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/home", backends, rw, r)
	})
}

func process(client *http.Client, path string, backends []backend, rw http.ResponseWriter, r *http.Request) {
	results := make(chan KeyValue, len(backends))

	var wg sync.WaitGroup

	for _, b := range backends {
		wg.Add(1)

		go func(b backend, results chan<- KeyValue) {
			defer wg.Done()
			// TODO(jabley): capture the response time
			// start := time.Now()
			// defer doSomething(b, time.Since(start))
			fetch(client, path, b, results)
		}(b, results)
	}

	wg.Wait()

	values := make([]KeyValue, len(backends))
	for i := range values {
		values[i] = <-results
	}

	rw.Header().Set("Content-Type", "text/html; charset=UTF-8")
	rw.Header().Set("Cache-Control", "private, no-cache, no-store, must-revalidate")

	if err := tmpl.Execute(rw, values); err != nil {
		panic(err)
	}
}

func fetch(client *http.Client, path string, b backend, results chan<- KeyValue) {
	res, err := client.Get("http://" + b.address + path)

	if err != nil {
		results <- KeyValue{b.name, err.Error()}
	} else {
		defer res.Body.Close()
		results <- KeyValue{b.name, res.Status}
	}
}

func productListing(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/products", backends, rw, r)
	})
}

func productDetail(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/products/"+hash(r.URL.Path), backends, rw, r)
	})
}

func categoryListing(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/categories", backends, rw, r)
	})
}

func categoryDetail(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/categories/"+hash(r.URL.Path), backends, rw, r)
	})
}

func search(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/search?q="+hash(r.URL.Path), backends, rw, r)
	})
}

func account(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/account", backends, rw, r)
	})
}

func checkout(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/checkout", backends, rw, r)
	})
}

func hash(s string) string {
	h := fnv.New32a()
	h.Write([]byte(s))
	return fmt.Sprintf("%s", h.Sum32())
}

func unreliableHandler(percentageFailures int) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)
		if rand.Intn(100) < percentageFailures {
			rw.WriteHeader(http.StatusBadRequest)
			rw.Write([]byte(`{
  "errors": [
    {
      "status": "400",
      "source": { "pointer": "/data/attributes/first-name" },
      "title":  "Invalid Attribute",
      "detail": "First name must contain at least three characters."
    }
  ]
}`))
		} else {
			rw.WriteHeader(http.StatusOK)
			rw.Write([]byte(`{
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!",
      "body": "The shortest article. Ever.",
      "created": "2015-05-22T14:56:29.000Z",
      "updated": "2015-05-22T14:56:28.000Z"
    },
    "relationships": {
      "author": {
        "data": {"id": "42", "type": "people"}
      }
    }
  }],
  "included": [
    {
      "type": "people",
      "id": "42",
      "attributes": {
        "name": "John",
        "age": 80,
        "gender": "male"
      }
    }
  ]
}`))
		}
	})
}
<commit_msg>Add predictable response times for backends<commit_after>package main

import (
	"context"
	"fmt"
	"hash/fnv"
	"math/rand"
	"net/http"
	"sync"
	"time"
)

func requestID(next http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		ctx := newContextWithRequestID(r.Context(), r)
		next.ServeHTTP(rw, r.WithContext(ctx))
	})
}

func newContextWithRequestID(ctx context.Context, r *http.Request) context.Context {
	reqID := r.Header.Get("X-Request-ID")
	if reqID == "" {
		reqID = generateRandomID()
	}

	return context.WithValue(ctx, requestIDKey, reqID)
}

func serviceTime(next http.Handler) http.Handler {
	record := func(r *http.Request, duration time.Duration) {
		// TODO(jabley): send data to a metrics gathering service
	}

	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		start := time.Now()
		defer record(r, time.Now().Sub(start))
		next.ServeHTTP(rw, r)
	})
}

func instrument(next http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		ctx := newInstrumentedContext(r.Context())
		next.ServeHTTP(rw, r.WithContext(ctx))
	})
}

func newInstrumentedContext(ctx context.Context) context.Context {
	// TODO(jabley): add metrics gathering objects to the request context.
	return ctx
}

func mainHandler(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/home", backends, rw, r)
	})
}

func process(client *http.Client, path string, backends []backend, rw http.ResponseWriter, r *http.Request) {
	results := make(chan KeyValue, len(backends))

	var wg sync.WaitGroup

	for _, b := range backends {
		wg.Add(1)

		go func(b backend, results chan<- KeyValue) {
			defer wg.Done()
			// TODO(jabley): capture the response time
			// start := time.Now()
			// defer doSomething(b, time.Since(start))
			fetch(client, path, b, results)
		}(b, results)
	}

	wg.Wait()

	values := make([]KeyValue, len(backends))
	for i := range values {
		values[i] = <-results
	}

	rw.Header().Set("Content-Type", "text/html; charset=UTF-8")
	rw.Header().Set("Cache-Control", "private, no-cache, no-store, must-revalidate")

	if err := tmpl.Execute(rw, values); err != nil {
		panic(err)
	}
}

func fetch(client *http.Client, path string, b backend, results chan<- KeyValue) {
	URL := "http://" + b.address + path
	res, err := client.Get(URL)

	if err != nil {
		results <- KeyValue{b.name, err.Error()}
	} else {
		defer res.Body.Close()
		results <- KeyValue{b.name, res.Status}
	}
}

func productListing(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/products", backends, rw, r)
	})
}

func productDetail(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/products/"+hash(r.URL.Path), backends, rw, r)
	})
}

func categoryListing(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/categories", backends, rw, r)
	})
}

func categoryDetail(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/categories/"+hash(r.URL.Path), backends, rw, r)
	})
}

func search(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/search?q="+hash(r.URL.Path), backends, rw, r)
	})
}

func account(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/account", backends, rw, r)
	})
}

func checkout(client *http.Client, backends []backend) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		process(client, "/checkout", backends, rw, r)
	})
}

func hash(s string) string {
	return fmt.Sprintf("%d", hashAsUint(s))
}

func hashAsUint(s string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(s))
	return h.Sum32()
}

// predictableResponseTime gives a broadly similar response time for a given URL.
// This is used to fake the processing time to talk to a database, etc.
// For a given set of URLs, we want predictable behaviour. This is to show that
// certain customers / etc are slow. We should be able to see in a monitoring
// that requests for certain resources are slow.
func predictableResponseTime(r *http.Request) {
	crc := hashAsUint(r.URL.Path)
	if crc%5 == 0 {
		// perturb the response time for this one in a repeatable fashion
		time.Sleep(time.Duration(rand.Intn(200)+200) * time.Millisecond)
	}

	// This is our fake normal service time
	time.Sleep(time.Duration(time.Duration(rand.Intn(20)) * time.Millisecond))
}

func unreliableHandler(percentageFailures int) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		predictableResponseTime(r)
		if rand.Intn(100) < percentageFailures {
			rw.WriteHeader(http.StatusBadRequest)
			rw.Write([]byte(`{
  "errors": [
    {
      "status": "400",
      "source": { "pointer": "/data/attributes/first-name" },
      "title":  "Invalid Attribute",
      "detail": "First name must contain at least three characters."
    }
  ]
}`))
		} else {
			rw.WriteHeader(http.StatusOK)
			rw.Write([]byte(`{
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!",
      "body": "The shortest article. Ever.",
      "created": "2015-05-22T14:56:29.000Z",
      "updated": "2015-05-22T14:56:28.000Z"
    },
    "relationships": {
      "author": {
        "data": {"id": "42", "type": "people"}
      }
    }
  }],
  "included": [
    {
      "type": "people",
      "id": "42",
      "attributes": {
        "name": "John",
        "age": 80,
        "gender": "male"
      }
    }
  ]
}`))
		}
	})
}
<|endoftext|>


<commit_before>package evaluation

import (
	"fmt"
	"strings"

	"github.com/Zac-Garby/pluto/ast"
)

type args map[string]Object
type builtinFn func(args, *Context) Object

type Builtin struct {
	Pattern []string
	Fn      builtinFn
}

func NewBuiltin(ptn string, fn builtinFn, types map[string]Type) Builtin {
	pattern := strings.Split(ptn, " ")

	typedFn := func(args args, ctx *Context) Object {
		for key, t := range types {
			val := args[key]

			if !is(val, t) {
				return Err(
					ctx,
					"the $%s parameter of %s must be of type %s, not %s",
					"TypeError",
					key, ptn,
					t, val.Type(),
				)
			}
		}

		return fn(args, ctx)
	}

	return Builtin{
		Pattern: pattern,
		Fn:      typedFn,
	}
}

var empty = make(map[string]Type)

var builtins = []Builtin{}

func GetBuiltins() []Builtin {
	if len(builtins) == 0 {
		builtins = []Builtin{
			NewBuiltin("print $obj", printObj, empty),

			NewBuiltin("do $block", doBlock, map[string]Type{
				"block": BLOCK,
			}),

			NewBuiltin("do $block with $args", doBlockWithArgs, map[string]Type{
				"block": BLOCK,
				"args":  COLLECTION,
			}),

			NewBuiltin("do $block on $arg", doBlockOnArg, map[string]Type{
				"block": BLOCK,
			}),

			NewBuiltin("map $block over $collection", mapBlockOverCollection, map[string]Type{
				"block":      BLOCK,
				"collection": COLLECTION,
			}),

			NewBuiltin("format $format with $args", formatWithArgs, map[string]Type{
				"format": STRING,
				"args":   COLLECTION,
			}),

			NewBuiltin("$start to $end", startToEnd, map[string]Type{
				"start": NUMBER,
				"end":   NUMBER,
			}),

			NewBuiltin(
				"slice $collection from $start to $end",
				sliceCollectionFromStartToEnd,
				map[string]Type{
					"collection": COLLECTION,
					"start":      NUMBER,
					"end":        NUMBER,
				},
			),

			NewBuiltin(
				"slice $collection from $start",
				sliceCollectionFromStart,
				map[string]Type{
					"collection": COLLECTION,
					"start":      NUMBER,
				},
			),

			NewBuiltin(
				"slice $collection to $end",
				sliceCollectionToEnd,
				map[string]Type{
					"collection": COLLECTION,
					"end":        NUMBER,
				},
			),
		}
	}

	return builtins
}

// print $obj
func printObj(args args, ctx *Context) Object {
	fmt.Println(args["obj"])

	return O_NULL
}

// format $format with $args
func formatWithArgs(args args, ctx *Context) Object {
	var (
		format  = args["format"].(*String)
		formats = args["args"].(Collection)
	)

	// if format = "Hello, {}!" and args = ["world"]
	// the result will be "Hello, world!"

	result := format.Value

	for _, f := range formats.Elements() {
		result = strings.Replace(result, "{}", f.String(), 1)
	}

	return &String{Value: result}
}

func evalBlock(block *Block, args []Object, ctx *Context) Object {
	if len(block.Params) != len(args) {
		return err(
			ctx,
			"wrong number of arguments applied to a block. expected %d, got %d", "TypeError",
			len(block.Params),
			len(args),
		)
	}

	apArgs := make(map[string]Object)

	for i, param := range block.Params {
		apArgs[param.(*ast.Identifier).Value] = args[i]
	}

	return eval(block.Body, ctx.EncloseWith(apArgs))
}

// do $block
func doBlock(args args, ctx *Context) Object {
	block := args["block"].(*Block)

	return evalBlock(block, []Object{}, ctx)
}

// do $block with $args
func doBlockWithArgs(args args, ctx *Context) Object {
	var (
		block = args["block"].(*Block)
		col   = args["args"].(Collection)
	)

	return evalBlock(block, col.Elements(), ctx)
}

// do $block on $arg
func doBlockOnArg(args args, ctx *Context) Object {
	var (
		block = args["block"].(*Block)
		arg   = args["arg"]
	)

	return evalBlock(block, []Object{arg}, ctx)
}

// map $block over $collection
func mapBlockOverCollection(args args, ctx *Context) Object {
	var (
		block = args["block"].(*Block)
		col   = args["collection"].(Collection)
	)

	var result []Object

	for i, item := range col.Elements() {
		mapped := evalBlock(block, []Object{
			&Number{Value: float64(i)},
			item,
		}, ctx)

		if isErr(mapped) {
			return mapped
		}

		result = append(result, mapped)
	}

	return MakeCollection(col.Type(), result, ctx)
}

// $start to $end
func startToEnd(args args, ctx *Context) Object {
	var (
		start = args["start"].(*Number)
		end   = args["end"].(*Number)

		sVal = int(start.Value)
		eVal = int(end.Value)
	)

	if eVal < sVal {
		result := &Array{Value: []Object{}}

		for i := sVal; i >= eVal; i-- {
			result.Value = append(result.Value, &Number{Value: float64(i)})
		}

		return result
	} else if eVal > sVal {
		result := &Array{Value: []Object{}}

		for i := sVal; i < eVal+1; i++ {
			result.Value = append(result.Value, &Number{Value: float64(i)})
		}

		return result
	}

	return &Array{Value: []Object{start}}
}

func sliceCollectionFromStartToEnd(args args, ctx *Context) Object {
	var (
		col   = args["collection"].(Collection)
		start = args["start"].(*Number)
		end   = args["end"].(*Number)

		elems = col.Elements()
		sVal  = int(start.Value)
		eVal  = int(end.Value)
	)

	if sVal >= eVal {
		return err(ctx, "$start must be less than $end", "OutOfBoundsError")
	}

	if sVal < 0 || eVal < 0 {
		return err(ctx, "neither $start nor $end can be less than 0", "OutOfBoundsError")
	}

	if eVal >= len(elems) {
		return err(ctx, "$end must be contained by $collection", "OutOfBoundsError")
	}

	return &Array{Value: elems[sVal:eVal]}
}

func sliceCollectionFromStart(args args, ctx *Context) Object {
	var (
		col   = args["collection"].(Collection)
		start = args["start"].(*Number)

		elems = col.Elements()
		index = int(start.Value)
	)

	if index < 0 || index >= len(elems) {
		return err(ctx, "$start is out of bounds", "OutOfBoundsError")
	}

	return &Array{Value: elems[index:]}
}

func sliceCollectionToEnd(args args, ctx *Context) Object {
	var (
		col = args["collection"].(Collection)
		end = args["end"].(*Number)

		elems = col.Elements()
		index = int(end.Value)
	)

	if index < 0 || index >= len(elems) {
		return err(ctx, "$end is out of bounds", "OutOfBoundsError")
	}

	return &Array{Value: elems[:index]}
}
<commit_msg>Add builtin: filter $collection by $predicate<commit_after>package evaluation

import (
	"fmt"
	"strings"

	"github.com/Zac-Garby/pluto/ast"
)

type args map[string]Object
type builtinFn func(args, *Context) Object

type Builtin struct {
	Pattern []string
	Fn      builtinFn
}

func NewBuiltin(ptn string, fn builtinFn, types map[string]Type) Builtin {
	pattern := strings.Split(ptn, " ")

	typedFn := func(args args, ctx *Context) Object {
		for key, t := range types {
			val := args[key]

			if !is(val, t) {
				return Err(
					ctx,
					"the $%s parameter of %s must be of type %s, not %s",
					"TypeError",
					key, ptn,
					t, val.Type(),
				)
			}
		}

		return fn(args, ctx)
	}

	return Builtin{
		Pattern: pattern,
		Fn:      typedFn,
	}
}

var empty = make(map[string]Type)

var builtins = []Builtin{}

func GetBuiltins() []Builtin {
	if len(builtins) == 0 {
		builtins = []Builtin{
			NewBuiltin("print $obj", printObj, empty),

			NewBuiltin("do $block", doBlock, map[string]Type{
				"block": BLOCK,
			}),

			NewBuiltin("do $block with $args", doBlockWithArgs, map[string]Type{
				"block": BLOCK,
				"args":  COLLECTION,
			}),

			NewBuiltin("do $block on $arg", doBlockOnArg, map[string]Type{
				"block": BLOCK,
			}),

			NewBuiltin("map $block over $collection", mapBlockOverCollection, map[string]Type{
				"block":      BLOCK,
				"collection": COLLECTION,
			}),

			NewBuiltin("format $format with $args", formatWithArgs, map[string]Type{
				"format": STRING,
				"args":   COLLECTION,
			}),

			NewBuiltin("$start to $end", startToEnd, map[string]Type{
				"start": NUMBER,
				"end":   NUMBER,
			}),

			NewBuiltin(
				"slice $collection from $start to $end",
				sliceCollectionFromStartToEnd,
				map[string]Type{
					"collection": COLLECTION,
					"start":      NUMBER,
					"end":        NUMBER,
				},
			),

			NewBuiltin(
				"slice $collection from $start",
				sliceCollectionFromStart,
				map[string]Type{
					"collection": COLLECTION,
					"start":      NUMBER,
				},
			),

			NewBuiltin(
				"slice $collection to $end",
				sliceCollectionToEnd,
				map[string]Type{
					"collection": COLLECTION,
					"end":        NUMBER,
				},
			),

			NewBuiltin(
				"filter $collection by $predicate",
				filterCollectionByPredicate,
				map[string]Type{
					"collection": COLLECTION,
					"predicate":  BLOCK,
				},
			),
		}
	}

	return builtins
}

// print $obj
func printObj(args args, ctx *Context) Object {
	fmt.Println(args["obj"])

	return O_NULL
}

// format $format with $args
func formatWithArgs(args args, ctx *Context) Object {
	var (
		format  = args["format"].(*String)
		formats = args["args"].(Collection)
	)

	// if format = "Hello, {}!" and args = ["world"]
	// the result will be "Hello, world!"

	result := format.Value

	for _, f := range formats.Elements() {
		result = strings.Replace(result, "{}", f.String(), 1)
	}

	return &String{Value: result}
}

func evalBlock(block *Block, args []Object, ctx *Context) Object {
	if len(block.Params) != len(args) {
		return err(
			ctx,
			"wrong number of arguments applied to a block. expected %d, got %d", "TypeError",
			len(block.Params),
			len(args),
		)
	}

	apArgs := make(map[string]Object)

	for i, param := range block.Params {
		apArgs[param.(*ast.Identifier).Value] = args[i]
	}

	return eval(block.Body, ctx.EncloseWith(apArgs))
}

// do $block
func doBlock(args args, ctx *Context) Object {
	block := args["block"].(*Block)

	return evalBlock(block, []Object{}, ctx)
}

// do $block with $args
func doBlockWithArgs(args args, ctx *Context) Object {
	var (
		block = args["block"].(*Block)
		col   = args["args"].(Collection)
	)

	return evalBlock(block, col.Elements(), ctx)
}

// do $block on $arg
func doBlockOnArg(args args, ctx *Context) Object {
	var (
		block = args["block"].(*Block)
		arg   = args["arg"]
	)

	return evalBlock(block, []Object{arg}, ctx)
}

// map $block over $collection
func mapBlockOverCollection(args args, ctx *Context) Object {
	var (
		block = args["block"].(*Block)
		col   = args["collection"].(Collection)
	)

	var result []Object

	for i, item := range col.Elements() {
		mapped := evalBlock(block, []Object{
			&Number{Value: float64(i)},
			item,
		}, ctx)

		if isErr(mapped) {
			return mapped
		}

		result = append(result, mapped)
	}

	return MakeCollection(col.Type(), result, ctx)
}

// $start to $end
func startToEnd(args args, ctx *Context) Object {
	var (
		start = args["start"].(*Number)
		end   = args["end"].(*Number)

		sVal = int(start.Value)
		eVal = int(end.Value)
	)

	if eVal < sVal {
		result := &Array{Value: []Object{}}

		for i := sVal; i >= eVal; i-- {
			result.Value = append(result.Value, &Number{Value: float64(i)})
		}

		return result
	} else if eVal > sVal {
		result := &Array{Value: []Object{}}

		for i := sVal; i < eVal+1; i++ {
			result.Value = append(result.Value, &Number{Value: float64(i)})
		}

		return result
	}

	return &Array{Value: []Object{start}}
}

func sliceCollectionFromStartToEnd(args args, ctx *Context) Object {
	var (
		col   = args["collection"].(Collection)
		start = args["start"].(*Number)
		end   = args["end"].(*Number)

		elems = col.Elements()
		sVal  = int(start.Value)
		eVal  = int(end.Value)
	)

	if sVal >= eVal {
		return err(ctx, "$start must be less than $end", "OutOfBoundsError")
	}

	if sVal < 0 || eVal < 0 {
		return err(ctx, "neither $start nor $end can be less than 0", "OutOfBoundsError")
	}

	if eVal >= len(elems) {
		return err(ctx, "$end must be contained by $collection", "OutOfBoundsError")
	}

	return &Array{Value: elems[sVal:eVal]}
}

func sliceCollectionFromStart(args args, ctx *Context) Object {
	var (
		col   = args["collection"].(Collection)
		start = args["start"].(*Number)

		elems = col.Elements()
		index = int(start.Value)
	)

	if index < 0 || index >= len(elems) {
		return err(ctx, "$start is out of bounds", "OutOfBoundsError")
	}

	return &Array{Value: elems[index:]}
}

func sliceCollectionToEnd(args args, ctx *Context) Object {
	var (
		col = args["collection"].(Collection)
		end = args["end"].(*Number)

		elems = col.Elements()
		index = int(end.Value)
	)

	if index < 0 || index >= len(elems) {
		return err(ctx, "$end is out of bounds", "OutOfBoundsError")
	}

	return &Array{Value: elems[:index]}
}

func filterCollectionByPredicate(args args, ctx *Context) Object {
	var (
		col  = args["collection"].(Collection)
		pred = args["predicate"].(*Block)

		filtered = []Object{}
	)

	for i, item := range col.Elements() {
		result := evalBlock(pred, []Object{
			&Number{Value: float64(i)},
			item,
		}, ctx)

		if isErr(result) {
			return result
		}

		if isTruthy(result) {
			filtered = append(filtered, item)
		}
	}

	return MakeCollection(col.Type(), filtered, ctx)
}
<|endoftext|>


<commit_before>package main

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * 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.
 *
 * main.go: http server application for cassandra-summit-cfp-review
 *
 */

import (
	"encoding/json"
	"fmt"
	"github.com/gocql/gocql"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"time"
)

func RootHandler(w http.ResponseWriter, r *http.Request) {
	// check for auth but ignore the result: this will initialize
	// the cookie on page load
	checkAuth(w, r)
	http.ServeFile(w, r, "./public/index.html")
}

func AbstractsHandler(w http.ResponseWriter, r *http.Request) {
	if !checkAuth(w, r) {
		return
	}

	a := Abstract{}
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&a)

	switch r.Method {
	case "GET":
		alist, err := ListAbstracts(cass)
		if err != nil {
			http.Error(w, fmt.Sprintf("Failed to list abstracts: %s", err), 500)
		}
		jsonOut(w, r, alist)
		return
	case "PUT":
		if err != nil {
			http.Error(w, fmt.Sprintf("AbstractsHandler/PUT invalid json data: %s", err), 500)
		}

		a.Id = gocql.TimeUUID()
		a.Created = time.Now()
	case "PATCH":
		if err != nil {
			http.Error(w, fmt.Sprintf("AbstractsHandler/PATCH invalid json data: %s", err), 500)
		}
	default:
		http.Error(w, fmt.Sprintf("method '%s' not implemented", r.Method), 500)
		return
	}

	err = a.Save(cass)
	if err != nil {
		http.Error(w, fmt.Sprintf("AbstractsHandler/PUT a.Save() failed: %s", err), 500)
	}

	jsonOut(w, r, a)
}

func AbstractHandler(w http.ResponseWriter, r *http.Request) {
	if !checkAuth(w, r) {
		return
	}

	vars := mux.Vars(r)
	id, err := gocql.ParseUUID(vars["id"])
	if err != nil {
		http.Error(w, fmt.Sprintf("could not parse uuid: '%s'", err), 500)
	}
	a, _ := GetAbstract(cass, id)
	jsonOut(w, r, a)
}

func ScoreUpdateHandler(w http.ResponseWriter, r *http.Request) {
	if !checkAuth(w, r) {
		return
	}
	scores := make(ScoreUpdates, 7)
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&scores)
	if err != nil {
		log.Printf("invalid score update json: %s\n", err)
		http.Error(w, fmt.Sprintf("invalid score update json: %s", err), 500)
	}

	err = scores.Save(cass)
	if err != nil {
		log.Printf("score update failed: %s\n", err)
		http.Error(w, fmt.Sprintf("score update failed: %s", err), 500)
	}

	jsonOut(w, r, scores)
}

// returns the email string if authenticated (via persona), it won't
// be there at all if the user didn't authenticate
func checkAuth(w http.ResponseWriter, r *http.Request) bool {
	log.Println("checkAuth()")
	sess, err := store.Get(r, sessCookie)
	if err != nil {
		http.Error(w, fmt.Sprintf("failed to read cookie: %s\n", err), 400)
		return false
	}

	if sess.IsNew {
		log.Printf("Saving session ID '%s' to Cassandra.\n", sess.ID)
		sess.Save(r, w)
	}

	if sess.Values["email"] != nil {
		email := sess.Values["email"].(string)
		log.Printf("sess.Values[email]: '%s'\n", sess.Values["email"])
		if email != "" {
			return true
		}
	}

	return false
}
<commit_msg>add bare minimum error handling<commit_after>package main

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * 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.
 *
 * main.go: http server application for cassandra-summit-cfp-review
 *
 */

import (
	"encoding/json"
	"fmt"
	"github.com/gocql/gocql"
	"github.com/gorilla/mux"
	"log"
	"net/http"
	"time"
)

func RootHandler(w http.ResponseWriter, r *http.Request) {
	// check for auth but ignore the result: this will initialize
	// the cookie on page load
	checkAuth(w, r)
	http.ServeFile(w, r, "./public/index.html")
}

func AbstractsHandler(w http.ResponseWriter, r *http.Request) {
	if !checkAuth(w, r) {
		return
	}

	a := Abstract{}
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&a)

	switch r.Method {
	case "GET":
		alist, err := ListAbstracts(cass)
		if err != nil {
			http.Error(w, fmt.Sprintf("Failed to list abstracts: %s", err), 500)
		}
		jsonOut(w, r, alist)
		return
	case "PUT":
		if err != nil {
			http.Error(w, fmt.Sprintf("AbstractsHandler/PUT invalid json data: %s", err), 500)
		}

		a.Id = gocql.TimeUUID()
		a.Created = time.Now()
	case "PATCH":
		if err != nil {
			http.Error(w, fmt.Sprintf("AbstractsHandler/PATCH invalid json data: %s", err), 500)
		}
	default:
		http.Error(w, fmt.Sprintf("method '%s' not implemented", r.Method), 500)
		return
	}

	// bare minimum input checking
	if a.Title == "" || a.Body == "" || len(a.Attrs) == 0 || len(a.Authors) == 0 {
		http.Error(w, "required field missing", 500)
		return
	}

	err = a.Save(cass)
	if err != nil {
		http.Error(w, fmt.Sprintf("AbstractsHandler/PUT a.Save() failed: %s", err), 500)
	}

	jsonOut(w, r, a)
}

func AbstractHandler(w http.ResponseWriter, r *http.Request) {
	if !checkAuth(w, r) {
		return
	}

	vars := mux.Vars(r)
	id, err := gocql.ParseUUID(vars["id"])
	if err != nil {
		http.Error(w, fmt.Sprintf("could not parse uuid: '%s'", err), 500)
	}
	a, _ := GetAbstract(cass, id)
	jsonOut(w, r, a)
}

func ScoreUpdateHandler(w http.ResponseWriter, r *http.Request) {
	if !checkAuth(w, r) {
		return
	}
	scores := make(ScoreUpdates, 7)
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&scores)
	if err != nil {
		log.Printf("invalid score update json: %s\n", err)
		http.Error(w, fmt.Sprintf("invalid score update json: %s", err), 500)
	}

	err = scores.Save(cass)
	if err != nil {
		log.Printf("score update failed: %s\n", err)
		http.Error(w, fmt.Sprintf("score update failed: %s", err), 500)
	}

	jsonOut(w, r, scores)
}

// returns the email string if authenticated (via persona), it won't
// be there at all if the user didn't authenticate
func checkAuth(w http.ResponseWriter, r *http.Request) bool {
	log.Println("checkAuth()")
	sess, err := store.Get(r, sessCookie)
	if err != nil {
		http.Error(w, fmt.Sprintf("failed to read cookie: %s\n", err), 400)
		return false
	}

	if sess.IsNew {
		log.Printf("Saving session ID '%s' to Cassandra.\n", sess.ID)
		sess.Save(r, w)
	}

	if sess.Values["email"] != nil {
		email := sess.Values["email"].(string)
		log.Printf("sess.Values[email]: '%s'\n", sess.Values["email"])
		if email != "" {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package v1alpha1

import (
	v1 "k8s.io/api/core/v1"
	resource "k8s.io/apimachinery/pkg/api/resource"
)

// Default Values
var (
	DefaultTensorflowVersion  = "1.12"
	DefaultXGBoostVersion     = "1.12"
	DefaultScikitLearnVersion = "1.12"

	DefaultMemoryRequests = resource.MustParse("2Gi")
	DefaultCPURequests    = resource.MustParse("1")
)

// Default implements https://godoc.org/sigs.k8s.io/controller-runtime/pkg/webhook/admission#Defaulter
func (kfsvc *KFService) Default() {
	logger.Info("Defaulting KFService", "namespace", kfsvc.Namespace, "name", kfsvc.Name)
	setModelSpecDefaults(&kfsvc.Spec.Default)
	if kfsvc.Spec.Canary != nil {
		setModelSpecDefaults(&kfsvc.Spec.Canary.ModelSpec)
	}
}

func setModelSpecDefaults(modelSpec *ModelSpec) {
	if modelSpec.Tensorflow != nil {
		setTensorflowDefaults(modelSpec.Tensorflow)
	}
	if modelSpec.XGBoost != nil {
		setXGBoostDefaults(modelSpec.XGBoost)
	}
	if modelSpec.ScikitLearn != nil {
		setScikitLearnDefaults(modelSpec.ScikitLearn)
	}
}

func setTensorflowDefaults(tensorflowSpec *TensorflowSpec) {
	if tensorflowSpec.RuntimeVersion == "" {
		tensorflowSpec.RuntimeVersion = DefaultTensorflowVersion
	}
	setResourceRequirementDefaults(&tensorflowSpec.Resources)
}

func setXGBoostDefaults(xgBoostSpec *XGBoostSpec) {
	if xgBoostSpec.RuntimeVersion == "" {
		xgBoostSpec.RuntimeVersion = DefaultXGBoostVersion
	}
	setResourceRequirementDefaults(&xgBoostSpec.Resources)
}

func setScikitLearnDefaults(scikitLearnSpec *ScikitLearnSpec) {
	if scikitLearnSpec.RuntimeVersion == "" {
		scikitLearnSpec.RuntimeVersion = DefaultScikitLearnVersion
	}
	setResourceRequirementDefaults(&scikitLearnSpec.Resources)
}

func setResourceRequirementDefaults(requirements *v1.ResourceRequirements) {
	if requirements.Requests == nil {
		requirements.Requests = v1.ResourceList{}
	}

	if _, ok := requirements.Requests[v1.ResourceCPU]; !ok {
		requirements.Requests[v1.ResourceCPU] = DefaultCPURequests
	}
	if _, ok := requirements.Requests[v1.ResourceMemory]; !ok {
		requirements.Requests[v1.ResourceMemory] = DefaultMemoryRequests
	}
}
<commit_msg>Fix default tensorflow image (#66)<commit_after>package v1alpha1

import (
	v1 "k8s.io/api/core/v1"
	resource "k8s.io/apimachinery/pkg/api/resource"
)

// Default Values
var (
	DefaultTensorflowServingVersion  = "1.13.0"
	DefaultXGBoostServingVersion     = "0.1.0"
	DefaultScikitLearnServingVersion = "0.1.0"

	DefaultMemoryRequests = resource.MustParse("2Gi")
	DefaultCPURequests    = resource.MustParse("1")
)

// Default implements https://godoc.org/sigs.k8s.io/controller-runtime/pkg/webhook/admission#Defaulter
func (kfsvc *KFService) Default() {
	logger.Info("Defaulting KFService", "namespace", kfsvc.Namespace, "name", kfsvc.Name)
	setModelSpecDefaults(&kfsvc.Spec.Default)
	if kfsvc.Spec.Canary != nil {
		setModelSpecDefaults(&kfsvc.Spec.Canary.ModelSpec)
	}
}

func setModelSpecDefaults(modelSpec *ModelSpec) {
	if modelSpec.Tensorflow != nil {
		setTensorflowDefaults(modelSpec.Tensorflow)
	}
	if modelSpec.XGBoost != nil {
		setXGBoostDefaults(modelSpec.XGBoost)
	}
	if modelSpec.ScikitLearn != nil {
		setScikitLearnDefaults(modelSpec.ScikitLearn)
	}
}

func setTensorflowDefaults(tensorflowSpec *TensorflowSpec) {
	if tensorflowSpec.RuntimeVersion == "" {
		tensorflowSpec.RuntimeVersion = DefaultTensorflowServingVersion
	}
	setResourceRequirementDefaults(&tensorflowSpec.Resources)
}

func setXGBoostDefaults(xgBoostSpec *XGBoostSpec) {
	if xgBoostSpec.RuntimeVersion == "" {
		xgBoostSpec.RuntimeVersion = DefaultXGBoostServingVersion
	}
	setResourceRequirementDefaults(&xgBoostSpec.Resources)
}

func setScikitLearnDefaults(scikitLearnSpec *ScikitLearnSpec) {
	if scikitLearnSpec.RuntimeVersion == "" {
		scikitLearnSpec.RuntimeVersion = DefaultScikitLearnServingVersion
	}
	setResourceRequirementDefaults(&scikitLearnSpec.Resources)
}

func setResourceRequirementDefaults(requirements *v1.ResourceRequirements) {
	if requirements.Requests == nil {
		requirements.Requests = v1.ResourceList{}
	}

	if _, ok := requirements.Requests[v1.ResourceCPU]; !ok {
		requirements.Requests[v1.ResourceCPU] = DefaultCPURequests
	}
	if _, ok := requirements.Requests[v1.ResourceMemory]; !ok {
		requirements.Requests[v1.ResourceMemory] = DefaultMemoryRequests
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package versioning

import (
	"io"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// NewCodecForScheme is a convenience method for callers that are using a scheme.
func NewCodecForScheme(
	// TODO: I should be a scheme interface?
	scheme *runtime.Scheme,
	encoder runtime.Encoder,
	decoder runtime.Decoder,
	encodeVersion runtime.GroupVersioner,
	decodeVersion runtime.GroupVersioner,
) runtime.Codec {
	return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, nil, encodeVersion, decodeVersion)
}

// NewDefaultingCodecForScheme is a convenience method for callers that are using a scheme.
func NewDefaultingCodecForScheme(
	// TODO: I should be a scheme interface?
	scheme *runtime.Scheme,
	encoder runtime.Encoder,
	decoder runtime.Decoder,
	encodeVersion runtime.GroupVersioner,
	decodeVersion runtime.GroupVersioner,
) runtime.Codec {
	return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion)
}

// NewCodec takes objects in their internal versions and converts them to external versions before
// serializing them. It assumes the serializer provided to it only deals with external versions.
// This class is also a serializer, but is generally used with a specific version.
func NewCodec(
	encoder runtime.Encoder,
	decoder runtime.Decoder,
	convertor runtime.ObjectConvertor,
	creater runtime.ObjectCreater,
	typer runtime.ObjectTyper,
	defaulter runtime.ObjectDefaulter,
	encodeVersion runtime.GroupVersioner,
	decodeVersion runtime.GroupVersioner,
) runtime.Codec {
	internal := &codec{
		encoder:   encoder,
		decoder:   decoder,
		convertor: convertor,
		creater:   creater,
		typer:     typer,
		defaulter: defaulter,

		encodeVersion: encodeVersion,
		decodeVersion: decodeVersion,
	}
	return internal
}

type codec struct {
	encoder   runtime.Encoder
	decoder   runtime.Decoder
	convertor runtime.ObjectConvertor
	creater   runtime.ObjectCreater
	typer     runtime.ObjectTyper
	defaulter runtime.ObjectDefaulter

	encodeVersion runtime.GroupVersioner
	decodeVersion runtime.GroupVersioner
}

// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
// successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an
// into that matches the serialized version.
func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
	versioned, isVersioned := into.(*runtime.VersionedObjects)
	if isVersioned {
		into = versioned.Last()
	}

	obj, gvk, err := c.decoder.Decode(data, defaultGVK, into)
	if err != nil {
		return nil, gvk, err
	}

	if d, ok := obj.(runtime.NestedObjectDecoder); ok {
		if err := d.DecodeNestedObjects(DirectDecoder{c.decoder}); err != nil {
			return nil, gvk, err
		}
	}

	// if we specify a target, use generic conversion.
	if into != nil {
		if into == obj {
			if isVersioned {
				return versioned, gvk, nil
			}
			return into, gvk, nil
		}

		// perform defaulting if requested
		if c.defaulter != nil {
			// create a copy to ensure defaulting is not applied to the original versioned objects
			if isVersioned {
				versioned.Objects = []runtime.Object{obj.DeepCopyObject()}
			}
			c.defaulter.Default(obj)
		} else {
			if isVersioned {
				versioned.Objects = []runtime.Object{obj}
			}
		}

		if err := c.convertor.Convert(obj, into, c.decodeVersion); err != nil {
			return nil, gvk, err
		}

		if isVersioned {
			versioned.Objects = append(versioned.Objects, into)
			return versioned, gvk, nil
		}
		return into, gvk, nil
	}

	// Convert if needed.
	if isVersioned {
		// create a copy, because ConvertToVersion does not guarantee non-mutation of objects
		versioned.Objects = []runtime.Object{obj.DeepCopyObject()}
	}

	// perform defaulting if requested
	if c.defaulter != nil {
		c.defaulter.Default(obj)
	}

	out, err := c.convertor.ConvertToVersion(obj, c.decodeVersion)
	if err != nil {
		return nil, gvk, err
	}
	if isVersioned {
		if versioned.Last() != out {
			versioned.Objects = append(versioned.Objects, out)
		}
		return versioned, gvk, nil
	}
	return out, gvk, nil
}

// Encode ensures the provided object is output in the appropriate group and version, invoking
// conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
	switch obj := obj.(type) {
	case *runtime.Unknown:
		return c.encoder.Encode(obj, w)
	case runtime.Unstructured:
		// avoid conversion roundtrip if GVK is the right one already or is empty (yes, this is a hack, but the old behaviour we rely on in kubectl)
		objGVK := obj.GetObjectKind().GroupVersionKind()
		if len(objGVK.Version) == 0 {
			return c.encoder.Encode(obj, w)
		}
		targetGVK, ok := c.encodeVersion.KindForGroupVersionKinds([]schema.GroupVersionKind{objGVK})
		if !ok {
			return runtime.NewNotRegisteredGVKErrForTarget(objGVK, c.encodeVersion)
		}
		if targetGVK == objGVK {
			return c.encoder.Encode(obj, w)
		}
	}

	gvks, isUnversioned, err := c.typer.ObjectKinds(obj)
	if err != nil {
		return err
	}

	if c.encodeVersion == nil || isUnversioned {
		if e, ok := obj.(runtime.NestedObjectEncoder); ok {
			if err := e.EncodeNestedObjects(DirectEncoder{Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
				return err
			}
		}
		objectKind := obj.GetObjectKind()
		old := objectKind.GroupVersionKind()
		objectKind.SetGroupVersionKind(gvks[0])
		err = c.encoder.Encode(obj, w)
		objectKind.SetGroupVersionKind(old)
		return err
	}

	// Perform a conversion if necessary
	objectKind := obj.GetObjectKind()
	old := objectKind.GroupVersionKind()
	out, err := c.convertor.ConvertToVersion(obj, c.encodeVersion)
	if err != nil {
		return err
	}

	if e, ok := out.(runtime.NestedObjectEncoder); ok {
		if err := e.EncodeNestedObjects(DirectEncoder{Version: c.encodeVersion, Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
			return err
		}
	}

	// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
	err = c.encoder.Encode(out, w)
	// restore the old GVK, in case conversion returned the same object
	objectKind.SetGroupVersionKind(old)
	return err
}

// DirectEncoder serializes an object and ensures the GVK is set.
type DirectEncoder struct {
	Version runtime.GroupVersioner
	runtime.Encoder
	runtime.ObjectTyper
}

// Encode does not do conversion. It sets the gvk during serialization.
func (e DirectEncoder) Encode(obj runtime.Object, stream io.Writer) error {
	gvks, _, err := e.ObjectTyper.ObjectKinds(obj)
	if err != nil {
		if runtime.IsNotRegisteredError(err) {
			return e.Encoder.Encode(obj, stream)
		}
		return err
	}
	kind := obj.GetObjectKind()
	oldGVK := kind.GroupVersionKind()
	gvk := gvks[0]
	if e.Version != nil {
		preferredGVK, ok := e.Version.KindForGroupVersionKinds(gvks)
		if ok {
			gvk = preferredGVK
		}
	}
	kind.SetGroupVersionKind(gvk)
	err = e.Encoder.Encode(obj, stream)
	kind.SetGroupVersionKind(oldGVK)
	return err
}

// DirectDecoder clears the group version kind of a deserialized object.
type DirectDecoder struct {
	runtime.Decoder
}

// Decode does not do conversion. It removes the gvk during deserialization.
func (d DirectDecoder) Decode(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
	obj, gvk, err := d.Decoder.Decode(data, defaults, into)
	if obj != nil {
		kind := obj.GetObjectKind()
		// clearing the gvk is just a convention of a codec
		kind.SetGroupVersionKind(schema.GroupVersionKind{})
	}
	return obj, gvk, err
}
<commit_msg>Do not bypass same version unstructed conversion if it is a list<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package versioning

import (
	"io"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// NewCodecForScheme is a convenience method for callers that are using a scheme.
func NewCodecForScheme(
	// TODO: I should be a scheme interface?
	scheme *runtime.Scheme,
	encoder runtime.Encoder,
	decoder runtime.Decoder,
	encodeVersion runtime.GroupVersioner,
	decodeVersion runtime.GroupVersioner,
) runtime.Codec {
	return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, nil, encodeVersion, decodeVersion)
}

// NewDefaultingCodecForScheme is a convenience method for callers that are using a scheme.
func NewDefaultingCodecForScheme(
	// TODO: I should be a scheme interface?
	scheme *runtime.Scheme,
	encoder runtime.Encoder,
	decoder runtime.Decoder,
	encodeVersion runtime.GroupVersioner,
	decodeVersion runtime.GroupVersioner,
) runtime.Codec {
	return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion)
}

// NewCodec takes objects in their internal versions and converts them to external versions before
// serializing them. It assumes the serializer provided to it only deals with external versions.
// This class is also a serializer, but is generally used with a specific version.
func NewCodec(
	encoder runtime.Encoder,
	decoder runtime.Decoder,
	convertor runtime.ObjectConvertor,
	creater runtime.ObjectCreater,
	typer runtime.ObjectTyper,
	defaulter runtime.ObjectDefaulter,
	encodeVersion runtime.GroupVersioner,
	decodeVersion runtime.GroupVersioner,
) runtime.Codec {
	internal := &codec{
		encoder:   encoder,
		decoder:   decoder,
		convertor: convertor,
		creater:   creater,
		typer:     typer,
		defaulter: defaulter,

		encodeVersion: encodeVersion,
		decodeVersion: decodeVersion,
	}
	return internal
}

type codec struct {
	encoder   runtime.Encoder
	decoder   runtime.Decoder
	convertor runtime.ObjectConvertor
	creater   runtime.ObjectCreater
	typer     runtime.ObjectTyper
	defaulter runtime.ObjectDefaulter

	encodeVersion runtime.GroupVersioner
	decodeVersion runtime.GroupVersioner
}

// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
// successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an
// into that matches the serialized version.
func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
	versioned, isVersioned := into.(*runtime.VersionedObjects)
	if isVersioned {
		into = versioned.Last()
	}

	obj, gvk, err := c.decoder.Decode(data, defaultGVK, into)
	if err != nil {
		return nil, gvk, err
	}

	if d, ok := obj.(runtime.NestedObjectDecoder); ok {
		if err := d.DecodeNestedObjects(DirectDecoder{c.decoder}); err != nil {
			return nil, gvk, err
		}
	}

	// if we specify a target, use generic conversion.
	if into != nil {
		if into == obj {
			if isVersioned {
				return versioned, gvk, nil
			}
			return into, gvk, nil
		}

		// perform defaulting if requested
		if c.defaulter != nil {
			// create a copy to ensure defaulting is not applied to the original versioned objects
			if isVersioned {
				versioned.Objects = []runtime.Object{obj.DeepCopyObject()}
			}
			c.defaulter.Default(obj)
		} else {
			if isVersioned {
				versioned.Objects = []runtime.Object{obj}
			}
		}

		if err := c.convertor.Convert(obj, into, c.decodeVersion); err != nil {
			return nil, gvk, err
		}

		if isVersioned {
			versioned.Objects = append(versioned.Objects, into)
			return versioned, gvk, nil
		}
		return into, gvk, nil
	}

	// Convert if needed.
	if isVersioned {
		// create a copy, because ConvertToVersion does not guarantee non-mutation of objects
		versioned.Objects = []runtime.Object{obj.DeepCopyObject()}
	}

	// perform defaulting if requested
	if c.defaulter != nil {
		c.defaulter.Default(obj)
	}

	out, err := c.convertor.ConvertToVersion(obj, c.decodeVersion)
	if err != nil {
		return nil, gvk, err
	}
	if isVersioned {
		if versioned.Last() != out {
			versioned.Objects = append(versioned.Objects, out)
		}
		return versioned, gvk, nil
	}
	return out, gvk, nil
}

// Encode ensures the provided object is output in the appropriate group and version, invoking
// conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
	switch obj := obj.(type) {
	case *runtime.Unknown:
		return c.encoder.Encode(obj, w)
	case runtime.Unstructured:
		// An unstructured list can contain objects of multiple group version kinds. don't short-circuit just
		// because the top-level type matches our desired destination type. actually send the object to the converter
		// to give it a chance to convert the list items if needed.
		if _, ok := obj.(*unstructured.UnstructuredList); !ok {
			// avoid conversion roundtrip if GVK is the right one already or is empty (yes, this is a hack, but the old behaviour we rely on in kubectl)
			objGVK := obj.GetObjectKind().GroupVersionKind()
			if len(objGVK.Version) == 0 {
				return c.encoder.Encode(obj, w)
			}
			targetGVK, ok := c.encodeVersion.KindForGroupVersionKinds([]schema.GroupVersionKind{objGVK})
			if !ok {
				return runtime.NewNotRegisteredGVKErrForTarget(objGVK, c.encodeVersion)
			}
			if targetGVK == objGVK {
				return c.encoder.Encode(obj, w)
			}
		}
	}

	gvks, isUnversioned, err := c.typer.ObjectKinds(obj)
	if err != nil {
		return err
	}

	if c.encodeVersion == nil || isUnversioned {
		if e, ok := obj.(runtime.NestedObjectEncoder); ok {
			if err := e.EncodeNestedObjects(DirectEncoder{Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
				return err
			}
		}
		objectKind := obj.GetObjectKind()
		old := objectKind.GroupVersionKind()
		objectKind.SetGroupVersionKind(gvks[0])
		err = c.encoder.Encode(obj, w)
		objectKind.SetGroupVersionKind(old)
		return err
	}

	// Perform a conversion if necessary
	objectKind := obj.GetObjectKind()
	old := objectKind.GroupVersionKind()
	out, err := c.convertor.ConvertToVersion(obj, c.encodeVersion)
	if err != nil {
		return err
	}

	if e, ok := out.(runtime.NestedObjectEncoder); ok {
		if err := e.EncodeNestedObjects(DirectEncoder{Version: c.encodeVersion, Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
			return err
		}
	}

	// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
	err = c.encoder.Encode(out, w)
	// restore the old GVK, in case conversion returned the same object
	objectKind.SetGroupVersionKind(old)
	return err
}

// DirectEncoder serializes an object and ensures the GVK is set.
type DirectEncoder struct {
	Version runtime.GroupVersioner
	runtime.Encoder
	runtime.ObjectTyper
}

// Encode does not do conversion. It sets the gvk during serialization.
func (e DirectEncoder) Encode(obj runtime.Object, stream io.Writer) error {
	gvks, _, err := e.ObjectTyper.ObjectKinds(obj)
	if err != nil {
		if runtime.IsNotRegisteredError(err) {
			return e.Encoder.Encode(obj, stream)
		}
		return err
	}
	kind := obj.GetObjectKind()
	oldGVK := kind.GroupVersionKind()
	gvk := gvks[0]
	if e.Version != nil {
		preferredGVK, ok := e.Version.KindForGroupVersionKinds(gvks)
		if ok {
			gvk = preferredGVK
		}
	}
	kind.SetGroupVersionKind(gvk)
	err = e.Encoder.Encode(obj, stream)
	kind.SetGroupVersionKind(oldGVK)
	return err
}

// DirectDecoder clears the group version kind of a deserialized object.
type DirectDecoder struct {
	runtime.Decoder
}

// Decode does not do conversion. It removes the gvk during deserialization.
func (d DirectDecoder) Decode(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
	obj, gvk, err := d.Decoder.Decode(data, defaults, into)
	if obj != nil {
		kind := obj.GetObjectKind()
		// clearing the gvk is just a convention of a codec
		kind.SetGroupVersionKind(schema.GroupVersionKind{})
	}
	return obj, gvk, err
}
<|endoftext|>


<commit_before>package license

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/url"
	"time"

	"github.com/admpub/license_gen/lib"
	"github.com/admpub/log"
	"github.com/webx-top/com"
	"github.com/webx-top/echo"
)

// Check 检查权限
func Check(ctx echo.Context, content ...[]byte) error {
	if SkipLicenseCheck {
		return nil
	}
	var validateRemote bool
	if licenseMode == ModeDomain && len(Domain()) > 0 {
		licenseError = validateFromOfficial(ctx)
		if licenseError != ErrConnectionFailed {
			return licenseError
		}
	} else {
		validateRemote = true
	}
	//当官方服务器不可用时才验证本地许可证
	licenseError = Validate(content...)
	if licenseError == nil && validateRemote {
		licenseError = validateFromOfficial(ctx)
		if licenseError != ErrConnectionFailed {
			return licenseError
		}
	}
	return licenseError
}

func Ok(ctx echo.Context) bool {
	if SkipLicenseCheck {
		return true
	}
	switch licenseError {
	case nil:
		if licenseData == nil {
			licenseError = lib.UnlicensedVersion
			return false
		}
		if !licenseData.Info.Expiration.IsZero() && time.Now().After(licenseData.Info.Expiration) {
			licenseError = lib.ExpiredLicense
			return false
		}
		return true
	default:
		err := Check(ctx)
		if err == nil {
			licenseError = nil
			return true
		}
		log.Warn(err)
	}
	return false
}

// Validation 定义验证器
type Validation struct {
	NowVersions []string
}

// Validate 参数验证器
func (v *Validation) Validate(data *lib.LicenseData) error {
	if err := data.CheckExpiration(); err != nil {
		return err
	}
	if err := data.CheckVersion(v.NowVersions...); err != nil {
		return err
	}
	switch licenseMode {
	case ModeMachineID:
		mid, err := MachineID()
		if err != nil {
			return err
		}
		if data.Info.MachineID != mid {
			return lib.InvalidMachineID
		}
	case ModeDomain:
		if len(Domain()) == 0 {
			SetDomain(data.Info.Domain)
			return nil
		}
		return data.CheckDomain(Domain())
	default:
		panic(fmt.Sprintf(`unsupported license mode: %d`, licenseMode))
	}
	return nil
}

// Validate 验证授权
func Validate(content ...[]byte) (err error) {
	var b []byte
	if len(content) > 0 && len(content[0]) > 0 {
		b = content[0]
	} else {
		licenseExists = com.FileExists(FilePath())
		if !licenseExists {
			licenseError = ErrLicenseNotFound
			return licenseError
		}
		b, err = ioutil.ReadFile(FilePath())
		if err != nil {
			return
		}
	}
	validator := &Validation{
		NowVersions: []string{licenseVersion},
	}
	licenseData, err = lib.CheckLicenseStringAndReturning(string(b), PublicKey(), validator)
	return
}

func CheckSiteURL(siteURL string) error {
	u, err := url.Parse(siteURL)
	if err != nil {
		err = fmt.Errorf(`%s: %w`, siteURL, err)
		return err
	}
	if SkipLicenseCheck || LicenseMode() != ModeDomain {
		return nil
	}
	rootDomain := Domain()
	if len(rootDomain) == 0 {
		err = errors.New(`please set up the license first`)
		return err
	}
	fullDomain := u.Hostname()
	if !EqDomain(fullDomain, rootDomain) {
		err = fmt.Errorf(`domain "%s" and licensed domain "%s" is mismatched`, fullDomain, rootDomain)
		return err
	}
	return err
}
<commit_msg>improved<commit_after>package license

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/url"
	"time"

	"github.com/admpub/license_gen/lib"
	"github.com/admpub/log"
	"github.com/webx-top/com"
	"github.com/webx-top/echo"
)

// Check 检查权限
func Check(ctx echo.Context, content ...[]byte) error {
	if SkipLicenseCheck {
		return nil
	}
	var validateRemote bool
	if licenseMode == ModeDomain && len(Domain()) > 0 {
		licenseError = validateFromOfficial(ctx)
		if licenseError != ErrConnectionFailed {
			return licenseError
		}
	} else {
		validateRemote = true
	}
	//当官方服务器不可用时才验证本地许可证
	licenseError = Validate(content...)
	if licenseError == nil && validateRemote {
		licenseError = validateFromOfficial(ctx)
		if licenseError != ErrConnectionFailed {
			return licenseError
		}
	}
	return licenseError
}

func Ok(ctx echo.Context) bool {
	if SkipLicenseCheck {
		return true
	}
	switch licenseError {
	case nil:
		if licenseData == nil {
			licenseError = lib.UnlicensedVersion
			return false
		}
		if !licenseData.Info.Expiration.IsZero() && time.Now().After(licenseData.Info.Expiration) {
			licenseError = lib.ExpiredLicense
			return false
		}
		return true
	default:
		err := Check(ctx)
		if err == nil {
			licenseError = nil
			return true
		}
		log.Warn(err)
	}
	return false
}

// Validation 定义验证器
type Validation struct {
	NowVersions []string
}

// Validate 参数验证器
func (v *Validation) Validate(data *lib.LicenseData) error {
	if err := data.CheckExpiration(); err != nil {
		return err
	}
	if err := data.CheckVersion(v.NowVersions...); err != nil {
		return err
	}
	switch licenseMode {
	case ModeMachineID:
		mid, err := MachineID()
		if err != nil {
			return err
		}
		if data.Info.MachineID != mid {
			return lib.InvalidMachineID
		}
	case ModeDomain:
		if len(Domain()) == 0 {
			SetDomain(data.Info.Domain)
			return nil
		}
		return data.CheckDomain(Domain())
	default:
		panic(fmt.Sprintf(`unsupported license mode: %d`, licenseMode))
	}
	return nil
}

// Validate 验证授权
func Validate(content ...[]byte) (err error) {
	var b []byte
	if len(content) > 0 && len(content[0]) > 0 {
		b = content[0]
	} else {
		licenseExists = com.FileExists(FilePath())
		if !licenseExists {
			licenseError = ErrLicenseNotFound
			return licenseError
		}
		b, err = ioutil.ReadFile(FilePath())
		if err != nil {
			return
		}
	}
	validator := &Validation{
		NowVersions: []string{licenseVersion},
	}
	licenseData, err = lib.CheckLicenseStringAndReturning(string(b), PublicKey(), validator)
	return
}

func CheckSiteURL(siteURL string) error {
	if SkipLicenseCheck || LicenseMode() != ModeDomain {
		return nil
	}
	u, err := url.Parse(siteURL)
	if err != nil {
		err = fmt.Errorf(`%s: %w`, siteURL, err)
		return err
	}
	rootDomain := Domain()
	if len(rootDomain) == 0 {
		err = errors.New(`please set up the license first`)
		return err
	}
	fullDomain := u.Hostname()
	if !EqDomain(fullDomain, rootDomain) {
		err = fmt.Errorf(`domain "%s" and licensed domain "%s" is mismatched`, fullDomain, rootDomain)
		return err
	}
	return err
}
<|endoftext|>