<commit_before>// Copyright 2018 The Go Cloud Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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 constantvar

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/google/go-cloud/runtimevar"
	"github.com/google/go-cloud/runtimevar/driver"
	"github.com/google/go-cloud/runtimevar/drivertest"
)

type harness struct {
	// vars stores the variable value(s) that have been set using CreateVariable.
	vars map[string][]byte
}

func newHarness(t *testing.T) (drivertest.Harness, error) {
	return &harness{vars: map[string][]byte{}}, nil
}

func (h *harness) MakeWatcher(ctx context.Context, name string, decoder *runtimevar.Decoder) (driver.Watcher, error) {
	rawVal, found := h.vars[name]
	if !found {
		// The variable isn't set. Create a Variable that always returns an error.
		return &watcher{err: errors.New("not found")}, nil
	}
	val, err := decoder.Decode(rawVal)
	if err != nil {
		// The variable didn't decode.
		return &watcher{err: errors.New("not found")}, nil
	}
	return &watcher{value: val, t: time.Now()}, nil
}

func (h *harness) CreateVariable(ctx context.Context, name string, val []byte) error {
	h.vars[name] = val
	return nil
}

func (h *harness) UpdateVariable(ctx context.Context, name string, val []byte) error {
	return errors.New("not supported")
}

func (h *harness) DeleteVariable(ctx context.Context, name string) error {
	return errors.New("not supported")
}

func (h *harness) Close() {}

func (h *harness) Mutable() bool { return false }

func TestConformance(t *testing.T) {
	drivertest.RunConformanceTests(t, newHarness)
}
<commit_msg>runtimevar/constantvar: add explicit tests for New (#817)<commit_after>// Copyright 2018 The Go Cloud Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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 constantvar

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/google/go-cloud/runtimevar"
	"github.com/google/go-cloud/runtimevar/driver"
	"github.com/google/go-cloud/runtimevar/drivertest"
)

type harness struct {
	// vars stores the variable value(s) that have been set using CreateVariable.
	vars map[string][]byte
}

func newHarness(t *testing.T) (drivertest.Harness, error) {
	return &harness{vars: map[string][]byte{}}, nil
}

func (h *harness) MakeWatcher(ctx context.Context, name string, decoder *runtimevar.Decoder) (driver.Watcher, error) {
	rawVal, found := h.vars[name]
	if !found {
		// The variable isn't set. Create a Variable that always returns an error.
		return &watcher{err: errors.New("not found")}, nil
	}
	val, err := decoder.Decode(rawVal)
	if err != nil {
		// The variable didn't decode.
		return &watcher{err: errors.New("not found")}, nil
	}
	return &watcher{value: val, t: time.Now()}, nil
}

func (h *harness) CreateVariable(ctx context.Context, name string, val []byte) error {
	h.vars[name] = val
	return nil
}

func (h *harness) UpdateVariable(ctx context.Context, name string, val []byte) error {
	return errors.New("not supported")
}

func (h *harness) DeleteVariable(ctx context.Context, name string) error {
	return errors.New("not supported")
}

func (h *harness) Close() {}

func (h *harness) Mutable() bool { return false }

func TestConformance(t *testing.T) {
	drivertest.RunConformanceTests(t, newHarness)
}

func TestNew(t *testing.T) {
	ctx := context.Background()

	// Use New with an error value; it should be plumbed through as a Value.
	errFail := errors.New("fail")
	v := New(errFail)
	val, err := v.Watch(ctx)
	if err != nil {
		t.Fatal(err)
	}
	if val.Value != errFail {
		t.Errorf("got %v want %v", val.Value, errFail)
	}
}

func TestNewError(t *testing.T) {
	ctx := context.Background()

	v := NewError(errors.New("fail"))
	_, err := v.Watch(ctx)
	if err == nil {
		t.Errorf("got nil err want fail err")
	}
}
<|endoftext|>


<commit_before>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you 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 (
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

// ============================================================================================================================
// Asset Definitions - The ledger will store marbles and owners
// ============================================================================================================================

// ----- Marbles ----- //
type Marble struct {
	ObjectType string        `json:"docType"` //field for couchdb
	Id       string          `json:"id"`      //the fieldtags are needed to keep case from bouncing around
	Color      string        `json:"color"`
	Size       int           `json:"size"`    //size in mm of marble
	Owner      OwnerRelation `json:"owner"`
}

// ----- Owners ----- //
type Owner struct {
	ObjectType string `json:"docType"`     //field for couchdb
	Id         string `json:"id"`
	Username   string `json:"username"`
	Company    string `json:"company"`
	Enabled    bool   `json:"enabled"`     //disabled owners will not be visible to the application
}

type OwnerRelation struct {
	Id         string `json:"id"`
	Username   string `json:"username"`    //this is mostly cosmetic/handy, the real relation is by Id not Username
	Company    string `json:"company"`     //this is mostly cosmetic/handy, the real relation is by Id not Company
}

// ============================================================================================================================
// Main
// ============================================================================================================================
func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode - %s", err)
	}
}

// ============================================================================================================================
// Init - initialize the chaincode
//
// Marbles does not require initialization, so let's run a simple test instead.
//
// Shows off PutState() and how to pass an input argument to chaincode.
//
// Inputs - Array of strings
//  ["314"]
//
// Returns - shim.Success or error
// ============================================================================================================================
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("Marbles Is Starting Up")
	_, args := stub.GetFunctionAndParameters()
	var Aval int
	var err error

	fmt.Println("Init() args count:", len(args))
	fmt.Println("Init() args found:", args)

	// expecting 1 arg for instantiate or upgrade
	if len(args) == 1 {
		fmt.Println("Init() arg[0] length", len(args[0]))

		// expecting arg[0] to be length 0 for upgrade
		if len(args[0]) == 0 {
			fmt.Println("args[0] is empty... must be upgrading")
		} else {
			fmt.Println("args[0] is not empty, must be instantiating")

			// convert numeric string to integer
			Aval, err = strconv.Atoi(args[0])
			if err != nil {
				return shim.Error("Expecting a numeric string argument to Init() for instantiate")
			}

			// this is a very simple test. let's write to the ledger and error out on any errors
			// it's handy to read this right away to verify network is healthy if it wrote the correct value
			err = stub.PutState("selftest", []byte(strconv.Itoa(Aval)))
			if err != nil {
				return shim.Error(err.Error())                  //self-test fail
			}
		}
	}

	// store compaitible marbles application version
	err = stub.PutState("marbles_ui", []byte("4.0.0"))
	if err != nil {
		return shim.Error(err.Error())
	}

	fmt.Println(" - ready for action")                          //self-test pass
	return shim.Success(nil)
}

// ============================================================================================================================
// Invoke - Our entry point for Invocations
// ============================================================================================================================
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	function, args := stub.GetFunctionAndParameters()
	fmt.Println(" ")
	fmt.Println("starting invoke, for - " + function)

	// Handle different functions
	if function == "init" {                    //initialize the chaincode state, used as reset
		return t.Init(stub)
	} else if function == "read" {             //generic read ledger
		return read(stub, args)
	} else if function == "write" {            //generic writes to ledger
		return write(stub, args)
	} else if function == "delete_marble" {    //deletes a marble from state
		return delete_marble(stub, args)
	} else if function == "init_marble" {      //create a new marble
		return init_marble(stub, args)
	} else if function == "set_owner" {        //change owner of a marble
		return set_owner(stub, args)
	} else if function == "init_owner"{        //create a new marble owner
		return init_owner(stub, args)
	} else if function == "read_everything"{   //read everything, (owners + marbles + companies)
		return read_everything(stub)
	} else if function == "getHistory"{        //read history of a marble (audit)
		return getHistory(stub, args)
	} else if function == "getMarblesByRange"{ //read a bunch of marbles by start and stop id
		return getMarblesByRange(stub, args)
	} else if function == "disable_owner"{     //disable a marble owner from appearing on the UI
		return disable_owner(stub, args)
	}

	// error out
	fmt.Println("Received unknown invoke function name - " + function)
	return shim.Error("Received unknown invoke function name - '" + function + "'")
}

// ============================================================================================================================
// Query - legacy function
// ============================================================================================================================
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface) pb.Response {
	return shim.Error("Unknown supported call - Query()")
}
<commit_msg>update chaincode to show GetTxId() and GetStringArgs()<commit_after>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you 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 (
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

// ============================================================================================================================
// Asset Definitions - The ledger will store marbles and owners
// ============================================================================================================================

// ----- Marbles ----- //
type Marble struct {
	ObjectType string        `json:"docType"` //field for couchdb
	Id       string          `json:"id"`      //the fieldtags are needed to keep case from bouncing around
	Color      string        `json:"color"`
	Size       int           `json:"size"`    //size in mm of marble
	Owner      OwnerRelation `json:"owner"`
}

// ----- Owners ----- //
type Owner struct {
	ObjectType string `json:"docType"`     //field for couchdb
	Id         string `json:"id"`
	Username   string `json:"username"`
	Company    string `json:"company"`
	Enabled    bool   `json:"enabled"`     //disabled owners will not be visible to the application
}

type OwnerRelation struct {
	Id         string `json:"id"`
	Username   string `json:"username"`    //this is mostly cosmetic/handy, the real relation is by Id not Username
	Company    string `json:"company"`     //this is mostly cosmetic/handy, the real relation is by Id not Company
}

// ============================================================================================================================
// Main
// ============================================================================================================================
func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode - %s", err)
	}
}

// ============================================================================================================================
// Init - initialize the chaincode
//
// Marbles does not require initialization, so let's run a simple test instead.
//
// Shows off PutState() and how to pass an input argument to chaincode.
// Shows off GetFunctionAndParameters() and GetStringArgs()
// Shows off GetTxID() to get the transaction ID of the proposal
//
// Inputs - Array of strings
//  ["314"]
//
// Returns - shim.Success or error
// ============================================================================================================================
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("Marbles Is Starting Up")
	funcName, args := stub.GetFunctionAndParameters()
	var number int
	var err error
	txId := stub.GetTxID()

	fmt.Println("Init() is running")
	fmt.Println("Transaction ID:", txId)
	fmt.Println("  GetFunctionAndParameters() function:", funcName)
	fmt.Println("  GetFunctionAndParameters() args count:", len(args))
	fmt.Println("  GetFunctionAndParameters() args found:", args)

	// expecting 1 arg for instantiate or upgrade
	if len(args) == 1 {
		fmt.Println("  GetFunctionAndParameters() arg[0] length", len(args[0]))

		// expecting arg[0] to be length 0 for upgrade
		if len(args[0]) == 0 {
			fmt.Println("  Uh oh, args[0] is empty...")
		} else {
			fmt.Println("  Great news everyone, args[0] is not empty")

			// convert numeric string to integer
			number, err = strconv.Atoi(args[0])
			if err != nil {
				return shim.Error("Expecting a numeric string argument to Init() for instantiate")
			}

			// this is a very simple test. let's write to the ledger and error out on any errors
			// it's handy to read this right away to verify network is healthy if it wrote the correct value
			err = stub.PutState("selftest", []byte(strconv.Itoa(number)))
			if err != nil {
				return shim.Error(err.Error())                  //self-test fail
			}
		}
	}

	// showing the alternative argument shim function
	alt := stub.GetStringArgs()
	fmt.Println("  GetStringArgs() args count:", len(alt))
	fmt.Println("  GetStringArgs() args found:", alt)

	// store compatible marbles application version
	err = stub.PutState("marbles_ui", []byte("4.0.1"))
	if err != nil {
		return shim.Error(err.Error())
	}

	fmt.Println("Ready for action")                          //self-test pass
	return shim.Success(nil)
}

// ============================================================================================================================
// Invoke - Our entry point for Invocations
// ============================================================================================================================
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	function, args := stub.GetFunctionAndParameters()
	fmt.Println(" ")
	fmt.Println("starting invoke, for - " + function)

	// Handle different functions
	if function == "init" {                    //initialize the chaincode state, used as reset
		return t.Init(stub)
	} else if function == "read" {             //generic read ledger
		return read(stub, args)
	} else if function == "write" {            //generic writes to ledger
		return write(stub, args)
	} else if function == "delete_marble" {    //deletes a marble from state
		return delete_marble(stub, args)
	} else if function == "init_marble" {      //create a new marble
		return init_marble(stub, args)
	} else if function == "set_owner" {        //change owner of a marble
		return set_owner(stub, args)
	} else if function == "init_owner"{        //create a new marble owner
		return init_owner(stub, args)
	} else if function == "read_everything"{   //read everything, (owners + marbles + companies)
		return read_everything(stub)
	} else if function == "getHistory"{        //read history of a marble (audit)
		return getHistory(stub, args)
	} else if function == "getMarblesByRange"{ //read a bunch of marbles by start and stop id
		return getMarblesByRange(stub, args)
	} else if function == "disable_owner"{     //disable a marble owner from appearing on the UI
		return disable_owner(stub, args)
	}

	// error out
	fmt.Println("Received unknown invoke function name - " + function)
	return shim.Error("Received unknown invoke function name - '" + function + "'")
}

// ============================================================================================================================
// Query - legacy function
// ============================================================================================================================
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface) pb.Response {
	return shim.Error("Unknown supported call - Query()")
}
<|endoftext|>


<commit_before>package chredis

import(
  "fmt"
  "github.com/mediocregopher/radix.v2/redis"
)

type Rreader interface {
  Cmd(cmd string, args ...interface{}) *redis.Resp
  ReadRhash(key string) (map[string]string, error)
  Close() error
}

// Read a redis hash from the redis server.
func (rc *Rclient) ReadRhash(key string) (map[string]string, error) {
  m, err := rc.Cmd("HGETALL", key).Map()
  fmt.Println(m)
  return m, err
}
<commit_msg>Now Rreeader returns a Rhash struct instead of a plain map.<commit_after>package chredis

import(
  "fmt"
  "github.com/mediocregopher/radix.v2/redis"
)

type Rreader interface {
  Cmd(cmd string, args ...interface{}) *redis.Resp
  ReadRhash(key string) (Rhash, error)
  Close() error
}

// Read a redis hash from the redis server.
func (rc *Rclient) readHash(key string) (map[string]string, error) {
  m, err := rc.Cmd("HGETALL", key).Map()
  return m, err
}

// Given a key, read a hash, returning a Rhash struct.
func (rc *Rclient) ReadRhash(key string) (Rhash, error) {
  m, err := rc.readHash(key)
  if err != nil {
    // Just propagate err upstream for handling.
    return Rhash{}, err
  }
  fmt.Println("")
  return Rhash{key, m}, nil
}

// TODO: Goroutine that retrieves a stream of keys, reads hashes, and shoves Rhash'es into ch --> 2nd ch just prints the hashes (as a demo implementation).

<|endoftext|>


<commit_before>package cluster

import (
	"net"
	"runtime"
	"time"

	"github.com/sirupsen/logrus"

	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/cni"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/config"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/ovn"

	kapi "k8s.io/api/core/v1"
	"k8s.io/client-go/tools/cache"
)

const (
	windowsOS = "windows"
)

// StartClusterNode learns the subnet assigned to it by the master controller
// and calls the SetupNode script which establishes the logical switch
func (cluster *OvnClusterController) StartClusterNode(name string) error {
	count := 300
	var err error
	var node *kapi.Node
	var subnet *net.IPNet
	var clusterSubnets []string

	for _, clusterSubnet := range cluster.ClusterIPNet {
		clusterSubnets = append(clusterSubnets, clusterSubnet.CIDR.String())
	}

	for count > 0 {
		if count != 300 {
			time.Sleep(time.Second)
		}
		count--

		// setup the node, create the logical switch
		node, err = cluster.Kube.GetNode(name)
		if err != nil {
			logrus.Errorf("Error starting node %s, no node found - %v", name, err)
			continue
		}

		sub, ok := node.Annotations[OvnHostSubnet]
		if !ok {
			logrus.Errorf("Error starting node %s, no annotation found on node for subnet - %v", name, err)
			continue
		}
		_, subnet, err = net.ParseCIDR(sub)
		if err != nil {
			logrus.Errorf("Invalid hostsubnet found for node %s - %v", node.Name, err)
			return err
		}
		break
	}

	if count == 0 {
		logrus.Errorf("Failed to get node/node-annotation for %s - %v", name, err)
		return err
	}

	logrus.Infof("Node %s ready for ovn initialization with subnet %s", node.Name, subnet.String())

	err = setupOVNNode(name)
	if err != nil {
		return err
	}

	err = ovn.CreateManagementPort(node.Name, subnet.String(),
		cluster.ClusterServicesSubnet,
		clusterSubnets)
	if err != nil {
		return err
	}

	if cluster.GatewayInit {
		if runtime.GOOS == windowsOS {
			panic("Windows is not supported as a gateway node")
		}
		err = cluster.initGateway(node.Name, clusterSubnets, subnet.String())
		if err != nil {
			return err
		}
	}

	if err = config.WriteCNIConfig(); err != nil {
		return err
	}

	if cluster.OvnHA {
		err = cluster.watchNamespaceUpdate(node, subnet.String())
		return err
	}

	// start the cni server
	cniServer := cni.NewCNIServer("")
	err = cniServer.Start(cni.HandleCNIRequest)

	return err
}

// If default namespace MasterOverlayIP annotation has been chaged, update
// config.OvnNorth and config.OvnSouth auth with new ovn-nb and ovn-remote
// IP address
func (cluster *OvnClusterController) updateOvnNode(masterIP string,
	node *kapi.Node, subnet string) error {
	err := config.UpdateOvnNodeAuth(masterIP)
	if err != nil {
		return err
	}
	err = setupOVNNode(node.Name)
	if err != nil {
		logrus.Errorf("Failed to setup OVN node (%v)", err)
		return err
	}

	var clusterSubnets []string

	for _, clusterSubnet := range cluster.ClusterIPNet {
		clusterSubnets = append(clusterSubnets, clusterSubnet.CIDR.String())
	}

	// Recreate logical switch and management port for this node
	err = ovn.CreateManagementPort(node.Name, subnet,
		cluster.ClusterServicesSubnet,
		clusterSubnets)
	if err != nil {
		return err
	}

	// Reinit Gateway for this node if the --init-gateways flag is set
	if cluster.GatewayInit {
		if runtime.GOOS == windowsOS {
			panic("Windows is not supported as a gateway node")
		}
		err = cluster.initGateway(node.Name, clusterSubnets, subnet)
		if err != nil {
			return err
		}
	}

	return nil
}

// watchNamespaceUpdate starts watching namespace resources and calls back
// the update handler logic if there is any namspace update event
func (cluster *OvnClusterController) watchNamespaceUpdate(node *kapi.Node,
	subnet string) error {
	_, err := cluster.watchFactory.AddNamespaceHandler(
		cache.ResourceEventHandlerFuncs{
			UpdateFunc: func(old, newer interface{}) {
				oldNs := old.(*kapi.Namespace)
				oldMasterIP := oldNs.Annotations[MasterOverlayIP]
				newNs := newer.(*kapi.Namespace)
				newMasterIP := newNs.Annotations[MasterOverlayIP]
				if newMasterIP != oldMasterIP {
					err := cluster.updateOvnNode(newMasterIP, node, subnet)
					if err != nil {
						logrus.Errorf("Failed to update OVN node with new ",
							"masterIP %s: %v", newMasterIP, err)
					}
				}
			},
		}, nil)
	return err
}
<commit_msg>pkg/cluster: fix lint error in node.go<commit_after>package cluster

import (
	"net"
	"runtime"
	"time"

	"github.com/sirupsen/logrus"

	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/cni"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/config"
	"github.com/openvswitch/ovn-kubernetes/go-controller/pkg/ovn"

	kapi "k8s.io/api/core/v1"
	"k8s.io/client-go/tools/cache"
)

const (
	windowsOS = "windows"
)

// StartClusterNode learns the subnet assigned to it by the master controller
// and calls the SetupNode script which establishes the logical switch
func (cluster *OvnClusterController) StartClusterNode(name string) error {
	count := 300
	var err error
	var node *kapi.Node
	var subnet *net.IPNet
	var clusterSubnets []string

	for _, clusterSubnet := range cluster.ClusterIPNet {
		clusterSubnets = append(clusterSubnets, clusterSubnet.CIDR.String())
	}

	for count > 0 {
		if count != 300 {
			time.Sleep(time.Second)
		}
		count--

		// setup the node, create the logical switch
		node, err = cluster.Kube.GetNode(name)
		if err != nil {
			logrus.Errorf("Error starting node %s, no node found - %v", name, err)
			continue
		}

		sub, ok := node.Annotations[OvnHostSubnet]
		if !ok {
			logrus.Errorf("Error starting node %s, no annotation found on node for subnet - %v", name, err)
			continue
		}
		_, subnet, err = net.ParseCIDR(sub)
		if err != nil {
			logrus.Errorf("Invalid hostsubnet found for node %s - %v", node.Name, err)
			return err
		}
		break
	}

	if count == 0 {
		logrus.Errorf("Failed to get node/node-annotation for %s - %v", name, err)
		return err
	}

	logrus.Infof("Node %s ready for ovn initialization with subnet %s", node.Name, subnet.String())

	err = setupOVNNode(name)
	if err != nil {
		return err
	}

	err = ovn.CreateManagementPort(node.Name, subnet.String(),
		cluster.ClusterServicesSubnet,
		clusterSubnets)
	if err != nil {
		return err
	}

	if cluster.GatewayInit {
		if runtime.GOOS == windowsOS {
			panic("Windows is not supported as a gateway node")
		}
		err = cluster.initGateway(node.Name, clusterSubnets, subnet.String())
		if err != nil {
			return err
		}
	}

	if err = config.WriteCNIConfig(); err != nil {
		return err
	}

	if cluster.OvnHA {
		err = cluster.watchNamespaceUpdate(node, subnet.String())
		return err
	}

	// start the cni server
	cniServer := cni.NewCNIServer("")
	err = cniServer.Start(cni.HandleCNIRequest)

	return err
}

// If default namespace MasterOverlayIP annotation has been chaged, update
// config.OvnNorth and config.OvnSouth auth with new ovn-nb and ovn-remote
// IP address
func (cluster *OvnClusterController) updateOvnNode(masterIP string,
	node *kapi.Node, subnet string) error {
	err := config.UpdateOvnNodeAuth(masterIP)
	if err != nil {
		return err
	}
	err = setupOVNNode(node.Name)
	if err != nil {
		logrus.Errorf("Failed to setup OVN node (%v)", err)
		return err
	}

	var clusterSubnets []string

	for _, clusterSubnet := range cluster.ClusterIPNet {
		clusterSubnets = append(clusterSubnets, clusterSubnet.CIDR.String())
	}

	// Recreate logical switch and management port for this node
	err = ovn.CreateManagementPort(node.Name, subnet,
		cluster.ClusterServicesSubnet,
		clusterSubnets)
	if err != nil {
		return err
	}

	// Reinit Gateway for this node if the --init-gateways flag is set
	if cluster.GatewayInit {
		if runtime.GOOS == windowsOS {
			panic("Windows is not supported as a gateway node")
		}
		err = cluster.initGateway(node.Name, clusterSubnets, subnet)
		if err != nil {
			return err
		}
	}

	return nil
}

// watchNamespaceUpdate starts watching namespace resources and calls back
// the update handler logic if there is any namspace update event
func (cluster *OvnClusterController) watchNamespaceUpdate(node *kapi.Node,
	subnet string) error {
	_, err := cluster.watchFactory.AddNamespaceHandler(
		cache.ResourceEventHandlerFuncs{
			UpdateFunc: func(old, newer interface{}) {
				oldNs := old.(*kapi.Namespace)
				oldMasterIP := oldNs.Annotations[MasterOverlayIP]
				newNs := newer.(*kapi.Namespace)
				newMasterIP := newNs.Annotations[MasterOverlayIP]
				if newMasterIP != oldMasterIP {
					err := cluster.updateOvnNode(newMasterIP, node, subnet)
					if err != nil {
						logrus.Errorf("Failed to update OVN node with new "+
							"masterIP %s: %v", newMasterIP, err)
					}
				}
			},
		}, nil)
	return err
}
<|endoftext|>


<commit_before>package release

import (
	"context"
	"log"
	"os"
	"os/exec"

	"github.com/google/go-github/github"
	"github.com/goreleaser/releaser/config"
	"github.com/goreleaser/releaser/split"
	"golang.org/x/oauth2"
	"golang.org/x/sync/errgroup"
)

// Pipe for github release
type Pipe struct{}

// Name of the pipe
func (Pipe) Name() string {
	return "GithubRelease"
}

// Run the pipe
func (Pipe) Run(config config.ProjectConfig) error {
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: config.Token},
	)
	tc := oauth2.NewClient(context.Background(), ts)
	client := github.NewClient(tc)

	r, err := getOrCreateRelease(client, config)
	if err != nil {
		return err
	}
	var g errgroup.Group
	for _, system := range config.Build.Oses {
		for _, arch := range config.Build.Arches {
			system := system
			arch := arch
			g.Go(func() error {
				return upload(client, *r.ID, system, arch, config)
			})
		}
	}
	return g.Wait()
}

func getOrCreateRelease(client *github.Client, config config.ProjectConfig) (*github.RepositoryRelease, error) {
	owner, repo := split.OnSlash(config.Repo)
	data := &github.RepositoryRelease{
		Name:    github.String(config.Git.CurrentTag),
		TagName: github.String(config.Git.CurrentTag),
		Body:    github.String(description(config.Git.Diff)),
	}
	r, res, err := client.Repositories.GetReleaseByTag(owner, repo, config.Git.CurrentTag)
	if err != nil && res.StatusCode == 404 {
		log.Println("Creating release", config.Git.CurrentTag, "on", config.Repo, "...")
		r, _, err = client.Repositories.CreateRelease(owner, repo, data)
		return r, err
	}
	log.Println("Updating existing release", config.Git.CurrentTag, "on", config.Repo, "...")
	r, _, err = client.Repositories.EditRelease(owner, repo, *r.ID, data)
	return r, err
}

func description(diff string) string {
	result := "## Changelog\n" + diff + "\n\n--\nAutomated with @goreleaser"
	cmd := exec.Command("go", "version")
	bts, err := cmd.CombinedOutput()
	if err != nil {
		return result
	}
	return result + "\nBuilt with " + string(bts)
}

func upload(client *github.Client, releaseID int, system, arch string, config config.ProjectConfig) error {
	owner, repo := split.OnSlash(config.Repo)
	name, err := config.ArchiveName(system, arch)
	if err != nil {
		return err
	}
	name = name + "." + config.Archive.Format
	file, err := os.Open("dist/" + name)
	if err != nil {
		return err
	}
	defer func() {
		_ = file.Close()
	}()
	log.Println("Uploading", file.Name(), "...")
	_, _, err = client.Repositories.UploadReleaseAsset(
		owner,
		repo,
		releaseID,
		&github.UploadOptions{Name: name},
		file,
	)
	return err
}
<commit_msg>release<commit_after>package release

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

	"github.com/google/go-github/github"
	"github.com/goreleaser/releaser/context"
	"golang.org/x/oauth2"
	"golang.org/x/sync/errgroup"
)

// Pipe for github release
type Pipe struct{}

// Name of the pipe
func (Pipe) Name() string {
	return "GithubRelease"
}

// Run the pipe
func (Pipe) Run(context *context.Context) error {
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: context.Token},
	)
	tc := oauth2.NewClient(context.Background(), ts)
	client := github.NewClient(tc)

	r, err := getOrCreateRelease(client, context)
	if err != nil {
		return err
	}
	var g errgroup.Group
	for _, archive := range context.Archives {
		archive := archive
		g.Go(func() error {
			return upload(client, *r.ID, archive, context)
		})

	}
	return g.Wait()
}

func getOrCreateRelease(client *github.Client, context *context.Context) (*github.RepositoryRelease, error) {
	owner := context.Repo.Owner
	repo := context.Repo.Name
	data := &github.RepositoryRelease{
		Name:    github.String(context.Git.CurrentTag),
		TagName: github.String(context.Git.CurrentTag),
		Body:    github.String(description(context.Git.Diff)),
	}
	r, res, err := client.Repositories.GetReleaseByTag(owner, repo, context.Git.CurrentTag)
	if err != nil && res.StatusCode == 404 {
		log.Println("Creating release", config.Git.CurrentTag, "on", context.Config.Repo, "...")
		r, _, err = client.Repositories.CreateRelease(owner, repo, data)
		return r, err
	}
	log.Println("Updating existing release", config.Git.CurrentTag, "on", context.Config.Repo, "...")
	r, _, err = client.Repositories.EditRelease(owner, repo, *r.ID, data)
	return r, err
}

func description(diff string) string {
	result := "## Changelog\n" + diff + "\n\n--\nAutomated with @goreleaser"
	cmd := exec.Command("go", "version")
	bts, err := cmd.CombinedOutput()
	if err != nil {
		return result
	}
	return result + "\nBuilt with " + string(bts)
}

func upload(client *github.Client, releaseID int, archive string, context *context.Context) error {
	archive = archive + "." + context.Config.Archive.Format
	file, err := os.Open("dist/" + archive)
	if err != nil {
		return err
	}
	defer func() { _ = file.Close() }()
	log.Println("Uploading", file.Name(), "...")
	_, _, err = client.Repositories.UploadReleaseAsset(
		context.Repo.Owner,
		context.Repo.Name,
		releaseID,
		&github.UploadOptions{Name: archive},
		file,
	)
	return err
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package timestampvm

import (
	"errors"
	"time"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
	"github.com/ava-labs/avalanchego/utils/hashing"
)

var (
	errTimestampTooEarly = errors.New("block's timestamp is earlier than its parent's timestamp")
	errDatabaseGet       = errors.New("error while retrieving data from database")
	errTimestampTooLate  = errors.New("block's timestamp is more than 1 hour ahead of local time")
	errBlockType         = errors.New("unexpected block type")

	_ Block = &TimeBlock{}
)

type Block interface {
	snowman.Block
	Initialize(bytes []byte, status choices.Status, vm *VM)
	Data() [dataLen]byte
}

// Block is a block on the chain.
// Each block contains:
// 1) A piece of data (a string)
// 2) A timestamp
type TimeBlock struct {
	PrntID ids.ID        `serialize:"true" json:"parentID"`  // parent's ID
	Hght   uint64        `serialize:"true" json:"height"`    // This block's height. The genesis block is at height 0.
	Tmstmp int64         `serialize:"true" json:"timestamp"` // Time this block was proposed at
	Dt     [dataLen]byte `serialize:"true" json:"data"`      // Arbitrary data

	id     ids.ID
	bytes  []byte
	status choices.Status
	vm     *VM
}

// Verify returns nil iff this block is valid.
// To be valid, it must be that:
// b.parent.Timestamp < b.Timestamp <= [local time] + 1 hour
func (b *TimeBlock) Verify() error {
	if b.Status() == choices.Accepted {
		return nil
	}

	// Get [b]'s parent
	parentID := b.Parent()
	parentIntf, err := b.vm.GetBlock(parentID)
	if err != nil {
		return errDatabaseGet
	}
	parent, ok := parentIntf.(*TimeBlock)
	if !ok {
		return errBlockType
	}

	// Ensure [b]'s timestamp is after its parent's timestamp.
	if b.Timestamp().Unix() < parent.Timestamp().Unix() {
		return errTimestampTooEarly
	}

	// Ensure [b]'s timestamp is not more than an hour
	// ahead of this node's time
	if b.Timestamp().Unix() >= time.Now().Add(time.Hour).Unix() {
		return errTimestampTooLate
	}

	b.vm.currentBlocks[b.id] = b

	return nil
}

// Initialize sets [b.bytes] to [bytes], sets [b.id] to hash([b.bytes])
// Checks if [b]'s status is already stored in state. If so, [b] gets that status.
// Otherwise [b]'s status is Unknown.
func (b *TimeBlock) Initialize(bytes []byte, status choices.Status, vm *VM) {
	b.vm = vm
	b.bytes = bytes
	b.id = hashing.ComputeHash256Array(b.bytes)
	b.status = status
}

// Accept sets this block's status to Accepted and sets lastAccepted to this
// block's ID and saves this info to b.vm.DB
func (b *TimeBlock) Accept() error {
	b.SetStatus(choices.Accepted) // Change state of this block
	blkID := b.ID()

	// Persist data
	if err := b.vm.state.PutBlock(b); err != nil {
		return err
	}

	if err := b.vm.state.SetLastAccepted(blkID); err != nil {
		return err
	}
	return b.vm.state.Commit()
}

// Reject sets this block's status to Rejected and saves the status in state
// Recall that b.vm.DB.Commit() must be called to persist to the DB
func (b *TimeBlock) Reject() error {
	b.SetStatus(choices.Rejected)
	if err := b.vm.state.PutBlock(b); err != nil {
		return err
	}
	return b.vm.state.Commit()
}

// ID returns the ID of this block
func (b *TimeBlock) ID() ids.ID { return b.id }

// ParentID returns [b]'s parent's ID
func (b *TimeBlock) Parent() ids.ID { return b.PrntID }

// Height returns this block's height. The genesis block has height 0.
func (b *TimeBlock) Height() uint64 { return b.Hght }

// Timestamp returns this block's time. The genesis block has time 0.
func (b *TimeBlock) Timestamp() time.Time { return time.Unix(b.Tmstmp, 0) }

// Status returns the status of this block
func (b *TimeBlock) Status() choices.Status { return b.status }

// Bytes returns the byte repr. of this block
func (b *TimeBlock) Bytes() []byte { return b.bytes }

// Data returns the data of this block
func (b *TimeBlock) Data() [dataLen]byte { return b.Dt }

// SetStatus sets the status of this block
func (b *TimeBlock) SetStatus(status choices.Status) { b.status = status }

func newTimeBlock(parentID ids.ID, height uint64, data [dataLen]byte, timestamp time.Time) *TimeBlock {
	// Create our new block
	return &TimeBlock{
		PrntID: parentID,
		Hght:   height,
		Tmstmp: timestamp.Unix(),
		Dt:     data,
	}
}
<commit_msg>add height check<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package timestampvm

import (
	"errors"
	"fmt"
	"time"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
	"github.com/ava-labs/avalanchego/utils/hashing"
)

var (
	errTimestampTooEarly = errors.New("block's timestamp is earlier than its parent's timestamp")
	errDatabaseGet       = errors.New("error while retrieving data from database")
	errTimestampTooLate  = errors.New("block's timestamp is more than 1 hour ahead of local time")
	errBlockType         = errors.New("unexpected block type")
	errBlockNil          = errors.New("block is nil")

	_ Block = &TimeBlock{}
)

type Block interface {
	snowman.Block
	Initialize(bytes []byte, status choices.Status, vm *VM)
	Data() [dataLen]byte
}

// Block is a block on the chain.
// Each block contains:
// 1) A piece of data (a string)
// 2) A timestamp
type TimeBlock struct {
	PrntID ids.ID        `serialize:"true" json:"parentID"`  // parent's ID
	Hght   uint64        `serialize:"true" json:"height"`    // This block's height. The genesis block is at height 0.
	Tmstmp int64         `serialize:"true" json:"timestamp"` // Time this block was proposed at
	Dt     [dataLen]byte `serialize:"true" json:"data"`      // Arbitrary data

	id     ids.ID
	bytes  []byte
	status choices.Status
	vm     *VM
}

// Verify returns nil iff this block is valid.
// To be valid, it must be that:
// b.parent.Timestamp < b.Timestamp <= [local time] + 1 hour
func (b *TimeBlock) Verify() error {
	if b == nil {
		return errBlockNil
	}

	if b.Status() == choices.Accepted {
		return nil
	}

	// Get [b]'s parent
	parentID := b.Parent()
	parentIntf, err := b.vm.GetBlock(parentID)
	if err != nil {
		return errDatabaseGet
	}
	parent, ok := parentIntf.(*TimeBlock)
	if !ok {
		return errBlockType
	}
	if expectedHeight := parent.Height() + 1; expectedHeight != b.Hght {
		return fmt.Errorf(
			"expected block to have height %d, but found %d",
			expectedHeight,
			b.Hght,
		)
	}

	// Ensure [b]'s timestamp is after its parent's timestamp.
	if b.Timestamp().Unix() < parent.Timestamp().Unix() {
		return errTimestampTooEarly
	}

	// Ensure [b]'s timestamp is not more than an hour
	// ahead of this node's time
	if b.Timestamp().Unix() >= time.Now().Add(time.Hour).Unix() {
		return errTimestampTooLate
	}

	b.vm.currentBlocks[b.id] = b

	return nil
}

// Initialize sets [b.bytes] to [bytes], sets [b.id] to hash([b.bytes])
// Checks if [b]'s status is already stored in state. If so, [b] gets that status.
// Otherwise [b]'s status is Unknown.
func (b *TimeBlock) Initialize(bytes []byte, status choices.Status, vm *VM) {
	b.vm = vm
	b.bytes = bytes
	b.id = hashing.ComputeHash256Array(b.bytes)
	b.status = status
}

// Accept sets this block's status to Accepted and sets lastAccepted to this
// block's ID and saves this info to b.vm.DB
func (b *TimeBlock) Accept() error {
	b.SetStatus(choices.Accepted) // Change state of this block
	blkID := b.ID()

	// Persist data
	if err := b.vm.state.PutBlock(b); err != nil {
		return err
	}

	if err := b.vm.state.SetLastAccepted(blkID); err != nil {
		return err
	}
	return b.vm.state.Commit()
}

// Reject sets this block's status to Rejected and saves the status in state
// Recall that b.vm.DB.Commit() must be called to persist to the DB
func (b *TimeBlock) Reject() error {
	b.SetStatus(choices.Rejected)
	if err := b.vm.state.PutBlock(b); err != nil {
		return err
	}
	return b.vm.state.Commit()
}

// ID returns the ID of this block
func (b *TimeBlock) ID() ids.ID { return b.id }

// ParentID returns [b]'s parent's ID
func (b *TimeBlock) Parent() ids.ID { return b.PrntID }

// Height returns this block's height. The genesis block has height 0.
func (b *TimeBlock) Height() uint64 { return b.Hght }

// Timestamp returns this block's time. The genesis block has time 0.
func (b *TimeBlock) Timestamp() time.Time { return time.Unix(b.Tmstmp, 0) }

// Status returns the status of this block
func (b *TimeBlock) Status() choices.Status { return b.status }

// Bytes returns the byte repr. of this block
func (b *TimeBlock) Bytes() []byte { return b.bytes }

// Data returns the data of this block
func (b *TimeBlock) Data() [dataLen]byte { return b.Dt }

// SetStatus sets the status of this block
func (b *TimeBlock) SetStatus(status choices.Status) { b.status = status }

func newTimeBlock(parentID ids.ID, height uint64, data [dataLen]byte, timestamp time.Time) *TimeBlock {
	// Create our new block
	return &TimeBlock{
		PrntID: parentID,
		Hght:   height,
		Tmstmp: timestamp.Unix(),
		Dt:     data,
	}
}
<|endoftext|>


<commit_before>package question

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"

	"github.com/dgraph-io/gru/admin/server"
	"github.com/dgraph-io/gru/admin/tag"
	"github.com/dgraph-io/gru/dgraph"
	"github.com/gorilla/mux"
)

type Question struct {
	Uid      string `json:"_uid_"`
	Name     string
	Text     string
	Positive float64
	Negative float64
	Notes    string
	Tags     []tag.Tag
	Options  []Option
}

type Option struct {
	Uid string `json:"_uid_"`
	// TODO - Change this to text later.
	Text      string `json:"name"`
	IsCorrect bool   `json:"is_correct"`
}

func add(q Question) string {
	m := `mutation {
		set {
		  <root> <question> <_new_:qn> .
		  <_new_:qn> <name> "` + q.Name + `" .
		  <_new_:qn> <text> "` + q.Text + `" .
		  <_new_:qn> <positive> "` + strconv.FormatFloat(q.Positive, 'g', -1, 64) + `" .
		  <_new_:qn> <negative> "` + strconv.FormatFloat(q.Negative, 'g', -1, 64) + `" .
		  <_new_:qn> <notes> "` + q.Notes + `" .`

	correct := 0
	for i, opt := range q.Options {
		idx := strconv.Itoa(i)
		m += `
		<_new_:qn> <question.option> <_new_:o` + idx + `> .
		<_new_:o` + idx + `> <name> "` + opt.Text + `" .`
		if opt.IsCorrect {
			m += `
			<_new_:qn> <question.correct> <_new_:o` + idx + `> .`
			correct++
		}
	}

	for i, t := range q.Tags {
		idx := strconv.Itoa(i)
		if t.Uid != "" {
			m += `
			<_new_:qn> <question.tag> <_uid_:` + t.Uid + `> .
			<_uid_:` + t.Uid + `> <tag.question> <_new_:qn> . `
		} else {
			m += `
			<_new_:t` + idx + `> <name> "` + t.Name + `" .
			<_new_:qn> <question.tag> <_new_:t` + idx + `> .
			<_new_:t` + idx + `> <tag.question> <_new_:qn> . `
		}
	}

	if correct > 1 {
		m += `
		<_new_:qn> <multiple> "true" . `
	} else {
		m += `
		<_new_:qn> <multiple> "false" . `
	}
	m += `
	  }
  }	`
	return m
}

// TODO - Move this inline with add, like we have for edit.
func validateQuestion(q Question) error {
	if q.Name == "" || q.Text == "" {
		return fmt.Errorf("Question name/text can't be empty")
	}
	// TODO - Have validation on score.
	if q.Positive == 0 || q.Negative == 0 {
		return fmt.Errorf("Positive or negative score can't be zero.")
	}
	if len(q.Options) == 0 {
		return fmt.Errorf("Question should have atleast one option")
	}
	correct := 0
	for _, opt := range q.Options {
		if opt.IsCorrect {
			correct++
		}
	}
	if correct == 0 {
		fmt.Errorf("Atleast one option should be correct")
	}
	return nil
}

// API for "Adding Question" to Database
func Add(w http.ResponseWriter, r *http.Request) {
	sr := server.Response{}
	var q Question
	err := json.NewDecoder(r.Body).Decode(&q)
	if err != nil {
		sr.Error = "Couldn't decode JSON"
		w.WriteHeader(http.StatusBadRequest)
		w.Write(server.MarshalResponse(sr))
		return
	}

	if err := validateQuestion(q); err != nil {
		sr.Error = err.Error()
		w.WriteHeader(http.StatusBadRequest)
		w.Write(server.MarshalResponse(sr))
		return
	}

	m := add(q)
	res, err := dgraph.SendMutation(m)
	if err != nil {
		sr.Write(w, "", err.Error(), http.StatusInternalServerError)
		return
	}
	if res.Code != "ErrorOk" {
		sr.Write(w, res.Message, "", http.StatusInternalServerError)
		return
	}

	sr.Success = true
	sr.Message = "Question Successfully Saved!"
	w.Write(server.MarshalResponse(sr))
}

type qid struct {
	Id string
}

func Index(w http.ResponseWriter, r *http.Request) {
	sr := server.Response{}
	var q qid
	err := json.NewDecoder(r.Body).Decode(&q)
	if err != nil {
		sr.Write(w, "", "Couldn't decode JSON", http.StatusBadRequest)
		return
	}

	var query string
	// TODO - Maybe dont call with debug, and request appropriate fields.
	if q.Id != "" {
		query = "{debug(_xid_: root) { question (after: " + q.Id + ", first: 20) { _uid_ name text negative positive question.tag { name } question.option { name } question.correct { name } }  } }"
	} else {
		query = "{debug(_xid_: root) { question (first:20) { _uid_ name text negative positive question.tag { name } question.option { name } question.correct { name } }  } }"
	}

	b, err := dgraph.Query(query)
	if err != nil {
		sr.Write(w, "", err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write(b)
}

type QuestionAPIResponse struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	UIDS    struct {
		Question uint64 `json:question`
	}
}

// method to parse question response
func parseQuestionResponse(question_body []byte) (*QuestionAPIResponse, error) {
	var question_response = new(QuestionAPIResponse)
	err := json.Unmarshal(question_body, &question_response)
	if err != nil {
		log.Fatal(err)
	}
	return question_response, err
}

type TagFilter struct {
	UID string
}

// FILTER QUESTION HANDLER: Fileter By Tags
// TODO - Clean this up.
func Filter(w http.ResponseWriter, r *http.Request) {
	var tag TagFilter

	err := json.NewDecoder(r.Body).Decode(&tag)
	if err != nil {
		panic(err)
	}

	filter_query := "{root(_uid_: " + tag.UID + ") { tag.question { text }}"
	filter_response, err := http.Post("http://localhost:8080/query", "application/x-www-form-urlencoded", strings.NewReader(filter_query))
	if err != nil {
		panic(err)
	}
	defer filter_response.Body.Close()
	filter_body, err := ioutil.ReadAll(filter_response.Body)
	if err != nil {
		panic(err)
	}
	jsonResp, err := json.Marshal(string(filter_body))
	if err != nil {
		panic(err)
	}

	w.Write(jsonResp)
}

// get question information

func get(questionId string) string {
	return `
	{
		root(_uid_:` + questionId + `) {
			_uid_
			name
			text
			positive
			negative
			notes
			question.option	{
				_uid_
				name
			}
			question.correct {
				_uid_
				name
			}
			question.tag {
				_uid_
				name
			}
		}
	}`
}

func Get(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	qid := vars["id"]

	q := get(qid)
	res, err := dgraph.Query(q)
	if err != nil {
		sr := server.Response{}
		sr.Write(w, "", err.Error(), http.StatusInternalServerError)
		return
	} // TODO - Check if Dgraph returns record not found and wrap it with an error.
	w.Write(res)
}

// update question
func edit(q Question) (string, error) {
	m := new(dgraph.Mutation)
	if q.Name == "" || q.Text == "" {
		return "", fmt.Errorf("Question name/text can't be empty.")
	}
	m.Set(`<_uid_:` + q.Uid + `> <name> "` + q.Name + `" .`)
	m.Set(`<_uid_:` + q.Uid + `> <text> "` + q.Text + `" .`)
	m.Set(`<_uid_:` + q.Uid + `> <notes> "` + q.Notes + `" .`)

	if q.Positive == 0 || q.Negative == 0 {
		return "", fmt.Errorf("Positive/Negative score can't be zero.")
	}
	m.Set(`<_uid_:` + q.Uid + `> <positive> "` + strconv.FormatFloat(q.Positive, 'g', -1, 64) + `" .`)
	m.Set(`<_uid_:` + q.Uid + `> <negative> "` + strconv.FormatFloat(q.Negative, 'g', -1, 64) + `" .`)

	correct := 0
	if len(q.Options) == 0 {
		return "", fmt.Errorf("Question should have atleast one option")
	}
	for _, opt := range q.Options {
		if opt.Text == "" {
			return "", fmt.Errorf("Option text can't be empty.")
		}
		m.Set(`<_uid_:` + opt.Uid + `> <name> "` + opt.Text + `" .`)
		m.Set(`<_uid_:` + q.Uid + `> <question.option> <_uid_:` + opt.Uid + `> . `)
		if opt.IsCorrect {
			correct++
			m.Set(`<_uid_:` + q.Uid + `> <question.correct> <_uid_:` + opt.Uid + `> .`)
		} else {
			m.Del(`<_uid_:` + q.Uid + `> <question.correct> <_uid_:` + opt.Uid + `> .`)
		}
	}

	// Create and associate Tags
	for i, t := range q.Tags {
		if t.Uid != "" && t.Is_delete {
			m.Del(`<_uid_:` + q.Uid + `> <question.tag> <_uid_:` + t.Uid + `> .`)
			m.Del(`<_uid_:` + t.Uid + `> <tag.question> <_uid_:` + q.Uid + `> . `)

		} else if t.Uid != "" {
			m.Set(`<_uid_:` + q.Uid + `> <question.tag> <_uid_:` + t.Uid + `> .`)
			m.Set(`<_uid_:` + t.Uid + `> <tag.question> <_uid_:` + q.Uid + `> . `)

		} else if t.Uid == "" {
			if t.Name == "" {
				return "", fmt.Errorf("Tag name can't be empty.")
			}
			idx := strconv.Itoa(i)
			m.Set(`<_new_:tag` + idx + `> <name> "` + t.Name + `" .`)
			m.Set(`<_uid_:` + q.Uid + `> <question.tag> <_new_:tag` + idx + `> .`)
			m.Set(`<_new_:tag` + idx + `> <tag.question> <_uid_:` + q.Uid + `> . `)
		}
	}
	// TODO - There should be atleast one tag associated with a question.

	if correct == 0 {
		return "", fmt.Errorf("Atleast one option should be correct.")
	} else if correct > 1 {
		m.Set(`<_uid_:` + q.Uid + `> <multiple> "true" . `)
	} else {
		m.Set(`<_uid_:` + q.Uid + `> <multiple> "false" . `)
	}
	return m.String(), nil
}

func Edit(w http.ResponseWriter, r *http.Request) {
	sr := server.Response{}
	// vars := mux.Vars(r)
	// qid := vars["id"]
	// TODO - Id should be obtained from url not the body.
	var q Question
	err := json.NewDecoder(r.Body).Decode(&q)
	if err != nil {
		sr.Write(w, "", "Couldn't decode JSON", http.StatusBadRequest)
		return
	}

	var m string
	if m, err = edit(q); err != nil {
		sr.Write(w, "", err.Error(), http.StatusBadRequest)
		return
	}

	mr, err := dgraph.SendMutation(m)
	if err != nil {
		sr.Write(w, "", err.Error(), http.StatusInternalServerError)
		return
	}
	if mr.Code != "ErrorOk" {
		sr.Write(w, mr.Message, "", http.StatusInternalServerError)
		return
	}

	sr.Success = true
	sr.Message = "Question updated successfully."
	w.Write(server.MarshalResponse(sr))
}
<commit_msg>Send dummy values for notes as of now<commit_after>package question

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"

	"github.com/dgraph-io/gru/admin/server"
	"github.com/dgraph-io/gru/admin/tag"
	"github.com/dgraph-io/gru/dgraph"
	"github.com/gorilla/mux"
)

type Question struct {
	Uid      string `json:"_uid_"`
	Name     string
	Text     string
	Positive float64
	Negative float64
	Notes    string
	Tags     []tag.Tag
	Options  []Option
}

type Option struct {
	Uid string `json:"_uid_"`
	// TODO - Change this to text later.
	Text      string `json:"name"`
	IsCorrect bool   `json:"is_correct"`
}

func add(q Question) string {
	// TODO - Remove later.
	if q.Notes == "" {
		q.Notes = "Dummy notes"
	}
	m := `mutation {
		set {
		  <root> <question> <_new_:qn> .
		  <_new_:qn> <name> "` + q.Name + `" .
		  <_new_:qn> <text> "` + q.Text + `" .
		  <_new_:qn> <positive> "` + strconv.FormatFloat(q.Positive, 'g', -1, 64) + `" .
		  <_new_:qn> <negative> "` + strconv.FormatFloat(q.Negative, 'g', -1, 64) + `" .
		  <_new_:qn> <notes> "` + q.Notes + `" .`

	correct := 0
	for i, opt := range q.Options {
		idx := strconv.Itoa(i)
		m += `
		<_new_:qn> <question.option> <_new_:o` + idx + `> .
		<_new_:o` + idx + `> <name> "` + opt.Text + `" .`
		if opt.IsCorrect {
			m += `
			<_new_:qn> <question.correct> <_new_:o` + idx + `> .`
			correct++
		}
	}

	for i, t := range q.Tags {
		idx := strconv.Itoa(i)
		if t.Uid != "" {
			m += `
			<_new_:qn> <question.tag> <_uid_:` + t.Uid + `> .
			<_uid_:` + t.Uid + `> <tag.question> <_new_:qn> . `
		} else {
			m += `
			<_new_:t` + idx + `> <name> "` + t.Name + `" .
			<_new_:qn> <question.tag> <_new_:t` + idx + `> .
			<_new_:t` + idx + `> <tag.question> <_new_:qn> . `
		}
	}

	if correct > 1 {
		m += `
		<_new_:qn> <multiple> "true" . `
	} else {
		m += `
		<_new_:qn> <multiple> "false" . `
	}
	m += `
	  }
  }	`
	return m
}

// TODO - Move this inline with add, like we have for edit.
func validateQuestion(q Question) error {
	if q.Name == "" || q.Text == "" {
		return fmt.Errorf("Question name/text can't be empty")
	}
	// TODO - Have validation on score.
	if q.Positive == 0 || q.Negative == 0 {
		return fmt.Errorf("Positive or negative score can't be zero.")
	}
	if len(q.Options) == 0 {
		return fmt.Errorf("Question should have atleast one option")
	}
	correct := 0
	for _, opt := range q.Options {
		if opt.IsCorrect {
			correct++
		}
	}
	if correct == 0 {
		fmt.Errorf("Atleast one option should be correct")
	}
	return nil
}

// API for "Adding Question" to Database
func Add(w http.ResponseWriter, r *http.Request) {
	sr := server.Response{}
	var q Question
	err := json.NewDecoder(r.Body).Decode(&q)
	if err != nil {
		sr.Error = "Couldn't decode JSON"
		w.WriteHeader(http.StatusBadRequest)
		w.Write(server.MarshalResponse(sr))
		return
	}

	if err := validateQuestion(q); err != nil {
		sr.Error = err.Error()
		w.WriteHeader(http.StatusBadRequest)
		w.Write(server.MarshalResponse(sr))
		return
	}

	m := add(q)
	res, err := dgraph.SendMutation(m)
	if err != nil {
		sr.Write(w, "", err.Error(), http.StatusInternalServerError)
		return
	}
	if res.Code != "ErrorOk" {
		sr.Write(w, res.Message, "", http.StatusInternalServerError)
		return
	}

	sr.Success = true
	sr.Message = "Question Successfully Saved!"
	w.Write(server.MarshalResponse(sr))
}

type qid struct {
	Id string
}

func Index(w http.ResponseWriter, r *http.Request) {
	sr := server.Response{}
	var q qid
	err := json.NewDecoder(r.Body).Decode(&q)
	if err != nil {
		sr.Write(w, "", "Couldn't decode JSON", http.StatusBadRequest)
		return
	}

	var query string
	// TODO - Maybe dont call with debug, and request appropriate fields.
	if q.Id != "" {
		query = "{debug(_xid_: root) { question (after: " + q.Id + ", first: 20) { _uid_ name text negative positive question.tag { name } question.option { name } question.correct { name } }  } }"
	} else {
		query = "{debug(_xid_: root) { question (first:20) { _uid_ name text negative positive question.tag { name } question.option { name } question.correct { name } }  } }"
	}

	b, err := dgraph.Query(query)
	if err != nil {
		sr.Write(w, "", err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write(b)
}

type QuestionAPIResponse struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	UIDS    struct {
		Question uint64 `json:question`
	}
}

// method to parse question response
func parseQuestionResponse(question_body []byte) (*QuestionAPIResponse, error) {
	var question_response = new(QuestionAPIResponse)
	err := json.Unmarshal(question_body, &question_response)
	if err != nil {
		log.Fatal(err)
	}
	return question_response, err
}

type TagFilter struct {
	UID string
}

// FILTER QUESTION HANDLER: Fileter By Tags
// TODO - Clean this up.
func Filter(w http.ResponseWriter, r *http.Request) {
	var tag TagFilter

	err := json.NewDecoder(r.Body).Decode(&tag)
	if err != nil {
		panic(err)
	}

	filter_query := "{root(_uid_: " + tag.UID + ") { tag.question { text }}"
	filter_response, err := http.Post("http://localhost:8080/query", "application/x-www-form-urlencoded", strings.NewReader(filter_query))
	if err != nil {
		panic(err)
	}
	defer filter_response.Body.Close()
	filter_body, err := ioutil.ReadAll(filter_response.Body)
	if err != nil {
		panic(err)
	}
	jsonResp, err := json.Marshal(string(filter_body))
	if err != nil {
		panic(err)
	}

	w.Write(jsonResp)
}

// get question information

func get(questionId string) string {
	return `
	{
		root(_uid_:` + questionId + `) {
			_uid_
			name
			text
			positive
			negative
			notes
			question.option	{
				_uid_
				name
			}
			question.correct {
				_uid_
				name
			}
			question.tag {
				_uid_
				name
			}
		}
	}`
}

func Get(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	qid := vars["id"]

	q := get(qid)
	res, err := dgraph.Query(q)
	if err != nil {
		sr := server.Response{}
		sr.Write(w, "", err.Error(), http.StatusInternalServerError)
		return
	} // TODO - Check if Dgraph returns record not found and wrap it with an error.
	w.Write(res)
}

// update question
func edit(q Question) (string, error) {
	m := new(dgraph.Mutation)
	if q.Name == "" || q.Text == "" {
		return "", fmt.Errorf("Question name/text can't be empty.")
	}
	// TODO - Remove later.
	if q.Notes == "" {
		q.Notes = "Dummy notes"
	}
	m.Set(`<_uid_:` + q.Uid + `> <name> "` + q.Name + `" .`)
	m.Set(`<_uid_:` + q.Uid + `> <text> "` + q.Text + `" .`)
	m.Set(`<_uid_:` + q.Uid + `> <notes> "` + q.Notes + `" .`)

	if q.Positive == 0 || q.Negative == 0 {
		return "", fmt.Errorf("Positive/Negative score can't be zero.")
	}
	m.Set(`<_uid_:` + q.Uid + `> <positive> "` + strconv.FormatFloat(q.Positive, 'g', -1, 64) + `" .`)
	m.Set(`<_uid_:` + q.Uid + `> <negative> "` + strconv.FormatFloat(q.Negative, 'g', -1, 64) + `" .`)

	correct := 0
	if len(q.Options) == 0 {
		return "", fmt.Errorf("Question should have atleast one option")
	}
	for _, opt := range q.Options {
		if opt.Text == "" {
			return "", fmt.Errorf("Option text can't be empty.")
		}
		m.Set(`<_uid_:` + opt.Uid + `> <name> "` + opt.Text + `" .`)
		m.Set(`<_uid_:` + q.Uid + `> <question.option> <_uid_:` + opt.Uid + `> . `)
		if opt.IsCorrect {
			correct++
			m.Set(`<_uid_:` + q.Uid + `> <question.correct> <_uid_:` + opt.Uid + `> .`)
		} else {
			m.Del(`<_uid_:` + q.Uid + `> <question.correct> <_uid_:` + opt.Uid + `> .`)
		}
	}

	// Create and associate Tags
	for i, t := range q.Tags {
		if t.Uid != "" && t.Is_delete {
			m.Del(`<_uid_:` + q.Uid + `> <question.tag> <_uid_:` + t.Uid + `> .`)
			m.Del(`<_uid_:` + t.Uid + `> <tag.question> <_uid_:` + q.Uid + `> . `)

		} else if t.Uid != "" {
			m.Set(`<_uid_:` + q.Uid + `> <question.tag> <_uid_:` + t.Uid + `> .`)
			m.Set(`<_uid_:` + t.Uid + `> <tag.question> <_uid_:` + q.Uid + `> . `)

		} else if t.Uid == "" {
			if t.Name == "" {
				return "", fmt.Errorf("Tag name can't be empty.")
			}
			idx := strconv.Itoa(i)
			m.Set(`<_new_:tag` + idx + `> <name> "` + t.Name + `" .`)
			m.Set(`<_uid_:` + q.Uid + `> <question.tag> <_new_:tag` + idx + `> .`)
			m.Set(`<_new_:tag` + idx + `> <tag.question> <_uid_:` + q.Uid + `> . `)
		}
	}
	// TODO - There should be atleast one tag associated with a question.

	if correct == 0 {
		return "", fmt.Errorf("Atleast one option should be correct.")
	} else if correct > 1 {
		m.Set(`<_uid_:` + q.Uid + `> <multiple> "true" . `)
	} else {
		m.Set(`<_uid_:` + q.Uid + `> <multiple> "false" . `)
	}
	return m.String(), nil
}

func Edit(w http.ResponseWriter, r *http.Request) {
	sr := server.Response{}
	// vars := mux.Vars(r)
	// qid := vars["id"]
	// TODO - Id should be obtained from url not the body.
	var q Question
	err := json.NewDecoder(r.Body).Decode(&q)
	if err != nil {
		sr.Write(w, "", "Couldn't decode JSON", http.StatusBadRequest)
		return
	}

	var m string
	if m, err = edit(q); err != nil {
		sr.Write(w, "", err.Error(), http.StatusBadRequest)
		return
	}

	mr, err := dgraph.SendMutation(m)
	if err != nil {
		sr.Write(w, "", err.Error(), http.StatusInternalServerError)
		return
	}
	if mr.Code != "ErrorOk" {
		sr.Write(w, mr.Message, "", http.StatusInternalServerError)
		return
	}

	sr.Success = true
	sr.Message = "Question updated successfully."
	w.Write(server.MarshalResponse(sr))
}
<|endoftext|>


<commit_before>package jobsrunner

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"os/exec"
	"sync"
	"time"
)

// Runtime of jobrunner.
type Runtime struct {
	// TODO: Logger dependency.
	Conf Config
	//Logger log.Logger

	ctx context.Context
}

// NewFromFile creates Runtime with Config parsed from the specified file.
func NewFromFile(filename string) (Runtime, error) {
	conf, err := NewConfigFromFile(filename)
	if err != nil {
		return Runtime{}, fmt.Errorf("can't create config: %s", err)
	}
	return Runtime{
		Conf: conf,
	}, nil
}

// Run the configured jobs.
func (r *Runtime) Run(ctx context.Context) {
	var wg sync.WaitGroup
	r.ctx = ctx
	for i, job := range r.Conf.Jobs {
		wg.Add(1)
		go func() {
			r.startJob(job)
			log.Printf("Job #%d finished", i)
			wg.Done()
		}()
	}
	wg.Wait()
}

func (r *Runtime) startJob(job ConfigJob) {
	r.runCmd(job.Cmd)
	for {
		select {
		case <-r.ctx.Done():
			return
		case <-time.Tick(time.Duration(job.Interval)):
			r.runCmd(job.Cmd)
		}
	}
}

func (r *Runtime) runCmd(cmd string) {
	c := exec.Command("sh", "-c", cmd)

	stderr, err := c.StderrPipe()
	if err != nil {
		log.Printf(`"%v" didn'r run: failed to pipe stderr: %s`, cmd, err)
		return
	}

	stdout, err := c.StdoutPipe()
	if err != nil {
		log.Printf(`"%v" didn'r run: failed to pipe stdout: %s`, cmd, err)
		return
	}

	if err := c.Start(); err != nil {
		log.Printf(`"%v" didn'r run: failed to start: %s`, cmd, err)
		return
	}
	b := time.Now()
	var (
		slurp, _  = ioutil.ReadAll(stderr)
		output, _ = ioutil.ReadAll(stdout)
	)
	if err := c.Wait(); err != nil {
		if errExit, ok := err.(*exec.ExitError); ok {
			o := fmt.Sprintf(`"%v" finished with %s`, cmd, errExit)
			if len(slurp) != 0 {
				o = fmt.Sprintf("%s, stderr: %s", o, slurp)
			}
			if len(output) != 0 {
				o = fmt.Sprintf("%s, stdout: %s", o, output)
			}
			log.Println(o)
		} else {
			log.Printf(`"%v" finished with error %s`, cmd, err)
		}
	}
	log.Printf(`"%v" took %s`, cmd, time.Now().Sub(b))
}
<commit_msg>Fixed an error that only a single job was run<commit_after>package jobsrunner

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"os/exec"
	"sync"
	"time"
)

// Runtime of jobrunner.
type Runtime struct {
	// TODO: Logger dependency.
	Conf Config
	//Logger log.Logger

	ctx context.Context
}

// NewFromFile creates Runtime with Config parsed from the specified file.
func NewFromFile(filename string) (Runtime, error) {
	conf, err := NewConfigFromFile(filename)
	if err != nil {
		return Runtime{}, fmt.Errorf("can't create config: %s", err)
	}
	return Runtime{
		Conf: conf,
	}, nil
}

// Run the configured jobs.
func (r *Runtime) Run(ctx context.Context) {
	var wg sync.WaitGroup
	r.ctx = ctx
	for i, job := range r.Conf.Jobs {
		wg.Add(1)
		// TODO: Write a test that all jobs are started.
		go func(job ConfigJob) {
			r.runWithInterval(job)
			log.Printf("Job #%d finished", i)
			wg.Done()
		}(job)
	}
	wg.Wait()
}

func (r *Runtime) runWithInterval(job ConfigJob) {
	r.runCmd(job.Cmd)
	for {
		select {
		case <-r.ctx.Done():
			return
		case <-time.Tick(time.Duration(job.Interval)):
			r.runCmd(job.Cmd)
		}
	}
}

func (r *Runtime) runCmd(cmd string) {
	c := exec.Command("sh", "-c", cmd)

	stderr, err := c.StderrPipe()
	if err != nil {
		log.Printf(`"%v" didn'r run: failed to pipe stderr: %s`, cmd, err)
		return
	}

	stdout, err := c.StdoutPipe()
	if err != nil {
		log.Printf(`"%v" didn'r run: failed to pipe stdout: %s`, cmd, err)
		return
	}

	if err := c.Start(); err != nil {
		log.Printf(`"%v" didn'r run: failed to start: %s`, cmd, err)
		return
	}
	b := time.Now()
	var (
		slurp, _  = ioutil.ReadAll(stderr)
		output, _ = ioutil.ReadAll(stdout)
	)
	if err := c.Wait(); err != nil {
		if errExit, ok := err.(*exec.ExitError); ok {
			o := fmt.Sprintf(`"%v" finished with %s`, cmd, errExit)
			if len(slurp) != 0 {
				o = fmt.Sprintf("%s, stderr: %s", o, slurp)
			}
			if len(output) != 0 {
				o = fmt.Sprintf("%s, stdout: %s", o, output)
			}
			log.Println(o)
		} else {
			log.Printf(`"%v" finished with error %s`, cmd, err)
		}
	}
	log.Printf(`"%v" took %s`, cmd, time.Now().Sub(b))
}
<|endoftext|>


<commit_before>package controller

import (
	"github.com/MG-RAST/AWE/lib/conf"
	"github.com/MG-RAST/AWE/lib/core"
	e "github.com/MG-RAST/AWE/lib/errors"
	"github.com/MG-RAST/AWE/lib/logger"
	"github.com/MG-RAST/AWE/lib/logger/event"
	"github.com/MG-RAST/AWE/lib/request"
	"github.com/MG-RAST/AWE/lib/user"
	"github.com/MG-RAST/golib/goweb"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

type WorkController struct{}

// OPTIONS: /work
func (cr *WorkController) Options(cx *goweb.Context) {
	LogRequest(cx.Request)
	cx.RespondWithOK()
	return
}

// GET: /work/{id}
// get a workunit by id, read-only
func (cr *WorkController) Read(id string, cx *goweb.Context) {
	LogRequest(cx.Request)

	// Gather query params
	query := &Query{Li: cx.Request.URL.Query()}

	if (query.Has("datatoken") || query.Has("privateenv")) && query.Has("client") {
		cg, err := request.AuthenticateClientGroup(cx.Request)
		if err != nil {
			if err.Error() == e.NoAuth || err.Error() == e.UnAuth || err.Error() == e.InvalidAuth {
				if conf.CLIENT_AUTH_REQ == true {
					cx.RespondWithError(http.StatusUnauthorized)
					return
				}
			} else {
				logger.Error("Err@AuthenticateClientGroup: " + err.Error())
				cx.RespondWithError(http.StatusInternalServerError)
				return
			}
		}
		// check that clientgroup auth token matches group of client
		clientid := query.Value("client")
		client, err := core.QMgr.GetClient(clientid)
		if err != nil {
			cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
			return
		}
		if cg != nil && client.Group != cg.Name {
			cx.RespondWithErrorMessage("Clientgroup name in token does not match that in the client configuration.", http.StatusBadRequest)
			return
		}

		if query.Has("datatoken") { //a client is requesting data token for this job
			token, err := core.QMgr.FetchDataToken(id, clientid)
			if err != nil {
				cx.RespondWithErrorMessage("error in getting token for job "+id, http.StatusBadRequest)
				return
			}
			//cx.RespondWithData(token)
			RespondTokenInHeader(cx, token)
			return
		}

		if query.Has("privateenv") { //a client is requesting data token for this job
			envs, err := core.QMgr.FetchPrivateEnv(id, clientid)
			if err != nil {
				cx.RespondWithErrorMessage("error in getting token for job "+id, http.StatusBadRequest)
				return
			}
			//cx.RespondWithData(token)
			RespondPrivateEnvInHeader(cx, envs)
			return
		}
	}

	// Try to authenticate user.
	u, err := request.Authenticate(cx.Request)
	if err != nil && err.Error() != e.NoAuth {
		cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized)
		return
	}

	// If no auth was provided, and anonymous read is allowed, use the public user
	if u == nil {
		if conf.ANON_READ == true {
			u = &user.User{Uuid: "public"}
		} else {
			cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized)
			return
		}
	}

	// Load workunit by id
	workunit, err := core.QMgr.GetWorkById(id)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}

	jobid, err := core.GetJobIdByWorkId(id)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}

	job, err := core.LoadJob(jobid)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}

	// User must have read permissions on job or be job owner or be an admin
	rights := job.Acl.Check(u.Uuid)
	if job.Acl.Owner != u.Uuid && rights["read"] == false && u.Admin == false {
		cx.RespondWithErrorMessage(e.UnAuth, http.StatusUnauthorized)
		return
	}

	if query.Has("report") { //retrieve report: stdout or stderr or worknotes
		reportmsg, err := core.QMgr.GetReportMsg(id, query.Value("report"))
		if err != nil {
			cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
			return
		}
		cx.RespondWithData(reportmsg)
		return
	}

	if err != nil {
		if err.Error() != e.QueueEmpty {
			logger.Error("Err@work_Read:core.QMgr.GetWorkById(): " + err.Error())
		}
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}
	// Base case respond with workunit in json
	cx.RespondWithData(workunit)
	return
}

// GET: /work
// checkout a workunit with earliest submission time
// to-do: to support more options for workunit checkout
func (cr *WorkController) ReadMany(cx *goweb.Context) {

	// Gather query params
	query := &Query{Li: cx.Request.URL.Query()}

	if !query.Has("client") { //view workunits
		// Try to authenticate user.
		u, err := request.Authenticate(cx.Request)
		if err != nil && err.Error() != e.NoAuth {
			cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized)
			return
		}

		// If no auth was provided, and anonymous read is allowed, use the public user
		if u == nil {
			if conf.ANON_READ == true {
				u = &user.User{Uuid: "public"}
			} else {
				cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized)
				return
			}
		}

		var workunits []*core.Workunit
		if query.Has("state") {
			workunits = core.QMgr.ShowWorkunitsByUser(query.Value("state"), u)
		} else {
			workunits = core.QMgr.ShowWorkunitsByUser("", u)
		}
		cx.RespondWithData(workunits)
		return
	}

	cg, err := request.AuthenticateClientGroup(cx.Request)
	if err != nil {
		if err.Error() == e.NoAuth || err.Error() == e.UnAuth || err.Error() == e.InvalidAuth {
			if conf.CLIENT_AUTH_REQ == true {
				cx.RespondWithError(http.StatusUnauthorized)
				return
			}
		} else {
			logger.Error("Err@AuthenticateClientGroup: " + err.Error())
			cx.RespondWithError(http.StatusInternalServerError)
			return
		}
	}

	// check that clientgroup auth token matches group of client
	clientid := query.Value("client")
	client, err := core.QMgr.GetClient(clientid)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}
	if cg != nil && client.Group != cg.Name {
		cx.RespondWithErrorMessage("Clientgroup name in token does not match that in the client configuration.", http.StatusBadRequest)
		return
	}

	if core.Service == "proxy" { //drive proxy workStealer to checkout work from server
		core.ProxyWorkChan <- true
	}

	//checkout a workunit in FCFS order
	workunits, err := core.QMgr.CheckoutWorkunits("FCFS", clientid, 1)

	if err != nil {
		if err.Error() != e.QueueEmpty && err.Error() != e.NoEligibleWorkunitFound && err.Error() != e.ClientNotFound && err.Error() != e.ClientSuspended {
			logger.Error("Err@work_ReadMany:core.QMgr.GetWorkByFCFS(): " + err.Error() + ";client=" + clientid)
		}
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}

	//log access info only when the queue is not empty, save some log
	LogRequest(cx.Request)

	//log event about workunit checkout (WO)
	workids := []string{}
	for _, work := range workunits {
		workids = append(workids, work.Id)
	}

	logger.Event(event.WORK_CHECKOUT,
		"workids="+strings.Join(workids, ","),
		"clientid="+clientid)

	// Base case respond with node in json
	cx.RespondWithData(workunits[0])
	return
}

// PUT: /work/{id} -> status update
func (cr *WorkController) Update(id string, cx *goweb.Context) {
	LogRequest(cx.Request)
	// Gather query params
	query := &Query{Li: cx.Request.URL.Query()}
	if !query.Has("client") {
		cx.RespondWithErrorMessage("This request type requires the client=clientid parameter.", http.StatusBadRequest)
		return
	}

	// Check auth
	cg, err := request.AuthenticateClientGroup(cx.Request)
	if err != nil {
		if err.Error() == e.NoAuth || err.Error() == e.UnAuth || err.Error() == e.InvalidAuth {
			if conf.CLIENT_AUTH_REQ == true {
				cx.RespondWithError(http.StatusUnauthorized)
				return
			}
		} else {
			logger.Error("Err@AuthenticateClientGroup: " + err.Error())
			cx.RespondWithError(http.StatusInternalServerError)
			return
		}
	}

	// check that clientgroup auth token matches group of client
	clientid := query.Value("client")
	client, err := core.QMgr.GetClient(clientid)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}
	if cg != nil && client.Group != cg.Name {
		cx.RespondWithErrorMessage("Clientgroup name in token does not match that in the client configuration.", http.StatusBadRequest)
		return
	}

	if query.Has("status") && query.Has("client") { //notify execution result: "done" or "fail"
		notice := core.Notice{WorkId: id, Status: query.Value("status"), ClientId: query.Value("client"), Notes: ""}
		if query.Has("computetime") {
			if comptime, err := strconv.Atoi(query.Value("computetime")); err == nil {
				notice.ComputeTime = comptime
			}
		}
		if query.Has("report") { // if "report" is specified in query, parse performance statistics or errlog
			if _, files, err := ParseMultipartForm(cx.Request); err == nil {
				if _, ok := files["perf"]; ok {
					core.QMgr.FinalizeWorkPerf(id, files["perf"].Path)
				}
				if _, ok := files["notes"]; ok {
					if notes, err := ioutil.ReadFile(files["notes"].Path); err == nil {
						notice.Notes = string(notes)
					}
				}
				if _, ok := files["stdout"]; ok {
					core.QMgr.SaveStdLog(id, "stdout", files["stdout"].Path)
				}
				if _, ok := files["stderr"]; ok {
					core.QMgr.SaveStdLog(id, "stderr", files["stderr"].Path)
				}
				if _, ok := files["worknotes"]; ok {
					core.QMgr.SaveStdLog(id, "worknotes", files["worknotes"].Path)
				}
			}
		}
		core.QMgr.NotifyWorkStatus(notice)
	}
	cx.RespondWithData("ok")
	return
}
<commit_msg>add pagination to work query<commit_after>package controller

import (
	"github.com/MG-RAST/AWE/lib/conf"
	"github.com/MG-RAST/AWE/lib/core"
	e "github.com/MG-RAST/AWE/lib/errors"
	"github.com/MG-RAST/AWE/lib/logger"
	"github.com/MG-RAST/AWE/lib/logger/event"
	"github.com/MG-RAST/AWE/lib/request"
	"github.com/MG-RAST/AWE/lib/user"
	"github.com/MG-RAST/golib/goweb"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

type WorkController struct{}

// OPTIONS: /work
func (cr *WorkController) Options(cx *goweb.Context) {
	LogRequest(cx.Request)
	cx.RespondWithOK()
	return
}

// GET: /work/{id}
// get a workunit by id, read-only
func (cr *WorkController) Read(id string, cx *goweb.Context) {
	LogRequest(cx.Request)

	// Gather query params
	query := &Query{Li: cx.Request.URL.Query()}

	if (query.Has("datatoken") || query.Has("privateenv")) && query.Has("client") {
		cg, err := request.AuthenticateClientGroup(cx.Request)
		if err != nil {
			if err.Error() == e.NoAuth || err.Error() == e.UnAuth || err.Error() == e.InvalidAuth {
				if conf.CLIENT_AUTH_REQ == true {
					cx.RespondWithError(http.StatusUnauthorized)
					return
				}
			} else {
				logger.Error("Err@AuthenticateClientGroup: " + err.Error())
				cx.RespondWithError(http.StatusInternalServerError)
				return
			}
		}
		// check that clientgroup auth token matches group of client
		clientid := query.Value("client")
		client, err := core.QMgr.GetClient(clientid)
		if err != nil {
			cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
			return
		}
		if cg != nil && client.Group != cg.Name {
			cx.RespondWithErrorMessage("Clientgroup name in token does not match that in the client configuration.", http.StatusBadRequest)
			return
		}

		if query.Has("datatoken") { //a client is requesting data token for this job
			token, err := core.QMgr.FetchDataToken(id, clientid)
			if err != nil {
				cx.RespondWithErrorMessage("error in getting token for job "+id, http.StatusBadRequest)
				return
			}
			//cx.RespondWithData(token)
			RespondTokenInHeader(cx, token)
			return
		}

		if query.Has("privateenv") { //a client is requesting data token for this job
			envs, err := core.QMgr.FetchPrivateEnv(id, clientid)
			if err != nil {
				cx.RespondWithErrorMessage("error in getting token for job "+id, http.StatusBadRequest)
				return
			}
			//cx.RespondWithData(token)
			RespondPrivateEnvInHeader(cx, envs)
			return
		}
	}

	// Try to authenticate user.
	u, err := request.Authenticate(cx.Request)
	if err != nil && err.Error() != e.NoAuth {
		cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized)
		return
	}

	// If no auth was provided, and anonymous read is allowed, use the public user
	if u == nil {
		if conf.ANON_READ == true {
			u = &user.User{Uuid: "public"}
		} else {
			cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized)
			return
		}
	}

	// Load workunit by id
	workunit, err := core.QMgr.GetWorkById(id)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}

	jobid, err := core.GetJobIdByWorkId(id)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}

	job, err := core.LoadJob(jobid)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}

	// User must have read permissions on job or be job owner or be an admin
	rights := job.Acl.Check(u.Uuid)
	if job.Acl.Owner != u.Uuid && rights["read"] == false && u.Admin == false {
		cx.RespondWithErrorMessage(e.UnAuth, http.StatusUnauthorized)
		return
	}

	if query.Has("report") { //retrieve report: stdout or stderr or worknotes
		reportmsg, err := core.QMgr.GetReportMsg(id, query.Value("report"))
		if err != nil {
			cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
			return
		}
		cx.RespondWithData(reportmsg)
		return
	}

	if err != nil {
		if err.Error() != e.QueueEmpty {
			logger.Error("Err@work_Read:core.QMgr.GetWorkById(): " + err.Error())
		}
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}
	// Base case respond with workunit in json
	cx.RespondWithData(workunit)
	return
}

// GET: /work
// checkout a workunit with earliest submission time
// to-do: to support more options for workunit checkout
func (cr *WorkController) ReadMany(cx *goweb.Context) {

	// Gather query params
	query := &Query{Li: cx.Request.URL.Query()}

	if !query.Has("client") { //view workunits
		// Try to authenticate user.
		u, err := request.Authenticate(cx.Request)
		if err != nil && err.Error() != e.NoAuth {
			cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized)
			return
		}

		// If no auth was provided, and anonymous read is allowed, use the public user
		if u == nil {
			if conf.ANON_READ == true {
				u = &user.User{Uuid: "public"}
			} else {
				cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized)
				return
			}
		}

		// get pagination options
		limit := conf.DEFAULT_PAGE_SIZE
		offset := 0
		if query.Has("limit") {
			limit, err = strconv.Atoi(query.Value("limit"))
			if err != nil {
				cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
				return
			}
		}
		if query.Has("offset") {
			offset, err = strconv.Atoi(query.Value("offset"))
			if err != nil {
				cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
				return
			}
		}

		var workunits []*core.Workunit
		if query.Has("state") {
			workunits = core.QMgr.ShowWorkunitsByUser(query.Value("state"), u)
		} else {
			workunits = core.QMgr.ShowWorkunitsByUser("", u)
		}
		cx.RespondWithPaginatedData(workunits, limit, offset, len(workunits))
		return
	}

	cg, err := request.AuthenticateClientGroup(cx.Request)
	if err != nil {
		if err.Error() == e.NoAuth || err.Error() == e.UnAuth || err.Error() == e.InvalidAuth {
			if conf.CLIENT_AUTH_REQ == true {
				cx.RespondWithError(http.StatusUnauthorized)
				return
			}
		} else {
			logger.Error("Err@AuthenticateClientGroup: " + err.Error())
			cx.RespondWithError(http.StatusInternalServerError)
			return
		}
	}

	// check that clientgroup auth token matches group of client
	clientid := query.Value("client")
	client, err := core.QMgr.GetClient(clientid)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}
	if cg != nil && client.Group != cg.Name {
		cx.RespondWithErrorMessage("Clientgroup name in token does not match that in the client configuration.", http.StatusBadRequest)
		return
	}

	if core.Service == "proxy" { //drive proxy workStealer to checkout work from server
		core.ProxyWorkChan <- true
	}

	//checkout a workunit in FCFS order
	workunits, err := core.QMgr.CheckoutWorkunits("FCFS", clientid, 1)

	if err != nil {
		if err.Error() != e.QueueEmpty && err.Error() != e.NoEligibleWorkunitFound && err.Error() != e.ClientNotFound && err.Error() != e.ClientSuspended {
			logger.Error("Err@work_ReadMany:core.QMgr.GetWorkByFCFS(): " + err.Error() + ";client=" + clientid)
		}
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}

	//log access info only when the queue is not empty, save some log
	LogRequest(cx.Request)

	//log event about workunit checkout (WO)
	workids := []string{}
	for _, work := range workunits {
		workids = append(workids, work.Id)
	}

	logger.Event(event.WORK_CHECKOUT,
		"workids="+strings.Join(workids, ","),
		"clientid="+clientid)

	// Base case respond with node in json
	cx.RespondWithData(workunits[0])
	return
}

// PUT: /work/{id} -> status update
func (cr *WorkController) Update(id string, cx *goweb.Context) {
	LogRequest(cx.Request)
	// Gather query params
	query := &Query{Li: cx.Request.URL.Query()}
	if !query.Has("client") {
		cx.RespondWithErrorMessage("This request type requires the client=clientid parameter.", http.StatusBadRequest)
		return
	}

	// Check auth
	cg, err := request.AuthenticateClientGroup(cx.Request)
	if err != nil {
		if err.Error() == e.NoAuth || err.Error() == e.UnAuth || err.Error() == e.InvalidAuth {
			if conf.CLIENT_AUTH_REQ == true {
				cx.RespondWithError(http.StatusUnauthorized)
				return
			}
		} else {
			logger.Error("Err@AuthenticateClientGroup: " + err.Error())
			cx.RespondWithError(http.StatusInternalServerError)
			return
		}
	}

	// check that clientgroup auth token matches group of client
	clientid := query.Value("client")
	client, err := core.QMgr.GetClient(clientid)
	if err != nil {
		cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
		return
	}
	if cg != nil && client.Group != cg.Name {
		cx.RespondWithErrorMessage("Clientgroup name in token does not match that in the client configuration.", http.StatusBadRequest)
		return
	}

	if query.Has("status") && query.Has("client") { //notify execution result: "done" or "fail"
		notice := core.Notice{WorkId: id, Status: query.Value("status"), ClientId: query.Value("client"), Notes: ""}
		if query.Has("computetime") {
			if comptime, err := strconv.Atoi(query.Value("computetime")); err == nil {
				notice.ComputeTime = comptime
			}
		}
		if query.Has("report") { // if "report" is specified in query, parse performance statistics or errlog
			if _, files, err := ParseMultipartForm(cx.Request); err == nil {
				if _, ok := files["perf"]; ok {
					core.QMgr.FinalizeWorkPerf(id, files["perf"].Path)
				}
				if _, ok := files["notes"]; ok {
					if notes, err := ioutil.ReadFile(files["notes"].Path); err == nil {
						notice.Notes = string(notes)
					}
				}
				if _, ok := files["stdout"]; ok {
					core.QMgr.SaveStdLog(id, "stdout", files["stdout"].Path)
				}
				if _, ok := files["stderr"]; ok {
					core.QMgr.SaveStdLog(id, "stderr", files["stderr"].Path)
				}
				if _, ok := files["worknotes"]; ok {
					core.QMgr.SaveStdLog(id, "worknotes", files["worknotes"].Path)
				}
			}
		}
		core.QMgr.NotifyWorkStatus(notice)
	}
	cx.RespondWithData("ok")
	return
}
<|endoftext|>