<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 l2plugin

import (
	"fmt"

	govppapi "git.fd.io/govpp.git/api"
	log "github.com/ligato/cn-infra/logging/logrus"
	"github.com/ligato/cn-infra/utils/safeclose"
	"github.com/ligato/vpp-agent/idxvpp"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/ifplugin/ifaceidx"
	l2ba "github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/bin_api/l2"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/model/l2"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/vppcalls"
	"github.com/ligato/vpp-agent/plugins/govppmux"
)

// XConnectConfigurator implements PluginHandlerVPP
type XConnectConfigurator struct {
	GoVppmux    *govppmux.GOVPPPlugin
	SwIfIndexes ifaceidx.SwIfIndex
	XcIndexes   idxvpp.NameToIdxRW
	XcIndexSeq  uint32
	vppChan     *govppapi.Channel
}

// XConnectMeta meta hold info about transmit interface
type XConnectMeta struct {
	TransmitInterface string
	configured        bool // true if already configured on VPP, false if still waiting for creation of the rx or tx interface
}

// Init members (channels...) and start go routines
func (plugin *XConnectConfigurator) Init() (err error) {

	log.DefaultLogger().Debug("Initializing L2 xConnect")

	// Init VPP API channel
	plugin.vppChan, err = plugin.GoVppmux.NewAPIChannel()
	if err != nil {
		return err
	}

	// check bin api message compatibility
	err = vppcalls.CheckMsgCompatibilityForL2XConnect(plugin.vppChan)
	if err != nil {
		return err
	}

	return nil
}

// Close GOVPP channel
func (plugin *XConnectConfigurator) Close() error {
	return safeclose.Close(plugin.vppChan)
}

// ConfigureXConnectPair process the NB config and propagates it to bin api calls
func (plugin *XConnectConfigurator) ConfigureXConnectPair(xConnectPairInput *l2.XConnectPairs_XConnectPair) error {
	log.DefaultLogger().Println("Configuring L2 xConnect pair", xConnectPairInput.ReceiveInterface)

	// Find interfaces
	receiveInterfaceIndex, _, rxFound := plugin.SwIfIndexes.LookupIdx(xConnectPairInput.ReceiveInterface)
	if !rxFound {
		log.DefaultLogger().WithField("Interface", xConnectPairInput.ReceiveInterface).Warn("Receive interface not found.")
	}
	transmitInterfaceIndex, _, txFound := plugin.SwIfIndexes.LookupIdx(xConnectPairInput.TransmitInterface)
	if !txFound {
		log.DefaultLogger().WithField("Interface", xConnectPairInput.TransmitInterface).Warn("Transmit interface not found.")
	}

	if rxFound && txFound {
		// can be configured now
		err := vppcalls.VppSetL2XConnect(receiveInterfaceIndex, transmitInterfaceIndex, plugin.vppChan)
		if err != nil {
			log.DefaultLogger().WithField("Error", err).Error("Failed to create l2xConnect")
			return err
		}
	} else {
		log.DefaultLogger().Error("l2xConnect")
	}

	// Prepare meta
	meta := XConnectMeta{
		TransmitInterface: xConnectPairInput.TransmitInterface,
		configured:        rxFound && txFound,
	}

	// Register
	plugin.XcIndexes.RegisterName(xConnectPairInput.ReceiveInterface, plugin.XcIndexSeq, &meta)
	plugin.XcIndexSeq++

	return nil
}

// ModifyXConnectPair processes the NB config and propagates it to bin api calls
func (plugin *XConnectConfigurator) ModifyXConnectPair(newConfig *l2.XConnectPairs_XConnectPair, oldConfig *l2.XConnectPairs_XConnectPair) error {
	log.DefaultLogger().Println("Modifying L2 xConnect pair")

	// interfaces
	receiveInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(newConfig.ReceiveInterface)
	if !found {
		log.DefaultLogger().WithField("Interface", newConfig.ReceiveInterface).Error("Receive interface not found.")
		return nil
	}
	newTransmitInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(newConfig.TransmitInterface)
	if !found {
		log.DefaultLogger().WithField("Interface", newConfig.TransmitInterface).Error("New transmit interface not found.")
		return nil
	}
	oldTransmitInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(oldConfig.TransmitInterface)
	if !found {
		log.DefaultLogger().WithField("Interface", newConfig.TransmitInterface).Debug("Old transmit interface not found.")
		oldTransmitInterfaceIndex = 0
		// do not return, not an error
	}
	if oldTransmitInterfaceIndex == newTransmitInterfaceIndex {
		// nothing to update
		return nil
	} else if oldTransmitInterfaceIndex == 0 {
		// create new xConnect only
		err := vppcalls.VppSetL2XConnect(receiveInterfaceIndex, newTransmitInterfaceIndex, plugin.vppChan)
		if err != nil {
			log.DefaultLogger().WithField("Error", err).Error("Failed to set l2xConnect")
			return err
		}
	} else {
		errDel := vppcalls.VppUnsetL2XConnect(receiveInterfaceIndex, oldTransmitInterfaceIndex, plugin.vppChan)
		if errDel != nil {
			log.DefaultLogger().WithField("Error", errDel).Error("Failed to remove l2xConnect")
			return errDel
		}
		errCreate := vppcalls.VppSetL2XConnect(receiveInterfaceIndex, newTransmitInterfaceIndex, plugin.vppChan)
		if errCreate != nil {
			log.DefaultLogger().WithField("Error", errCreate).Error("Failed to set l2xConnect")
			return errCreate
		}
	}

	meta := XConnectMeta{
		TransmitInterface: newConfig.TransmitInterface,
	}
	plugin.XcIndexes.RegisterName(newConfig.ReceiveInterface, receiveInterfaceIndex, &meta)
	plugin.XcIndexSeq++

	return nil
}

// DeleteXConnectPair process the NB config and propagates it to bin api calls
func (plugin *XConnectConfigurator) DeleteXConnectPair(xConnectPairInput *l2.XConnectPairs_XConnectPair) error {
	return plugin.deleteL2XConnectPair(xConnectPairInput.ReceiveInterface, xConnectPairInput.TransmitInterface)
}

// LookupXConnectPairs registers missing l2 xConnect pairs
func (plugin *XConnectConfigurator) LookupXConnectPairs() error {
	req := &l2ba.L2XconnectDump{}
	reqContext := plugin.vppChan.SendMultiRequest(req)
	var index uint32 = 1
	for {
		msg := &l2ba.L2XconnectDetails{}
		stop, err := reqContext.ReceiveReply(msg)
		if err != nil {
			log.DefaultLogger().Error(err)
			return err
		}
		if stop {
			break
		}
		// Store name if missing
		_, _, found := plugin.XcIndexes.LookupName(index)
		xcIdentifier := string(msg.RxSwIfIndex)
		if !found {
			log.DefaultLogger().WithFields(log.Fields{"Name": xcIdentifier, "Index": index}).Debug("L2 xConnect pair registered.")
			plugin.XcIndexes.RegisterName(xcIdentifier, index, nil)
		} else {
			log.DefaultLogger().WithFields(log.Fields{"Name": xcIdentifier, "Index": index}).Debug("L2 xConnect pair already registered.")
		}
		index++
	}

	return nil
}

// ResolveCreatedInterface configures xconnect pairs that use the interface as rx or tx and have not been configured yet
func (plugin *XConnectConfigurator) ResolveCreatedInterface(interfaceName string, interfaceIndex uint32) error {
	log.DefaultLogger().Println("Resolving L2 xConnect pairs for created interface ", interfaceName)
	var err error

	// lookup for the interface in rx interfaces
	err = plugin.resolveRxInterface(interfaceName, true)

	// lookup for the interface in tx interfaces
	rxIfs := plugin.XcIndexes.LookupNameByMetadata("TransmitInterface", interfaceName)
	for _, rxIf := range rxIfs {
		err = plugin.resolveRxInterface(rxIf, true)
	}

	return err
}

// ResolveDeletedInterface deltes xconnect pairs that have not been deleted yet and use the interface as rx or tx
func (plugin *XConnectConfigurator) ResolveDeletedInterface(interfaceName string) error {
	log.DefaultLogger().Println("Resolving L2 xConnect pairs for deleted interface ", interfaceName)

	var err error

	// lookup for the interface in rx interfaces
	err = plugin.resolveRxInterface(interfaceName, false)

	// lookup for the interface in tx interfaces
	rxIfs := plugin.XcIndexes.LookupNameByMetadata("TransmitInterface", interfaceName)
	for _, rxIf := range rxIfs {
		err = plugin.resolveRxInterface(rxIf, false)
	}

	return err
}

// resolveRxInterface creates/deletes 2sxconnect in XcIndexes that has not been created yet/need to be deleted
// and have the rx interface name matching with the provided argument
func (plugin *XConnectConfigurator) resolveRxInterface(rxIfName string, create bool) error {
	var err error

	_, meta, exists := plugin.XcIndexes.LookupIdx(rxIfName)
	if exists {
		meta := meta.(*XConnectMeta)
		if create {
			// the l2xconn needs to be created
			if !meta.configured {
				// not yet configured, configure now
				err = plugin.configureL2XConnectPair(rxIfName, meta.TransmitInterface)
				meta.configured = true
			}
		} else {
			// the l2xconn needs to be delted
			err = plugin.deleteL2XConnectPair(rxIfName, meta.TransmitInterface)
			// meta deleted in deleteL2XConnectPair
		}
	}

	return err
}

func (plugin *XConnectConfigurator) configureL2XConnectPair(rxIf, txIf string) error {
	log.DefaultLogger().Println("Configuring L2 xConnect pair", rxIf, txIf)

	// find interface idx-es
	receiveInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(rxIf)
	if !found {
		log.DefaultLogger().WithField("Interface", rxIf).Warn("Receive interface not found.")
		return fmt.Errorf("Receive interface '%s' not found", rxIf)
	}
	transmitInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(txIf)
	if !found {
		log.DefaultLogger().WithField("Interface", txIf).Warn("Transmit interface not found.")
		return fmt.Errorf("Transmit interface '%s' not found", txIf)
	}

	// configure l2xconnect
	err := vppcalls.VppSetL2XConnect(receiveInterfaceIndex, transmitInterfaceIndex, plugin.vppChan)
	if err != nil {
		log.DefaultLogger().WithField("Error", err).Error("Failed to create l2xConnect")
		return err
	}

	return nil
}

func (plugin *XConnectConfigurator) deleteL2XConnectPair(rxIf, txIf string) error {
	log.DefaultLogger().Println("Deleting L2 xConnect pair", rxIf, txIf)

	// find interface idx-es
	receiveInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(rxIf)
	if !found {
		log.DefaultLogger().WithField("Interface", rxIf).Warn("Receive interface not found.")
		return fmt.Errorf("Receive interface '%s' not found", rxIf)
	}
	transmitInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(txIf)
	if !found {
		log.DefaultLogger().WithField("Interface", txIf).Warn("Transmit interface not found.")
		return fmt.Errorf("Transmit interface '%s' not found", txIf)
	}

	err := vppcalls.VppUnsetL2XConnect(receiveInterfaceIndex, transmitInterfaceIndex, plugin.vppChan)
	if err != nil {
		log.DefaultLogger().WithField("Error", err).Error("Failed to remove l2xConnect")
		return err
	}

	// unregister
	plugin.XcIndexes.UnregisterName(rxIf)
	log.DefaultLogger().WithFields(log.Fields{"RecIface": rxIf, "Idex": receiveInterfaceIndex}).Debug("XConnect pair unregistered.")

	return nil
}
<commit_msg>fix lxconnect configurator<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 l2plugin

import (
	"fmt"

	govppapi "git.fd.io/govpp.git/api"
	log "github.com/ligato/cn-infra/logging/logrus"
	"github.com/ligato/cn-infra/utils/safeclose"
	"github.com/ligato/vpp-agent/idxvpp"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/ifplugin/ifaceidx"
	l2ba "github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/bin_api/l2"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/model/l2"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/vppcalls"
	"github.com/ligato/vpp-agent/plugins/govppmux"
)

// XConnectConfigurator implements PluginHandlerVPP
type XConnectConfigurator struct {
	GoVppmux    *govppmux.GOVPPPlugin
	SwIfIndexes ifaceidx.SwIfIndex
	XcIndexes   idxvpp.NameToIdxRW
	XcIndexSeq  uint32
	vppChan     *govppapi.Channel
}

// XConnectMeta meta hold info about transmit interface
type XConnectMeta struct {
	TransmitInterface string
	configured        bool // true if already configured on VPP, false if still waiting for creation of the rx or tx interface
}

// Init members (channels...) and start go routines
func (plugin *XConnectConfigurator) Init() (err error) {

	log.DefaultLogger().Debug("Initializing L2 xConnect")

	// Init VPP API channel
	plugin.vppChan, err = plugin.GoVppmux.NewAPIChannel()
	if err != nil {
		return err
	}

	// check bin api message compatibility
	err = vppcalls.CheckMsgCompatibilityForL2XConnect(plugin.vppChan)
	if err != nil {
		return err
	}

	return nil
}

// Close GOVPP channel
func (plugin *XConnectConfigurator) Close() error {
	return safeclose.Close(plugin.vppChan)
}

// ConfigureXConnectPair process the NB config and propagates it to bin api calls
func (plugin *XConnectConfigurator) ConfigureXConnectPair(xConnectPairInput *l2.XConnectPairs_XConnectPair) error {
	log.DefaultLogger().Println("Configuring L2 xConnect pair", xConnectPairInput.ReceiveInterface)

	// Find interfaces
	receiveInterfaceIndex, _, rxFound := plugin.SwIfIndexes.LookupIdx(xConnectPairInput.ReceiveInterface)
	if !rxFound {
		log.DefaultLogger().WithField("Interface", xConnectPairInput.ReceiveInterface).Warn("Receive interface not found.")
	}
	transmitInterfaceIndex, _, txFound := plugin.SwIfIndexes.LookupIdx(xConnectPairInput.TransmitInterface)
	if !txFound {
		log.DefaultLogger().WithField("Interface", xConnectPairInput.TransmitInterface).Warn("Transmit interface not found.")
	}

	if rxFound && txFound {
		// can be configured now
		err := vppcalls.VppSetL2XConnect(receiveInterfaceIndex, transmitInterfaceIndex, plugin.vppChan)
		if err != nil {
			log.DefaultLogger().WithField("Error", err).Error("Failed to create l2xConnect")
			return err
		}
	} else {
		log.DefaultLogger().Error("l2xConnect")
	}

	// Prepare meta
	meta := XConnectMeta{
		TransmitInterface: xConnectPairInput.TransmitInterface,
		configured:        rxFound && txFound,
	}

	// Register
	plugin.XcIndexes.RegisterName(xConnectPairInput.ReceiveInterface, plugin.XcIndexSeq, &meta)
	plugin.XcIndexSeq++

	return nil
}

// ModifyXConnectPair processes the NB config and propagates it to bin api calls
func (plugin *XConnectConfigurator) ModifyXConnectPair(newConfig *l2.XConnectPairs_XConnectPair, oldConfig *l2.XConnectPairs_XConnectPair) error {
	log.DefaultLogger().Println("Modifying L2 xConnect pair")

	// interfaces
	receiveInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(newConfig.ReceiveInterface)
	if !found {
		log.DefaultLogger().WithField("Interface", newConfig.ReceiveInterface).Error("Receive interface not found.")
		return nil
	}
	newTransmitInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(newConfig.TransmitInterface)
	if !found {
		log.DefaultLogger().WithField("Interface", newConfig.TransmitInterface).Error("New transmit interface not found.")
		return nil
	}
	oldTransmitInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(oldConfig.TransmitInterface)
	if !found {
		log.DefaultLogger().WithField("Interface", newConfig.TransmitInterface).Debug("Old transmit interface not found.")
		oldTransmitInterfaceIndex = 0
		// do not return, not an error
	}
	if oldTransmitInterfaceIndex == newTransmitInterfaceIndex {
		// nothing to update
		return nil
	} else if oldTransmitInterfaceIndex == 0 {
		// create new xConnect only
		err := vppcalls.VppSetL2XConnect(receiveInterfaceIndex, newTransmitInterfaceIndex, plugin.vppChan)
		if err != nil {
			log.DefaultLogger().WithField("Error", err).Error("Failed to set l2xConnect")
			return err
		}
	} else {
		errDel := vppcalls.VppUnsetL2XConnect(receiveInterfaceIndex, oldTransmitInterfaceIndex, plugin.vppChan)
		if errDel != nil {
			log.DefaultLogger().WithField("Error", errDel).Error("Failed to remove l2xConnect")
			return errDel
		}
		errCreate := vppcalls.VppSetL2XConnect(receiveInterfaceIndex, newTransmitInterfaceIndex, plugin.vppChan)
		if errCreate != nil {
			log.DefaultLogger().WithField("Error", errCreate).Error("Failed to set l2xConnect")
			return errCreate
		}
	}

	meta := XConnectMeta{
		TransmitInterface: newConfig.TransmitInterface,
	}
	plugin.XcIndexes.RegisterName(newConfig.ReceiveInterface, receiveInterfaceIndex, &meta)
	plugin.XcIndexSeq++

	return nil
}

// DeleteXConnectPair process the NB config and propagates it to bin api calls
func (plugin *XConnectConfigurator) DeleteXConnectPair(xConnectPairInput *l2.XConnectPairs_XConnectPair) error {
	return plugin.deleteL2XConnectPair(xConnectPairInput.ReceiveInterface, xConnectPairInput.TransmitInterface)
}

// LookupXConnectPairs registers missing l2 xConnect pairs
func (plugin *XConnectConfigurator) LookupXConnectPairs() error {
	req := &l2ba.L2XconnectDump{}
	reqContext := plugin.vppChan.SendMultiRequest(req)
	var index uint32 = 1
	for {
		msg := &l2ba.L2XconnectDetails{}
		stop, err := reqContext.ReceiveReply(msg)
		if err != nil {
			log.DefaultLogger().Error(err)
			return err
		}
		if stop {
			break
		}
		// Store name if missing
		_, _, found := plugin.XcIndexes.LookupName(index)
		xcIdentifier := string(msg.RxSwIfIndex)
		if !found {
			log.DefaultLogger().WithFields(log.Fields{"Name": xcIdentifier, "Index": index}).Debug("L2 xConnect pair registered.")
			plugin.XcIndexes.RegisterName(xcIdentifier, index, nil)
		} else {
			log.DefaultLogger().WithFields(log.Fields{"Name": xcIdentifier, "Index": index}).Debug("L2 xConnect pair already registered.")
		}
		index++
	}

	return nil
}

// ResolveCreatedInterface configures xconnect pairs that use the interface as rx or tx and have not been configured yet
func (plugin *XConnectConfigurator) ResolveCreatedInterface(interfaceName string, interfaceIndex uint32) error {
	log.DefaultLogger().Println("Resolving L2 xConnect pairs for created interface ", interfaceName)
	var err error

	// lookup for the interface in rx interfaces
	err = plugin.resolveRxInterface(interfaceName, true)

	// lookup for the interface in tx interfaces
	rxIfs := plugin.XcIndexes.LookupNameByMetadata("TransmitInterface", interfaceName)
	for _, rxIf := range rxIfs {
		err = plugin.resolveRxInterface(rxIf, true)
	}

	return err
}

// ResolveDeletedInterface deltes xconnect pairs that have not been deleted yet and use the interface as rx or tx
func (plugin *XConnectConfigurator) ResolveDeletedInterface(interfaceName string) error {
	log.DefaultLogger().Println("Resolving L2 xConnect pairs for deleted interface ", interfaceName)

	var err error

	// lookup for the interface in rx interfaces
	err = plugin.resolveRxInterface(interfaceName, false)

	// lookup for the interface in tx interfaces
	rxIfs := plugin.XcIndexes.LookupNameByMetadata("TransmitInterface", interfaceName)
	for _, rxIf := range rxIfs {
		err = plugin.resolveRxInterface(rxIf, false)
	}

	return err
}

// resolveRxInterface creates/deletes 2sxconnect in XcIndexes that has not been created yet/need to be deleted
// and have the rx interface name matching with the provided argument
func (plugin *XConnectConfigurator) resolveRxInterface(rxIfName string, create bool) error {
	var err error

	_, meta, exists := plugin.XcIndexes.LookupIdx(rxIfName)
	if exists {
		meta := meta.(*XConnectMeta)
		if create {
			// the l2xconn needs to be created
			if !meta.configured {
				// not yet configured, try to configure now
				err = plugin.configureL2XConnectPair(rxIfName, meta.TransmitInterface)
				if err != nil {
					meta.configured = true
				}
			}
		} else {
			// the l2xconn needs to be delted
			err = plugin.deleteL2XConnectPair(rxIfName, meta.TransmitInterface)
			// meta deleted in deleteL2XConnectPair
		}
	}

	return err
}

func (plugin *XConnectConfigurator) configureL2XConnectPair(rxIf, txIf string) error {
	log.DefaultLogger().Println("Configuring L2 xConnect pair", rxIf, txIf)

	// find interface idx-es
	receiveInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(rxIf)
	if !found {
		log.DefaultLogger().WithField("Interface", rxIf).Warn("Receive interface not found.")
		return fmt.Errorf("Receive interface '%s' not found", rxIf)
	}
	transmitInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(txIf)
	if !found {
		log.DefaultLogger().WithField("Interface", txIf).Warn("Transmit interface not found.")
		return fmt.Errorf("Transmit interface '%s' not found", txIf)
	}

	// configure l2xconnect
	err := vppcalls.VppSetL2XConnect(receiveInterfaceIndex, transmitInterfaceIndex, plugin.vppChan)
	if err != nil {
		log.DefaultLogger().WithField("Error", err).Error("Failed to create l2xConnect")
		return err
	}

	return nil
}

func (plugin *XConnectConfigurator) deleteL2XConnectPair(rxIf, txIf string) error {
	log.DefaultLogger().Println("Deleting L2 xConnect pair", rxIf, txIf)

	// find interface idx-es
	receiveInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(rxIf)
	if !found {
		log.DefaultLogger().WithField("Interface", rxIf).Warn("Receive interface not found.")
		return fmt.Errorf("Receive interface '%s' not found", rxIf)
	}
	transmitInterfaceIndex, _, found := plugin.SwIfIndexes.LookupIdx(txIf)
	if !found {
		log.DefaultLogger().WithField("Interface", txIf).Warn("Transmit interface not found.")
		return fmt.Errorf("Transmit interface '%s' not found", txIf)
	}

	err := vppcalls.VppUnsetL2XConnect(receiveInterfaceIndex, transmitInterfaceIndex, plugin.vppChan)
	if err != nil {
		log.DefaultLogger().WithField("Error", err).Error("Failed to remove l2xConnect")
		return err
	}

	// unregister
	plugin.XcIndexes.UnregisterName(rxIf)
	log.DefaultLogger().WithFields(log.Fields{"RecIface": rxIf, "Idex": receiveInterfaceIndex}).Debug("XConnect pair unregistered.")

	return nil
}
<|endoftext|>


<commit_before><commit_msg>Implemented ToUpper for strings<commit_after><|endoftext|>


<commit_before>package wiki

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"time"

	"github.com/cooper/quiki/wikifier"
	"gopkg.in/src-d/go-billy.v4"

	"github.com/cooper/go-git/v4"
	"github.com/cooper/go-git/v4/config"
	"github.com/cooper/go-git/v4/plumbing"
	"github.com/cooper/go-git/v4/plumbing/object"
	"github.com/pkg/errors"
)

// CommitOpts describes the options for a wiki revision.
type CommitOpts struct {

	// Comment is the commit description.
	Comment string

	// Name is the fullname of the user committing changes.
	Name string

	// Email is the email address of the user committing changes.
	Email string

	// Time is the timestamp to associate with the revision.
	// If unspecified, current time is used.
	Time time.Time
}

// default options for commits made by quiki itself
var quikiCommitOpts = &git.CommitOptions{
	Author: &object.Signature{
		Name:  "quiki",
		Email: "quiki@quiki.app",
		When:  time.Now(),
	},
}

// repo fetches the wiki's git repository, creating it if needed.
func (w *Wiki) repo() (repo *git.Repository, err error) {

	// we've already loaded the repository
	if w._repo != nil {
		repo = w._repo
		return
	}

	// open it
	repo, err = git.PlainOpen(w.Dir())

	// it doesn't exist- let's initialize it
	if err == git.ErrRepositoryNotExists {
		repo, err = w.createRepo()
	} else if err != nil {
		// error in open other than nonexist

		err = errors.Wrap(err, "git:PlainOpen")
		return
	}

	// success
	w._repo = repo
	return
}

// create new repository
func (w *Wiki) createRepo() (repo *git.Repository, err error) {

	/// initialize new repo
	repo, err = git.PlainInit(w.Dir(), false)

	// error in init
	if err != nil {
		return nil, errors.Wrap(err, "git:PlainInit")
	}

	// initialized ok

	// create master branch
	err = repo.CreateBranch(&config.Branch{
		Name:  "master",
		Merge: plumbing.Master,
	})
	if err != nil {
		return nil, errors.Wrap(err, "git:repo:CreateBranch")
	}

	// TODO: default .gitignore

	// add all files and initial commit
	wt, err := repo.Worktree()
	if err != nil {
		return nil, errors.Wrap(err, "git:repo:Worktree")
	}

	// add .
	_, err = wt.Add(".")
	if err != nil {
		return nil, err
	}

	// commit
	_, err = wt.Commit("Initial commit", quikiCommitOpts)
	if err != nil {
		return nil, errors.Wrap(err, "git:worktree:Commit")
	}

	return repo, nil
}

// // IsRepo returns true if the wiki directory is versioned by git.
// func (w *Wiki) IsRepo() bool {
// 	_, err := os.Stat(w.Dir(".git"))
// 	return err == nil
// }

// // InitRepo initializes a git repository for the wiki.
// // If the wiki is already versioned by git, no error is produced.
// func (w *Wiki) InitRepo() error {
// 	if w.IsRepo() {
// 		return nil
// 	}
// 	_, err := git.Init(ginit.Quiet, ginit.Directory(w.Dir())
// 	return err
// }

// BranchNames returns the revision branches available.
func (w *Wiki) BranchNames() ([]string, error) {
	repo, err := w.repo()
	var names []string
	if err != nil {
		return nil, err
	}
	branches, err := repo.Branches()
	if err != nil {
		return nil, err
	}
	branches.ForEach(func(ref *plumbing.Reference) error {
		names = append(names, ref.Name().Short())
		return nil
	})
	return names, nil
}

// ensure a branch exists in git
func (w *Wiki) hasBranch(name string) (bool, error) {
	names, err := w.BranchNames()
	if err != nil {
		return false, err
	}
	for _, branchName := range names {
		if branchName == name {
			return true, nil
		}
	}
	return false, nil
}

// checks out a branch in another directory. returns the directory
func (w *Wiki) checkoutBranch(name string) (string, error) {

	// never checkout master in a linked repo
	if name == "master" {
		return "", errors.New("cannot check out master in a linked repo")
	}

	// TODO: make sure name is a simple string with no path elements

	// make cache/branch/ if needed
	wikifier.MakeDir(filepath.Join(w.Opt.Dir.Cache, "branch"), "")

	// e.g. cache/branch/mybranchname
	targetDir := filepath.Join(w.Opt.Dir.Cache, "branch", name)

	// directory already exists, so I'm good with saying the branch is there
	if fi, err := os.Stat(targetDir); err == nil && fi.IsDir() {
		return targetDir, nil
	}

	repo, err := w.repo()
	if err != nil {
		return "", err
	}

	// create the linked repository
	if _, err = repo.PlainAddWorktree(name, targetDir, &git.AddWorktreeOptions{}); err != nil {
		return "", err
	}

	return targetDir, nil
}

// the "and commit" portion of the *andCommit functions
func (w *Wiki) andCommit(wt *git.Worktree, comment string, commit CommitOpts) error {
	if commit.Comment != "" {
		comment += ": " + commit.Comment
	}

	// time defaults to now
	if commit.Time.IsZero() {
		commit.Time = time.Now()
	}

	// commit
	_, err := wt.Commit(comment, &git.CommitOptions{
		Author: &object.Signature{
			Name:  commit.Name,
			Email: commit.Email,
			When:  commit.Time,
		},
	})
	if err != nil {
		return errors.Wrap(err, "git:worktree:Commit")
	}

	return nil
}

// addAndCommit adds a file and then commits changes
func (w *Wiki) addAndCommit(path string, commit CommitOpts) error {

	// get repo
	repo, err := w.repo()
	if err != nil {
		return err
	}

	// get worktree
	wt, err := repo.Worktree()
	if err != nil {
		return errors.Wrap(err, "git:repo:Worktree")
	}

	// add the file
	_, err = wt.Add(path)
	if err != nil {
		return err
	}

	return w.andCommit(wt, "Update "+filepath.Base(path), commit)
}

// removeAndCommit removes a file and then commits changes
func (w *Wiki) removeAndCommit(path string, commit CommitOpts) error {

	// get repo
	repo, err := w.repo()
	if err != nil {
		return err
	}

	// get worktree
	wt, err := repo.Worktree()
	if err != nil {
		return errors.Wrap(err, "git:repo:Worktree")
	}

	// remove the file
	_, err = wt.Remove(path)
	if err != nil {
		return err
	}

	return w.andCommit(wt, "Delete "+filepath.Base(path), commit)
}

// Branch returns a Wiki instance for this wiki at another branch.
// If the branch does not exist, an error is returned.
func (w *Wiki) Branch(name string) (*Wiki, error) {

	// never checkout master in a linked repo
	if name == "master" {
		return w, nil
	}

	// find branch
	if exist, err := w.hasBranch(name); !exist {
		if err != nil {
			return nil, err
		}
		return nil, git.ErrBranchNotFound
	}

	// check out the branch in cache/branch/<name>;
	// if it already has been checked out, this does nothing
	dir, err := w.checkoutBranch(name)
	if err != nil {
		return nil, err
	}

	// create a new Wiki at this location
	return NewWiki(dir)
}

// NewBranch is like Branch, except it creates the branch at the
// current master revision if it does not yet exist.
func (w *Wiki) NewBranch(name string) (*Wiki, error) {
	repo, err := w.repo()
	if err != nil {
		return nil, err
	}

	// find branch
	if exist, err := w.hasBranch(name); !exist {
		if err != nil {
			return nil, err
		}

		// try to create it
		err := repo.CreateBranch(&config.Branch{
			Name:  name,
			Merge: plumbing.NewBranchReferenceName(name),
		})
		if err != nil {
			return nil, err
		}

		// determine where master is at
		fs := repo.Storer.(interface{ Filesystem() billy.Filesystem }).Filesystem()
		f1, err := fs.Open(fs.Join("refs", "heads", "master"))
		if err != nil {
			return nil, err
		}
		defer f1.Close()
		masterRef, err := ioutil.ReadAll(f1)
		if err != nil {
			return nil, err
		}

		// set refs/heads/<name> to same as master
		f2, err := fs.Create(fs.Join("refs", "heads", name))
		if err != nil {
			return nil, err
		}
		defer f2.Close()
		_, err = fmt.Fprintf(f2, "%s\n", string(masterRef))
		if err != nil {
			return nil, err
		}
	}

	// now that it exists, fetch it
	return w.Branch(name)
}

var branchNameRgx = regexp.MustCompile(`^[\w]+[\w\-/]*[\w]+$`)

// ValidBranchName returns whether a branch name is valid.
//
// quiki branch names may contain word-like characters `\w` and
// forward slash (`/`) but may not start or end with a slash.
//
func ValidBranchName(name string) bool {
	return branchNameRgx.MatchString(name)
}

// WritePage writes a page file.

// WriteFile writes a file in the wiki.
//
// The filename must be relative to the wiki directory.
// If the file does not exist and createOK is false, an error is returned.
// If the file exists and is a symbolic link, an error is returned.
//
// This is a low-level API that allows writing any file within the wiki
// directory, so it should not be utilized directly by frontends.
// Use WritePage, WriteModel, WriteImage, or WriteConfig instead.
//
func (w *Wiki) WriteFile(name string, content []byte, createOK bool, commit CommitOpts) error {
	path := w.UnresolvedAbsFilePath(name)
	fi, err := os.Lstat(path)

	if err != nil {
		// some error occurred

		if os.IsNotExist(err) {
			// file doesn't exist-- only care if createOK is false
			if !createOK {
				return err
			}
		} else {
			// all other errors are always bad
			return err
		}
	} else if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
		// no errors occurred, so file exists
		// check if it's a symlink
		return errors.New("symlink cannot be written with WriteFile")
	}

	// write file all at once
	if err := ioutil.WriteFile(path, content, 0644); err != nil {
		return err
	}

	// commit the change
	return w.addAndCommit(name, commit)
}

// DeleteFile deletes a file in the wiki.
//
// The filename must be relative to the wiki directory.
// If the file does not exist, an error is returned.
//
// This is a low-level API that allows deleting any file within the wiki
// directory, so it should not be utilized directly by frontends.
// Use DeletePage, DeleteModel, or DeleteImage instead.
//
func (w *Wiki) DeleteFile(name string, commit CommitOpts) error {

	// error running lstat on file, might not exist or whatev
	path := w.UnresolvedAbsFilePath(name)
	_, err := os.Lstat(path)
	if err != nil {
		return err
	}

	// delete the file and commit the change
	return w.removeAndCommit(path, commit)
}
<commit_msg>wiki/revision: DeleteFile clarify what happens to symlinks<commit_after>package wiki

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"time"

	"github.com/cooper/quiki/wikifier"
	"gopkg.in/src-d/go-billy.v4"

	"github.com/cooper/go-git/v4"
	"github.com/cooper/go-git/v4/config"
	"github.com/cooper/go-git/v4/plumbing"
	"github.com/cooper/go-git/v4/plumbing/object"
	"github.com/pkg/errors"
)

// CommitOpts describes the options for a wiki revision.
type CommitOpts struct {

	// Comment is the commit description.
	Comment string

	// Name is the fullname of the user committing changes.
	Name string

	// Email is the email address of the user committing changes.
	Email string

	// Time is the timestamp to associate with the revision.
	// If unspecified, current time is used.
	Time time.Time
}

// default options for commits made by quiki itself
var quikiCommitOpts = &git.CommitOptions{
	Author: &object.Signature{
		Name:  "quiki",
		Email: "quiki@quiki.app",
		When:  time.Now(),
	},
}

// repo fetches the wiki's git repository, creating it if needed.
func (w *Wiki) repo() (repo *git.Repository, err error) {

	// we've already loaded the repository
	if w._repo != nil {
		repo = w._repo
		return
	}

	// open it
	repo, err = git.PlainOpen(w.Dir())

	// it doesn't exist- let's initialize it
	if err == git.ErrRepositoryNotExists {
		repo, err = w.createRepo()
	} else if err != nil {
		// error in open other than nonexist

		err = errors.Wrap(err, "git:PlainOpen")
		return
	}

	// success
	w._repo = repo
	return
}

// create new repository
func (w *Wiki) createRepo() (repo *git.Repository, err error) {

	/// initialize new repo
	repo, err = git.PlainInit(w.Dir(), false)

	// error in init
	if err != nil {
		return nil, errors.Wrap(err, "git:PlainInit")
	}

	// initialized ok

	// create master branch
	err = repo.CreateBranch(&config.Branch{
		Name:  "master",
		Merge: plumbing.Master,
	})
	if err != nil {
		return nil, errors.Wrap(err, "git:repo:CreateBranch")
	}

	// TODO: default .gitignore

	// add all files and initial commit
	wt, err := repo.Worktree()
	if err != nil {
		return nil, errors.Wrap(err, "git:repo:Worktree")
	}

	// add .
	_, err = wt.Add(".")
	if err != nil {
		return nil, err
	}

	// commit
	_, err = wt.Commit("Initial commit", quikiCommitOpts)
	if err != nil {
		return nil, errors.Wrap(err, "git:worktree:Commit")
	}

	return repo, nil
}

// // IsRepo returns true if the wiki directory is versioned by git.
// func (w *Wiki) IsRepo() bool {
// 	_, err := os.Stat(w.Dir(".git"))
// 	return err == nil
// }

// // InitRepo initializes a git repository for the wiki.
// // If the wiki is already versioned by git, no error is produced.
// func (w *Wiki) InitRepo() error {
// 	if w.IsRepo() {
// 		return nil
// 	}
// 	_, err := git.Init(ginit.Quiet, ginit.Directory(w.Dir())
// 	return err
// }

// BranchNames returns the revision branches available.
func (w *Wiki) BranchNames() ([]string, error) {
	repo, err := w.repo()
	var names []string
	if err != nil {
		return nil, err
	}
	branches, err := repo.Branches()
	if err != nil {
		return nil, err
	}
	branches.ForEach(func(ref *plumbing.Reference) error {
		names = append(names, ref.Name().Short())
		return nil
	})
	return names, nil
}

// ensure a branch exists in git
func (w *Wiki) hasBranch(name string) (bool, error) {
	names, err := w.BranchNames()
	if err != nil {
		return false, err
	}
	for _, branchName := range names {
		if branchName == name {
			return true, nil
		}
	}
	return false, nil
}

// checks out a branch in another directory. returns the directory
func (w *Wiki) checkoutBranch(name string) (string, error) {

	// never checkout master in a linked repo
	if name == "master" {
		return "", errors.New("cannot check out master in a linked repo")
	}

	// TODO: make sure name is a simple string with no path elements

	// make cache/branch/ if needed
	wikifier.MakeDir(filepath.Join(w.Opt.Dir.Cache, "branch"), "")

	// e.g. cache/branch/mybranchname
	targetDir := filepath.Join(w.Opt.Dir.Cache, "branch", name)

	// directory already exists, so I'm good with saying the branch is there
	if fi, err := os.Stat(targetDir); err == nil && fi.IsDir() {
		return targetDir, nil
	}

	repo, err := w.repo()
	if err != nil {
		return "", err
	}

	// create the linked repository
	if _, err = repo.PlainAddWorktree(name, targetDir, &git.AddWorktreeOptions{}); err != nil {
		return "", err
	}

	return targetDir, nil
}

// the "and commit" portion of the *andCommit functions
func (w *Wiki) andCommit(wt *git.Worktree, comment string, commit CommitOpts) error {
	if commit.Comment != "" {
		comment += ": " + commit.Comment
	}

	// time defaults to now
	if commit.Time.IsZero() {
		commit.Time = time.Now()
	}

	// commit
	_, err := wt.Commit(comment, &git.CommitOptions{
		Author: &object.Signature{
			Name:  commit.Name,
			Email: commit.Email,
			When:  commit.Time,
		},
	})
	if err != nil {
		return errors.Wrap(err, "git:worktree:Commit")
	}

	return nil
}

// addAndCommit adds a file and then commits changes
func (w *Wiki) addAndCommit(path string, commit CommitOpts) error {

	// get repo
	repo, err := w.repo()
	if err != nil {
		return err
	}

	// get worktree
	wt, err := repo.Worktree()
	if err != nil {
		return errors.Wrap(err, "git:repo:Worktree")
	}

	// add the file
	_, err = wt.Add(path)
	if err != nil {
		return err
	}

	return w.andCommit(wt, "Update "+filepath.Base(path), commit)
}

// removeAndCommit removes a file and then commits changes
func (w *Wiki) removeAndCommit(path string, commit CommitOpts) error {

	// get repo
	repo, err := w.repo()
	if err != nil {
		return err
	}

	// get worktree
	wt, err := repo.Worktree()
	if err != nil {
		return errors.Wrap(err, "git:repo:Worktree")
	}

	// remove the file
	_, err = wt.Remove(path)
	if err != nil {
		return err
	}

	return w.andCommit(wt, "Delete "+filepath.Base(path), commit)
}

// Branch returns a Wiki instance for this wiki at another branch.
// If the branch does not exist, an error is returned.
func (w *Wiki) Branch(name string) (*Wiki, error) {

	// never checkout master in a linked repo
	if name == "master" {
		return w, nil
	}

	// find branch
	if exist, err := w.hasBranch(name); !exist {
		if err != nil {
			return nil, err
		}
		return nil, git.ErrBranchNotFound
	}

	// check out the branch in cache/branch/<name>;
	// if it already has been checked out, this does nothing
	dir, err := w.checkoutBranch(name)
	if err != nil {
		return nil, err
	}

	// create a new Wiki at this location
	return NewWiki(dir)
}

// NewBranch is like Branch, except it creates the branch at the
// current master revision if it does not yet exist.
func (w *Wiki) NewBranch(name string) (*Wiki, error) {
	repo, err := w.repo()
	if err != nil {
		return nil, err
	}

	// find branch
	if exist, err := w.hasBranch(name); !exist {
		if err != nil {
			return nil, err
		}

		// try to create it
		err := repo.CreateBranch(&config.Branch{
			Name:  name,
			Merge: plumbing.NewBranchReferenceName(name),
		})
		if err != nil {
			return nil, err
		}

		// determine where master is at
		fs := repo.Storer.(interface{ Filesystem() billy.Filesystem }).Filesystem()
		f1, err := fs.Open(fs.Join("refs", "heads", "master"))
		if err != nil {
			return nil, err
		}
		defer f1.Close()
		masterRef, err := ioutil.ReadAll(f1)
		if err != nil {
			return nil, err
		}

		// set refs/heads/<name> to same as master
		f2, err := fs.Create(fs.Join("refs", "heads", name))
		if err != nil {
			return nil, err
		}
		defer f2.Close()
		_, err = fmt.Fprintf(f2, "%s\n", string(masterRef))
		if err != nil {
			return nil, err
		}
	}

	// now that it exists, fetch it
	return w.Branch(name)
}

var branchNameRgx = regexp.MustCompile(`^[\w]+[\w\-/]*[\w]+$`)

// ValidBranchName returns whether a branch name is valid.
//
// quiki branch names may contain word-like characters `\w` and
// forward slash (`/`) but may not start or end with a slash.
//
func ValidBranchName(name string) bool {
	return branchNameRgx.MatchString(name)
}

// WritePage writes a page file.

// WriteFile writes a file in the wiki.
//
// The filename must be relative to the wiki directory.
// If the file does not exist and createOK is false, an error is returned.
// If the file exists and is a symbolic link, an error is returned.
//
// This is a low-level API that allows writing any file within the wiki
// directory, so it should not be utilized directly by frontends.
// Use WritePage, WriteModel, WriteImage, or WriteConfig instead.
//
func (w *Wiki) WriteFile(name string, content []byte, createOK bool, commit CommitOpts) error {
	path := w.UnresolvedAbsFilePath(name)
	fi, err := os.Lstat(path)

	if err != nil {
		// some error occurred

		if os.IsNotExist(err) {
			// file doesn't exist-- only care if createOK is false
			if !createOK {
				return err
			}
		} else {
			// all other errors are always bad
			return err
		}
	} else if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
		// no errors occurred, so file exists
		// check if it's a symlink
		return errors.New("symlink cannot be written with WriteFile")
	}

	// write file all at once
	if err := ioutil.WriteFile(path, content, 0644); err != nil {
		return err
	}

	// commit the change
	return w.addAndCommit(name, commit)
}

// DeleteFile deletes a file in the wiki.
//
// The filename must be relative to the wiki directory.
// If the file does not exist, an error is returned.
// If the file exists and is a symbolic link, the link itself is deleted,
// not the target file.
//
// This is a low-level API that allows deleting any file within the wiki
// directory, so it should not be utilized directly by frontends.
// Use DeletePage, DeleteModel, or DeleteImage instead.
//
func (w *Wiki) DeleteFile(name string, commit CommitOpts) error {

	// error running lstat on file, might not exist or whatev
	path := w.UnresolvedAbsFilePath(name)
	_, err := os.Lstat(path)
	if err != nil {
		return err
	}

	// delete the file and commit the change
	return w.removeAndCommit(path, commit)
}
<|endoftext|>


<commit_before>package worker

import (
	"flag"
	"io"
	"net"
	"net/rpc"

	"github.com/dgraph-io/dgraph/conn"
	"github.com/dgraph-io/dgraph/posting"
	"github.com/dgraph-io/dgraph/store"
	"github.com/dgraph-io/dgraph/task"
	"github.com/dgraph-io/dgraph/x"
	"github.com/dgryski/go-farm"
	"github.com/google/flatbuffers/go"
)

var workerPort = flag.String("workerport", ":12345",
	"Port used by worker for internal communication.")

var glog = x.Log("worker")
var dataStore, uidStore *store.Store
var pools []*conn.Pool
var numInstances, instanceIdx uint64

var addrs []string

func Init(ps, uStore *store.Store, workerList []string, idx, numInst uint64) {
	dataStore = ps
	uidStore = uStore
	addrs = workerList
	instanceIdx = idx
	numInstances = numInst
}

func Connect() {
	w := new(Worker)
	if err := rpc.Register(w); err != nil {
		glog.Fatal(err)
	}
	if err := runServer(*workerPort); err != nil {
		glog.Fatal(err)
	}

	for _, addr := range addrs {
		if len(addr) == 0 {
			continue
		}
		pool := conn.NewPool(addr, 5)
		query := new(conn.Query)
		query.Data = []byte("hello")
		reply := new(conn.Reply)
		if err := pool.Call("Worker.Hello", query, reply); err != nil {
			glog.WithField("call", "Worker.Hello").Fatal(err)
		}
		glog.WithField("reply", string(reply.Data)).WithField("addr", addr).
			Info("Got reply from server")
		pools = append(pools, pool)
	}

	glog.Info("Server started. Clients connected.")
}

func ProcessTaskOverNetwork(qu []byte) (result []byte, rerr error) {
	uo := flatbuffers.GetUOffsetT(qu)
	q := new(task.Query)
	q.Init(qu, uo)

	attr := string(q.Attr())
	idx := farm.Fingerprint64([]byte(attr)) % numInstances
	if idx == instanceIdx {
		return ProcessTask(qu)
	} else {
		pool := pools[idx]
		addr := addrs[idx]
		query := new(conn.Query)
		query.Data = qu
		reply := new(conn.Reply)
		if err := pool.Call("Worker.ServeTask", query, reply); err != nil {
			glog.WithField("call", "Worker.ServeTask").Fatal(err)
		}
		glog.WithField("reply", string(reply.Data)).WithField("addr", addr).
			Info("Got reply from server")

		return reply.Data, nil
	}
}

func ProcessTask(query []byte) (result []byte, rerr error) {
	uo := flatbuffers.GetUOffsetT(query)
	q := new(task.Query)
	q.Init(query, uo)

	b := flatbuffers.NewBuilder(0)
	voffsets := make([]flatbuffers.UOffsetT, q.UidsLength())
	uoffsets := make([]flatbuffers.UOffsetT, q.UidsLength())

	attr := string(q.Attr())

	for i := 0; i < q.UidsLength(); i++ {
		uid := q.Uids(i)
		key := posting.Key(uid, attr)
		pl := posting.GetOrCreate(key, dataStore)

		var valoffset flatbuffers.UOffsetT
		if val, err := pl.Value(); err != nil {
			valoffset = b.CreateByteVector(x.Nilbyte)
		} else {
			valoffset = b.CreateByteVector(val)
		}
		task.ValueStart(b)
		task.ValueAddVal(b, valoffset)
		voffsets[i] = task.ValueEnd(b)

		ulist := pl.GetUids()
		uoffsets[i] = x.UidlistOffset(b, ulist)
	}
	task.ResultStartValuesVector(b, len(voffsets))
	for i := len(voffsets) - 1; i >= 0; i-- {
		b.PrependUOffsetT(voffsets[i])
	}
	valuesVent := b.EndVector(len(voffsets))

	task.ResultStartUidmatrixVector(b, len(uoffsets))
	for i := len(uoffsets) - 1; i >= 0; i-- {
		b.PrependUOffsetT(uoffsets[i])
	}
	matrixVent := b.EndVector(len(uoffsets))

	task.ResultStart(b)
	task.ResultAddValues(b, valuesVent)
	task.ResultAddUidmatrix(b, matrixVent)
	rend := task.ResultEnd(b)
	b.Finish(rend)
	return b.Bytes[b.Head():], nil
}

func NewQuery(attr string, uids []uint64) []byte {
	b := flatbuffers.NewBuilder(0)
	task.QueryStartUidsVector(b, len(uids))
	for i := len(uids) - 1; i >= 0; i-- {
		b.PrependUint64(uids[i])
	}
	vend := b.EndVector(len(uids))

	ao := b.CreateString(attr)
	task.QueryStart(b)
	task.QueryAddAttr(b, ao)
	task.QueryAddUids(b, vend)
	qend := task.QueryEnd(b)
	b.Finish(qend)
	return b.Bytes[b.Head():]
}

type Worker struct {
}

func (w *Worker) Hello(query *conn.Query, reply *conn.Reply) error {
	if string(query.Data) == "hello" {
		reply.Data = []byte("Oh hello there!")
	} else {
		reply.Data = []byte("Hey stranger!")
	}
	return nil
}

func (w *Worker) ServeTask(query *conn.Query, reply *conn.Reply) (rerr error) {
	uo := flatbuffers.GetUOffsetT(query.Data)
	q := new(task.Query)
	q.Init(query.Data, uo)

	attr := string(q.Attr())
	if farm.Fingerprint64([]byte(attr))%numInstances == instanceIdx {
		reply.Data, rerr = ProcessTask(query.Data)
	} else {
		glog.Fatalf("Request sent to wrong server")
	}
	return rerr
}

func serveRequests(irwc io.ReadWriteCloser) {
	for {
		sc := &conn.ServerCodec{
			Rwc: irwc,
		}
		rpc.ServeRequest(sc)
	}
}

func runServer(address string) error {
	ln, err := net.Listen("tcp", address)
	if err != nil {
		glog.Fatalf("While running server: %v", err)
		return err
	}
	glog.WithField("address", ln.Addr()).Info("Worker listening")

	go func() {
		for {
			cxn, err := ln.Accept()
			if err != nil {
				glog.Fatalf("listen(%q): %s\n", address, err)
				return
			}
			glog.WithField("local", cxn.LocalAddr()).
				WithField("remote", cxn.RemoteAddr()).
				Debug("Worker accepted connection")
			go serveRequests(cxn)
		}
	}()
	return nil
}
<commit_msg>handled uidStore querying which is on instance 0<commit_after>package worker

import (
	"flag"
	"io"
	"net"
	"net/rpc"

	"github.com/dgraph-io/dgraph/conn"
	"github.com/dgraph-io/dgraph/posting"
	"github.com/dgraph-io/dgraph/store"
	"github.com/dgraph-io/dgraph/task"
	"github.com/dgraph-io/dgraph/x"
	"github.com/dgryski/go-farm"
	"github.com/google/flatbuffers/go"
)

var workerPort = flag.String("workerport", ":12345",
	"Port used by worker for internal communication.")

var glog = x.Log("worker")
var dataStore, uidStore *store.Store
var pools []*conn.Pool
var numInstances, instanceIdx uint64

var addrs []string

func Init(ps, uStore *store.Store, workerList []string, idx, numInst uint64) {
	dataStore = ps
	uidStore = uStore
	addrs = workerList
	instanceIdx = idx
	numInstances = numInst
}

func Connect() {
	w := new(Worker)
	if err := rpc.Register(w); err != nil {
		glog.Fatal(err)
	}
	if err := runServer(*workerPort); err != nil {
		glog.Fatal(err)
	}

	for _, addr := range addrs {
		if len(addr) == 0 {
			continue
		}
		pool := conn.NewPool(addr, 5)
		query := new(conn.Query)
		query.Data = []byte("hello")
		reply := new(conn.Reply)
		if err := pool.Call("Worker.Hello", query, reply); err != nil {
			glog.WithField("call", "Worker.Hello").Fatal(err)
		}
		glog.WithField("reply", string(reply.Data)).WithField("addr", addr).
			Info("Got reply from server")
		pools = append(pools, pool)
	}

	glog.Info("Server started. Clients connected.")
}

func ProcessTaskOverNetwork(qu []byte) (result []byte, rerr error) {
	uo := flatbuffers.GetUOffsetT(qu)
	q := new(task.Query)
	q.Init(qu, uo)

	attr := string(q.Attr())
	idx := farm.Fingerprint64([]byte(attr)) % numInstances

	if attr == "_xid_" || attr == "_uid_" {
		if instanceIdx == 0 {
			return ProcessTask(qu)
		} else { // Send the request to instance 0 which has uidstore
			pool := pools[0]
			addr := addrs[0]
			query := new(conn.Query)
			query.Data = qu
			reply := new(conn.Reply)
			if err := pool.Call("Worker.ServeTask", query, reply); err != nil {
				glog.WithField("call", "Worker.ServeTask").Fatal(err)
			}
			glog.WithField("reply", string(reply.Data)).WithField("addr", addr).
				Info("Got reply from server")

			return reply.Data, nil
		}
	} else {
		if idx == instanceIdx {
			return ProcessTask(qu)
		} else {
			pool := pools[idx]
			addr := addrs[idx]
			query := new(conn.Query)
			query.Data = qu
			reply := new(conn.Reply)
			if err := pool.Call("Worker.ServeTask", query, reply); err != nil {
				glog.WithField("call", "Worker.ServeTask").Fatal(err)
			}
			glog.WithField("reply", string(reply.Data)).WithField("addr", addr).
				Info("Got reply from server")

			return reply.Data, nil
		}
	}
}

func ProcessTask(query []byte) (result []byte, rerr error) {
	uo := flatbuffers.GetUOffsetT(query)
	q := new(task.Query)
	q.Init(query, uo)

	b := flatbuffers.NewBuilder(0)
	voffsets := make([]flatbuffers.UOffsetT, q.UidsLength())
	uoffsets := make([]flatbuffers.UOffsetT, q.UidsLength())

	attr := string(q.Attr())

	for i := 0; i < q.UidsLength(); i++ {
		uid := q.Uids(i)
		key := posting.Key(uid, attr)
		pl := posting.GetOrCreate(key, dataStore)

		var valoffset flatbuffers.UOffsetT
		if val, err := pl.Value(); err != nil {
			valoffset = b.CreateByteVector(x.Nilbyte)
		} else {
			valoffset = b.CreateByteVector(val)
		}
		task.ValueStart(b)
		task.ValueAddVal(b, valoffset)
		voffsets[i] = task.ValueEnd(b)

		ulist := pl.GetUids()
		uoffsets[i] = x.UidlistOffset(b, ulist)
	}
	task.ResultStartValuesVector(b, len(voffsets))
	for i := len(voffsets) - 1; i >= 0; i-- {
		b.PrependUOffsetT(voffsets[i])
	}
	valuesVent := b.EndVector(len(voffsets))

	task.ResultStartUidmatrixVector(b, len(uoffsets))
	for i := len(uoffsets) - 1; i >= 0; i-- {
		b.PrependUOffsetT(uoffsets[i])
	}
	matrixVent := b.EndVector(len(uoffsets))

	task.ResultStart(b)
	task.ResultAddValues(b, valuesVent)
	task.ResultAddUidmatrix(b, matrixVent)
	rend := task.ResultEnd(b)
	b.Finish(rend)
	return b.Bytes[b.Head():], nil
}

func NewQuery(attr string, uids []uint64) []byte {
	b := flatbuffers.NewBuilder(0)
	task.QueryStartUidsVector(b, len(uids))
	for i := len(uids) - 1; i >= 0; i-- {
		b.PrependUint64(uids[i])
	}
	vend := b.EndVector(len(uids))

	ao := b.CreateString(attr)
	task.QueryStart(b)
	task.QueryAddAttr(b, ao)
	task.QueryAddUids(b, vend)
	qend := task.QueryEnd(b)
	b.Finish(qend)
	return b.Bytes[b.Head():]
}

type Worker struct {
}

func (w *Worker) Hello(query *conn.Query, reply *conn.Reply) error {
	if string(query.Data) == "hello" {
		reply.Data = []byte("Oh hello there!")
	} else {
		reply.Data = []byte("Hey stranger!")
	}
	return nil
}

func (w *Worker) ServeTask(query *conn.Query, reply *conn.Reply) (rerr error) {
	uo := flatbuffers.GetUOffsetT(query.Data)
	q := new(task.Query)
	q.Init(query.Data, uo)

	attr := string(q.Attr())
	if farm.Fingerprint64([]byte(attr))%numInstances == instanceIdx {
		reply.Data, rerr = ProcessTask(query.Data)
	} else {
		glog.Fatalf("Request sent to wrong server")
	}
	return rerr
}

func serveRequests(irwc io.ReadWriteCloser) {
	for {
		sc := &conn.ServerCodec{
			Rwc: irwc,
		}
		rpc.ServeRequest(sc)
	}
}

func runServer(address string) error {
	ln, err := net.Listen("tcp", address)
	if err != nil {
		glog.Fatalf("While running server: %v", err)
		return err
	}
	glog.WithField("address", ln.Addr()).Info("Worker listening")

	go func() {
		for {
			cxn, err := ln.Accept()
			if err != nil {
				glog.Fatalf("listen(%q): %s\n", address, err)
				return
			}
			glog.WithField("local", cxn.LocalAddr()).
				WithField("remote", cxn.RemoteAddr()).
				Debug("Worker accepted connection")
			go serveRequests(cxn)
		}
	}()
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2020 DSR Corporation
//
// 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 types

import (
	"encoding/json"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

/*
	Account Role
*/

type AccountRole string

const (
	Vendor              AccountRole = "Vendor"
	CertificationCenter AccountRole = "CertificationCenter"
	Trustee             AccountRole = "Trustee"
	NodeAdmin           AccountRole = "NodeAdmin"
)

var Roles = AccountRoles{Vendor, CertificationCenter, Trustee, NodeAdmin}

func (role AccountRole) Validate() error {
	for _, r := range Roles {
		if role == r {
			return nil
		}
	}

	return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "Invalid Account Role: %v. Supported roles: [%v]", role, Roles)
}

/*
	List of Account Roles
*/

type AccountRoles []AccountRole

/*
	Account
*/

type DCLAccountI interface {
	authtypes.AccountI

	GetRoles() []AccountRole
	GetVendorID() int32
	GetApprovals() []*Grant
}

// NewAccount creates a new Account object.
func NewAccount(ba *authtypes.BaseAccount, roles AccountRoles, approvals []*Grant, vendorID int32) *Account {
	return &Account{
		BaseAccount: ba,
		Roles:       roles,
		Approvals:   approvals,
		VendorID:    vendorID,
	}
}

// Validate checks for errors on the vesting and module account parameters.
func (acc Account) Validate() error {
	err := acc.BaseAccount.Validate()
	if err != nil {
		return err
	}

	for _, role := range acc.Roles {
		if err := role.Validate(); err != nil {
			return err
		}
	}

	// If creating an account with Vendor Role, we need to have a associated VendorID
	if acc.HasRole(Vendor) && acc.VendorID <= 0 {
		return ErrMissingVendorIDForVendorAccount()
	}

	return nil
}

func (acc Account) GetRoles() []AccountRole {
	return acc.Roles
}

func (acc Account) GetApprovals() []*Grant {
	return acc.Approvals
}

func (acc Account) GetVendorID() int32 {
	return acc.VendorID
}

func (acc Account) HasRole(targetRole AccountRole) bool {
	for _, role := range acc.Roles {
		if role == targetRole {
			return true
		}
	}

	return false
}

func (acc Account) String() string {
	out, _ := acc.MarshalYAML()
	return out.(string)
}

/*
	Pending Account
*/

// NewPendingAccount creates a new PendingAccount object.
func NewPendingAccount(acc *Account, approval sdk.AccAddress, info string, time int64) *PendingAccount {
	pendingAccount := &PendingAccount{
		Account: acc,
	}

	pendingAccount.Approvals = []*Grant{
		{
			Address: approval.String(),
			Time:    time,
			Info:    info,
		},
	}

	return pendingAccount
}

func (acc PendingAccount) HasApprovalFrom(address sdk.AccAddress) bool {
	addrStr := address.String()
	for _, approval := range acc.Approvals {
		if approval.Address == addrStr {
			return true
		}
	}

	return false
}

/*
	Pending Account Revocation
*/

// NewPendingAccountRevocation creates a new PendingAccountRevocation object.
func NewPendingAccountRevocation(address sdk.AccAddress,
	info string, time int64, approval sdk.AccAddress) PendingAccountRevocation {
	pendingAccountRevocation := PendingAccountRevocation{
		Address: address.String(),
	}
	pendingAccountRevocation.Approvals = []*Grant{
		{
			Address: approval.String(),
			Time:    time,
			Info:    info,
		},
	}
	return pendingAccountRevocation
}

// String implements fmt.Stringer.
func (revoc PendingAccountRevocation) String() string {
	bytes, err := json.Marshal(revoc)
	if err != nil {
		panic(err)
	}

	return string(bytes)
}

// Validate checks for errors on the vesting and module account parameters.
func (revoc PendingAccountRevocation) Validate() error {
	if revoc.Address == "" {
		return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest,
			"Invalid Pending Account Revocation: Value: %s. Error: Missing Address", revoc.Address,
		)
	}

	return nil
}

func (revoc PendingAccountRevocation) HasRevocationFrom(address sdk.AccAddress) bool {
	addrStr := address.String()
	for _, approvals := range revoc.Approvals {
		if approvals.Address == addrStr {
			return true
		}
	}

	return false
}
<commit_msg>Add function for create a new object type of RevokedAccount<commit_after>// Copyright 2020 DSR Corporation
//
// 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 types

import (
	"encoding/json"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

/*
	Account Role
*/

type AccountRole string

const (
	Vendor              AccountRole = "Vendor"
	CertificationCenter AccountRole = "CertificationCenter"
	Trustee             AccountRole = "Trustee"
	NodeAdmin           AccountRole = "NodeAdmin"
)

var Roles = AccountRoles{Vendor, CertificationCenter, Trustee, NodeAdmin}

func (role AccountRole) Validate() error {
	for _, r := range Roles {
		if role == r {
			return nil
		}
	}

	return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "Invalid Account Role: %v. Supported roles: [%v]", role, Roles)
}

/*
	List of Account Roles
*/

type AccountRoles []AccountRole

/*
	Account
*/

type DCLAccountI interface {
	authtypes.AccountI

	GetRoles() []AccountRole
	GetVendorID() int32
	GetApprovals() []*Grant
}

// NewAccount creates a new Account object.
func NewAccount(ba *authtypes.BaseAccount, roles AccountRoles, approvals []*Grant, vendorID int32) *Account {
	return &Account{
		BaseAccount: ba,
		Roles:       roles,
		Approvals:   approvals,
		VendorID:    vendorID,
	}
}

// Validate checks for errors on the vesting and module account parameters.
func (acc Account) Validate() error {
	err := acc.BaseAccount.Validate()
	if err != nil {
		return err
	}

	for _, role := range acc.Roles {
		if err := role.Validate(); err != nil {
			return err
		}
	}

	// If creating an account with Vendor Role, we need to have a associated VendorID
	if acc.HasRole(Vendor) && acc.VendorID <= 0 {
		return ErrMissingVendorIDForVendorAccount()
	}

	return nil
}

func (acc Account) GetRoles() []AccountRole {
	return acc.Roles
}

func (acc Account) GetApprovals() []*Grant {
	return acc.Approvals
}

func (acc Account) GetVendorID() int32 {
	return acc.VendorID
}

func (acc Account) HasRole(targetRole AccountRole) bool {
	for _, role := range acc.Roles {
		if role == targetRole {
			return true
		}
	}

	return false
}

func (acc Account) String() string {
	out, _ := acc.MarshalYAML()
	return out.(string)
}

/*
	Pending Account
*/

// NewPendingAccount creates a new PendingAccount object.
func NewPendingAccount(acc *Account, approval sdk.AccAddress, info string, time int64) *PendingAccount {
	pendingAccount := &PendingAccount{
		Account: acc,
	}

	pendingAccount.Approvals = []*Grant{
		{
			Address: approval.String(),
			Time:    time,
			Info:    info,
		},
	}

	return pendingAccount
}

// NewRevokedAccount creates a new RevokedAccount object
func NewRevokedAccount(acc *Account, approvals []*Grant, info string, time int64) *RevokedAccount {
	revokedAccount := &RevokedAccount{
		Account: acc,
	}

	revokedAccount.RevokeApprovals = approvals

	return revokedAccount
}

func (acc PendingAccount) HasApprovalFrom(address sdk.AccAddress) bool {
	addrStr := address.String()
	for _, approval := range acc.Approvals {
		if approval.Address == addrStr {
			return true
		}
	}

	return false
}

/*
	Pending Account Revocation
*/

// NewPendingAccountRevocation creates a new PendingAccountRevocation object.
func NewPendingAccountRevocation(address sdk.AccAddress,
	info string, time int64, approval sdk.AccAddress) PendingAccountRevocation {
	pendingAccountRevocation := PendingAccountRevocation{
		Address: address.String(),
	}
	pendingAccountRevocation.Approvals = []*Grant{
		{
			Address: approval.String(),
			Time:    time,
			Info:    info,
		},
	}
	return pendingAccountRevocation
}

// String implements fmt.Stringer.
func (revoc PendingAccountRevocation) String() string {
	bytes, err := json.Marshal(revoc)
	if err != nil {
		panic(err)
	}

	return string(bytes)
}

// Validate checks for errors on the vesting and module account parameters.
func (revoc PendingAccountRevocation) Validate() error {
	if revoc.Address == "" {
		return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest,
			"Invalid Pending Account Revocation: Value: %s. Error: Missing Address", revoc.Address,
		)
	}

	return nil
}

func (revoc PendingAccountRevocation) HasRevocationFrom(address sdk.AccAddress) bool {
	addrStr := address.String()
	for _, approvals := range revoc.Approvals {
		if approvals.Address == addrStr {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>// Copyright 2019 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 queue

import (
	"context"
	"testing"
	"time"

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

func TestChannelQueue(t *testing.T) {
	handleChan := make(chan *testData)
	handle := func(data ...Data) {
		for _, datum := range data {
			testDatum := datum.(*testData)
			handleChan <- testDatum
		}
	}

	nilFn := func(_ context.Context, _ func()) {}

	queue, err := NewChannelQueue(handle,
		ChannelQueueConfiguration{
			WorkerPoolConfiguration: WorkerPoolConfiguration{
				QueueLength:  0,
				MaxWorkers:   10,
				BlockTimeout: 1 * time.Second,
				BoostTimeout: 5 * time.Minute,
				BoostWorkers: 5,
			},
			Workers: 0,
			Name:    "TestChannelQueue",
		}, &testData{})
	assert.NoError(t, err)

	assert.Equal(t, queue.(*ChannelQueue).WorkerPool.boostWorkers, 5)

	go queue.Run(nilFn, nilFn)

	test1 := testData{"A", 1}
	go queue.Push(&test1)
	result1 := <-handleChan
	assert.Equal(t, test1.TestString, result1.TestString)
	assert.Equal(t, test1.TestInt, result1.TestInt)

	err = queue.Push(test1)
	assert.Error(t, err)
}

func TestChannelQueue_Batch(t *testing.T) {
	handleChan := make(chan *testData)
	handle := func(data ...Data) {
		assert.True(t, len(data) == 2)
		for _, datum := range data {
			testDatum := datum.(*testData)
			handleChan <- testDatum
		}
	}

	nilFn := func(_ context.Context, _ func()) {}

	queue, err := NewChannelQueue(handle,
		ChannelQueueConfiguration{
			WorkerPoolConfiguration: WorkerPoolConfiguration{
				QueueLength:  20,
				BatchLength:  2,
				BlockTimeout: 1 * time.Second,
				BoostTimeout: 5 * time.Minute,
				BoostWorkers: 5,
				MaxWorkers:   10,
			},
			Workers: 1,
		}, &testData{})
	assert.NoError(t, err)

	go queue.Run(nilFn, nilFn)

	test1 := testData{"A", 1}
	test2 := testData{"B", 2}

	queue.Push(&test1)
	go queue.Push(&test2)

	result1 := <-handleChan
	assert.Equal(t, test1.TestString, result1.TestString)
	assert.Equal(t, test1.TestInt, result1.TestInt)

	result2 := <-handleChan
	assert.Equal(t, test2.TestString, result2.TestString)
	assert.Equal(t, test2.TestInt, result2.TestInt)

	err = queue.Push(test1)
	assert.Error(t, err)
}
<commit_msg>Prevent race in TestChannelQueue_Batch (#15703)<commit_after>// Copyright 2019 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 queue

import (
	"context"
	"testing"
	"time"

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

func TestChannelQueue(t *testing.T) {
	handleChan := make(chan *testData)
	handle := func(data ...Data) {
		for _, datum := range data {
			testDatum := datum.(*testData)
			handleChan <- testDatum
		}
	}

	nilFn := func(_ context.Context, _ func()) {}

	queue, err := NewChannelQueue(handle,
		ChannelQueueConfiguration{
			WorkerPoolConfiguration: WorkerPoolConfiguration{
				QueueLength:  0,
				MaxWorkers:   10,
				BlockTimeout: 1 * time.Second,
				BoostTimeout: 5 * time.Minute,
				BoostWorkers: 5,
			},
			Workers: 0,
			Name:    "TestChannelQueue",
		}, &testData{})
	assert.NoError(t, err)

	assert.Equal(t, queue.(*ChannelQueue).WorkerPool.boostWorkers, 5)

	go queue.Run(nilFn, nilFn)

	test1 := testData{"A", 1}
	go queue.Push(&test1)
	result1 := <-handleChan
	assert.Equal(t, test1.TestString, result1.TestString)
	assert.Equal(t, test1.TestInt, result1.TestInt)

	err = queue.Push(test1)
	assert.Error(t, err)
}

func TestChannelQueue_Batch(t *testing.T) {
	handleChan := make(chan *testData)
	handle := func(data ...Data) {
		assert.True(t, len(data) == 2)
		for _, datum := range data {
			testDatum := datum.(*testData)
			handleChan <- testDatum
		}
	}

	nilFn := func(_ context.Context, _ func()) {}

	queue, err := NewChannelQueue(handle,
		ChannelQueueConfiguration{
			WorkerPoolConfiguration: WorkerPoolConfiguration{
				QueueLength:  20,
				BatchLength:  2,
				BlockTimeout: 0,
				BoostTimeout: 0,
				BoostWorkers: 0,
				MaxWorkers:   10,
			},
			Workers: 1,
		}, &testData{})
	assert.NoError(t, err)

	go queue.Run(nilFn, nilFn)

	test1 := testData{"A", 1}
	test2 := testData{"B", 2}

	queue.Push(&test1)
	go queue.Push(&test2)

	result1 := <-handleChan
	assert.Equal(t, test1.TestString, result1.TestString)
	assert.Equal(t, test1.TestInt, result1.TestInt)

	result2 := <-handleChan
	assert.Equal(t, test2.TestString, result2.TestString)
	assert.Equal(t, test2.TestInt, result2.TestInt)

	err = queue.Push(test1)
	assert.Error(t, err)
}
<|endoftext|>


<commit_before>package contractor

import (
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// ProcessConsensusChange will be called by the consensus set every time there
// is a change in the blockchain. Updates will always be called in order.
func (c *Contractor) ProcessConsensusChange(cc modules.ConsensusChange) {
	c.mu.Lock()
	defer c.mu.Unlock()

	for _, block := range cc.RevertedBlocks {
		if block.ID() != types.GenesisBlock.ID() {
			c.blockHeight--
		}
	}
	for _, block := range cc.AppliedBlocks {
		if block.ID() != types.GenesisBlock.ID() {
			c.blockHeight++
		}
	}

	// renew contracts
	if c.blockHeight+c.allowance.RenewWindow >= c.renewHeight {
		c.renewHeight += c.allowance.Period
		go c.threadedRenewContracts(c.allowance, c.renewHeight)
		// reset the spentPeriod metric
		c.spentPeriod = types.ZeroCurrency
	}

	c.lastChange = cc.ID
}
<commit_msg>disable renewing<commit_after>package contractor

import (
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// ProcessConsensusChange will be called by the consensus set every time there
// is a change in the blockchain. Updates will always be called in order.
func (c *Contractor) ProcessConsensusChange(cc modules.ConsensusChange) {
	c.mu.Lock()
	defer c.mu.Unlock()

	for _, block := range cc.RevertedBlocks {
		if block.ID() != types.GenesisBlock.ID() {
			c.blockHeight--
		}
	}
	for _, block := range cc.AppliedBlocks {
		if block.ID() != types.GenesisBlock.ID() {
			c.blockHeight++
		}
	}

	// renew contracts
	// TODO: re-enable this functionality
	// if c.blockHeight+c.allowance.RenewWindow >= c.renewHeight {
	// 	c.renewHeight += c.allowance.Period
	// 	go c.threadedRenewContracts(c.allowance, c.renewHeight)
	// 	// reset the spentPeriod metric
	// 	c.spentPeriod = types.ZeroCurrency
	// }

	c.lastChange = cc.ID
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/smancke/guble/testutil"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

func Test_Subscribe_on_random_node(t *testing.T) {
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8080",
		NodeID:     1,
		NodePort:   20000,
		Remotes:    "localhost:20000",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8081",
		NodeID:     2,
		NodePort:   20001,
		Remotes:    "localhost:20000",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(fcmTopic, "1")

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	a.NoError(err)

	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)
}

func Test_Subscribe_working_After_Node_Restart(t *testing.T) {
	// defer testutil.EnableDebugForMethod()()
	testutil.SkipIfShort(t)
	a := assert.New(t)

	nodeConfig1 := testClusterNodeConfig{
		HttpListen: "localhost:8082",
		NodeID:     1,
		NodePort:   20002,
		Remotes:    "localhost:20002",
	}
	node1 := newTestClusterNode(t, nodeConfig1)
	a.NotNil(node1)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8083",
		NodeID:     2,
		NodePort:   20003,
		Remotes:    "localhost:20002",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(fcmTopic, "1")

	// connect a clinet and send a message
	client1, err := node1.client("user1", 1000, true)
	a.NoError(err)
	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// stop a node, cleanup without removing directories
	node1.cleanup(false)
	time.Sleep(time.Millisecond * 150)

	// restart the service
	restartedNode1 := newTestClusterNode(t, nodeConfig1)
	a.NotNil(restartedNode1)
	defer restartedNode1.cleanup(true)

	restartedNode1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// send a message to the former subscription.
	client1, err = restartedNode1.client("user1", 1000, true)
	a.NoError(err)
	time.Sleep(time.Second)

	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err, "Subscription should work even after node restart")

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	restartedNode1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)
}

func Test_Independent_Receiving(t *testing.T) {
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8084",
		NodeID:     1,
		NodePort:   20004,
		Remotes:    "localhost:20004",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8085",
		NodeID:     2,
		NodePort:   20005,
		Remotes:    "localhost:20004",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(fcmTopic, "1")

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// reset the counter
	node1.FCM.reset()

	// NOW connect to second node
	client2, err := node2.client("user2", 1000, true)
	a.NoError(err)
	err = client2.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the second node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(0)
	node2.FCM.checkReceived(1)
}

func Test_NoReceiving_After_Unsubscribe(t *testing.T) {
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8086",
		NodeID:     1,
		NodePort:   20006,
		Remotes:    "localhost:20006",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "localhost:8087",
		NodeID:     2,
		NodePort:   20007,
		Remotes:    "localhost:20006",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(fcmTopic, "1")
	time.Sleep(50 * time.Millisecond)

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// Unsubscribe
	node2.Unsubscribe(fcmTopic, "1")
	time.Sleep(50 * time.Millisecond)

	// reset the counter
	node1.FCM.reset()

	// and send a message again. No one should receive it
	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the second node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(0)
	node2.FCM.checkReceived(0)
}
<commit_msg>using 127.0.0.1<commit_after>package server

import (
	"github.com/smancke/guble/testutil"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

func Test_Subscribe_on_random_node(t *testing.T) {
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "127.0.0.1:8080",
		NodeID:     1,
		NodePort:   20000,
		Remotes:    "127.0.0.1:20000",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "127.0.0.1:8081",
		NodeID:     2,
		NodePort:   20001,
		Remotes:    "127.0.0.1:20000",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(fcmTopic, "1")

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	a.NoError(err)

	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)
}

func Test_Subscribe_working_After_Node_Restart(t *testing.T) {
	// defer testutil.EnableDebugForMethod()()
	testutil.SkipIfShort(t)
	a := assert.New(t)

	nodeConfig1 := testClusterNodeConfig{
		HttpListen: "127.0.0.1:8082",
		NodeID:     1,
		NodePort:   20002,
		Remotes:    "127.0.0.1:20002",
	}
	node1 := newTestClusterNode(t, nodeConfig1)
	a.NotNil(node1)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "127.0.0.1:8083",
		NodeID:     2,
		NodePort:   20003,
		Remotes:    "127.0.0.1:20002",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(fcmTopic, "1")

	// connect a clinet and send a message
	client1, err := node1.client("user1", 1000, true)
	a.NoError(err)
	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// stop a node, cleanup without removing directories
	node1.cleanup(false)
	time.Sleep(time.Millisecond * 150)

	// restart the service
	restartedNode1 := newTestClusterNode(t, nodeConfig1)
	a.NotNil(restartedNode1)
	defer restartedNode1.cleanup(true)

	restartedNode1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// send a message to the former subscription.
	client1, err = restartedNode1.client("user1", 1000, true)
	a.NoError(err)
	time.Sleep(time.Second)

	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err, "Subscription should work even after node restart")

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	restartedNode1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)
}

func Test_Independent_Receiving(t *testing.T) {
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "127.0.0.1:8084",
		NodeID:     1,
		NodePort:   20004,
		Remotes:    "127.0.0.1:20004",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "127.0.0.1:8085",
		NodeID:     2,
		NodePort:   20005,
		Remotes:    "127.0.0.1:20004",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(fcmTopic, "1")

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// reset the counter
	node1.FCM.reset()

	// NOW connect to second node
	client2, err := node2.client("user2", 1000, true)
	a.NoError(err)
	err = client2.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the second node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(0)
	node2.FCM.checkReceived(1)
}

func Test_NoReceiving_After_Unsubscribe(t *testing.T) {
	testutil.SkipIfShort(t)
	a := assert.New(t)

	node1 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "127.0.0.1:8086",
		NodeID:     1,
		NodePort:   20006,
		Remotes:    "127.0.0.1:20006",
	})
	a.NotNil(node1)
	defer node1.cleanup(true)

	node2 := newTestClusterNode(t, testClusterNodeConfig{
		HttpListen: "127.0.0.1:8087",
		NodeID:     2,
		NodePort:   20007,
		Remotes:    "127.0.0.1:20006",
	})
	a.NotNil(node2)
	defer node2.cleanup(true)

	node1.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)
	node2.FCM.setupRoundTripper(20*time.Millisecond, 10, testutil.SuccessFCMResponse)

	// subscribe on first node
	node1.Subscribe(fcmTopic, "1")
	time.Sleep(50 * time.Millisecond)

	// connect a client and send a message
	client1, err := node1.client("user1", 1000, true)
	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the first node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(1)
	node2.FCM.checkReceived(0)

	// Unsubscribe
	node2.Unsubscribe(fcmTopic, "1")
	time.Sleep(50 * time.Millisecond)

	// reset the counter
	node1.FCM.reset()

	// and send a message again. No one should receive it
	err = client1.Send(fcmTopic, "body", "{jsonHeader:1}")
	a.NoError(err)

	// only one message should be received but only on the second node.
	// Every message should be delivered only once.
	node1.FCM.checkReceived(0)
	node2.FCM.checkReceived(0)
}
<|endoftext|>


<commit_before>package repo

import (
	"context"
	"fmt"
	"net/url"
	"strings"

	"github.com/bleenco/abstruse/pkg/gitscm"
	"github.com/bleenco/abstruse/server/core"
	"github.com/drone/go-scm/scm"
	"github.com/jinzhu/gorm"
)

// New returns a new RepositoryStore.
func New(db *gorm.DB) core.RepositoryStore {
	return repositoryStore{db}
}

type repositoryStore struct {
	db *gorm.DB
}

func (s repositoryStore) Find(id, userID uint) (core.Repository, error) {
	var repo core.Repository

	db := s.db.Preload("Provider")
	db = db.Joins("LEFT JOIN permissions ON permissions.repository_id = repositories.id").
		Joins("LEFT JOIN teams ON teams.id = permissions.team_id").
		Joins("LEFT JOIN team_users ON team_users.team_id = teams.id")
	db = db.Where("repositories.id = ? AND repositories.user_id = ?", id, userID).
		Or("repositories.id = ? AND team_users.user_id = ? AND permissions.read = ?", id, userID, true)

	err := db.First(&repo).Error
	if err != nil {
		return repo, err
	}
	repo.Perms = s.GetPermissions(repo.ID, userID)

	return repo, err
}

func (s repositoryStore) FindUID(uid string) (core.Repository, error) {
	var repo core.Repository
	err := s.db.Where("uid = ?", uid).Preload("Provider").First(&repo).Error
	return repo, err
}

func (s repositoryStore) FindClone(clone string) (core.Repository, error) {
	var repo core.Repository
	err := s.db.Where("clone = ?", clone).Preload("Provider").First(&repo).Error
	return repo, err
}

func (s repositoryStore) FindToken(token string) (*core.Repository, error) {
	var repo core.Repository
	err := s.db.Where("token = ?", token).First(&repo).Error
	return &repo, err
}

func (s repositoryStore) List(filters core.RepositoryFilter) ([]core.Repository, int, error) {
	var repos []core.Repository
	var count int
	var err error
	keyword := fmt.Sprintf("%%%s%%", filters.Keyword)

	db := s.db.Preload("Provider")

	db = db.
		Joins("LEFT JOIN permissions ON permissions.repository_id = repositories.id").
		Joins("LEFT JOIN teams ON teams.id = permissions.team_id").
		Joins("LEFT JOIN team_users ON team_users.team_id = teams.id")

	if filters.UserID != 0 {
		db = db.Where("repositories.user_id = ? AND repositories.full_name LIKE ?", filters.UserID, keyword).
			Or("team_users.user_id = ? AND permissions.read = ? AND repositories.full_name LIKE ?", filters.UserID, true, keyword)
	}

	if filters.Limit != 0 && filters.Offset != 0 {
		db = db.Limit(filters.Limit).Offset(filters.Offset)
	}

	err = db.Order("active desc, name asc").Group("repositories.id").Find(&repos).Count(&count).Error
	if err != nil || count == 0 {
		return repos, count, err
	}

	for i, repo := range repos {
		repos[i].Perms = s.GetPermissions(repo.ID, filters.UserID)
	}

	return repos, count, err
}

func (s repositoryStore) Create(repo core.Repository) error {
	return s.db.Create(&repo).Error
}

func (s repositoryStore) Update(repo core.Repository) error {
	return s.db.Model(&repo).Updates(&repo).Error
}

func (s repositoryStore) CreateOrUpdate(repo core.Repository) error {
	if s.db.Where("uid = ? AND clone = ?", repo.UID, repo.Clone).First(&repo).RecordNotFound() {
		return s.db.Create(&repo).Error
	}

	return s.db.Model(&repo).Where("uid = ? AND clone = ?", repo.UID, repo.Clone).Updates(&repo).Error
}

func (s repositoryStore) Delete(repo core.Repository) error {
	return s.db.Delete(&repo).Error
}

func (s repositoryStore) SetActive(id uint, active bool) error {
	var repo core.Repository
	if s.db.Where("id = ?", id).First(&repo).RecordNotFound() {
		return fmt.Errorf("repository not found")
	}

	return s.db.Model(&repo).Update("active", active).Error
}

func (s repositoryStore) GetPermissions(id, userID uint) core.Perms {
	perms := core.Perms{Read: false, Write: false, Exec: false}

	var user core.User
	err := s.db.Model(&user).Where("id = ?", userID).First(&user).Error
	if err != nil {
		return perms
	}
	if user.Role == "admin" {
		return core.Perms{Read: true, Write: true, Exec: true}
	}

	var repo core.Repository
	if err := s.db.Where("id = ?", id).First(&repo).Error; err == nil {
		if repo.UserID == userID {
			perms.Read = true
			perms.Write = true
			perms.Exec = true
			return perms
		}
	}

	db := s.db

	db = db.
		Joins("LEFT JOIN team_users ON team_users.team_id = permissions.team_id").
		Where("team_users.user_id = ? AND permissions.repository_id = ?", userID, id)

	var permissions []*core.Permission
	err = db.Find(&permissions).Error
	if err != nil {
		return perms
	}

	r, w, x := false, false, false

	for _, p := range permissions {
		if p.Read {
			r = true
		}
		if p.Write {
			w = true
		}
		if p.Exec {
			x = true
		}
	}

	return core.Perms{Read: r, Write: w, Exec: x}
}

func (s repositoryStore) ListHooks(id, userID uint) ([]*scm.Hook, error) {
	repo, err := s.Find(id, userID)
	if err != nil {
		return nil, err
	}

	gitscm, err := gitscm.New(context.Background(), repo.Provider.Name, repo.Provider.URL, repo.Provider.AccessToken)
	if err != nil {
		return nil, err
	}

	hooks, err := gitscm.ListHooks(repo.FullName)
	if err != nil {
		return nil, err
	}

	return filterHooks(hooks, repo.Provider), nil
}

func (s repositoryStore) CreateHook(id, userID uint, data gitscm.HookForm) error {
	if err := s.DeleteHooks(id, userID); err != nil {
		return err
	}

	repo, err := s.Find(id, userID)
	if err != nil {
		return err
	}

	gitscm, err := gitscm.New(context.Background(), repo.Provider.Name, repo.Provider.URL, repo.Provider.AccessToken)
	if err != nil {
		return err
	}

	if !data.Branch && !data.PullRequest && !data.Push && !data.Tag {
		return nil
	}

	target := fmt.Sprintf("%s/webhooks/%s", repo.Provider.Host, repo.Provider.Name)
	_, err = gitscm.CreateHook(repo.FullName, target, repo.Provider.Name, repo.Provider.Secret, data)
	return err
}

func (s repositoryStore) DeleteHooks(id, userID uint) error {
	repo, err := s.Find(id, userID)
	if err != nil {
		return err
	}

	gitscm, err := gitscm.New(context.Background(), repo.Provider.Name, repo.Provider.URL, repo.Provider.AccessToken)
	if err != nil {
		return err
	}

	hooks, err := gitscm.ListHooks(repo.FullName)
	if err != nil {
		return err
	}

	webhooks := filterHooks(hooks, repo.Provider)

	for _, webhook := range webhooks {
		if err := gitscm.DeleteHook(repo.FullName, webhook.ID); err != nil {
			return err
		}
	}

	return nil
}

func filterHooks(hooks []*scm.Hook, provider core.Provider) []*scm.Hook {
	var webhooks []*scm.Hook

	for _, hook := range hooks {
		url, _ := url.Parse(hook.Target)
		if strings.HasPrefix(hook.Target, provider.Host) && strings.HasSuffix(url.Path, fmt.Sprintf("/webhooks/%s", provider.Name)) {
			webhooks = append(webhooks, hook)
		}
	}

	return webhooks
}
<commit_msg>fix(repos): webhook url fix<commit_after>package repo

import (
	"context"
	"fmt"
	"net/url"
	"strings"

	"github.com/bleenco/abstruse/pkg/gitscm"
	"github.com/bleenco/abstruse/server/core"
	"github.com/drone/go-scm/scm"
	"github.com/jinzhu/gorm"
)

// New returns a new RepositoryStore.
func New(db *gorm.DB) core.RepositoryStore {
	return repositoryStore{db}
}

type repositoryStore struct {
	db *gorm.DB
}

func (s repositoryStore) Find(id, userID uint) (core.Repository, error) {
	var repo core.Repository

	db := s.db.Preload("Provider")
	db = db.Joins("LEFT JOIN permissions ON permissions.repository_id = repositories.id").
		Joins("LEFT JOIN teams ON teams.id = permissions.team_id").
		Joins("LEFT JOIN team_users ON team_users.team_id = teams.id")
	db = db.Where("repositories.id = ? AND repositories.user_id = ?", id, userID).
		Or("repositories.id = ? AND team_users.user_id = ? AND permissions.read = ?", id, userID, true)

	err := db.First(&repo).Error
	if err != nil {
		return repo, err
	}
	repo.Perms = s.GetPermissions(repo.ID, userID)

	return repo, err
}

func (s repositoryStore) FindUID(uid string) (core.Repository, error) {
	var repo core.Repository
	err := s.db.Where("uid = ?", uid).Preload("Provider").First(&repo).Error
	return repo, err
}

func (s repositoryStore) FindClone(clone string) (core.Repository, error) {
	var repo core.Repository
	err := s.db.Where("clone = ?", clone).Preload("Provider").First(&repo).Error
	return repo, err
}

func (s repositoryStore) FindToken(token string) (*core.Repository, error) {
	var repo core.Repository
	err := s.db.Where("token = ?", token).First(&repo).Error
	return &repo, err
}

func (s repositoryStore) List(filters core.RepositoryFilter) ([]core.Repository, int, error) {
	var repos []core.Repository
	var count int
	var err error
	keyword := fmt.Sprintf("%%%s%%", filters.Keyword)

	db := s.db.Preload("Provider")

	db = db.
		Joins("LEFT JOIN permissions ON permissions.repository_id = repositories.id").
		Joins("LEFT JOIN teams ON teams.id = permissions.team_id").
		Joins("LEFT JOIN team_users ON team_users.team_id = teams.id")

	if filters.UserID != 0 {
		db = db.Where("repositories.user_id = ? AND repositories.full_name LIKE ?", filters.UserID, keyword).
			Or("team_users.user_id = ? AND permissions.read = ? AND repositories.full_name LIKE ?", filters.UserID, true, keyword)
	}

	if filters.Limit != 0 && filters.Offset != 0 {
		db = db.Limit(filters.Limit).Offset(filters.Offset)
	}

	err = db.Order("active desc, name asc").Group("repositories.id").Find(&repos).Count(&count).Error
	if err != nil || count == 0 {
		return repos, count, err
	}

	for i, repo := range repos {
		repos[i].Perms = s.GetPermissions(repo.ID, filters.UserID)
	}

	return repos, count, err
}

func (s repositoryStore) Create(repo core.Repository) error {
	return s.db.Create(&repo).Error
}

func (s repositoryStore) Update(repo core.Repository) error {
	return s.db.Model(&repo).Updates(&repo).Error
}

func (s repositoryStore) CreateOrUpdate(repo core.Repository) error {
	if s.db.Where("uid = ? AND clone = ?", repo.UID, repo.Clone).First(&repo).RecordNotFound() {
		return s.db.Create(&repo).Error
	}

	return s.db.Model(&repo).Where("uid = ? AND clone = ?", repo.UID, repo.Clone).Updates(&repo).Error
}

func (s repositoryStore) Delete(repo core.Repository) error {
	return s.db.Delete(&repo).Error
}

func (s repositoryStore) SetActive(id uint, active bool) error {
	var repo core.Repository
	if s.db.Where("id = ?", id).First(&repo).RecordNotFound() {
		return fmt.Errorf("repository not found")
	}

	return s.db.Model(&repo).Update("active", active).Error
}

func (s repositoryStore) GetPermissions(id, userID uint) core.Perms {
	perms := core.Perms{Read: false, Write: false, Exec: false}

	var user core.User
	err := s.db.Model(&user).Where("id = ?", userID).First(&user).Error
	if err != nil {
		return perms
	}
	if user.Role == "admin" {
		return core.Perms{Read: true, Write: true, Exec: true}
	}

	var repo core.Repository
	if err := s.db.Where("id = ?", id).First(&repo).Error; err == nil {
		if repo.UserID == userID {
			perms.Read = true
			perms.Write = true
			perms.Exec = true
			return perms
		}
	}

	db := s.db

	db = db.
		Joins("LEFT JOIN team_users ON team_users.team_id = permissions.team_id").
		Where("team_users.user_id = ? AND permissions.repository_id = ?", userID, id)

	var permissions []*core.Permission
	err = db.Find(&permissions).Error
	if err != nil {
		return perms
	}

	r, w, x := false, false, false

	for _, p := range permissions {
		if p.Read {
			r = true
		}
		if p.Write {
			w = true
		}
		if p.Exec {
			x = true
		}
	}

	return core.Perms{Read: r, Write: w, Exec: x}
}

func (s repositoryStore) ListHooks(id, userID uint) ([]*scm.Hook, error) {
	repo, err := s.Find(id, userID)
	if err != nil {
		return nil, err
	}

	gitscm, err := gitscm.New(context.Background(), repo.Provider.Name, repo.Provider.URL, repo.Provider.AccessToken)
	if err != nil {
		return nil, err
	}

	hooks, err := gitscm.ListHooks(repo.FullName)
	if err != nil {
		return nil, err
	}

	return filterHooks(hooks, repo.Provider), nil
}

func (s repositoryStore) CreateHook(id, userID uint, data gitscm.HookForm) error {
	if err := s.DeleteHooks(id, userID); err != nil {
		return err
	}

	repo, err := s.Find(id, userID)
	if err != nil {
		return err
	}

	gitscm, err := gitscm.New(context.Background(), repo.Provider.Name, repo.Provider.URL, repo.Provider.AccessToken)
	if err != nil {
		return err
	}

	if !data.Branch && !data.PullRequest && !data.Push && !data.Tag {
		return nil
	}

	target := fmt.Sprintf("%s/webhooks", repo.Provider.Host)
	_, err = gitscm.CreateHook(repo.FullName, target, repo.Provider.Name, repo.Provider.Secret, data)
	return err
}

func (s repositoryStore) DeleteHooks(id, userID uint) error {
	repo, err := s.Find(id, userID)
	if err != nil {
		return err
	}

	gitscm, err := gitscm.New(context.Background(), repo.Provider.Name, repo.Provider.URL, repo.Provider.AccessToken)
	if err != nil {
		return err
	}

	hooks, err := gitscm.ListHooks(repo.FullName)
	if err != nil {
		return err
	}

	webhooks := filterHooks(hooks, repo.Provider)

	for _, webhook := range webhooks {
		if err := gitscm.DeleteHook(repo.FullName, webhook.ID); err != nil {
			return err
		}
	}

	return nil
}

func filterHooks(hooks []*scm.Hook, provider core.Provider) []*scm.Hook {
	var webhooks []*scm.Hook

	for _, hook := range hooks {
		url, _ := url.Parse(hook.Target)
		if strings.HasPrefix(hook.Target, provider.Host) && strings.HasSuffix(url.Path, "/webhooks") {
			webhooks = append(webhooks, hook)
		}
	}

	return webhooks
}
<|endoftext|>