<commit_before>//go:generate sh -c "go run github.com/gomatic/renderizer --name=node --resource=node --type=Node --title=Node --article=a swagger_operations.tmpl > node_swagger.go"
//go:generate sh -c "go run github.com/gomatic/renderizer --name=node --resource=node --type=Node --title=Node swagger_definitions.tmpl > node_swagger.json"

/*
 * Copyright (C) 2020 Sylvain Baubeau
 *
 * 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 ofthe 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 specificlanguage governing permissions and
 * limitations under the License.
 *
 */

package server

import (
	"time"

	"github.com/skydive-project/skydive/api/types"
	"github.com/skydive-project/skydive/graffiti/api/rest"
	api "github.com/skydive-project/skydive/graffiti/api/server"
	"github.com/skydive-project/skydive/graffiti/graph"
	shttp "github.com/skydive-project/skydive/graffiti/http"
)

// NodeResourceHandler aims to creates and manage a new Alert.
type NodeResourceHandler struct {
	rest.ResourceHandler
}

// NodeAPIHandler aims to exposes the Alert API.
type NodeAPIHandler struct {
	g *graph.Graph
}

// New creates a new node
func (h *NodeAPIHandler) New() rest.Resource {
	return &types.Node{}
}

// Name returns resource name "node"
func (h *NodeAPIHandler) Name() string {
	return "node"
}

// Index returns the list of existing nodes
func (h *NodeAPIHandler) Index() map[string]rest.Resource {
	h.g.RLock()
	nodes := h.g.GetNodes(nil)
	nodeMap := make(map[string]rest.Resource, len(nodes))
	for _, node := range nodes {
		n := types.Node(*node)
		nodeMap[string(node.ID)] = &n
	}
	h.g.RUnlock()
	return nodeMap
}

// Get returns a node with the specified id
func (h *NodeAPIHandler) Get(id string) (rest.Resource, bool) {
	h.g.RLock()
	defer h.g.RUnlock()

	n := h.g.GetNode(graph.Identifier(id))
	if n == nil {
		return nil, false
	}
	node := (*types.Node)(n)
	return node, true
}

// Decorate the specified node
func (h *NodeAPIHandler) Decorate(resource rest.Resource) {
}

// Create adds the specified node to the graph
func (h *NodeAPIHandler) Create(resource rest.Resource, createOpts *rest.CreateOptions) error {
	node := resource.(*types.Node)
	graphNode := graph.Node(*node)
	if graphNode.CreatedAt.IsZero() {
		graphNode.CreatedAt = graph.Time(time.Now())
	}
	if graphNode.UpdatedAt.IsZero() {
		graphNode.UpdatedAt = graphNode.CreatedAt
	}
	if graphNode.Origin == "" {
		graphNode.Origin = h.g.GetOrigin()
	}
	if graphNode.Metadata == nil {
		graphNode.Metadata = graph.Metadata{}
	}

	h.g.Lock()
	err := h.g.AddNode(&graphNode)
	h.g.Unlock()
	return err
}

// Delete the node with the specified id from the graph
func (h *NodeAPIHandler) Delete(id string) error {
	h.g.Lock()
	defer h.g.Unlock()

	node := h.g.GetNode(graph.Identifier(id))
	if node == nil {
		return rest.ErrNotFound
	}

	return h.g.DelNode(node)
}

// Update a node metadata
func (h *NodeAPIHandler) Update(id string, resource rest.Resource) (rest.Resource, bool, error) {
	h.g.Lock()
	defer h.g.Unlock()

	// Current node, to be updated
	n := h.g.GetNode(graph.Identifier(id))
	if n == nil {
		return nil, false, rest.ErrNotFound
	}

	// Node containing the metadata updated
	patchedNode := resource.(*types.Node)

	// Do not modify/replace Metadata.(TID|Name|Type), use actual node values
	if actualTID, _ := n.Metadata.GetFieldString("TID"); actualTID != "" {
		patchedNode.Metadata.SetField("TID", actualTID)
	}
	actualName, _ := n.Metadata.GetFieldString("Name")
	patchedNode.Metadata.SetField("Name", actualName)
	actualType, _ := n.Metadata.GetFieldString("Type")
	patchedNode.Metadata.SetField("Type", actualType)

	// Update actual node Metadata with new patched node
	previousRevision := n.Revision
	if err := h.g.SetMetadata(n, patchedNode.Metadata); err != nil {
		return nil, false, err
	}

	return (*types.Node)(n), n.Revision != previousRevision, nil
}

// RegisterNodeAPI registers the node API
func RegisterNodeAPI(apiServer *api.Server, g *graph.Graph, authBackend shttp.AuthenticationBackend) (*NodeAPIHandler, error) {
	nodeAPIHandler := &NodeAPIHandler{
		g: g,
	}
	if err := apiServer.RegisterAPIHandler(nodeAPIHandler, authBackend); err != nil {
		return nil, err
	}
	return nodeAPIHandler, nil
}
<commit_msg>api: create node and edge with the 'api' service type<commit_after>//go:generate sh -c "go run github.com/gomatic/renderizer --name=node --resource=node --type=Node --title=Node --article=a swagger_operations.tmpl > node_swagger.go"
//go:generate sh -c "go run github.com/gomatic/renderizer --name=node --resource=node --type=Node --title=Node swagger_definitions.tmpl > node_swagger.json"

/*
 * Copyright (C) 2020 Sylvain Baubeau
 *
 * 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 ofthe 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 specificlanguage governing permissions and
 * limitations under the License.
 *
 */

package server

import (
	"time"

	"github.com/skydive-project/skydive/api/types"
	"github.com/skydive-project/skydive/graffiti/api/rest"
	api "github.com/skydive-project/skydive/graffiti/api/server"
	"github.com/skydive-project/skydive/graffiti/graph"
	shttp "github.com/skydive-project/skydive/graffiti/http"
)

const apiOrigin = "api"

// NodeResourceHandler aims to creates and manage a new Alert.
type NodeResourceHandler struct {
	rest.ResourceHandler
}

// NodeAPIHandler aims to exposes the Alert API.
type NodeAPIHandler struct {
	g *graph.Graph
}

// New creates a new node
func (h *NodeAPIHandler) New() rest.Resource {
	return &types.Node{}
}

// Name returns resource name "node"
func (h *NodeAPIHandler) Name() string {
	return "node"
}

// Index returns the list of existing nodes
func (h *NodeAPIHandler) Index() map[string]rest.Resource {
	h.g.RLock()
	nodes := h.g.GetNodes(nil)
	nodeMap := make(map[string]rest.Resource, len(nodes))
	for _, node := range nodes {
		n := types.Node(*node)
		nodeMap[string(node.ID)] = &n
	}
	h.g.RUnlock()
	return nodeMap
}

// Get returns a node with the specified id
func (h *NodeAPIHandler) Get(id string) (rest.Resource, bool) {
	h.g.RLock()
	defer h.g.RUnlock()

	n := h.g.GetNode(graph.Identifier(id))
	if n == nil {
		return nil, false
	}
	node := (*types.Node)(n)
	return node, true
}

// Decorate the specified node
func (h *NodeAPIHandler) Decorate(resource rest.Resource) {
}

// Create adds the specified node to the graph
func (h *NodeAPIHandler) Create(resource rest.Resource, createOpts *rest.CreateOptions) error {
	node := resource.(*types.Node)
	graphNode := graph.Node(*node)
	if graphNode.CreatedAt.IsZero() {
		graphNode.CreatedAt = graph.Time(time.Now())
	}
	if graphNode.UpdatedAt.IsZero() {
		graphNode.UpdatedAt = graphNode.CreatedAt
	}
	if graphNode.Origin == "" {
		graphNode.Origin = graph.Origin(h.g.GetHost(), apiOrigin)
	}
	if graphNode.Metadata == nil {
		graphNode.Metadata = graph.Metadata{}
	}

	h.g.Lock()
	err := h.g.AddNode(&graphNode)
	h.g.Unlock()
	return err
}

// Delete the node with the specified id from the graph
func (h *NodeAPIHandler) Delete(id string) error {
	h.g.Lock()
	defer h.g.Unlock()

	node := h.g.GetNode(graph.Identifier(id))
	if node == nil {
		return rest.ErrNotFound
	}

	return h.g.DelNode(node)
}

// Update a node metadata
func (h *NodeAPIHandler) Update(id string, resource rest.Resource) (rest.Resource, bool, error) {
	h.g.Lock()
	defer h.g.Unlock()

	// Current node, to be updated
	n := h.g.GetNode(graph.Identifier(id))
	if n == nil {
		return nil, false, rest.ErrNotFound
	}

	// Node containing the metadata updated
	patchedNode := resource.(*types.Node)

	// Do not modify/replace Metadata.(TID|Name|Type), use actual node values
	if actualTID, _ := n.Metadata.GetFieldString("TID"); actualTID != "" {
		patchedNode.Metadata.SetField("TID", actualTID)
	}
	actualName, _ := n.Metadata.GetFieldString("Name")
	patchedNode.Metadata.SetField("Name", actualName)
	actualType, _ := n.Metadata.GetFieldString("Type")
	patchedNode.Metadata.SetField("Type", actualType)

	// Update actual node Metadata with new patched node
	previousRevision := n.Revision
	if err := h.g.SetMetadata(n, patchedNode.Metadata); err != nil {
		return nil, false, err
	}

	return (*types.Node)(n), n.Revision != previousRevision, nil
}

// RegisterNodeAPI registers the node API
func RegisterNodeAPI(apiServer *api.Server, g *graph.Graph, authBackend shttp.AuthenticationBackend) (*NodeAPIHandler, error) {
	nodeAPIHandler := &NodeAPIHandler{
		g: g,
	}
	if err := apiServer.RegisterAPIHandler(nodeAPIHandler, authBackend); err != nil {
		return nil, err
	}
	return nodeAPIHandler, nil
}
<|endoftext|>


<commit_before>package edit

import (
	"fmt"
	"os"
	"strings"

	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/eval"
)

// This file contains several "registries", data structure that are written
// during program initialization and later used when initializing the Editor.

var builtinMaps = map[string]map[string]*BuiltinFn{}

// registerBuiltins registers builtins under a subnamespace of edit:, to be used
// during the initialization of the Editor. It should be called for global
// variable initializations to make sure every subnamespace is registered before
// makeBindings is ever called.
func registerBuiltins(module string, impls map[string]func(*Editor)) struct{} {
	if _, ok := builtinMaps[module]; !ok {
		builtinMaps[module] = make(map[string]*BuiltinFn)
	}
	for name, impl := range impls {
		var fullName string
		if module == "" {
			fullName = "edit:" + eval.FnPrefix + name
		} else {
			fullName = "edit:" + module + ":" + eval.FnPrefix + name
		}
		builtinMaps[module][name] = &BuiltinFn{fullName, impl}
	}
	return struct{}{}
}

func makeNamespaceFromBuiltins(builtins map[string]*BuiltinFn) eval.Namespace {
	ns := eval.Namespace{}
	for name, builtin := range builtins {
		ns[eval.FnPrefix+name] = eval.NewPtrVariable(builtin)
	}
	return ns
}

var keyBindings = map[string]map[ui.Key]eval.CallableValue{}

// registerBindings registers default bindings for a mode to initialize the
// global keyBindings map. Builtin names are resolved in the defaultMod
// subnamespace using information from builtinMaps. It should be called in init
// functions.
func registerBindings(
	mt string, defaultMod string, bindingData map[ui.Key]string) struct{} {

	if _, ok := keyBindings[mt]; !ok {
		keyBindings[mt] = map[ui.Key]eval.CallableValue{}
	}
	for key, fullName := range bindingData {
		// break fullName into mod and name.
		var mod, name string
		nameParts := strings.SplitN(fullName, ":", 2)
		if len(nameParts) == 2 {
			mod, name = nameParts[0], nameParts[1]
		} else {
			mod, name = defaultMod, nameParts[0]
		}
		if m, ok := builtinMaps[mod]; ok {
			if builtin, ok := m[name]; ok {
				keyBindings[mt][key] = builtin
			} else {
				fmt.Fprintln(os.Stderr, "Internal warning: no such builtin", name, "in mod", mod)
			}
		} else {
			fmt.Fprintln(os.Stderr, "Internal warning: no such mod:", mod)
		}
	}
	return struct{}{}
}

var variableMakers = map[string]func() eval.Variable{}

// registerVariables registers a variable, its name and a func used to derive
// its value, later to be used during Editor initialization to populate
// Editor.variables as well as the edit: namespace.
func registerVariable(name string, maker func() eval.Variable) struct{} {
	variableMakers[name] = maker
	return struct{}{}
}

func makeVariables() map[string]eval.Variable {
	m := make(map[string]eval.Variable, len(variableMakers))
	for name, maker := range variableMakers {
		m[name] = maker()
	}
	return m
}
<commit_msg>edit: Document purpose of registry.go.<commit_after>package edit

import (
	"fmt"
	"os"
	"strings"

	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/eval"
)

// This file contains several "registries", data structure that are written
// during program initialization and later used when initializing the Editor.
//
// The purpose of these registries is to decentralize the definition for
// builtins, default bindings, and variables (e.g. $edit:prompt). For instance,
// the definition for $edit:prompt can live in prompt.go instead of api.go.

var builtinMaps = map[string]map[string]*BuiltinFn{}

// registerBuiltins registers builtins under a subnamespace of edit:, to be used
// during the initialization of the Editor. It should be called for global
// variable initializations to make sure every subnamespace is registered before
// makeBindings is ever called.
func registerBuiltins(module string, impls map[string]func(*Editor)) struct{} {
	if _, ok := builtinMaps[module]; !ok {
		builtinMaps[module] = make(map[string]*BuiltinFn)
	}
	for name, impl := range impls {
		var fullName string
		if module == "" {
			fullName = "edit:" + eval.FnPrefix + name
		} else {
			fullName = "edit:" + module + ":" + eval.FnPrefix + name
		}
		builtinMaps[module][name] = &BuiltinFn{fullName, impl}
	}
	return struct{}{}
}

func makeNamespaceFromBuiltins(builtins map[string]*BuiltinFn) eval.Namespace {
	ns := eval.Namespace{}
	for name, builtin := range builtins {
		ns[eval.FnPrefix+name] = eval.NewPtrVariable(builtin)
	}
	return ns
}

var keyBindings = map[string]map[ui.Key]eval.CallableValue{}

// registerBindings registers default bindings for a mode to initialize the
// global keyBindings map. Builtin names are resolved in the defaultMod
// subnamespace using information from builtinMaps. It should be called in init
// functions.
func registerBindings(
	mt string, defaultMod string, bindingData map[ui.Key]string) struct{} {

	if _, ok := keyBindings[mt]; !ok {
		keyBindings[mt] = map[ui.Key]eval.CallableValue{}
	}
	for key, fullName := range bindingData {
		// break fullName into mod and name.
		var mod, name string
		nameParts := strings.SplitN(fullName, ":", 2)
		if len(nameParts) == 2 {
			mod, name = nameParts[0], nameParts[1]
		} else {
			mod, name = defaultMod, nameParts[0]
		}
		if m, ok := builtinMaps[mod]; ok {
			if builtin, ok := m[name]; ok {
				keyBindings[mt][key] = builtin
			} else {
				fmt.Fprintln(os.Stderr, "Internal warning: no such builtin", name, "in mod", mod)
			}
		} else {
			fmt.Fprintln(os.Stderr, "Internal warning: no such mod:", mod)
		}
	}
	return struct{}{}
}

var variableMakers = map[string]func() eval.Variable{}

// registerVariables registers a variable, its name and a func used to derive
// its value, later to be used during Editor initialization to populate
// Editor.variables as well as the edit: namespace.
func registerVariable(name string, maker func() eval.Variable) struct{} {
	variableMakers[name] = maker
	return struct{}{}
}

func makeVariables() map[string]eval.Variable {
	m := make(map[string]eval.Variable, len(variableMakers))
	for name, maker := range variableMakers {
		m[name] = maker()
	}
	return m
}
<|endoftext|>


<commit_before>package engine

import (
	"errors"
	"strconv"

	garden "github.com/cloudfoundry-incubator/garden/api"
	"github.com/concourse/atc"
	"github.com/concourse/atc/db"
	"github.com/pivotal-golang/lager"
)

var ErrBuildNotActive = errors.New("build not yet active")

//go:generate counterfeiter . BuildDB
type BuildDB interface {
	GetBuild(int) (db.Build, error)
	GetBuildEvents(int, uint) (db.EventSource, error)
	StartBuild(int, string, string) (bool, error)

	AbortBuild(int) error
	AbortNotifier(int) (db.Notifier, error)
}

//go:generate counterfeiter . BuildLocker
type BuildLocker interface {
	AcquireWriteLockImmediately([]db.NamedLock) (db.Lock, error)
}

func NewDBEngine(engine Engine, buildDB BuildDB, locker BuildLocker) Engine {
	return &dbEngine{
		engine: engine,

		db:     buildDB,
		locker: locker,
	}
}

type dbEngine struct {
	engine Engine

	db     BuildDB
	locker BuildLocker
}

func (*dbEngine) Name() string {
	return "db"
}

func (engine *dbEngine) CreateBuild(build db.Build, plan atc.BuildPlan) (Build, error) {
	createdBuild, err := engine.engine.CreateBuild(build, plan)
	if err != nil {
		return nil, err
	}

	started, err := engine.db.StartBuild(build.ID, engine.engine.Name(), createdBuild.Metadata())
	if err != nil {
		return nil, err
	}

	if !started {
		createdBuild.Abort()
	}

	return &dbBuild{
		id: build.ID,

		engine: engine.engine,

		db:     engine.db,
		locker: engine.locker,
	}, nil
}

func (engine *dbEngine) LookupBuild(build db.Build) (Build, error) {
	return &dbBuild{
		id: build.ID,

		engine: engine.engine,

		db:     engine.db,
		locker: engine.locker,
	}, nil
}

type dbBuild struct {
	id int

	engine Engine

	db     BuildDB
	locker BuildLocker
}

func (build *dbBuild) Metadata() string {
	return strconv.Itoa(build.id)
}

func (build *dbBuild) Abort() error {
	// the order below is very important to avoid races with build creation.

	lock, err := build.locker.AcquireWriteLockImmediately([]db.NamedLock{db.BuildTrackingLock(build.id)})
	if err != nil {
		// someone else is tracking the build; abort it, which will notify them
		return build.db.AbortBuild(build.id)
	}

	defer lock.Release()

	// no one is tracking the build; abort it ourselves

	// first save the status so that CreateBuild will see a conflict when it
	// tries to mark the build as started.
	err = build.db.AbortBuild(build.id)
	if err != nil {
		return err
	}

	// reload the model *after* saving the status for the following check to see
	// if it was already started
	model, err := build.db.GetBuild(build.id)
	if err != nil {
		return err
	}

	// if there's an engine, there's a real build to abort
	if model.Engine == "" {
		// otherwise, CreateBuild had not yet tried to start the build, and so it
		// will see the conflict when it tries to transition, and abort itself.
		return nil
	}

	// find the real build to abort...
	engineBuild, err := build.engine.LookupBuild(model)
	if err != nil {
		return err
	}

	// ...and abort it.
	return engineBuild.Abort()
}

func (build *dbBuild) Hijack(spec garden.ProcessSpec, io garden.ProcessIO) (garden.Process, error) {
	model, err := build.db.GetBuild(build.id)
	if err != nil {
		return nil, err
	}

	if model.Engine == "" {
		return nil, ErrBuildNotActive
	}

	engineBuild, err := build.engine.LookupBuild(model)
	if err != nil {
		return nil, err
	}

	return engineBuild.Hijack(spec, io)
}

func (build *dbBuild) Resume(logger lager.Logger) error {
	lock, err := build.locker.AcquireWriteLockImmediately([]db.NamedLock{db.BuildTrackingLock(build.id)})
	if err != nil {
		return nil
	}

	defer lock.Release()

	model, err := build.db.GetBuild(build.id)
	if err != nil {
		return err
	}

	if model.Engine == "" {
		return nil
	}

	engineBuild, err := build.engine.LookupBuild(model)
	if err != nil {
		return err
	}

	aborts, err := build.db.AbortNotifier(build.id)
	if err != nil {
		return err
	}

	defer aborts.Close()

	done := make(chan struct{})
	defer close(done)

	go func() {
		select {
		case <-aborts.Notify():
			engineBuild.Abort()
		case <-done:
		}
	}()

	return engineBuild.Resume(logger)
}
<commit_msg>add logging and comments to db engine<commit_after>package engine

import (
	"errors"
	"strconv"

	garden "github.com/cloudfoundry-incubator/garden/api"
	"github.com/concourse/atc"
	"github.com/concourse/atc/db"
	"github.com/pivotal-golang/lager"
)

var ErrBuildNotActive = errors.New("build not yet active")

//go:generate counterfeiter . BuildDB
type BuildDB interface {
	GetBuild(int) (db.Build, error)
	GetBuildEvents(int, uint) (db.EventSource, error)
	StartBuild(int, string, string) (bool, error)

	AbortBuild(int) error
	AbortNotifier(int) (db.Notifier, error)
}

//go:generate counterfeiter . BuildLocker
type BuildLocker interface {
	AcquireWriteLockImmediately([]db.NamedLock) (db.Lock, error)
}

func NewDBEngine(engine Engine, buildDB BuildDB, locker BuildLocker) Engine {
	return &dbEngine{
		engine: engine,

		db:     buildDB,
		locker: locker,
	}
}

type dbEngine struct {
	engine Engine

	db     BuildDB
	locker BuildLocker
}

func (*dbEngine) Name() string {
	return "db"
}

func (engine *dbEngine) CreateBuild(build db.Build, plan atc.BuildPlan) (Build, error) {
	createdBuild, err := engine.engine.CreateBuild(build, plan)
	if err != nil {
		return nil, err
	}

	started, err := engine.db.StartBuild(build.ID, engine.engine.Name(), createdBuild.Metadata())
	if err != nil {
		return nil, err
	}

	if !started {
		createdBuild.Abort()
	}

	return &dbBuild{
		id: build.ID,

		engine: engine.engine,

		db:     engine.db,
		locker: engine.locker,
	}, nil
}

func (engine *dbEngine) LookupBuild(build db.Build) (Build, error) {
	return &dbBuild{
		id: build.ID,

		engine: engine.engine,

		db:     engine.db,
		locker: engine.locker,
	}, nil
}

type dbBuild struct {
	id int

	engine Engine

	db     BuildDB
	locker BuildLocker
}

func (build *dbBuild) Metadata() string {
	return strconv.Itoa(build.id)
}

func (build *dbBuild) Abort() error {
	// the order below is very important to avoid races with build creation.

	lock, err := build.locker.AcquireWriteLockImmediately([]db.NamedLock{db.BuildTrackingLock(build.id)})
	if err != nil {
		// someone else is tracking the build; abort it, which will notify them
		return build.db.AbortBuild(build.id)
	}

	defer lock.Release()

	// no one is tracking the build; abort it ourselves

	// first save the status so that CreateBuild will see a conflict when it
	// tries to mark the build as started.
	err = build.db.AbortBuild(build.id)
	if err != nil {
		return err
	}

	// reload the model *after* saving the status for the following check to see
	// if it was already started
	model, err := build.db.GetBuild(build.id)
	if err != nil {
		return err
	}

	// if there's an engine, there's a real build to abort
	if model.Engine == "" {
		// otherwise, CreateBuild had not yet tried to start the build, and so it
		// will see the conflict when it tries to transition, and abort itself.
		return nil
	}

	// find the real build to abort...
	engineBuild, err := build.engine.LookupBuild(model)
	if err != nil {
		return err
	}

	// ...and abort it.
	return engineBuild.Abort()
}

func (build *dbBuild) Hijack(spec garden.ProcessSpec, io garden.ProcessIO) (garden.Process, error) {
	model, err := build.db.GetBuild(build.id)
	if err != nil {
		return nil, err
	}

	if model.Engine == "" {
		return nil, ErrBuildNotActive
	}

	engineBuild, err := build.engine.LookupBuild(model)
	if err != nil {
		return nil, err
	}

	return engineBuild.Hijack(spec, io)
}

func (build *dbBuild) Resume(logger lager.Logger) error {
	lock, err := build.locker.AcquireWriteLockImmediately([]db.NamedLock{db.BuildTrackingLock(build.id)})
	if err != nil {
		// already being tracked somewhere; short-circuit
		return nil
	}

	defer lock.Release()

	model, err := build.db.GetBuild(build.id)
	if err != nil {
		logger.Error("failed-to-load-build-from-db", err)
		return err
	}

	if model.Engine == "" {
		logger.Error("build-has-no-engine", err)
		return nil
	}

	engineBuild, err := build.engine.LookupBuild(model)
	if err != nil {
		logger.Error("failed-to-lookup-build-from-engine", err)
		return err
	}

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

	defer aborts.Close()

	done := make(chan struct{})
	defer close(done)

	go func() {
		select {
		case <-aborts.Notify():
			logger.Info("aborting")

			err := engineBuild.Abort()
			if err != nil {
				logger.Error("failed-to-abort", err)
			}
		case <-done:
		}
	}()

	return engineBuild.Resume(logger)
}
<|endoftext|>


<commit_before>// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
	"code.gitea.io/gitea/modules/timeutil"

	"xorm.io/xorm"
	"xorm.io/xorm/schemas"
)

type improveActionTableIndicesAction struct {
	ID          int64 `xorm:"pk autoincr"`
	UserID      int64 // Receiver user id.
	OpType      int
	ActUserID   int64 // Action user id.
	RepoID      int64
	CommentID   int64 `xorm:"INDEX"`
	IsDeleted   bool  `xorm:"NOT NULL DEFAULT false"`
	RefName     string
	IsPrivate   bool               `xorm:"NOT NULL DEFAULT false"`
	Content     string             `xorm:"TEXT"`
	CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

// TableName sets the name of this table
func (a *improveActionTableIndicesAction) TableName() string {
	return "action"
}

// TableIndices implements xorm's TableIndices interface
func (a *improveActionTableIndicesAction) TableIndices() []*schemas.Index {
	actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
	actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")

	repoIndex := schemas.NewIndex("r_c_u_d", schemas.IndexType)
	repoIndex.AddColumn("repo_id", "created_unix", "user_id", "is_deleted")

	return []*schemas.Index{actUserIndex, repoIndex}
}

func improveActionTableIndices(x *xorm.Engine) error {
	{
		type Action struct {
			ID          int64 `xorm:"pk autoincr"`
			UserID      int64 `xorm:"INDEX"` // Receiver user id.
			OpType      int
			ActUserID   int64 `xorm:"INDEX"` // Action user id.
			RepoID      int64 `xorm:"INDEX"`
			CommentID   int64 `xorm:"INDEX"`
			IsDeleted   bool  `xorm:"INDEX NOT NULL DEFAULT false"`
			RefName     string
			IsPrivate   bool               `xorm:"INDEX NOT NULL DEFAULT false"`
			Content     string             `xorm:"TEXT"`
			CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
		}
		if err := x.Sync2(&Action{}); err != nil {
			return err
		}
		if err := x.DropIndexes(&Action{}); err != nil {
			return err
		}
	}
	return x.Sync2(&improveActionTableIndicesAction{})
}
<commit_msg>Simplify and fix migration 216 (#20035)<commit_after>// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
	"code.gitea.io/gitea/modules/timeutil"

	"xorm.io/xorm"
	"xorm.io/xorm/schemas"
)

type improveActionTableIndicesAction struct {
	ID          int64 `xorm:"pk autoincr"`
	UserID      int64 // Receiver user id.
	OpType      int
	ActUserID   int64 // Action user id.
	RepoID      int64
	CommentID   int64 `xorm:"INDEX"`
	IsDeleted   bool  `xorm:"NOT NULL DEFAULT false"`
	RefName     string
	IsPrivate   bool               `xorm:"NOT NULL DEFAULT false"`
	Content     string             `xorm:"TEXT"`
	CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

// TableName sets the name of this table
func (a *improveActionTableIndicesAction) TableName() string {
	return "action"
}

// TableIndices implements xorm's TableIndices interface
func (a *improveActionTableIndicesAction) TableIndices() []*schemas.Index {
	actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
	actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")

	repoIndex := schemas.NewIndex("r_c_u_d", schemas.IndexType)
	repoIndex.AddColumn("repo_id", "created_unix", "user_id", "is_deleted")

	return []*schemas.Index{actUserIndex, repoIndex}
}

func improveActionTableIndices(x *xorm.Engine) error {
	return x.Sync2(&improveActionTableIndicesAction{})
}
<|endoftext|>


<commit_before>package cloudmutex

import (
	"bytes"
	"errors"
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	storage "google.golang.org/api/storage/v1"
	"sync"
	"time"
)

type cloudmutex struct {
	project string
	bucket  string
	object  string
	service *storage.Service
}

// TimedLock will wait up to duruation d for l.Lock() to succeed.
func TimedLock(l sync.Locker, d time.Duration) error {
	done := make(chan bool, 1)
	go func() {
		l.Lock()
		done <- true
	}()
	select {
	case <-done:
		return nil
	case <-time.After(d):
		return errors.New("lock request timed out")
	}
}

// TimedUnlock will wait up to duruation d for l.Unlock() to succeed.
func TimedUnlock(l sync.Locker, d time.Duration) error {
	done := make(chan bool, 1)
	go func() {
		l.Unlock()
		done <- true
	}()
	select {
	case <-done:
		return nil
	case <-time.After(d):
		return errors.New("unlock request timed out")
	}
}

func (m cloudmutex) Lock() {
	object := &storage.Object{Name: m.object}
	for {
		_, err := m.service.Objects.Insert(m.bucket, object).Media(bytes.NewReader([]byte("1"))).Do()
		if err == nil {
			return
		}
	}
}

func (m cloudmutex) Unlock() {
	for {
		err := m.service.Objects.Delete(m.bucket, m.object).Do()
		if err == nil {
			return
		}
	}
}

// New creates a GCS-based sync.Locker.
// It uses Application Default Credentials to make authenticated requests
// to Google Cloud Storage. See the DefaultClient function of the
// golang.org/x/oauth2/google package for App Default Credentials details.
//
// If ctx argument is nil, context.Background is used.
//
func New(ctx context.Context, project, bucket, object string) (sync.Locker, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	scope := storage.DevstorageFullControlScope
	client, err := google.DefaultClient(ctx, scope)
	if err != nil {
		return nil, err
	}
	service, err := storage.New(client)
	if err != nil {
		return nil, err
	}
	m := &cloudmutex{
		project: project,
		bucket:  bucket,
		object:  object,
		service: service,
	}
	return m, nil
}
<commit_msg>improve perf by passing empty struct instead of a bool across channel<commit_after>package cloudmutex

import (
	"bytes"
	"errors"
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	storage "google.golang.org/api/storage/v1"
	"sync"
	"time"
)

type cloudmutex struct {
	project string
	bucket  string
	object  string
	service *storage.Service
}

// TimedLock will wait up to duruation d for l.Lock() to succeed.
func TimedLock(l sync.Locker, d time.Duration) error {
	done := make(chan struct{}, 1)
	go func() {
		l.Lock()
		done <- struct{}{}
	}()
	select {
	case <-done:
		return nil
	case <-time.After(d):
		return errors.New("lock request timed out")
	}
}

// TimedUnlock will wait up to duruation d for l.Unlock() to succeed.
func TimedUnlock(l sync.Locker, d time.Duration) error {
	done := make(chan struct{}, 1)
	go func() {
		l.Unlock()
		done <- struct{}{}
	}()
	select {
	case <-done:
		return nil
	case <-time.After(d):
		return errors.New("unlock request timed out")
	}
}

func (m cloudmutex) Lock() {
	object := &storage.Object{Name: m.object}
	for {
		_, err := m.service.Objects.Insert(m.bucket, object).Media(bytes.NewReader([]byte("1"))).Do()
		if err == nil {
			return
		}
	}
}

func (m cloudmutex) Unlock() {
	for {
		err := m.service.Objects.Delete(m.bucket, m.object).Do()
		if err == nil {
			return
		}
	}
}

// New creates a GCS-based sync.Locker.
// It uses Application Default Credentials to make authenticated requests
// to Google Cloud Storage. See the DefaultClient function of the
// golang.org/x/oauth2/google package for App Default Credentials details.
//
// If ctx argument is nil, context.Background is used.
//
func New(ctx context.Context, project, bucket, object string) (sync.Locker, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	scope := storage.DevstorageFullControlScope
	client, err := google.DefaultClient(ctx, scope)
	if err != nil {
		return nil, err
	}
	service, err := storage.New(client)
	if err != nil {
		return nil, err
	}
	m := &cloudmutex{
		project: project,
		bucket:  bucket,
		object:  object,
		service: service,
	}
	return m, nil
}
<|endoftext|>


<commit_before>/*
i-sudoku is an interactive command-line sudoku tool
*/

package main

import (
	"github.com/jkomoros/sudoku"
	"github.com/nsf/termbox-go"
	"log"
	"strings"
)

type mainModel struct {
	grid *sudoku.Grid
}

func main() {
	if err := termbox.Init(); err != nil {
		log.Fatal("Termbox initialization failed:", err)
	}
	defer termbox.Close()

	model := &mainModel{
		sudoku.NewGrid(),
	}

	model.grid.Fill()

	draw(model)

mainloop:
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyEsc, termbox.KeyCtrlC:
				break mainloop
			}
		}
		draw(model)
	}
}

func draw(model *mainModel) {
	drawGrid(model.grid)
	termbox.Flush()
}

func drawGrid(grid *sudoku.Grid) {
	for y, line := range strings.Split(grid.Diagram(), "\n") {
		x := 0
		//The first number in range will be byte offset, but for some items like the bullet, it's two bytes.
		//But what we care about is that each item is a character.
		for _, ch := range line {
			termbox.SetCell(x, y, ch, termbox.ColorGreen, termbox.ColorDefault)
			x++
		}
	}
}
<commit_msg>Made it so the backgrond 'number filled' rune is printed in a different color.<commit_after>/*
i-sudoku is an interactive command-line sudoku tool
*/

package main

import (
	"github.com/jkomoros/sudoku"
	"github.com/nsf/termbox-go"
	"log"
	"strings"
	"unicode/utf8"
)

type mainModel struct {
	grid *sudoku.Grid
}

func main() {
	if err := termbox.Init(); err != nil {
		log.Fatal("Termbox initialization failed:", err)
	}
	defer termbox.Close()

	model := &mainModel{
		sudoku.NewGrid(),
	}

	model.grid.Fill()

	draw(model)

mainloop:
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyEsc, termbox.KeyCtrlC:
				break mainloop
			}
		}
		draw(model)
	}
}

func draw(model *mainModel) {
	drawGrid(model.grid)
	termbox.Flush()
}

func drawGrid(grid *sudoku.Grid) {
	for y, line := range strings.Split(grid.Diagram(), "\n") {
		x := 0
		//The first number in range will be byte offset, but for some items like the bullet, it's two bytes.
		//But what we care about is that each item is a character.
		for _, ch := range line {

			defaultColor := termbox.ColorGreen

			numberRune, _ := utf8.DecodeRuneInString(sudoku.DIAGRAM_NUMBER)

			if ch == numberRune {
				defaultColor = termbox.ColorBlue
			}

			termbox.SetCell(x, y, ch, defaultColor, termbox.ColorDefault)
			x++
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time

import (
	"testing";
	"time";
)

export func TestTick(t *testing.T) {
	const (
		Delta uint64 = 100*1e6;
		Count uint64 = 10;
	);
	c := Tick(Delta);
	t0 := Nanoseconds();
	for i := 0; i < Count; i++ {
		<-c;
	}
	t1 := Nanoseconds();
	ns := t1 - t0;
	target := int64(Delta*Count);
	slop := target*2/10;
	if ns < target - slop || ns > target + slop {
		t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target));
	}
}
<commit_msg>Remove types from constants, since they didn't match what Tick() expected.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time

import (
	"testing";
	"time";
)

export func TestTick(t *testing.T) {
	const (
		Delta = 100*1e6;
		Count = 10;
	);
	c := Tick(Delta);
	t0 := Nanoseconds();
	for i := 0; i < Count; i++ {
		<-c;
	}
	t1 := Nanoseconds();
	ns := t1 - t0;
	target := int64(Delta*Count);
	slop := target*2/10;
	if ns < target - slop || ns > target + slop {
		t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target));
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 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.
*/

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	// TODO: move this somewhere central
	"k8s.io/contrib/mungegithub/mungers/jenkins"
	"k8s.io/kubernetes/pkg/util/sets"
)

// LatencyData represents the latency data for a set of RESTful API calls
type LatencyData struct {
	APICalls []APICallLatency `json:"apicalls"`
}

// APICallLatency represents the latency data for a (resource, verb) tuple
type APICallLatency struct {
	Latency  Histogram `json:"latency"`
	Resource string    `json:"resource"`
	Verb     string    `json:"verb"`
}

// Histogram is a map from bucket to latency (e.g. "Perc90" -> 23.5)
type Histogram map[string]float64

// ResourceToHistogram is a map from resource names (e.g. "pods") to the relevant latency data
type ResourceToHistogram map[string][]APICallLatency

// BuildLatencyData is a map from build number to latency data
type BuildLatencyData map[string]ResourceToHistogram

func (buildLatency *BuildLatencyData) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	data, err := json.Marshal(buildLatency)
	if err != nil {
		res.Header().Set("Content-type", "text/html")
		res.WriteHeader(http.StatusInternalServerError)
		res.Write([]byte(fmt.Sprintf("
Internal Error

%v", err)))
		return
	}
	res.Header().Set("Content-type", "application/json")
	res.WriteHeader(http.StatusOK)
	res.Write(data)
}

func getLatencyData(client *jenkins.JenkinsClient, job string) (BuildLatencyData, sets.String, sets.String, error) {
	buildLatency := BuildLatencyData{}
	resources := sets.NewString()
	methods := sets.NewString()

	queue, err := client.GetJob(job)
	if err != nil {
		return buildLatency, resources, methods, err
	}

	for ix := range queue.Builds {
		build := queue.Builds[ix]
		reader, err := client.GetConsoleLog(job, build.Number)
		if err != nil {
			fmt.Printf("error getting logs: %v", err)
			continue
		}
		defer reader.Close()
		scanner := bufio.NewScanner(reader)
		buff := &bytes.Buffer{}
		inLatency := false

		hist := ResourceToHistogram{}
		found := false
		testNameSeparator := "[It] [Skipped] [Performance suite]"
		testName := ""
		for scanner.Scan() {
			line := scanner.Text()
			if strings.Contains(line, testNameSeparator) {
				testName = strings.Trim(strings.Split(line, testNameSeparator)[1], " ")
			}
			// TODO: This is brittle, we should emit a tail delimiter too
			if strings.Contains(line, "INFO") || strings.Contains(line, "STEP") || strings.Contains(line, "Failure") {
				if inLatency {
					obj := LatencyData{}
					if err := json.Unmarshal(buff.Bytes(), &obj); err != nil {
						fmt.Printf("error parsing JSON in build %d: %v %s\n", build.Number, err, buff.String())
						// reset state and try again with more input
						inLatency = false
						continue
					}

					if testName == "should allow starting 30 pods per node" {
						for _, call := range obj.APICalls {
							list := hist[call.Resource]
							list = append(list, call)
							hist[call.Resource] = list
							resources.Insert(call.Resource)
							methods.Insert(call.Verb)
						}
					}

					buff.Reset()
				}
				inLatency = false
			}
			if strings.Contains(line, "API calls latencies") {
				found = true
				inLatency = true
				ix = strings.Index(line, "{")
				line = line[ix:]
			}
			if inLatency {
				buff.WriteString(line + " ")
			}
		}
		if !found {
			continue
		}

		buildLatency[fmt.Sprintf("%d", build.Number)] = hist
	}
	return buildLatency, resources, methods, nil
}

func generateCSV(buildLatency BuildLatencyData, resources, methods sets.String, out io.Writer) error {
	header := []string{"build"}
	for _, rsrc := range resources.List() {
		header = append(header, fmt.Sprintf("%s_50", rsrc))
		header = append(header, fmt.Sprintf("%s_90", rsrc))
		header = append(header, fmt.Sprintf("%s_99", rsrc))
	}
	if _, err := fmt.Fprintln(out, strings.Join(header, ",")); err != nil {
		return err
	}

	for _, method := range methods.List() {
		if _, err := fmt.Fprintln(out, method); err != nil {
			return err
		}
		for build, data := range buildLatency {
			line := []string{fmt.Sprintf("%d", build)}
			for _, rsrc := range resources.List() {
				podData := data[rsrc]
				line = append(line, fmt.Sprintf("%g", findMethod(method, "Perc50", podData)))
				line = append(line, fmt.Sprintf("%g", findMethod(method, "Perc90", podData)))
				line = append(line, fmt.Sprintf("%g", findMethod(method, "Perc99", podData)))
			}
			if _, err := fmt.Fprintln(out, strings.Join(line, ",")); err != nil {
				return err
			}
		}
	}
	return nil
}

func findMethod(method, item string, data []APICallLatency) float64 {
	for _, datum := range data {
		if datum.Verb == method {
			return datum.Latency[item]
		}
	}
	return -1
}

var (
	www         = flag.Bool("www", false, "If true, start a web-server to server performance data")
	addr        = flag.String("address", ":8080", "The address to serve web data on, only used if -www is true")
	wwwDir      = flag.String("dir", "", "If non-empty, add a file server for this directory at the root of the web server")
	jenkinsHost = flag.String("jenkins-host", "", "The URL for the jenkins server.")

	pollDuration = 10 * time.Minute
	errorDelay   = 10 * time.Second
)

func main() {
	flag.Parse()
	job := "kubernetes-e2e-gce-scalability"
	client := &jenkins.JenkinsClient{
		Host: *jenkinsHost,
	}

	if !*www {
		buildLatency, resources, methods, err := getLatencyData(client, job)
		if err != nil {
			fmt.Printf("Failed to get data: %v\n", err)
			os.Exit(1)
		}
		generateCSV(buildLatency, resources, methods, os.Stdout)
		return
	}

	buildLatency := BuildLatencyData{}
	resources := sets.String{}
	methods := sets.String{}
	var err error
	go func() {
		for {
			buildLatency, resources, methods, err = getLatencyData(client, job)
			if err != nil {
				fmt.Printf("Error fetching data: %v\n", err)
				time.Sleep(errorDelay)
				continue
			}
			time.Sleep(pollDuration)
		}
	}()

	http.Handle("/api", &buildLatency)
	http.Handle("/", http.FileServer(http.Dir(*wwwDir)))
	http.ListenAndServe(*addr, nil)
}
<commit_msg>Update perfdash with new performance tag<commit_after>/*
Copyright 2015 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.
*/

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	// TODO: move this somewhere central
	"k8s.io/contrib/mungegithub/mungers/jenkins"
	"k8s.io/kubernetes/pkg/util/sets"
)

// LatencyData represents the latency data for a set of RESTful API calls
type LatencyData struct {
	APICalls []APICallLatency `json:"apicalls"`
}

// APICallLatency represents the latency data for a (resource, verb) tuple
type APICallLatency struct {
	Latency  Histogram `json:"latency"`
	Resource string    `json:"resource"`
	Verb     string    `json:"verb"`
}

// Histogram is a map from bucket to latency (e.g. "Perc90" -> 23.5)
type Histogram map[string]float64

// ResourceToHistogram is a map from resource names (e.g. "pods") to the relevant latency data
type ResourceToHistogram map[string][]APICallLatency

// BuildLatencyData is a map from build number to latency data
type BuildLatencyData map[string]ResourceToHistogram

func (buildLatency *BuildLatencyData) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	data, err := json.Marshal(buildLatency)
	if err != nil {
		res.Header().Set("Content-type", "text/html")
		res.WriteHeader(http.StatusInternalServerError)
		res.Write([]byte(fmt.Sprintf("
Internal Error

%v", err)))
		return
	}
	res.Header().Set("Content-type", "application/json")
	res.WriteHeader(http.StatusOK)
	res.Write(data)
}

func getLatencyData(client *jenkins.JenkinsClient, job string) (BuildLatencyData, sets.String, sets.String, error) {
	buildLatency := BuildLatencyData{}
	resources := sets.NewString()
	methods := sets.NewString()

	queue, err := client.GetJob(job)
	if err != nil {
		return buildLatency, resources, methods, err
	}

	for ix := range queue.Builds {
		build := queue.Builds[ix]
		reader, err := client.GetConsoleLog(job, build.Number)
		if err != nil {
			fmt.Printf("error getting logs: %v", err)
			continue
		}
		defer reader.Close()
		scanner := bufio.NewScanner(reader)
		buff := &bytes.Buffer{}
		inLatency := false

		hist := ResourceToHistogram{}
		found := false
		testNameSeparator := "[It] [Feature:Performance]"
		testName := ""
		for scanner.Scan() {
			line := scanner.Text()
			if strings.Contains(line, testNameSeparator) {
				testName = strings.Trim(strings.Split(line, testNameSeparator)[1], " ")
			}
			// TODO: This is brittle, we should emit a tail delimiter too
			if strings.Contains(line, "INFO") || strings.Contains(line, "STEP") || strings.Contains(line, "Failure") {
				if inLatency {
					obj := LatencyData{}
					if err := json.Unmarshal(buff.Bytes(), &obj); err != nil {
						fmt.Printf("error parsing JSON in build %d: %v %s\n", build.Number, err, buff.String())
						// reset state and try again with more input
						inLatency = false
						continue
					}

					if testName == "should allow starting 30 pods per node" {
						for _, call := range obj.APICalls {
							list := hist[call.Resource]
							list = append(list, call)
							hist[call.Resource] = list
							resources.Insert(call.Resource)
							methods.Insert(call.Verb)
						}
					}

					buff.Reset()
				}
				inLatency = false
			}
			if strings.Contains(line, "API calls latencies") {
				found = true
				inLatency = true
				ix = strings.Index(line, "{")
				line = line[ix:]
			}
			if inLatency {
				buff.WriteString(line + " ")
			}
		}
		if !found {
			continue
		}

		buildLatency[fmt.Sprintf("%d", build.Number)] = hist
	}
	return buildLatency, resources, methods, nil
}

func generateCSV(buildLatency BuildLatencyData, resources, methods sets.String, out io.Writer) error {
	header := []string{"build"}
	for _, rsrc := range resources.List() {
		header = append(header, fmt.Sprintf("%s_50", rsrc))
		header = append(header, fmt.Sprintf("%s_90", rsrc))
		header = append(header, fmt.Sprintf("%s_99", rsrc))
	}
	if _, err := fmt.Fprintln(out, strings.Join(header, ",")); err != nil {
		return err
	}

	for _, method := range methods.List() {
		if _, err := fmt.Fprintln(out, method); err != nil {
			return err
		}
		for build, data := range buildLatency {
			line := []string{fmt.Sprintf("%d", build)}
			for _, rsrc := range resources.List() {
				podData := data[rsrc]
				line = append(line, fmt.Sprintf("%g", findMethod(method, "Perc50", podData)))
				line = append(line, fmt.Sprintf("%g", findMethod(method, "Perc90", podData)))
				line = append(line, fmt.Sprintf("%g", findMethod(method, "Perc99", podData)))
			}
			if _, err := fmt.Fprintln(out, strings.Join(line, ",")); err != nil {
				return err
			}
		}
	}
	return nil
}

func findMethod(method, item string, data []APICallLatency) float64 {
	for _, datum := range data {
		if datum.Verb == method {
			return datum.Latency[item]
		}
	}
	return -1
}

var (
	www         = flag.Bool("www", false, "If true, start a web-server to server performance data")
	addr        = flag.String("address", ":8080", "The address to serve web data on, only used if -www is true")
	wwwDir      = flag.String("dir", "", "If non-empty, add a file server for this directory at the root of the web server")
	jenkinsHost = flag.String("jenkins-host", "", "The URL for the jenkins server.")

	pollDuration = 10 * time.Minute
	errorDelay   = 10 * time.Second
)

func main() {
	flag.Parse()
	job := "kubernetes-e2e-gce-scalability"
	client := &jenkins.JenkinsClient{
		Host: *jenkinsHost,
	}

	if !*www {
		buildLatency, resources, methods, err := getLatencyData(client, job)
		if err != nil {
			fmt.Printf("Failed to get data: %v\n", err)
			os.Exit(1)
		}
		generateCSV(buildLatency, resources, methods, os.Stdout)
		return
	}

	buildLatency := BuildLatencyData{}
	resources := sets.String{}
	methods := sets.String{}
	var err error
	go func() {
		for {
			buildLatency, resources, methods, err = getLatencyData(client, job)
			if err != nil {
				fmt.Printf("Error fetching data: %v\n", err)
				time.Sleep(errorDelay)
				continue
			}
			time.Sleep(pollDuration)
		}
	}()

	http.Handle("/api", &buildLatency)
	http.Handle("/", http.FileServer(http.Dir(*wwwDir)))
	http.ListenAndServe(*addr, nil)
}
<|endoftext|>


<commit_before>package market

import (
	// Stdlib
	"encoding/json"

	// RPC
	"github.com/asuleymanov/golos-go/interfaces"
	"github.com/asuleymanov/golos-go/internal/rpc"

	// Vendor
	"github.com/pkg/errors"
)

const APIID = "market_history_api"

type API struct {
	id     int
	caller interfaces.Caller
}

func NewAPI(caller interfaces.Caller) (*API, error) {
	id, err := rpc.GetNumericAPIID(caller, APIID)
	if err != nil {
		return nil, err
	}
	return &API{id, caller}, nil
}

func (api *API) call(method string, params, resp interface{}) error {
	return api.caller.Call("call", []interface{}{api.id, method, params}, resp)
}

func (api *API) GetOrderBookRaw(limit uint32) (*json.RawMessage, error) {
	var resp json.RawMessage
	if limit > 1000 {
		return nil, errors.New("golos-go: market_history_api: get_order_book -> limit must not exceed 1000")
	}
	if err := api.caller.Call("get_order_book", []interface{}{limit}, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

func (api *API) GetOrderBook(limit uint32) (*OrderBook, error) {
	raw, err := api.GetOrderBookRaw(limit)
	if err != nil {
		return nil, err
	}
	var resp *OrderBook
	if err := json.Unmarshal([]byte(*raw), &resp); err != nil {
		return nil, errors.Wrap(err, "golos-go: market_history_api: failed to unmarshal get_order_book response")
	}
	return resp, nil
}
<commit_msg>Bug fix<commit_after>package market

import (
	// Stdlib
	"encoding/json"

	// RPC
	"github.com/asuleymanov/golos-go/interfaces"
	"github.com/asuleymanov/golos-go/internal/rpc"

	// Vendor
	"github.com/pkg/errors"
)

const APIID = "market_history_api"

var EmptyParams = []string{}

type API struct {
	id     int
	caller interfaces.Caller
}

func NewAPI(caller interfaces.Caller) (*API, error) {
	id, err := rpc.GetNumericAPIID(caller, APIID)
	if err != nil {
		return nil, err
	}
	return &API{id, caller}, nil
}

func (api *API) Raw(method string, params interface{}) (*json.RawMessage, error) {
	var resp json.RawMessage
	if err := api.caller.Call("call", []interface{}{api.id, method, params}, &resp); err != nil {
		return nil, errors.Wrapf(err, "golos-go: %v: failed to call %v\n", APIID, method)
	}
	return &resp, nil
}

func (api *API) GetTickerRaw() (*json.RawMessage, error) {
	return api.Raw("get_ticker", EmptyParams)
}

func (api *API) GetTicker() (*Ticker, error) {
	raw, err := api.GetTickerRaw()
	if err != nil {
		return nil, err
	}
	var resp *Ticker
	if err := json.Unmarshal([]byte(*raw), &resp); err != nil {
		return nil, errors.Wrap(err, "golos-go: market_history_api: failed to unmarshal get_ticker response")
	}
	return resp, nil
}

func (api *API) GetVolumeRaw() (*json.RawMessage, error) {
	return api.Raw("get_volume", EmptyParams)
}

func (api *API) GetVolume() (*Volume, error) {
	raw, err := api.GetVolumeRaw()
	if err != nil {
		return nil, err
	}
	var resp *Volume
	if err := json.Unmarshal([]byte(*raw), &resp); err != nil {
		return nil, errors.Wrap(err, "golos-go: market_history_api: failed to unmarshal get_volume response")
	}
	return resp, nil
}

func (api *API) GetOrderBookRaw(limit uint32) (*json.RawMessage, error) {
	if limit > 1000 {
		return nil, errors.New("golos-go: market_history_api: get_order_book -> limit must not exceed 1000")
	}
	return api.Raw("get_order_book", []interface{}{limit})
}

func (api *API) GetOrderBook(limit uint32) (*OrderBook, error) {
	raw, err := api.GetOrderBookRaw(limit)
	if err != nil {
		return nil, err
	}
	var resp *OrderBook
	if err := json.Unmarshal([]byte(*raw), &resp); err != nil {
		return nil, errors.Wrap(err, "golos-go: market_history_api: failed to unmarshal get_order_book response")
	}
	return resp, nil
}

func (api *API) GetTradeHistoryRaw(start, end string, limit uint32) (*json.RawMessage, error) {
	if limit > 1000 {
		return nil, errors.New("golos-go: market_history_api: get_order_book -> limit must not exceed 1000")
	}
	return api.Raw("get_trade_history", []interface{}{start, end, limit})
}

func (api *API) GetTradeHistory(start, end string, limit uint32) ([]*Trades, error) {
	raw, err := api.GetTradeHistoryRaw(start, end, limit)
	if err != nil {
		return nil, err
	}
	var resp []*Trades
	if err := json.Unmarshal([]byte(*raw), &resp); err != nil {
		return nil, errors.Wrap(err, "golos-go: market_history_api: failed to unmarshal get_trade_history response")
	}
	return resp, nil
}

func (api *API) GetRecentTradesRaw(limit uint32) (*json.RawMessage, error) {
	if limit > 1000 {
		return nil, errors.New("golos-go: market_history_api: get_order_book -> limit must not exceed 1000")
	}
	return api.Raw("get_recent_trades", []interface{}{limit})
}

func (api *API) GetRecentTrades(limit uint32) ([]*Trades, error) {
	raw, err := api.GetRecentTradesRaw(limit)
	if err != nil {
		return nil, err
	}
	var resp []*Trades
	if err := json.Unmarshal([]byte(*raw), &resp); err != nil {
		return nil, errors.Wrap(err, "golos-go: market_history_api: failed to unmarshal get_recent_trades response")
	}
	return resp, nil
}

func (api *API) GetMarketHistoryRaw(b_sec uint32, start, end string) (*json.RawMessage, error) {
	if limit > 1000 {
		return nil, errors.New("golos-go: market_history_api: get_order_book -> limit must not exceed 1000")
	}
	return api.Raw("get_market_history", []interface{}{limit})
}

func (api *API) GetMarketHistory(limit uint32) ([]*MarketHistory, error) {
	raw, err := api.GetMarketHistoryRaw(limit)
	if err != nil {
		return nil, err
	}
	var resp []*MarketHistory
	if err := json.Unmarshal([]byte(*raw), &resp); err != nil {
		return nil, errors.Wrap(err, "golos-go: market_history_api: failed to unmarshal get_market_history response")
	}
	return resp, nil
}

func (api *API) GetMarketHistoryBucketsRaw() (*json.RawMessage, error) {
	return api.Raw("get_market_history_buckets", EmptyParams)
}

func (api *API) GetMarketHistoryBuckets() ([]uint32, error) {
	raw, err := api.GetMarketHistoryBucketsRaw()
	if err != nil {
		return nil, err
	}
	var resp []uint32
	if err := json.Unmarshal([]byte(*raw), &resp); err != nil {
		return nil, errors.Wrap(err, "golos-go: market_history_api: failed to unmarshal get_market_history_buckets response")
	}
	return resp, nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"code.google.com/p/go.crypto/ssh"
	"encoding/json"
	"github.com/buth/stocker/auth"
	"github.com/buth/stocker/backend"
	"github.com/buth/stocker/crypto"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

var serverConfig struct {
	SecretFilepath, PrivateFilepath, Backend, BackendNamespace, BackendProtocol, BackendAddress, Group, Address, ReadersURL, WritersURL string
}

var serverClient *http.Client

var Server = &Command{
	UsageLine: "server [options]",
	Short:     "start a stocker server",
}

func init() {
	Server.Run = serverRun
	Server.Flag.StringVar(&serverConfig.Address, "a", ":2022", "address to listen on")
	Server.Flag.StringVar(&serverConfig.Backend, "b", "redis", "backend to use")
	Server.Flag.StringVar(&serverConfig.BackendAddress, "h", ":6379", "backend address")
	Server.Flag.StringVar(&serverConfig.BackendNamespace, "n", "stocker", "backend namespace")
	Server.Flag.StringVar(&serverConfig.BackendProtocol, "t", "tcp", "backend connection protocol")
	Server.Flag.StringVar(&serverConfig.PrivateFilepath, "i", "/etc/stocker/id_rsa", "path to an ssh private key")
	Server.Flag.StringVar(&serverConfig.SecretFilepath, "k", "/etc/stocker/key", "path to encryption key")
	Server.Flag.StringVar(&serverConfig.ReadersURL, "r", "https://s3.amazonaws.com/newsdev-ops/keys.json", "retrieve reader public keys from this URL")
	Server.Flag.StringVar(&serverConfig.WritersURL, "w", "https://s3.amazonaws.com/newsdev-ops/keys.json", "retrieve writer public keys from this URL")

	serverClient = &http.Client{
		Transport: &http.Transport{
			MaxIdleConnsPerHost:   1,
			ResponseHeaderTimeout: time.Minute,
		},
	}
}

func serverFetchPublicKeys(url string) ([]ssh.PublicKey, error) {

	// Fetch the public keys.
	response, err := serverClient.Get(url)
	if err != nil {
		return nil, err
	}

	// Read out the entire body.
	jsonResponse, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	// Build a raw keys object that reflects the expected structure of the JSON.
	var rawKeys []struct {
		Key string
	}

	// Try to parse the body of the response as JSON.
	if err := json.Unmarshal(jsonResponse, &rawKeys); err != nil {
		return nil, err
	}

	// Build a new authorizer and iterate through the raw keys, parsing them
	// and then adding them.
	publicKeys := make([]ssh.PublicKey, 0, len(rawKeys))
	for _, rawKey := range rawKeys {

		// We're only interested in the key itself and whether or not there was an error.
		publicKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(rawKey.Key))
		if err != nil {
			return publicKeys, err
		}

		// Add the key to the list.
		publicKeys = publicKeys[:len(publicKeys)+1]
		publicKeys[len(publicKeys)-1] = publicKey
	}

	return publicKeys, nil
}

func serverRun(cmd *Command, args []string) {

	c, err := crypto.NewCrypterFromFile(serverConfig.SecretFilepath)
	if err != nil {
		log.Fatal(err)
	}

	b, err := backend.NewBackend(serverConfig.Backend, serverConfig.BackendNamespace, serverConfig.BackendProtocol, serverConfig.BackendAddress)
	if err != nil {
		log.Fatal(err)
	}

	privateBytes, err := ioutil.ReadFile(serverConfig.PrivateFilepath)
	if err != nil {
		log.Fatal("failed to load private key")
	}

	private, err := ssh.ParsePrivateKey(privateBytes)
	if err != nil {
		log.Fatal("failed to parse private key")
	}

	// Create a new server using the specified Backend and Crypter.
	server := auth.NewServer(b, c, private)

	readers, err := serverFetchPublicKeys(serverConfig.ReadersURL)
	if err != nil {
		log.Fatal(err)
	}

	writers, err := serverFetchPublicKeys(serverConfig.WritersURL)
	if err != nil {
		log.Fatal(err)
	}

	for _, reader := range readers {
		server.AddReadKey(reader)
	}

	for _, writer := range writers {
		server.AddWriteKey(writer)
	}

	// Start the server.
	log.Fatal(server.ListenAndServe(serverConfig.Address))
}
<commit_msg>no default key locations<commit_after>package cmd

import (
	"code.google.com/p/go.crypto/ssh"
	"encoding/json"
	"github.com/buth/stocker/auth"
	"github.com/buth/stocker/backend"
	"github.com/buth/stocker/crypto"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

var serverConfig struct {
	SecretFilepath, PrivateFilepath, Backend, BackendNamespace, BackendProtocol, BackendAddress, Group, Address, ReadersURL, WritersURL string
}

var serverClient *http.Client

var Server = &Command{
	UsageLine: "server [options]",
	Short:     "start a stocker server",
}

func init() {
	Server.Run = serverRun
	Server.Flag.StringVar(&serverConfig.Address, "a", ":2022", "address to listen on")
	Server.Flag.StringVar(&serverConfig.Backend, "b", "redis", "backend to use")
	Server.Flag.StringVar(&serverConfig.BackendAddress, "h", ":6379", "backend address")
	Server.Flag.StringVar(&serverConfig.BackendNamespace, "n", "stocker", "backend namespace")
	Server.Flag.StringVar(&serverConfig.BackendProtocol, "t", "tcp", "backend connection protocol")
	Server.Flag.StringVar(&serverConfig.PrivateFilepath, "i", "/etc/stocker/id_rsa", "path to an ssh private key")
	Server.Flag.StringVar(&serverConfig.SecretFilepath, "k", "/etc/stocker/key", "path to encryption key")
	Server.Flag.StringVar(&serverConfig.ReadersURL, "r", "", "retrieve reader public keys from this URL")
	Server.Flag.StringVar(&serverConfig.WritersURL, "w", "", "retrieve writer public keys from this URL")

	serverClient = &http.Client{
		Transport: &http.Transport{
			MaxIdleConnsPerHost:   1,
			ResponseHeaderTimeout: time.Minute,
		},
	}
}

func serverFetchPublicKeys(url string) ([]ssh.PublicKey, error) {

	// Fetch the public keys.
	response, err := serverClient.Get(url)
	if err != nil {
		return nil, err
	}

	// Read out the entire body.
	jsonResponse, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	// Build a raw keys object that reflects the expected structure of the JSON.
	var rawKeys []struct {
		Key string
	}

	// Try to parse the body of the response as JSON.
	if err := json.Unmarshal(jsonResponse, &rawKeys); err != nil {
		return nil, err
	}

	// Build a new authorizer and iterate through the raw keys, parsing them
	// and then adding them.
	publicKeys := make([]ssh.PublicKey, 0, len(rawKeys))
	for _, rawKey := range rawKeys {

		// We're only interested in the key itself and whether or not there was an error.
		publicKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(rawKey.Key))
		if err != nil {
			return publicKeys, err
		}

		// Add the key to the list.
		publicKeys = publicKeys[:len(publicKeys)+1]
		publicKeys[len(publicKeys)-1] = publicKey
	}

	return publicKeys, nil
}

func serverRun(cmd *Command, args []string) {

	c, err := crypto.NewCrypterFromFile(serverConfig.SecretFilepath)
	if err != nil {
		log.Fatal(err)
	}

	b, err := backend.NewBackend(serverConfig.Backend, serverConfig.BackendNamespace, serverConfig.BackendProtocol, serverConfig.BackendAddress)
	if err != nil {
		log.Fatal(err)
	}

	privateBytes, err := ioutil.ReadFile(serverConfig.PrivateFilepath)
	if err != nil {
		log.Fatal("failed to load private key")
	}

	private, err := ssh.ParsePrivateKey(privateBytes)
	if err != nil {
		log.Fatal("failed to parse private key")
	}

	// Create a new server using the specified Backend and Crypter.
	server := auth.NewServer(b, c, private)

	// Check if a URL was provided to pull reader keys from.
	if serverConfig.ReadersURL != "" {

		// Fetch the reader keys.
		readers, err := serverFetchPublicKeys(serverConfig.ReadersURL)
		if err != nil {
			log.Fatal(err)
		}

		// Add the reader keys to the server.
		for _, reader := range readers {
			server.AddReadKey(reader)
		}
	}

	// Check if a URL was provided to pull writer keys from.
	if serverConfig.WritersURL != "" {

		// Fetch the writer keys.
		writers, err := serverFetchPublicKeys(serverConfig.WritersURL)
		if err != nil {
			log.Fatal(err)
		}

		// Add the writer keys to the server.
		for _, writer := range writers {
			server.AddWriteKey(writer)
		}
	}

	// Start the server.
	log.Fatal(server.ListenAndServe(serverConfig.Address))
}
<|endoftext|>


<commit_before>package i2cm

import (
	"io"
	"testing"
)

type t8x8item struct {
	addr    Addr
	regaddr uint8
	wb, rb  []byte
	nw, nr  int
	err     error
}

// page verifying transactor for 24Cxx style EEPROMs
// it's always based at address (0xA0 >> 1).
// rollover inside a page is not supported as this
// behavior is not exploited by the EEPROM drivers
// in this package.
// also logs.
type PVT24 struct {
	t        *testing.T
	mem      []byte
	pagesize uint
	log      []t8x8item
}

func (p *PVT24) Transact8x8(addr Addr, regaddr uint8, wb, rb []byte) (int, int, error) {
	// read and write logic is intentionally kept simple and different
	// in style from the eeprom routines. maybe i will make different
	// mistakes both way around :)

	if len(wb) > 0 && len(rb) > 0 {
		p.t.Errorf("trying to write to and read from an EEPROM. this is not allowed.\n")
	}

	memaddr := ((uint(addr.GetBaseAddr()) & 0x07) << 8) + uint(regaddr)
	startpagebase := memaddr & ^(p.pagesize - 1)

	for _, b := range wb {
		newpagebase := memaddr & ^(p.pagesize - 1)
		if newpagebase != startpagebase {
			p.t.Errorf("EEPROM transaction started in page %#04x, continue to page %#04x", startpagebase, newpagebase)
		}

		waddr := (memaddr & (p.pagesize - 1)) | startpagebase

		p.mem[waddr] = b

		memaddr++
	}

	for i := range rb {
		rb[i] = p.mem[memaddr]
		memaddr++
	}

	p.log = append(p.log, t8x8item{addr, regaddr, wb, rb, len(wb), len(rb), nil})

	return len(wb), len(rb), nil
}

// has dummy methods for typing reasons

func (p *PVT24) Start() error                    { panic("not implemented") }
func (p *PVT24) Stop() error                     { panic("not implemented") }
func (p *PVT24) WriteByte(b byte) error          { panic("not implemented") }
func (p *PVT24) ReadByte(ack bool) (byte, error) { panic("not implemented") }

func newPVT24(conf EEPROM24Config, t *testing.T) *PVT24 {
	var p PVT24
	p.mem = make([]byte, conf.Size)
	p.pagesize = conf.PageSize
	p.t = t

	for i := range p.mem {
		p.mem[i] = 0x24 ^ uint8(i)
	}

	return &p
}

func TestEEPROM24EOF(t *testing.T) {
	conf := Conf_24C02
	pvt := newPVT24(conf, t)
	ee, err := NewEEPROM24(pvt, Addr7(0xa0>>1), conf)
	if err != nil {
		t.Fatalf("NewEEPROM24 should not fail in this context. it did with % T: %#v\n", err, err)
	}

	_ee, ok := ee.(*ee24)
	if !ok {
		t.Fatalf("expected to test an ee24, got %T: %#v\n", ee, ee)
	}

	_ee.p = conf.Size - 3
	{
		rb := make([]byte, 16)
		n, err := ee.Read(rb)
		if n != 3 {
			t.Fatalf("expected to read 3 bytes, got %d\n", n)
		}

		if err != io.EOF {
			if err == nil {
				n, err := ee.Read(rb)

				if n != 0 {
					t.Fatalf("expected to read 0 bytes with the second shot, got %d\n", n)
				}

				if err != io.EOF {
					t.Fatalf("did not get back io.EOF even though EEPROM24 was asked twice, got: %T: %#v\n", err, err)
				}
			} else {
				t.Fatalf("expected EOF, got %T: %#v", err)
			}
		}
	}

	_ee.p = conf.Size - 5
	{
		wb := make([]byte, 16)
		n, err := ee.Write(wb)
		if n != 5 {
			t.Fatalf("expected to write 5 bytes, wrote %d\n", n)
		}

		if err != io.EOF {
			if err == nil {
				n, err := ee.Write(wb)

				if n != 0 {
					t.Fatalf("expected to read 0 bytes with the second shot, got %d\n", n)
				}

				if err != io.EOF {
					t.Fatalf("did not get back io.EOF even though EEPROM24 was asked twice, got: %T: %#v\n", err, err)
				}
			} else {
				t.Fatalf("expected EOF, got %T: %#v", err)
			}
		}
	}
}

func TestEEPROM24Conf(t *testing.T) {
	defconf := EEPROM24Config{1, 1, 0}
	devaddr := Addr7(0xA0 >> 1)
	tr := newPVT24(defconf, t)

	// one valid configuration as a counter check
	{
		if _, err := NewEEPROM24(tr, devaddr, defconf); err != nil {
			t.Errorf("NewEEPROM24 failed on valid configuration %#v\n", err)
		}
	}

	// pagesize not power of 2
	{
		conf := EEPROM24Config{2048, 13, 0}
		if _, err := NewEEPROM24(tr, devaddr, conf); err == nil {
			t.Errorf("NewEEPROM24 did not fail on invalid configuration %#v", conf)
		}
	}

	// size not power of 2
	{
		conf := EEPROM24Config{100, 16, 0}
		if _, err := NewEEPROM24(tr, devaddr, conf); err == nil {
			t.Errorf("NewEEPROM24 did not fail on invalid configuration %#v", conf)
		}
	}

	// size and page size not power of 2
	{
		conf := EEPROM24Config{100, 13, 0}
		if _, err := NewEEPROM24(tr, devaddr, conf); err == nil {
			t.Errorf("NewEEPROM24 did not fail on invalid configuration %#v", conf)
		}
	}
}

// input/output testing for Read and Write. as this test employs pvt24,
// the test will fail on transaction which ignore page size and the offset
// in the page.
func TestEEPROM24InOut(t *testing.T) {
	cases := []struct {
		conf   EEPROM24Config
		offs   uint // offset to seek to at the start
		read   bool
		buf    []byte // with read buf is just used to indicate the size, data is verified with the pvt24 mem pattern
		nexp   int
		errexp error
	}{{EEPROM24Config{1024, 8, 0}, 6, true, []byte{0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f}, 6, nil},
		{EEPROM24Config{128, 8, 0}, 123, true, []byte{0x5f, 0x58, 0x59, 0x5a, 0x5b, 0x00, 0x00, 0x00, 0x00}, 5, nil}, // double shot EOF returns err==nil on first call
		{EEPROM24Config{2048, 4, 0}, 9, false, []byte{0x0fe}, 1, nil},                                                // single byte write
		{EEPROM24Config{2048, 4, 0}, 2040, false, []byte{0xfc, 0xfd, 0xfe, 0xff}, 4, nil},                            // full page
		{EEPROM24Config{2048, 4, 0}, 513, false, []byte{0x01, 0x02, 0x03, 0x04}, 4, nil},                             // 1 byte in next page
		{EEPROM24Config{512, 4, 0}, 239, false, []byte{1, 2, 3, 4, 5, 6}, 6, nil},                                    // 1 byte partial, 4 bytes full, 1 byte partial
		{EEPROM24Config{512, 8, 0}, 254, false, []byte{1, 2, 3}, 3, nil},                                             // span i2c device boundary
		{EEPROM24Config{1024, 16, 0}, 1022, false, []byte{1, 2, 3, 4}, 2, io.EOF},                                    // test EOF. write employs a single shot EOF strategy
	}

	for i, c := range cases {
		devaddr := Addr7(0xA0 >> 1)
		tr := newPVT24(c.conf, t)

		ee, err := NewEEPROM24(tr, devaddr, c.conf)
		if err != nil {
			t.Errorf("NewEEPROM failed unexpectedly on configuration %T: %#v", c.conf, c.conf)
			continue
		}

		_ee := ee.(*ee24)
		_ee.p = c.offs

		var n int
		var rb []byte

		if c.read {
			rb = make([]byte, len(c.buf))
			n, err = ee.Read(rb)

			if n != c.nexp {
				t.Errorf("case %d: expected ee24 to read %d bytes, it read %d\n", i, c.nexp, n)
				continue
			}

			if err != c.errexp {
				t.Errorf("case %d: expected ee24.Read to return error %#v, it returned %T: %#v", i, c.errexp, err)
				continue
			}

			if n > len(c.buf) {
				t.Errorf("case %d: ee.Read read %d bytes, even though the slice only contains %d bytes", i, n, len(c.buf))
				continue
			}

			expp := c.offs + uint(c.nexp)
			if _ee.p != expp {
				t.Errorf("case %d: expected file pointer to be at %d at the end of ee.Read, it is at %d", i, expp, _ee.p)
				continue
			}

			if string(rb[0:n]) != string(c.buf[0:c.nexp]) {
				t.Errorf("case %d: ee24.Read is expected to read bytes % x, it read % x", i, c.buf[0:c.nexp], rb[0:n])
				continue
			}
		} else {
			n, err = ee.Write(c.buf)

			if n != c.nexp {
				t.Errorf("case %d: expected to write %d bytes, ee24 wrote %d", i, n, c.nexp)
				continue
			}

			if err != c.errexp {
				t.Error("case %d: expected error %#v, got %T: #%v", i, c.errexp, err, err)
				continue
			}

			expp := c.offs + uint(c.nexp)
			if _ee.p != expp {
				t.Errorf("case %d: expected file pointer to be at %d at the end of ee.Write, it its at %d", i, expp, _ee.p)
				continue
			}

			wmem := tr.mem[c.offs : c.offs+uint(c.nexp)]
			wbuf := c.buf[:c.nexp]
			if string(wmem) != string(wbuf) {
				t.Errorf("case %d: expected ee.Write to have written % x, it wrote % x", i, wbuf, wmem)
				continue
			}
		}
	}
}
<commit_msg>more write EOF tests<commit_after>package i2cm

import (
	"io"
	"testing"
)

type t8x8item struct {
	addr    Addr
	regaddr uint8
	wb, rb  []byte
	nw, nr  int
	err     error
}

// page verifying transactor for 24Cxx style EEPROMs
// it's always based at address (0xA0 >> 1).
// rollover inside a page is not supported as this
// behavior is not exploited by the EEPROM drivers
// in this package.
// also logs.
type PVT24 struct {
	t        *testing.T
	mem      []byte
	pagesize uint
	log      []t8x8item
}

func (p *PVT24) Transact8x8(addr Addr, regaddr uint8, wb, rb []byte) (int, int, error) {
	// read and write logic is intentionally kept simple and different
	// in style from the eeprom routines. maybe i will make different
	// mistakes both way around :)

	if len(wb) > 0 && len(rb) > 0 {
		p.t.Errorf("trying to write to and read from an EEPROM. this is not allowed.\n")
	}

	memaddr := ((uint(addr.GetBaseAddr()) & 0x07) << 8) + uint(regaddr)
	startpagebase := memaddr & ^(p.pagesize - 1)

	for _, b := range wb {
		newpagebase := memaddr & ^(p.pagesize - 1)
		if newpagebase != startpagebase {
			p.t.Errorf("EEPROM transaction started in page %#04x, continue to page %#04x", startpagebase, newpagebase)
		}

		waddr := (memaddr & (p.pagesize - 1)) | startpagebase

		p.mem[waddr] = b

		memaddr++
	}

	for i := range rb {
		rb[i] = p.mem[memaddr]
		memaddr++
	}

	p.log = append(p.log, t8x8item{addr, regaddr, wb, rb, len(wb), len(rb), nil})

	return len(wb), len(rb), nil
}

// has dummy methods for typing reasons

func (p *PVT24) Start() error                    { panic("not implemented") }
func (p *PVT24) Stop() error                     { panic("not implemented") }
func (p *PVT24) WriteByte(b byte) error          { panic("not implemented") }
func (p *PVT24) ReadByte(ack bool) (byte, error) { panic("not implemented") }

func newPVT24(conf EEPROM24Config, t *testing.T) *PVT24 {
	var p PVT24
	p.mem = make([]byte, conf.Size)
	p.pagesize = conf.PageSize
	p.t = t

	for i := range p.mem {
		p.mem[i] = 0x24 ^ uint8(i)
	}

	return &p
}

func TestEEPROM24EOF(t *testing.T) {
	conf := Conf_24C02
	pvt := newPVT24(conf, t)
	ee, err := NewEEPROM24(pvt, Addr7(0xa0>>1), conf)
	if err != nil {
		t.Fatalf("NewEEPROM24 should not fail in this context. it did with % T: %#v\n", err, err)
	}

	_ee, ok := ee.(*ee24)
	if !ok {
		t.Fatalf("expected to test an ee24, got %T: %#v\n", ee, ee)
	}

	_ee.p = conf.Size - 3
	{
		rb := make([]byte, 16)
		n, err := ee.Read(rb)
		if n != 3 {
			t.Fatalf("expected to read 3 bytes, got %d\n", n)
		}

		if err != io.EOF {
			if err == nil {
				n, err := ee.Read(rb)

				if n != 0 {
					t.Fatalf("expected to read 0 bytes with the second shot, got %d\n", n)
				}

				if err != io.EOF {
					t.Fatalf("did not get back io.EOF even though EEPROM24 was asked twice, got: %T: %#v\n", err, err)
				}
			} else {
				t.Fatalf("expected EOF, got %T: %#v", err)
			}
		}
	}

	_ee.p = conf.Size - 5
	{
		wb := make([]byte, 16)
		n, err := ee.Write(wb)
		if n != 5 {
			t.Fatalf("expected to write 5 bytes, wrote %d\n", n)
		}

		if err != io.EOF {
			t.Fatalf("ee24.Write past EOF: expected EOF, got %T: %#v", err)
		}

		if _ee.p != conf.Size {
			t.Fatalf("ee24 file pointer at EOF should be at %#04x, however it is at %#04x", conf.Size, _ee.p)
		}

		n, err = ee.Write(wb)

		if n != 0 {
			t.Fatalf("expected to read 0 bytes with the second shot, got %d\n", n)
		}

		if err != io.EOF {
			t.Fatalf("ee24.Write at EOF: expected EOF, got: %T: %#v\n", err, err)
		}

		if _ee.p != conf.Size {
			t.Fatalf("ee24 file pointer at EOF should be at %#04x, however it is at %#04x", conf.Size, _ee.p)
		}
	}
}

func TestEEPROM24Conf(t *testing.T) {
	defconf := EEPROM24Config{1, 1, 0}
	devaddr := Addr7(0xA0 >> 1)
	tr := newPVT24(defconf, t)

	// one valid configuration as a counter check
	{
		if _, err := NewEEPROM24(tr, devaddr, defconf); err != nil {
			t.Errorf("NewEEPROM24 failed on valid configuration %#v\n", err)
		}
	}

	// pagesize not power of 2
	{
		conf := EEPROM24Config{2048, 13, 0}
		if _, err := NewEEPROM24(tr, devaddr, conf); err == nil {
			t.Errorf("NewEEPROM24 did not fail on invalid configuration %#v", conf)
		}
	}

	// size not power of 2
	{
		conf := EEPROM24Config{100, 16, 0}
		if _, err := NewEEPROM24(tr, devaddr, conf); err == nil {
			t.Errorf("NewEEPROM24 did not fail on invalid configuration %#v", conf)
		}
	}

	// size and page size not power of 2
	{
		conf := EEPROM24Config{100, 13, 0}
		if _, err := NewEEPROM24(tr, devaddr, conf); err == nil {
			t.Errorf("NewEEPROM24 did not fail on invalid configuration %#v", conf)
		}
	}
}

// input/output testing for Read and Write. as this test employs pvt24,
// the test will fail on transaction which ignore page size and the offset
// in the page.
func TestEEPROM24InOut(t *testing.T) {
	cases := []struct {
		conf   EEPROM24Config
		offs   uint // offset to seek to at the start
		read   bool
		buf    []byte // with read buf is just used to indicate the size, data is verified with the pvt24 mem pattern
		nexp   int
		errexp error
	}{{EEPROM24Config{1024, 8, 0}, 6, true, []byte{0x22, 0x23, 0x2c, 0x2d, 0x2e, 0x2f}, 6, nil},
		{EEPROM24Config{128, 8, 0}, 123, true, []byte{0x5f, 0x58, 0x59, 0x5a, 0x5b, 0x00, 0x00, 0x00, 0x00}, 5, nil}, // double shot EOF returns err==nil on first call
		{EEPROM24Config{2048, 4, 0}, 9, false, []byte{0x0fe}, 1, nil},                                                // single byte write
		{EEPROM24Config{2048, 4, 0}, 2040, false, []byte{0xfc, 0xfd, 0xfe, 0xff}, 4, nil},                            // full page
		{EEPROM24Config{2048, 4, 0}, 513, false, []byte{0x01, 0x02, 0x03, 0x04}, 4, nil},                             // 1 byte in next page
		{EEPROM24Config{512, 4, 0}, 239, false, []byte{1, 2, 3, 4, 5, 6}, 6, nil},                                    // 1 byte partial, 4 bytes full, 1 byte partial
		{EEPROM24Config{512, 8, 0}, 254, false, []byte{1, 2, 3}, 3, nil},                                             // span i2c device boundary
		{EEPROM24Config{1024, 16, 0}, 1022, false, []byte{1, 2, 3, 4}, 2, io.EOF},                                    // test EOF. write employs a single shot EOF strategy
	}

	for i, c := range cases {
		devaddr := Addr7(0xA0 >> 1)
		tr := newPVT24(c.conf, t)

		ee, err := NewEEPROM24(tr, devaddr, c.conf)
		if err != nil {
			t.Errorf("NewEEPROM failed unexpectedly on configuration %T: %#v", c.conf, c.conf)
			continue
		}

		_ee := ee.(*ee24)
		_ee.p = c.offs

		var n int
		var rb []byte

		if c.read {
			rb = make([]byte, len(c.buf))
			n, err = ee.Read(rb)

			if n != c.nexp {
				t.Errorf("case %d: expected ee24 to read %d bytes, it read %d\n", i, c.nexp, n)
				continue
			}

			if err != c.errexp {
				t.Errorf("case %d: expected ee24.Read to return error %#v, it returned %T: %#v", i, c.errexp, err)
				continue
			}

			if n > len(c.buf) {
				t.Errorf("case %d: ee.Read read %d bytes, even though the slice only contains %d bytes", i, n, len(c.buf))
				continue
			}

			expp := c.offs + uint(c.nexp)
			if _ee.p != expp {
				t.Errorf("case %d: expected file pointer to be at %d at the end of ee.Read, it is at %d", i, expp, _ee.p)
				continue
			}

			if string(rb[0:n]) != string(c.buf[0:c.nexp]) {
				t.Errorf("case %d: ee24.Read is expected to read bytes % x, it read % x", i, c.buf[0:c.nexp], rb[0:n])
				continue
			}
		} else {
			n, err = ee.Write(c.buf)

			if n != c.nexp {
				t.Errorf("case %d: expected to write %d bytes, ee24 wrote %d", i, n, c.nexp)
				continue
			}

			if err != c.errexp {
				t.Error("case %d: expected error %#v, got %T: #%v", i, c.errexp, err, err)
				continue
			}

			expp := c.offs + uint(c.nexp)
			if _ee.p != expp {
				t.Errorf("case %d: expected file pointer to be at %d at the end of ee.Write, it its at %d", i, expp, _ee.p)
				continue
			}

			wmem := tr.mem[c.offs : c.offs+uint(c.nexp)]
			wbuf := c.buf[:c.nexp]
			if string(wmem) != string(wbuf) {
				t.Errorf("case %d: expected ee.Write to have written % x, it wrote % x", i, wbuf, wmem)
				continue
			}
		}
	}
}
<|endoftext|>


<commit_before>package webhooks

import (
	log "github.com/Sirupsen/logrus"
	"github.com/majestrate/srndv2/lib/config"
	"github.com/majestrate/srndv2/lib/nntp"
	"github.com/majestrate/srndv2/lib/nntp/message"
	"github.com/majestrate/srndv2/lib/store"

	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"net/url"
	"regexp"
	"strings"
)

// web hook implementation
type httpWebhook struct {
	conf    *config.WebhookConfig
	storage store.Storage
	hdr     *message.HeaderIO
}

func (h *httpWebhook) SentArticleVia(msgid nntp.MessageID, name string) {
	// web hooks don't care about feed state
}

// we got a new article
func (h *httpWebhook) GotArticle(msgid nntp.MessageID, group nntp.Newsgroup) {
	f, err := h.storage.OpenArticle(msgid.String())
	if err == nil {
		c := textproto.NewConn(f)
		var hdr textproto.MIMEHeader
		hdr, err = c.ReadMIMEHeader()
		if err == nil {
			ctype := hdr.Get("Content-Type")
			if ctype == "" {
				ctype = "text/plain"
			}
			ctype = strings.Replace(strings.ToLower(ctype), "multipart/mixed", "multipart/form-data", 1)
			u, _ := url.Parse(h.conf.URL)
			q := u.Query()
			for k, vs := range hdr {
				for _, v := range vs {
					q.Add(k, v)
				}
			}
			q.Set("Content-Type", ctype)
			u.RawQuery = q.Encode()

			var body io.Reader

			if strings.HasPrefix(ctype, "multipart") {
				pr, pw := io.Pipe()
				log.Debug("using pipe")
				body = pr
				go func(in io.Reader, out io.WriteCloser) {
					_, params, _ := mime.ParseMediaType(ctype)
					if params == nil {
						// send as whatever lol
						io.Copy(out, in)
					} else {
						boundary, _ := params["boundary"]
						mpr := multipart.NewReader(in, boundary)
						mpw := multipart.NewWriter(out)
						mpw.SetBoundary(boundary)
						for {
							part, err := mpr.NextPart()
							if err == io.EOF {
								err = nil
								break
							} else if err == nil {
								// get part header
								h := part.Header
								// rewrite header part for php
								cd := h.Get("Content-Disposition")
								r := regexp.MustCompile(`; name=".*"`)
								cd = r.ReplaceAllString(cd, `; name="attachment[]";`)
								log.Debug(cd)
								h.Set("Content-Disposition", cd)
								// make write part
								wp, err := mpw.CreatePart(h)
								if err == nil {
									// write part out
									io.Copy(wp, part)
								} else {
									log.Errorf("error writng webhook part: %s", err.Error())
								}
							}
							part.Close()
						}
						mpw.Close()
					}
					out.Close()
				}(c.R, pw)
			} else {
				body = c.R
			}

			var r *http.Response
			r, err = http.Post(u.String(), ctype, body)
			if err == nil {
				_, err = io.Copy(ioutil.Discard, r.Body)
				r.Body.Close()
				log.Infof("hook called for %s", msgid)
			}
		} else {
			f.Close()
		}
	}
	if err != nil {
		log.Errorf("error calling web hook %s: %s", h.conf.Name, err.Error())
	}
}
<commit_msg>try fix for webhooks<commit_after>package webhooks

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/majestrate/srndv2/lib/config"
	"github.com/majestrate/srndv2/lib/nntp"
	"github.com/majestrate/srndv2/lib/nntp/message"
	"github.com/majestrate/srndv2/lib/store"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"net/url"
	"regexp"
	"strings"
)

// web hook implementation
type httpWebhook struct {
	conf    *config.WebhookConfig
	storage store.Storage
	hdr     *message.HeaderIO
}

func (h *httpWebhook) SentArticleVia(msgid nntp.MessageID, name string) {
	// web hooks don't care about feed state
}

// we got a new article
func (h *httpWebhook) GotArticle(msgid nntp.MessageID, group nntp.Newsgroup) {
	f, err := h.storage.OpenArticle(msgid.String())
	if err == nil {
		c := textproto.NewConn(f)
		var hdr textproto.MIMEHeader
		hdr, err = c.ReadMIMEHeader()
		if err == nil {
			ctype := hdr.Get("Content-Type")
			if ctype == "" {
				ctype = "text/plain"
			}
			ctype = strings.Replace(strings.ToLower(ctype), "multipart/mixed", "multipart/form-data", 1)
			u, _ := url.Parse(h.conf.URL)
			q := u.Query()
			for k, vs := range hdr {
				for _, v := range vs {
					q.Add(k, v)
				}
			}
			q.Set("Content-Type", ctype)
			u.RawQuery = q.Encode()

			var body io.Reader

			if strings.HasPrefix(ctype, "multipart") {
				pr, pw := io.Pipe()
				log.Debug("using pipe")
				body = pr
				go func(in io.Reader, out io.WriteCloser) {
					_, params, _ := mime.ParseMediaType(ctype)
					if params == nil {
						// send as whatever lol
						io.Copy(out, in)
					} else {
						boundary, _ := params["boundary"]
						mpr := multipart.NewReader(in, boundary)
						mpw := multipart.NewWriter(out)
						mpw.SetBoundary(boundary)
						for {
							part, err := mpr.NextPart()
							if err == io.EOF {
								err = nil
								break
							} else if err == nil {
								// get part header
								h := part.Header
								// rewrite header part for php
								cd := h.Get("Content-Disposition")
								r := regexp.MustCompile(`; filename="(.*)"`)
								// YOLO
								fname := r.FindStringSubmatch(cd)[1]
								h.Set("Content-Disposition", fmt.Sprintf(`filename="%s"; name="attachment"`, fname))
								// make write part
								wp, err := mpw.CreatePart(h)
								if err == nil {
									// write part out
									io.Copy(wp, part)
								} else {
									log.Errorf("error writng webhook part: %s", err.Error())
								}
							}
							part.Close()
						}
						mpw.Close()
					}
					out.Close()
				}(c.R, pw)
			} else {
				body = c.R
			}

			var r *http.Response
			r, err = http.Post(u.String(), ctype, body)
			if err == nil {
				_, err = io.Copy(ioutil.Discard, r.Body)
				r.Body.Close()
				log.Infof("hook called for %s", msgid)
			}
		} else {
			f.Close()
		}
	}
	if err != nil {
		log.Errorf("error calling web hook %s: %s", h.conf.Name, err.Error())
	}
}
<|endoftext|>


<commit_before>package version

// VERSION ...
const VERSION = "1.1.14"
<commit_msg>v1.1.15<commit_after>package version

// VERSION ...
const VERSION = "1.1.15"
<|endoftext|>


<commit_before>package ccd_test

import (
	"bytes"
	"os"
	"path/filepath"
	"runtime/debug"
	"strings"
	"testing"

	"github.com/davecgh/go-spew/spew"
	"github.com/jteeuwen/go-pkg-xmlx"
	"github.com/kdar/health/ccd"
	"github.com/kdar/health/ccd/parsers/medtable"
)

func parseAndRecover(t *testing.T, c *ccd.CCD, path string, doc *xmlx.Document) (err error) {
	defer func() {
		if e := recover(); e != nil {
			lines := bytes.Split(debug.Stack(), []byte{'\n'})
			for i, _ := range lines {
				if lines[i][0] == '\t' {
					lines[i] = lines[i][1:]
				}
			}
			t.Fatalf("Error processing: %s\n\n%s", path, bytes.Join(lines, []byte{'\n'}))
		}
	}()

	if doc != nil {
		err = c.ParseDoc(doc)
	} else {
		err = c.ParseFile(path)
	}
	return
}

func walkAllCCDs(f filepath.WalkFunc) {
	filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			if info.Name()[0] == '.' {
				return filepath.SkipDir
			}

			return nil
		}

		if !strings.HasSuffix(path, "xml") && !strings.HasSuffix(path, "ccd") {
			return nil
		}

		return f(path, info, err)
	})
}

func TestParseAllCCDs(t *testing.T) {
	walkAllCCDs(func(path string, info os.FileInfo, err error) error {
		shouldfail := strings.HasPrefix(info.Name(), "fail_")

		doc := xmlx.New()
		err = doc.LoadFile(path, nil)
		if shouldfail && err != nil {
			return nil
		}

		c := ccd.NewDefaultCCD()
		err = parseAndRecover(t, c, path, doc)
		if shouldfail && err == nil {
			t.Fatalf("%s: Expected failure, instead received success.", path)
		} else if !shouldfail && err != nil {
			t.Fatalf("%s: Failed: %v", path, err)
		}

		return nil
	})
}

func TestInvalidCCD(t *testing.T) {
	c := ccd.NewDefaultCCD()
	err := parseAndRecover(t, c, "testdata/specific/invalid_ccd.xml", nil)
	if err == nil {
		t.Fatal("Expected parsing of CCD to fail and throw and error.")
	}

	err = parseAndRecover(t, c, "testdata/specific/valid_ccd.xml", nil)
	if err != nil {
		t.Fatal(err)
	}
}

func TestNewStuff(t *testing.T) {
	t.Skip("just for my own needs")

	c := ccd.NewDefaultCCD()
	c.AddParsers(medtable.Parser())
	err := parseAndRecover(t, c, "testdata/private/2013-08-26T04_03_24 - 0b7fddbdc631aecc6c96090043f690204f7d0d9d.xml", nil)
	//err := parseAndRecover(t, c, "testdata/public/ToC_CCDA_CCD_CompGuideSample_FullXML_v01a.xml", nil)
	//err := parseAndRecover(t, c, "testdata/public/SampleCCDDocument.xml", nil)
	if err != nil {
		t.Fatal(err)
	}

	_ = spew.Dump

	//spew.Dump(c.Patient)
}
<commit_msg>Improved ccd decoding tests to run all ccds<commit_after>package ccd_test

import (
	"bytes"
	"os"
	"path/filepath"
	"runtime/debug"
	"strings"
	"testing"

	"github.com/davecgh/go-spew/spew"
	"github.com/jteeuwen/go-pkg-xmlx"
	"github.com/kdar/health/ccd"
	"github.com/kdar/health/ccd/parsers/medtable"
)

func parseAndRecover(t *testing.T, c *ccd.CCD, path string, doc *xmlx.Document) (err error) {
	defer func() {
		if e := recover(); e != nil {
			lines := bytes.Split(debug.Stack(), []byte{'\n'})
			for i, _ := range lines {
				if len(lines[i]) >= 1 && lines[i][0] == '\t' {
					lines[i] = lines[i][1:]
				}
			}
			t.Errorf("Error processing: %s\n\n%s", path, bytes.Join(lines, []byte{'\n'}))
		}
	}()

	if doc != nil {
		err = c.ParseDoc(doc)
	} else {
		err = c.ParseFile(path)
	}
	return
}

func walkAllCCDs(f filepath.WalkFunc) {
	filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			if info.Name()[0] == '.' {
				return filepath.SkipDir
			}

			return nil
		}

		if !strings.HasSuffix(path, "xml") && !strings.HasSuffix(path, "ccd") {
			return nil
		}

		return f(path, info, err)
	})
}

func TestParseAllCCDs(t *testing.T) {
	walkAllCCDs(func(path string, info os.FileInfo, err error) error {
		shouldfail := strings.HasPrefix(info.Name(), "fail_")

		doc := xmlx.New()
		err = doc.LoadFile(path, nil)
		if shouldfail && err != nil {
			return nil
		}

		c := ccd.NewDefaultCCD()
		err = parseAndRecover(t, c, path, doc)
		if shouldfail && err == nil {
			t.Errorf("%s: Expected failure, instead received success.", path)
		} else if !shouldfail && err != nil {
			t.Errorf("%s: Failed: %v", path, err)
		}

		return nil
	})
}

func TestInvalidCCD(t *testing.T) {
	c := ccd.NewDefaultCCD()
	err := parseAndRecover(t, c, "testdata/specific/invalid_ccd.xml", nil)
	if err == nil {
		t.Fatal("Expected parsing of CCD to fail and throw and error.")
	}

	err = parseAndRecover(t, c, "testdata/specific/valid_ccd.xml", nil)
	if err != nil {
		t.Fatal(err)
	}
}

func TestNewStuff(t *testing.T) {
	t.Skip("just for my own needs")

	c := ccd.NewDefaultCCD()
	c.AddParsers(medtable.Parser())
	err := parseAndRecover(t, c, "testdata/private/2013-08-26T04_03_24 - 0b7fddbdc631aecc6c96090043f690204f7d0d9d.xml", nil)
	//err := parseAndRecover(t, c, "testdata/public/ToC_CCDA_CCD_CompGuideSample_FullXML_v01a.xml", nil)
	//err := parseAndRecover(t, c, "testdata/public/SampleCCDDocument.xml", nil)
	if err != nil {
		t.Fatal(err)
	}

	_ = spew.Dump

	//spew.Dump(c.Patient)
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package environs

import (
	"fmt"
	"io"
	"launchpad.net/juju-core/errors"
	"strings"
)

// EmptyStorage holds a StorageReader object that contains no files and
// offers no URLs.
var EmptyStorage StorageReader = emptyStorage{}

type emptyStorage struct{}

const verificationFilename string = "bootstrap-verify"
const verificationContent = "juju-core storage writing verified: ok"

func (s emptyStorage) Get(name string) (io.ReadCloser, error) {
	return nil, errors.NotFoundf("file %q", name)
}

func (s emptyStorage) URL(name string) (string, error) {
	return "", fmt.Errorf("file %q not found", name)
}

func (s emptyStorage) List(prefix string) ([]string, error) {
	return nil, nil
}

func VerifyStorage(storage Storage) error {
	reader := strings.NewReader(verificationContent)
	err := storage.Put(verificationFilename, reader,
		int64(len(verificationContent)))
	return err
}
<commit_msg>More review fixes.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package environs

import (
	"fmt"
	"io"
	"launchpad.net/juju-core/errors"
	"strings"
)

// EmptyStorage holds a StorageReader object that contains no files and
// offers no URLs.
var EmptyStorage StorageReader = emptyStorage{}

type emptyStorage struct{}

const verificationFilename string = "bootstrap-verify"
const verificationContent = "juju-core storage writing verified: ok\n"

func (s emptyStorage) Get(name string) (io.ReadCloser, error) {
	return nil, errors.NotFoundf("file %q", name)
}

func (s emptyStorage) URL(name string) (string, error) {
	return "", fmt.Errorf("file %q not found", name)
}

func (s emptyStorage) List(prefix string) ([]string, error) {
	return nil, nil
}

func VerifyStorage(storage Storage) error {
	reader := strings.NewReader(verificationContent)
	err := storage.Put(verificationFilename, reader,
		int64(len(verificationContent)))
	return err
}
<|endoftext|>