<commit_before>// Copyright (c) 2014, Google, Inc.,  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// File: simpleclient.go

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"

	taosupport "github.com/jlmucb/cloudproxy/go/apps/simpleexample/taosupport"
)

var simpleCfg = flag.String("domain_config",
	// "./tao.config",
	"/Domains/domain.simpleexample/tao.config",
	"path to tao configuration")
var simpleClientPath = flag.String("path",
	"/Domains/domain.simpleexample/SimpleClient",
	"path to SimpleClient files")
var serverHost = flag.String("host", "localhost", "address for client/server")
var serverPort = flag.String("port", "8123", "port for client/server")
var serverAddr string

func main() {

	// This holds the cloudproxy specific data for simpleclient
	// including the Program Cert and Program Private key.
	var clientProgramData taosupport.TaoProgramData

	// Make sure we zero keys when we're done.
	defer taosupport.ClearTaoProgramData(&clientProgramData)

	// Parse flags
	flag.Parse()
	serverAddr = *serverHost + ":" + *serverPort

	// If TaoParadigm completes without error, clientProgramData contains all the
	// Cloudproxy information needed throughout simpleclient execution.
	err := taosupport.TaoParadigm(simpleCfg, simpleClientPath, &clientProgramData)
	if err != nil {
		log.Fatalln("simpleclient: Can't establish Tao: ", err)
	}
	fmt.Printf("simpleclient: TaoParadigm complete, name: %s\n",
		clientProgramData.TaoName)

	// Open the Tao Channel using the Program key.  This program does all the
	// standard channel negotiation and presents the secure server name after
	// negotiation is complete.
	ms, serverName, err := taosupport.OpenTaoChannel(&clientProgramData,
		&serverAddr)
	if err != nil {
		log.Fatalln("simpleclient: Can't establish Tao Channel")
	}
	log.Printf("simpleclient: establish Tao Channel with %s, %s\n",
		serverAddr, serverName)

	// Send a simple request and get response.
	// We have a simple service protobuf for requests and reponsed between
	// simpleclient and simpleserver.  There's only on request: tell me the
	// secret.
	secretRequest := "SecretRequest"

	msg := new(taosupport.SimpleMessage)
	msg.RequestType = &secretRequest
	taosupport.SendRequest(ms, msg)
	if err != nil {
		log.Fatalln("simpleclient: Error in response to SendRequest\n")
	}
	respmsg, err := taosupport.GetResponse(ms)
	if err != nil {
		log.Fatalln("simpleclient: Error in response to GetResponse\n")
	}

	// This is the secret.
	retrieveSecret := respmsg.Data[0]

	// Encrypt and store the secret in simpleclient's save area.
	out, err := taosupport.Protect(clientProgramData.ProgramSymKeys, retrieveSecret)
	if err != nil {
		log.Fatalln("simpleclient: Error protecting data\n")
	}
	err = ioutil.WriteFile(path.Join(*simpleClientPath,
		"retrieved_secret"), out, os.ModePerm)
	if err != nil {
		log.Fatalln("simpleclient: error saving retrieved secret\n")
	}

	// Close down.
	log.Printf("simpleclient: secret is %s, done\n", retrieveSecret)
}
<commit_msg>old args<commit_after>// Copyright (c) 2014, Google, Inc.,  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//     http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// File: simpleclient.go

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"

	taosupport "github.com/jlmucb/cloudproxy/go/apps/simpleexample/taosupport"
)

var simpleCfg = flag.String("domain_config",
	"./tao.config",
	"path to tao configuration")
var simpleClientPath = flag.String("path",
	"./SimpleClient",
	"path to SimpleClient files")
var serverHost = flag.String("host", "localhost", "address for client/server")
var serverPort = flag.String("port", "8123", "port for client/server")
var serverAddr string

func main() {

	// This holds the cloudproxy specific data for simpleclient
	// including the Program Cert and Program Private key.
	var clientProgramData taosupport.TaoProgramData

	// Make sure we zero keys when we're done.
	defer taosupport.ClearTaoProgramData(&clientProgramData)

	// Parse flags
	flag.Parse()
	serverAddr = *serverHost + ":" + *serverPort

	// If TaoParadigm completes without error, clientProgramData contains all the
	// Cloudproxy information needed throughout simpleclient execution.
	err := taosupport.TaoParadigm(simpleCfg, simpleClientPath, &clientProgramData)
	if err != nil {
		log.Fatalln("simpleclient: Can't establish Tao: ", err)
	}
	fmt.Printf("simpleclient: TaoParadigm complete, name: %s\n",
		clientProgramData.TaoName)

	// Open the Tao Channel using the Program key.  This program does all the
	// standard channel negotiation and presents the secure server name after
	// negotiation is complete.
	ms, serverName, err := taosupport.OpenTaoChannel(&clientProgramData,
		&serverAddr)
	if err != nil {
		log.Fatalln("simpleclient: Can't establish Tao Channel")
	}
	log.Printf("simpleclient: establish Tao Channel with %s, %s\n",
		serverAddr, serverName)

	// Send a simple request and get response.
	// We have a simple service protobuf for requests and reponsed between
	// simpleclient and simpleserver.  There's only on request: tell me the
	// secret.
	secretRequest := "SecretRequest"

	msg := new(taosupport.SimpleMessage)
	msg.RequestType = &secretRequest
	taosupport.SendRequest(ms, msg)
	if err != nil {
		log.Fatalln("simpleclient: Error in response to SendRequest\n")
	}
	respmsg, err := taosupport.GetResponse(ms)
	if err != nil {
		log.Fatalln("simpleclient: Error in response to GetResponse\n")
	}

	// This is the secret.
	retrieveSecret := respmsg.Data[0]

	// Encrypt and store the secret in simpleclient's save area.
	out, err := taosupport.Protect(clientProgramData.ProgramSymKeys, retrieveSecret)
	if err != nil {
		log.Fatalln("simpleclient: Error protecting data\n")
	}
	err = ioutil.WriteFile(path.Join(*simpleClientPath,
		"retrieved_secret"), out, os.ModePerm)
	if err != nil {
		log.Fatalln("simpleclient: error saving retrieved secret\n")
	}

	// Close down.
	log.Printf("simpleclient: secret is %s, done\n", retrieveSecret)
}
<|endoftext|>


<commit_before>package master

import (
	"fmt"
	"sync"

	"github.com/golang/glog"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/watch"
	kapi "k8s.io/kubernetes/pkg/apis/core"

	"github.com/openshift/origin/pkg/network"
	networkapi "github.com/openshift/origin/pkg/network/apis/network"
	"github.com/openshift/origin/pkg/network/common"
	networkclient "github.com/openshift/origin/pkg/network/generated/internalclientset"
	pnetid "github.com/openshift/origin/pkg/network/master/netid"
)

type masterVNIDMap struct {
	// Synchronizes assign, revoke and update VNID
	lock         sync.Mutex
	ids          map[string]uint32
	netIDManager *pnetid.Allocator

	adminNamespaces  sets.String
	allowRenumbering bool
}

func newMasterVNIDMap(allowRenumbering bool) *masterVNIDMap {
	netIDRange, err := pnetid.NewNetIDRange(network.MinVNID, network.MaxVNID)
	if err != nil {
		panic(err)
	}

	return &masterVNIDMap{
		netIDManager:     pnetid.NewInMemory(netIDRange),
		adminNamespaces:  sets.NewString(metav1.NamespaceDefault),
		ids:              make(map[string]uint32),
		allowRenumbering: allowRenumbering,
	}
}

func (vmap *masterVNIDMap) getVNID(name string) (uint32, bool) {
	id, found := vmap.ids[name]
	return id, found
}

func (vmap *masterVNIDMap) setVNID(name string, id uint32) {
	vmap.ids[name] = id
}

func (vmap *masterVNIDMap) unsetVNID(name string) (uint32, bool) {
	id, found := vmap.ids[name]
	delete(vmap.ids, name)
	return id, found
}

func (vmap *masterVNIDMap) getVNIDCount(id uint32) int {
	count := 0
	for _, netid := range vmap.ids {
		if id == netid {
			count = count + 1
		}
	}
	return count
}

func (vmap *masterVNIDMap) isAdminNamespace(nsName string) bool {
	if vmap.adminNamespaces.Has(nsName) {
		return true
	}
	return false
}

func (vmap *masterVNIDMap) populateVNIDs(networkClient networkclient.Interface) error {
	netnsList, err := networkClient.Network().NetNamespaces().List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, netns := range netnsList.Items {
		vmap.setVNID(netns.NetName, netns.NetID)

		// Skip GlobalVNID, not part of netID allocation range
		if netns.NetID == network.GlobalVNID {
			continue
		}

		switch err := vmap.netIDManager.Allocate(netns.NetID); err {
		case nil: // Expected normal case
		case pnetid.ErrAllocated: // Expected when project networks are joined
		default:
			return fmt.Errorf("unable to allocate netid %d: %v", netns.NetID, err)
		}
	}
	return nil
}

func (vmap *masterVNIDMap) allocateNetID(nsName string) (uint32, bool, error) {
	// Nothing to do if the netid is in the vnid map
	exists := false
	if netid, found := vmap.getVNID(nsName); found {
		exists = true
		return netid, exists, nil
	}

	// NetNamespace not found, so allocate new NetID
	var netid uint32
	if vmap.isAdminNamespace(nsName) {
		netid = network.GlobalVNID
	} else {
		var err error
		netid, err = vmap.netIDManager.AllocateNext()
		if err != nil {
			return 0, exists, err
		}
	}

	vmap.setVNID(nsName, netid)
	glog.Infof("Allocated netid %d for namespace %q", netid, nsName)
	return netid, exists, nil
}

func (vmap *masterVNIDMap) releaseNetID(nsName string) error {
	// Remove NetID from vnid map
	netid, found := vmap.unsetVNID(nsName)
	if !found {
		return fmt.Errorf("netid not found for namespace %q", nsName)
	}

	// Skip network.GlobalVNID as it is not part of NetID allocation
	if netid == network.GlobalVNID {
		return nil
	}

	// Check if this netid is used by any other namespaces
	// If not, then release the netid
	if count := vmap.getVNIDCount(netid); count == 0 {
		if err := vmap.netIDManager.Release(netid); err != nil {
			return fmt.Errorf("Error while releasing netid %d for namespace %q, %v", netid, nsName, err)
		}
		glog.Infof("Released netid %d for namespace %q", netid, nsName)
	} else {
		glog.V(5).Infof("netid %d for namespace %q is still in use", netid, nsName)
	}
	return nil
}

func (vmap *masterVNIDMap) updateNetID(nsName string, action network.PodNetworkAction, args string) (uint32, error) {
	var netid uint32
	allocated := false

	// Check if the given namespace exists or not
	oldnetid, found := vmap.getVNID(nsName)
	if !found {
		return 0, fmt.Errorf("netid not found for namespace %q", nsName)
	}

	// Determine new network ID
	switch action {
	case network.GlobalPodNetwork:
		netid = network.GlobalVNID
	case network.JoinPodNetwork:
		joinNsName := args
		var found bool
		if netid, found = vmap.getVNID(joinNsName); !found {
			return 0, fmt.Errorf("netid not found for namespace %q", joinNsName)
		}
	case network.IsolatePodNetwork:
		// Check if the given namespace is already isolated
		if count := vmap.getVNIDCount(oldnetid); count == 1 {
			return oldnetid, nil
		}

		var err error
		netid, err = vmap.netIDManager.AllocateNext()
		if err != nil {
			return 0, err
		}
		allocated = true
	default:
		return 0, fmt.Errorf("invalid pod network action: %v", action)
	}

	// Release old network ID
	if err := vmap.releaseNetID(nsName); err != nil {
		if allocated {
			vmap.netIDManager.Release(netid)
		}
		return 0, err
	}

	// Set new network ID
	vmap.setVNID(nsName, netid)
	glog.Infof("Updated netid %d for namespace %q", netid, nsName)
	return netid, nil
}

// assignVNID, revokeVNID and updateVNID methods updates in-memory structs and persists etcd objects
func (vmap *masterVNIDMap) assignVNID(networkClient networkclient.Interface, nsName string) error {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	netid, exists, err := vmap.allocateNetID(nsName)
	if err != nil {
		return err
	}

	if !exists {
		// Create NetNamespace Object and update vnid map
		netns := &networkapi.NetNamespace{
			TypeMeta:   metav1.TypeMeta{Kind: "NetNamespace"},
			ObjectMeta: metav1.ObjectMeta{Name: nsName},
			NetName:    nsName,
			NetID:      netid,
		}
		_, err := networkClient.Network().NetNamespaces().Create(netns)
		if err != nil {
			vmap.releaseNetID(nsName)
			return err
		}
	}
	return nil
}

func (vmap *masterVNIDMap) revokeVNID(networkClient networkclient.Interface, nsName string) error {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	// Delete NetNamespace object
	if err := networkClient.Network().NetNamespaces().Delete(nsName, &metav1.DeleteOptions{}); err != nil {
		return err
	}

	if err := vmap.releaseNetID(nsName); err != nil {
		return err
	}
	return nil
}

func (vmap *masterVNIDMap) updateVNID(networkClient networkclient.Interface, origNetns *networkapi.NetNamespace) error {
	// Informer cache should not be mutated, so get a copy of the object
	netns := origNetns.DeepCopy()

	action, args, err := network.GetChangePodNetworkAnnotation(netns)
	if err == network.ErrorPodNetworkAnnotationNotFound {
		// Nothing to update
		return nil
	} else if !vmap.allowRenumbering {
		network.DeleteChangePodNetworkAnnotation(netns)
		_, _ = networkClient.Network().NetNamespaces().Update(netns)
		return fmt.Errorf("network plugin does not allow NetNamespace renumbering")
	}

	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	netid, err := vmap.updateNetID(netns.NetName, action, args)
	if err != nil {
		return err
	}
	netns.NetID = netid
	network.DeleteChangePodNetworkAnnotation(netns)

	if _, err := networkClient.Network().NetNamespaces().Update(netns); err != nil {
		return err
	}
	return nil
}

//--------------------- Master methods ----------------------

func (master *OsdnMaster) VnidStartMaster() error {
	err := master.vnids.populateVNIDs(master.networkClient)
	if err != nil {
		return err
	}

	master.watchNamespaces()
	master.watchNetNamespaces()
	return nil
}

func (master *OsdnMaster) watchNamespaces() {
	funcs := common.InformerFuncs(&kapi.Namespace{}, master.handleAddOrUpdateNamespace, master.handleDeleteNamespace)
	master.kubeInformers.Core().InternalVersion().Namespaces().Informer().AddEventHandler(funcs)
}

func (master *OsdnMaster) handleAddOrUpdateNamespace(obj, _ interface{}, eventType watch.EventType) {
	ns := obj.(*kapi.Namespace)
	glog.V(5).Infof("Watch %s event for Namespace %q", eventType, ns.Name)
	if err := master.vnids.assignVNID(master.networkClient, ns.Name); err != nil {
		utilruntime.HandleError(fmt.Errorf("Error assigning netid: %v", err))
	}
}

func (master *OsdnMaster) handleDeleteNamespace(obj interface{}) {
	ns := obj.(*kapi.Namespace)
	glog.V(5).Infof("Watch %s event for Namespace %q", watch.Deleted, ns.Name)
	if err := master.vnids.revokeVNID(master.networkClient, ns.Name); err != nil {
		utilruntime.HandleError(fmt.Errorf("Error revoking netid: %v", err))
	}
}

func (master *OsdnMaster) watchNetNamespaces() {
	funcs := common.InformerFuncs(&networkapi.NetNamespace{}, master.handleAddOrUpdateNetNamespace, nil)
	master.networkInformers.Network().InternalVersion().NetNamespaces().Informer().AddEventHandler(funcs)
}

func (master *OsdnMaster) handleAddOrUpdateNetNamespace(obj, _ interface{}, eventType watch.EventType) {
	netns := obj.(*networkapi.NetNamespace)
	glog.V(5).Infof("Watch %s event for NetNamespace %q", eventType, netns.Name)

	err := master.vnids.updateVNID(master.networkClient, netns)
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("Error updating netid: %v", err))
	}
}
<commit_msg>SDN master controller should not allow isolation for 'default' project<commit_after>package master

import (
	"fmt"
	"sync"

	"github.com/golang/glog"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/watch"
	kapi "k8s.io/kubernetes/pkg/apis/core"

	"github.com/openshift/origin/pkg/network"
	networkapi "github.com/openshift/origin/pkg/network/apis/network"
	"github.com/openshift/origin/pkg/network/common"
	networkclient "github.com/openshift/origin/pkg/network/generated/internalclientset"
	pnetid "github.com/openshift/origin/pkg/network/master/netid"
)

type masterVNIDMap struct {
	// Synchronizes assign, revoke and update VNID
	lock         sync.Mutex
	ids          map[string]uint32
	netIDManager *pnetid.Allocator

	adminNamespaces  sets.String
	allowRenumbering bool
}

func newMasterVNIDMap(allowRenumbering bool) *masterVNIDMap {
	netIDRange, err := pnetid.NewNetIDRange(network.MinVNID, network.MaxVNID)
	if err != nil {
		panic(err)
	}

	return &masterVNIDMap{
		netIDManager:     pnetid.NewInMemory(netIDRange),
		adminNamespaces:  sets.NewString(metav1.NamespaceDefault),
		ids:              make(map[string]uint32),
		allowRenumbering: allowRenumbering,
	}
}

func (vmap *masterVNIDMap) getVNID(name string) (uint32, bool) {
	id, found := vmap.ids[name]
	return id, found
}

func (vmap *masterVNIDMap) setVNID(name string, id uint32) {
	vmap.ids[name] = id
}

func (vmap *masterVNIDMap) unsetVNID(name string) (uint32, bool) {
	id, found := vmap.ids[name]
	delete(vmap.ids, name)
	return id, found
}

func (vmap *masterVNIDMap) getVNIDCount(id uint32) int {
	count := 0
	for _, netid := range vmap.ids {
		if id == netid {
			count = count + 1
		}
	}
	return count
}

func (vmap *masterVNIDMap) isAdminNamespace(nsName string) bool {
	if vmap.adminNamespaces.Has(nsName) {
		return true
	}
	return false
}

func (vmap *masterVNIDMap) populateVNIDs(networkClient networkclient.Interface) error {
	netnsList, err := networkClient.Network().NetNamespaces().List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, netns := range netnsList.Items {
		vmap.setVNID(netns.NetName, netns.NetID)

		// Skip GlobalVNID, not part of netID allocation range
		if netns.NetID == network.GlobalVNID {
			continue
		}

		switch err := vmap.netIDManager.Allocate(netns.NetID); err {
		case nil: // Expected normal case
		case pnetid.ErrAllocated: // Expected when project networks are joined
		default:
			return fmt.Errorf("unable to allocate netid %d: %v", netns.NetID, err)
		}
	}
	return nil
}

func (vmap *masterVNIDMap) allocateNetID(nsName string) (uint32, bool, error) {
	// Nothing to do if the netid is in the vnid map
	exists := false
	if netid, found := vmap.getVNID(nsName); found {
		exists = true
		return netid, exists, nil
	}

	// NetNamespace not found, so allocate new NetID
	var netid uint32
	if vmap.isAdminNamespace(nsName) {
		netid = network.GlobalVNID
	} else {
		var err error
		netid, err = vmap.netIDManager.AllocateNext()
		if err != nil {
			return 0, exists, err
		}
	}

	vmap.setVNID(nsName, netid)
	glog.Infof("Allocated netid %d for namespace %q", netid, nsName)
	return netid, exists, nil
}

func (vmap *masterVNIDMap) releaseNetID(nsName string) error {
	// Remove NetID from vnid map
	netid, found := vmap.unsetVNID(nsName)
	if !found {
		return fmt.Errorf("netid not found for namespace %q", nsName)
	}

	// Skip network.GlobalVNID as it is not part of NetID allocation
	if netid == network.GlobalVNID {
		return nil
	}

	// Check if this netid is used by any other namespaces
	// If not, then release the netid
	if count := vmap.getVNIDCount(netid); count == 0 {
		if err := vmap.netIDManager.Release(netid); err != nil {
			return fmt.Errorf("Error while releasing netid %d for namespace %q, %v", netid, nsName, err)
		}
		glog.Infof("Released netid %d for namespace %q", netid, nsName)
	} else {
		glog.V(5).Infof("netid %d for namespace %q is still in use", netid, nsName)
	}
	return nil
}

func (vmap *masterVNIDMap) updateNetID(nsName string, action network.PodNetworkAction, args string) (uint32, error) {
	var netid uint32
	allocated := false

	// Check if the given namespace exists or not
	oldnetid, found := vmap.getVNID(nsName)
	if !found {
		return 0, fmt.Errorf("netid not found for namespace %q", nsName)
	}

	// Determine new network ID
	switch action {
	case network.GlobalPodNetwork:
		netid = network.GlobalVNID
	case network.JoinPodNetwork:
		joinNsName := args
		var found bool
		if netid, found = vmap.getVNID(joinNsName); !found {
			return 0, fmt.Errorf("netid not found for namespace %q", joinNsName)
		}
	case network.IsolatePodNetwork:
		if nsName == kapi.NamespaceDefault {
			return 0, fmt.Errorf("network isolation for namespace %q is not allowed", nsName)
		}
		// Check if the given namespace is already isolated
		if count := vmap.getVNIDCount(oldnetid); count == 1 {
			return oldnetid, nil
		}

		var err error
		netid, err = vmap.netIDManager.AllocateNext()
		if err != nil {
			return 0, err
		}
		allocated = true
	default:
		return 0, fmt.Errorf("invalid pod network action: %v", action)
	}

	// Release old network ID
	if err := vmap.releaseNetID(nsName); err != nil {
		if allocated {
			vmap.netIDManager.Release(netid)
		}
		return 0, err
	}

	// Set new network ID
	vmap.setVNID(nsName, netid)
	glog.Infof("Updated netid %d for namespace %q", netid, nsName)
	return netid, nil
}

// assignVNID, revokeVNID and updateVNID methods updates in-memory structs and persists etcd objects
func (vmap *masterVNIDMap) assignVNID(networkClient networkclient.Interface, nsName string) error {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	netid, exists, err := vmap.allocateNetID(nsName)
	if err != nil {
		return err
	}

	if !exists {
		// Create NetNamespace Object and update vnid map
		netns := &networkapi.NetNamespace{
			TypeMeta:   metav1.TypeMeta{Kind: "NetNamespace"},
			ObjectMeta: metav1.ObjectMeta{Name: nsName},
			NetName:    nsName,
			NetID:      netid,
		}
		_, err := networkClient.Network().NetNamespaces().Create(netns)
		if err != nil {
			vmap.releaseNetID(nsName)
			return err
		}
	}
	return nil
}

func (vmap *masterVNIDMap) revokeVNID(networkClient networkclient.Interface, nsName string) error {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	// Delete NetNamespace object
	if err := networkClient.Network().NetNamespaces().Delete(nsName, &metav1.DeleteOptions{}); err != nil {
		return err
	}

	if err := vmap.releaseNetID(nsName); err != nil {
		return err
	}
	return nil
}

func (vmap *masterVNIDMap) updateVNID(networkClient networkclient.Interface, origNetns *networkapi.NetNamespace) error {
	// Informer cache should not be mutated, so get a copy of the object
	netns := origNetns.DeepCopy()

	action, args, err := network.GetChangePodNetworkAnnotation(netns)
	if err == network.ErrorPodNetworkAnnotationNotFound {
		// Nothing to update
		return nil
	} else if !vmap.allowRenumbering {
		network.DeleteChangePodNetworkAnnotation(netns)
		_, _ = networkClient.Network().NetNamespaces().Update(netns)
		return fmt.Errorf("network plugin does not allow NetNamespace renumbering")
	}

	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	netid, err := vmap.updateNetID(netns.NetName, action, args)
	if err != nil {
		return err
	}
	netns.NetID = netid
	network.DeleteChangePodNetworkAnnotation(netns)

	if _, err := networkClient.Network().NetNamespaces().Update(netns); err != nil {
		return err
	}
	return nil
}

//--------------------- Master methods ----------------------

func (master *OsdnMaster) VnidStartMaster() error {
	err := master.vnids.populateVNIDs(master.networkClient)
	if err != nil {
		return err
	}

	master.watchNamespaces()
	master.watchNetNamespaces()
	return nil
}

func (master *OsdnMaster) watchNamespaces() {
	funcs := common.InformerFuncs(&kapi.Namespace{}, master.handleAddOrUpdateNamespace, master.handleDeleteNamespace)
	master.kubeInformers.Core().InternalVersion().Namespaces().Informer().AddEventHandler(funcs)
}

func (master *OsdnMaster) handleAddOrUpdateNamespace(obj, _ interface{}, eventType watch.EventType) {
	ns := obj.(*kapi.Namespace)
	glog.V(5).Infof("Watch %s event for Namespace %q", eventType, ns.Name)
	if err := master.vnids.assignVNID(master.networkClient, ns.Name); err != nil {
		utilruntime.HandleError(fmt.Errorf("Error assigning netid: %v", err))
	}
}

func (master *OsdnMaster) handleDeleteNamespace(obj interface{}) {
	ns := obj.(*kapi.Namespace)
	glog.V(5).Infof("Watch %s event for Namespace %q", watch.Deleted, ns.Name)
	if err := master.vnids.revokeVNID(master.networkClient, ns.Name); err != nil {
		utilruntime.HandleError(fmt.Errorf("Error revoking netid: %v", err))
	}
}

func (master *OsdnMaster) watchNetNamespaces() {
	funcs := common.InformerFuncs(&networkapi.NetNamespace{}, master.handleAddOrUpdateNetNamespace, nil)
	master.networkInformers.Network().InternalVersion().NetNamespaces().Informer().AddEventHandler(funcs)
}

func (master *OsdnMaster) handleAddOrUpdateNetNamespace(obj, _ interface{}, eventType watch.EventType) {
	netns := obj.(*networkapi.NetNamespace)
	glog.V(5).Infof("Watch %s event for NetNamespace %q", eventType, netns.Name)

	err := master.vnids.updateVNID(master.networkClient, netns)
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("Error updating netid: %v", err))
	}
}
<|endoftext|>


<commit_before>package followingfeed

import (
	"fmt"
	"koding/messaging/rabbitmq"
	"socialapi/config"

	"github.com/streadway/amqp"
)

var (
	Consumer        *rabbitmq.Consumer
	WorkerQueueName = "TopicFeedWorkerQueue"
	WorkerQueueTag  = "TopicFeedWorkerConsumer"
)

func Listen(rmq *rabbitmq.RabbitMQ, startHandler func() func(delivery amqp.Delivery)) {
	exchange := rabbitmq.Exchange{
		Name:    config.EventExchangeName,
		Type:    "fanout",
		Durable: true,
	}

	queue := rabbitmq.Queue{
		Name:    WorkerQueueName,
		Durable: true,
	}

	binding := rabbitmq.BindingOptions{
		RoutingKey: "",
	}

	consumerOptions := rabbitmq.ConsumerOptions{
		Tag: WorkerQueueTag,
	}

	Consumer, err := rmq.NewConsumer(exchange, queue, binding, consumerOptions)
	if err != nil {
		fmt.Print(err)
		return
	}

	err = Consumer.QOS(10)
	if err != nil {
		panic(err)
	}

	Consumer.RegisterSignalHandler()
	Consumer.Consume(startHandler())
}
<commit_msg>Social: fix package name<commit_after>package topicfeed

import (
	"fmt"
	"koding/messaging/rabbitmq"
	"socialapi/config"

	"github.com/streadway/amqp"
)

var (
	Consumer        *rabbitmq.Consumer
	WorkerQueueName = "TopicFeedWorkerQueue"
	WorkerQueueTag  = "TopicFeedWorkerConsumer"
)

func Listen(rmq *rabbitmq.RabbitMQ, startHandler func() func(delivery amqp.Delivery)) {
	exchange := rabbitmq.Exchange{
		Name:    config.EventExchangeName,
		Type:    "fanout",
		Durable: true,
	}

	queue := rabbitmq.Queue{
		Name:    WorkerQueueName,
		Durable: true,
	}

	binding := rabbitmq.BindingOptions{
		RoutingKey: "",
	}

	consumerOptions := rabbitmq.ConsumerOptions{
		Tag: WorkerQueueTag,
	}

	Consumer, err := rmq.NewConsumer(exchange, queue, binding, consumerOptions)
	if err != nil {
		fmt.Print(err)
		return
	}

	err = Consumer.QOS(10)
	if err != nil {
		panic(err)
	}

	Consumer.RegisterSignalHandler()
	Consumer.Consume(startHandler())
}
<|endoftext|>


<commit_before>package oauth2

import (
	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/hellofresh/janus/pkg/router"
	"github.com/rs/cors"
	log "github.com/sirupsen/logrus"
	"github.com/ulule/limiter"
	"github.com/ulule/limiter/drivers/middleware/stdlib"
	storeMemory "github.com/ulule/limiter/drivers/store/memory"
)

// OAuthLoader handles the loading of the api specs
type OAuthLoader struct {
	register *proxy.Register
}

// NewOAuthLoader creates a new instance of the Loader
func NewOAuthLoader(register *proxy.Register) *OAuthLoader {
	return &OAuthLoader{register}
}

// LoadDefinitions loads all oauth servers from a data source
func (m *OAuthLoader) LoadDefinitions(repo Repository) {
	oAuthServers := m.getOAuthServers(repo)
	m.RegisterOAuthServers(oAuthServers, repo)
}

// RegisterOAuthServers register many oauth servers
func (m *OAuthLoader) RegisterOAuthServers(oauthServers []*Spec, repo Repository) {
	log.Debug("Loading OAuth servers configurations")

	for _, oauthServer := range oauthServers {
		var mw []router.Constructor

		logger := log.WithField("name", oauthServer.Name)
		logger.Debug("Registering OAuth server")

		corsHandler := cors.New(cors.Options{
		AllowedOrigins:     oauthServer.CorsMeta.Domains,
		AllowedMethods:     oauthServer.CorsMeta.Methods,
		AllowedHeaders:     oauthServer.CorsMeta.RequestHeaders,
		ExposedHeaders:     oauthServer.CorsMeta.ExposedHeaders,
		OptionsPassthrough: oauthServer.CorsMeta.OptionsPassthrough,
		AllowCredentials:   true,
		}).Handler

		mw = append(mw, corsHandler)

		if oauthServer.RateLimit.Enabled {
			rate, err := limiter.NewRateFromFormatted(oauthServer.RateLimit.Limit)
			if err != nil {
				logger.WithError(err).Error("Not able to create rate limit")
			}

			limiterStore := storeMemory.NewStore()
			limiterInstance := limiter.New(limiterStore, rate)
			rateLimitHandler := stdlib.NewMiddleware(limiterInstance).Handler

			mw = append(mw, rateLimitHandler)
		}

		endpoints := map[*proxy.RouterDefinition][]router.Constructor{
			proxy.NewRouterDefinition(oauthServer.Endpoints.Authorize):    mw,
			proxy.NewRouterDefinition(oauthServer.Endpoints.Token):        append(mw, NewSecretMiddleware(oauthServer).Handler),
			proxy.NewRouterDefinition(oauthServer.Endpoints.Introspect):   mw,
			proxy.NewRouterDefinition(oauthServer.Endpoints.Revoke):       mw,
			proxy.NewRouterDefinition(oauthServer.ClientEndpoints.Create): mw,
			proxy.NewRouterDefinition(oauthServer.ClientEndpoints.Remove): mw,
		}

		m.registerRoutes(endpoints)
		logger.Debug("OAuth server registered")
	}

	log.Debug("Done loading OAuth servers configurations")
}

func (m *OAuthLoader) getOAuthServers(repo Repository) []*Spec {
	oauthServers, err := repo.FindAll()
	if err != nil {
		log.Panic(err)
	}

	var specs []*Spec
	for _, oauthServer := range oauthServers {
		spec := new(Spec)
		spec.OAuth = oauthServer
		manager, err := m.getManager(oauthServer)
		if nil != err {
			log.WithError(err).Error("Oauth definition is not well configured, skipping...")
			continue
		}
		spec.Manager = manager
		specs = append(specs, spec)
	}

	return specs
}

func (m *OAuthLoader) getManager(oauthServer *OAuth) (Manager, error) {
	managerType, err := ParseType(oauthServer.TokenStrategy.Name)
	if nil != err {
		return nil, err
	}

	return NewManagerFactory(oauthServer).Build(managerType)
}

func (m *OAuthLoader) registerRoutes(endpoints map[*proxy.RouterDefinition][]router.Constructor) {
	for endpoint, middleware := range endpoints {
		if endpoint.Definition == nil || endpoint.Definition.ListenPath == "" {
			log.Debug("Endpoint not registered")
			continue
		}

		for _, mw := range middleware {
			endpoint.AddMiddleware(mw)
		}

		l := log.WithField("listen_path", endpoint.ListenPath)
		l.Debug("Registering OAuth endpoint")
		if isValid, err := endpoint.Validate(); isValid && err == nil {
			m.register.Add(endpoint)
			l.Debug("Endpoint registered")
		} else {
			l.WithError(err).Error("Error when registering endpoint")
		}
	}
}
<commit_msg>Fixed code formatting<commit_after>package oauth2

import (
	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/hellofresh/janus/pkg/router"
	"github.com/rs/cors"
	log "github.com/sirupsen/logrus"
	"github.com/ulule/limiter"
	"github.com/ulule/limiter/drivers/middleware/stdlib"
	storeMemory "github.com/ulule/limiter/drivers/store/memory"
)

// OAuthLoader handles the loading of the api specs
type OAuthLoader struct {
	register *proxy.Register
}

// NewOAuthLoader creates a new instance of the Loader
func NewOAuthLoader(register *proxy.Register) *OAuthLoader {
	return &OAuthLoader{register}
}

// LoadDefinitions loads all oauth servers from a data source
func (m *OAuthLoader) LoadDefinitions(repo Repository) {
	oAuthServers := m.getOAuthServers(repo)
	m.RegisterOAuthServers(oAuthServers, repo)
}

// RegisterOAuthServers register many oauth servers
func (m *OAuthLoader) RegisterOAuthServers(oauthServers []*Spec, repo Repository) {
	log.Debug("Loading OAuth servers configurations")

	for _, oauthServer := range oauthServers {
		var mw []router.Constructor

		logger := log.WithField("name", oauthServer.Name)
		logger.Debug("Registering OAuth server")

		corsHandler := cors.New(cors.Options{
			AllowedOrigins:     oauthServer.CorsMeta.Domains,
			AllowedMethods:     oauthServer.CorsMeta.Methods,
			AllowedHeaders:     oauthServer.CorsMeta.RequestHeaders,
			ExposedHeaders:     oauthServer.CorsMeta.ExposedHeaders,
			OptionsPassthrough: oauthServer.CorsMeta.OptionsPassthrough,
			AllowCredentials:   true,
		}).Handler

		mw = append(mw, corsHandler)

		if oauthServer.RateLimit.Enabled {
			rate, err := limiter.NewRateFromFormatted(oauthServer.RateLimit.Limit)
			if err != nil {
				logger.WithError(err).Error("Not able to create rate limit")
			}

			limiterStore := storeMemory.NewStore()
			limiterInstance := limiter.New(limiterStore, rate)
			rateLimitHandler := stdlib.NewMiddleware(limiterInstance).Handler

			mw = append(mw, rateLimitHandler)
		}

		endpoints := map[*proxy.RouterDefinition][]router.Constructor{
			proxy.NewRouterDefinition(oauthServer.Endpoints.Authorize):    mw,
			proxy.NewRouterDefinition(oauthServer.Endpoints.Token):        append(mw, NewSecretMiddleware(oauthServer).Handler),
			proxy.NewRouterDefinition(oauthServer.Endpoints.Introspect):   mw,
			proxy.NewRouterDefinition(oauthServer.Endpoints.Revoke):       mw,
			proxy.NewRouterDefinition(oauthServer.ClientEndpoints.Create): mw,
			proxy.NewRouterDefinition(oauthServer.ClientEndpoints.Remove): mw,
		}

		m.registerRoutes(endpoints)
		logger.Debug("OAuth server registered")
	}

	log.Debug("Done loading OAuth servers configurations")
}

func (m *OAuthLoader) getOAuthServers(repo Repository) []*Spec {
	oauthServers, err := repo.FindAll()
	if err != nil {
		log.Panic(err)
	}

	var specs []*Spec
	for _, oauthServer := range oauthServers {
		spec := new(Spec)
		spec.OAuth = oauthServer
		manager, err := m.getManager(oauthServer)
		if nil != err {
			log.WithError(err).Error("Oauth definition is not well configured, skipping...")
			continue
		}
		spec.Manager = manager
		specs = append(specs, spec)
	}

	return specs
}

func (m *OAuthLoader) getManager(oauthServer *OAuth) (Manager, error) {
	managerType, err := ParseType(oauthServer.TokenStrategy.Name)
	if nil != err {
		return nil, err
	}

	return NewManagerFactory(oauthServer).Build(managerType)
}

func (m *OAuthLoader) registerRoutes(endpoints map[*proxy.RouterDefinition][]router.Constructor) {
	for endpoint, middleware := range endpoints {
		if endpoint.Definition == nil || endpoint.Definition.ListenPath == "" {
			log.Debug("Endpoint not registered")
			continue
		}

		for _, mw := range middleware {
			endpoint.AddMiddleware(mw)
		}

		l := log.WithField("listen_path", endpoint.ListenPath)
		l.Debug("Registering OAuth endpoint")
		if isValid, err := endpoint.Validate(); isValid && err == nil {
			m.register.Add(endpoint)
			l.Debug("Endpoint registered")
		} else {
			l.WithError(err).Error("Error when registering endpoint")
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Skaffold Authors

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

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

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

package deploy

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"
)

type HelmDeployer struct {
	*latest.HelmDeploy

	kubeContext string
	namespace   string
	defaultRepo string
}

// NewHelmDeployer returns a new HelmDeployer for a DeployConfig filled
// with the needed configuration for `helm`
func NewHelmDeployer(cfg *latest.HelmDeploy, kubeContext string, namespace string, defaultRepo string) *HelmDeployer {
	return &HelmDeployer{
		HelmDeploy:  cfg,
		kubeContext: kubeContext,
		namespace:   namespace,
		defaultRepo: defaultRepo,
	}
}

func (h *HelmDeployer) Labels() map[string]string {
	return map[string]string{
		constants.Labels.Deployer: "helm",
	}
}

func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]Artifact, error) {
	deployResults := []Artifact{}
	for _, r := range h.Releases {
		results, err := h.deployRelease(ctx, out, r, builds)
		if err != nil {
			releaseName, _ := evaluateReleaseName(r.Name)
			return deployResults, errors.Wrapf(err, "deploying %s", releaseName)
		}
		deployResults = append(deployResults, results...)
	}
	return deployResults, nil
}

func (h *HelmDeployer) Dependencies() ([]string, error) {
	var deps []string
	for _, release := range h.Releases {
		deps = append(deps, release.ValuesFiles...)
		chartDepsDir := filepath.Join(release.ChartPath, "charts")
		err := filepath.Walk(release.ChartPath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return errors.Wrapf(err, "failure accessing path '%s'", path)
			}
			if !info.IsDir() && !strings.HasPrefix(path, chartDepsDir) {
				deps = append(deps, path)
			}
			return nil
		})
		if err != nil {
			return deps, errors.Wrap(err, "issue walking releases")
		}
	}
	sort.Strings(deps)
	return deps, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (h *HelmDeployer) Cleanup(ctx context.Context, out io.Writer) error {
	for _, r := range h.Releases {
		if err := h.deleteRelease(ctx, out, r); err != nil {
			releaseName, _ := evaluateReleaseName(r.Name)
			return errors.Wrapf(err, "deploying %s", releaseName)
		}
	}
	return nil
}

func (h *HelmDeployer) helm(ctx context.Context, out io.Writer, arg ...string) error {
	args := append([]string{"--kube-context", h.kubeContext}, arg...)

	cmd := exec.CommandContext(ctx, "helm", args...)
	cmd.Stdout = out
	cmd.Stderr = out

	return util.RunCmd(cmd)
}

func (h *HelmDeployer) deployRelease(ctx context.Context, out io.Writer, r latest.HelmRelease, builds []build.Artifact) ([]Artifact, error) {
	isInstalled := true

	releaseName, err := evaluateReleaseName(r.Name)
	if err != nil {
		return nil, errors.Wrap(err, "cannot parse the release name template")
	}
	if err := h.helm(ctx, out, "get", releaseName); err != nil {
		color.Red.Fprintf(out, "Helm release %s not installed. Installing...\n", releaseName)
		isInstalled = false
	}
	params, err := h.joinTagsToBuildResult(builds, r.Values)
	if err != nil {
		return nil, errors.Wrap(err, "matching build results to chart values")
	}

	var setOpts []string
	for k, v := range params {
		setOpts = append(setOpts, "--set")
		if r.ImageStrategy.HelmImageConfig.HelmConventionConfig != nil {
			dockerRef, err := docker.ParseReference(v.Tag)
			if err != nil {
				return nil, errors.Wrapf(err, "cannot parse the docker image reference %s", v.Tag)
			}
			imageRepositoryTag := fmt.Sprintf("%s.repository=%s,%s.tag=%s", k, dockerRef.BaseName, k, dockerRef.Tag)
			setOpts = append(setOpts, imageRepositoryTag)
		} else {
			setOpts = append(setOpts, fmt.Sprintf("%s=%s", k, v.Tag))
		}
	}

	// First build dependencies.
	logrus.Infof("Building helm dependencies...")
	if err := h.helm(ctx, out, "dep", "build", r.ChartPath); err != nil {
		return nil, errors.Wrap(err, "building helm dependencies")
	}

	var args []string
	if !isInstalled {
		args = append(args, "install", "--name", releaseName)
	} else {
		args = append(args, "upgrade", releaseName)
		if r.RecreatePods {
			args = append(args, "--recreate-pods")
		}
	}

	// There are 2 strategies:
	// 1) Deploy chart directly from filesystem path or from repository
	//    (like stable/kubernetes-dashboard). Version only applies to a
	//    chart from repository.
	// 2) Package chart into a .tgz archive with specific version and then deploy
	//    that packaged chart. This way user can apply any version and appVersion
	//    for the chart.
	if r.Packaged == nil {
		if r.Version != "" {
			args = append(args, "--version", r.Version)
		}
		args = append(args, r.ChartPath)
	} else {
		chartPath, err := h.packageChart(ctx, r)
		if err != nil {
			return nil, errors.WithMessage(err, "cannot package chart")
		}
		args = append(args, chartPath)
	}

	var ns string
	if h.namespace != "" {
		ns = h.namespace
	} else if r.Namespace != "" {
		ns = r.Namespace
	}
	if ns != "" {
		args = append(args, "--namespace", ns)
	}
	if len(r.Overrides) != 0 {
		overrides, err := yaml.Marshal(r.Overrides)
		if err != nil {
			return nil, errors.Wrap(err, "cannot marshal overrides to create overrides values.yaml")
		}
		overridesFile, err := os.Create(constants.HelmOverridesFilename)
		if err != nil {
			return nil, errors.Wrapf(err, "cannot create file %s", constants.HelmOverridesFilename)
		}
		defer func() {
			overridesFile.Close()
			os.Remove(constants.HelmOverridesFilename)
		}()
		if _, err := overridesFile.WriteString(string(overrides)); err != nil {
			return nil, errors.Wrapf(err, "failed to write file %s", constants.HelmOverridesFilename)
		}
		args = append(args, "-f", constants.HelmOverridesFilename)
	}
	for _, valuesFile := range r.ValuesFiles {
		args = append(args, "-f", valuesFile)
	}

	setValues := r.SetValues
	if setValues == nil {
		setValues = map[string]string{}
	}
	if len(r.SetValueTemplates) != 0 {
		envMap := map[string]string{}
		for idx, b := range builds {
			suffix := ""
			if idx > 0 {
				suffix = strconv.Itoa(idx + 1)
			}
			m := tag.CreateEnvVarMap(b.ImageName, extractTag(b.Tag))
			for k, v := range m {
				envMap[k+suffix] = v
			}
			color.Default.Fprintf(out, "EnvVarMap: %#v\n", envMap)
		}
		for k, v := range r.SetValueTemplates {
			t, err := util.ParseEnvTemplate(v)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to parse setValueTemplates")
			}
			result, err := util.ExecuteEnvTemplate(t, envMap)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to generate setValueTemplates")
			}
			setValues[k] = result
		}
	}
	for k, v := range setValues {
		setOpts = append(setOpts, "--set")
		setOpts = append(setOpts, fmt.Sprintf("%s=%s", k, v))
	}
	if r.Wait {
		args = append(args, "--wait")
	}
	args = append(args, setOpts...)

	helmErr := h.helm(ctx, out, args...)
	return h.getDeployResults(ctx, ns, releaseName), helmErr
}

// imageName if the given string includes a fully qualified docker image name then lets trim just the tag part out
func extractTag(imageName string) string {
	idx := strings.LastIndex(imageName, "/")
	if idx < 0 {
		return imageName
	}
	tag := imageName[idx+1:]
	idx = strings.Index(tag, ":")
	if idx > 0 {
		return tag[idx+1:]
	}
	return tag
}

// packageChart packages the chart and returns path to the chart archive file.
// If this function returns an error, it will always be wrapped.
func (h *HelmDeployer) packageChart(ctx context.Context, r latest.HelmRelease) (string, error) {
	tmp := os.TempDir()
	packageArgs := []string{"package", r.ChartPath, "--destination", tmp}
	if r.Packaged.Version != "" {
		v, err := concretize(r.Packaged.Version)
		if err != nil {
			return "", errors.Wrap(err, `concretize "packaged.version" template`)
		}
		packageArgs = append(packageArgs, "--version", v)
	}
	if r.Packaged.AppVersion != "" {
		av, err := concretize(r.Packaged.AppVersion)
		if err != nil {
			return "", errors.Wrap(err, `concretize "packaged.appVersion" template`)
		}
		packageArgs = append(packageArgs, "--app-version", av)
	}

	buf := &bytes.Buffer{}
	err := h.helm(ctx, buf, packageArgs...)
	output := strings.TrimSpace(buf.String())
	if err != nil {
		return "", errors.Wrapf(err, "package chart into a .tgz archive (%s)", output)
	}

	fpath, err := extractChartFilename(output, tmp)
	if err != nil {
		return "", err
	}

	return filepath.Join(tmp, fpath), nil
}

func (h *HelmDeployer) getReleaseInfo(ctx context.Context, release string) (*bufio.Reader, error) {
	var releaseInfo bytes.Buffer
	if err := h.helm(ctx, &releaseInfo, "get", release); err != nil {
		return nil, fmt.Errorf("error retrieving helm deployment info: %s", releaseInfo.String())
	}
	return bufio.NewReader(&releaseInfo), nil
}

// Retrieve info about all releases using helm get
// Skaffold labels will be applied to each deployed k8s object
// Since helm isn't always consistent with retrieving results, don't return errors here
func (h *HelmDeployer) getDeployResults(ctx context.Context, namespace string, release string) []Artifact {
	b, err := h.getReleaseInfo(ctx, release)
	if err != nil {
		logrus.Warnf(err.Error())
		return nil
	}
	return parseReleaseInfo(namespace, b)
}

func (h *HelmDeployer) deleteRelease(ctx context.Context, out io.Writer, r latest.HelmRelease) error {
	releaseName, err := evaluateReleaseName(r.Name)
	if err != nil {
		return errors.Wrap(err, "cannot parse the release name template")
	}

	if err := h.helm(ctx, out, "delete", releaseName, "--purge"); err != nil {
		logrus.Debugf("deleting release %s: %v\n", releaseName, err)
	}

	return nil
}

func (h *HelmDeployer) joinTagsToBuildResult(builds []build.Artifact, params map[string]string) (map[string]build.Artifact, error) {
	imageToBuildResult := map[string]build.Artifact{}
	for _, build := range builds {
		imageToBuildResult[build.ImageName] = build
	}

	paramToBuildResult := map[string]build.Artifact{}
	for param, imageName := range params {
		newImageName := util.SubstituteDefaultRepoIntoImage(h.defaultRepo, imageName)
		build, ok := imageToBuildResult[newImageName]
		if !ok {
			return nil, fmt.Errorf("no build present for %s", imageName)
		}
		paramToBuildResult[param] = build
	}
	return paramToBuildResult, nil
}

func evaluateReleaseName(nameTemplate string) (string, error) {
	tmpl, err := util.ParseEnvTemplate(nameTemplate)
	if err != nil {
		return "", errors.Wrap(err, "parsing template")
	}

	return util.ExecuteEnvTemplate(tmpl, nil)
}

// concretize parses and executes template s with OS environment variables.
// If s is not a template but a simple string, returns unchanged s.
func concretize(s string) (string, error) {
	tmpl, err := util.ParseEnvTemplate(s)
	if err != nil {
		return "", errors.Wrap(err, "parsing template")
	}

	tmpl.Option("missingkey=error")
	return util.ExecuteEnvTemplate(tmpl, nil)
}

func extractChartFilename(s, tmp string) (string, error) {
	s = strings.TrimSpace(s)
	idx := strings.Index(s, tmp)
	if idx == -1 {
		return "", errors.New("cannot locate packaged chart archive")
	}

	return s[idx+len(tmp):], nil
}
<commit_msg>Watch helm subcharts for changes too<commit_after>/*
Copyright 2018 The Skaffold Authors

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

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

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

package deploy

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"
)

type HelmDeployer struct {
	*latest.HelmDeploy

	kubeContext string
	namespace   string
	defaultRepo string
}

// NewHelmDeployer returns a new HelmDeployer for a DeployConfig filled
// with the needed configuration for `helm`
func NewHelmDeployer(cfg *latest.HelmDeploy, kubeContext string, namespace string, defaultRepo string) *HelmDeployer {
	return &HelmDeployer{
		HelmDeploy:  cfg,
		kubeContext: kubeContext,
		namespace:   namespace,
		defaultRepo: defaultRepo,
	}
}

func (h *HelmDeployer) Labels() map[string]string {
	return map[string]string{
		constants.Labels.Deployer: "helm",
	}
}

func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]Artifact, error) {
	deployResults := []Artifact{}
	for _, r := range h.Releases {
		results, err := h.deployRelease(ctx, out, r, builds)
		if err != nil {
			releaseName, _ := evaluateReleaseName(r.Name)
			return deployResults, errors.Wrapf(err, "deploying %s", releaseName)
		}
		deployResults = append(deployResults, results...)
	}
	return deployResults, nil
}

func (h *HelmDeployer) Dependencies() ([]string, error) {
	var deps []string
	for _, release := range h.Releases {
		deps = append(deps, release.ValuesFiles...)
		err := filepath.Walk(release.ChartPath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return errors.Wrapf(err, "failure accessing path '%s'", path)
			}
			if !info.IsDir() {
				deps = append(deps, path)
			}
			return nil
		})
		if err != nil {
			return deps, errors.Wrap(err, "issue walking releases")
		}
	}
	sort.Strings(deps)
	return deps, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (h *HelmDeployer) Cleanup(ctx context.Context, out io.Writer) error {
	for _, r := range h.Releases {
		if err := h.deleteRelease(ctx, out, r); err != nil {
			releaseName, _ := evaluateReleaseName(r.Name)
			return errors.Wrapf(err, "deploying %s", releaseName)
		}
	}
	return nil
}

func (h *HelmDeployer) helm(ctx context.Context, out io.Writer, arg ...string) error {
	args := append([]string{"--kube-context", h.kubeContext}, arg...)

	cmd := exec.CommandContext(ctx, "helm", args...)
	cmd.Stdout = out
	cmd.Stderr = out

	return util.RunCmd(cmd)
}

func (h *HelmDeployer) deployRelease(ctx context.Context, out io.Writer, r latest.HelmRelease, builds []build.Artifact) ([]Artifact, error) {
	isInstalled := true

	releaseName, err := evaluateReleaseName(r.Name)
	if err != nil {
		return nil, errors.Wrap(err, "cannot parse the release name template")
	}
	if err := h.helm(ctx, out, "get", releaseName); err != nil {
		color.Red.Fprintf(out, "Helm release %s not installed. Installing...\n", releaseName)
		isInstalled = false
	}
	params, err := h.joinTagsToBuildResult(builds, r.Values)
	if err != nil {
		return nil, errors.Wrap(err, "matching build results to chart values")
	}

	var setOpts []string
	for k, v := range params {
		setOpts = append(setOpts, "--set")
		if r.ImageStrategy.HelmImageConfig.HelmConventionConfig != nil {
			dockerRef, err := docker.ParseReference(v.Tag)
			if err != nil {
				return nil, errors.Wrapf(err, "cannot parse the docker image reference %s", v.Tag)
			}
			imageRepositoryTag := fmt.Sprintf("%s.repository=%s,%s.tag=%s", k, dockerRef.BaseName, k, dockerRef.Tag)
			setOpts = append(setOpts, imageRepositoryTag)
		} else {
			setOpts = append(setOpts, fmt.Sprintf("%s=%s", k, v.Tag))
		}
	}

	// First build dependencies.
	logrus.Infof("Building helm dependencies...")
	if err := h.helm(ctx, out, "dep", "build", r.ChartPath); err != nil {
		return nil, errors.Wrap(err, "building helm dependencies")
	}

	var args []string
	if !isInstalled {
		args = append(args, "install", "--name", releaseName)
	} else {
		args = append(args, "upgrade", releaseName)
		if r.RecreatePods {
			args = append(args, "--recreate-pods")
		}
	}

	// There are 2 strategies:
	// 1) Deploy chart directly from filesystem path or from repository
	//    (like stable/kubernetes-dashboard). Version only applies to a
	//    chart from repository.
	// 2) Package chart into a .tgz archive with specific version and then deploy
	//    that packaged chart. This way user can apply any version and appVersion
	//    for the chart.
	if r.Packaged == nil {
		if r.Version != "" {
			args = append(args, "--version", r.Version)
		}
		args = append(args, r.ChartPath)
	} else {
		chartPath, err := h.packageChart(ctx, r)
		if err != nil {
			return nil, errors.WithMessage(err, "cannot package chart")
		}
		args = append(args, chartPath)
	}

	var ns string
	if h.namespace != "" {
		ns = h.namespace
	} else if r.Namespace != "" {
		ns = r.Namespace
	}
	if ns != "" {
		args = append(args, "--namespace", ns)
	}
	if len(r.Overrides) != 0 {
		overrides, err := yaml.Marshal(r.Overrides)
		if err != nil {
			return nil, errors.Wrap(err, "cannot marshal overrides to create overrides values.yaml")
		}
		overridesFile, err := os.Create(constants.HelmOverridesFilename)
		if err != nil {
			return nil, errors.Wrapf(err, "cannot create file %s", constants.HelmOverridesFilename)
		}
		defer func() {
			overridesFile.Close()
			os.Remove(constants.HelmOverridesFilename)
		}()
		if _, err := overridesFile.WriteString(string(overrides)); err != nil {
			return nil, errors.Wrapf(err, "failed to write file %s", constants.HelmOverridesFilename)
		}
		args = append(args, "-f", constants.HelmOverridesFilename)
	}
	for _, valuesFile := range r.ValuesFiles {
		args = append(args, "-f", valuesFile)
	}

	setValues := r.SetValues
	if setValues == nil {
		setValues = map[string]string{}
	}
	if len(r.SetValueTemplates) != 0 {
		envMap := map[string]string{}
		for idx, b := range builds {
			suffix := ""
			if idx > 0 {
				suffix = strconv.Itoa(idx + 1)
			}
			m := tag.CreateEnvVarMap(b.ImageName, extractTag(b.Tag))
			for k, v := range m {
				envMap[k+suffix] = v
			}
			color.Default.Fprintf(out, "EnvVarMap: %#v\n", envMap)
		}
		for k, v := range r.SetValueTemplates {
			t, err := util.ParseEnvTemplate(v)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to parse setValueTemplates")
			}
			result, err := util.ExecuteEnvTemplate(t, envMap)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to generate setValueTemplates")
			}
			setValues[k] = result
		}
	}
	for k, v := range setValues {
		setOpts = append(setOpts, "--set")
		setOpts = append(setOpts, fmt.Sprintf("%s=%s", k, v))
	}
	if r.Wait {
		args = append(args, "--wait")
	}
	args = append(args, setOpts...)

	helmErr := h.helm(ctx, out, args...)
	return h.getDeployResults(ctx, ns, releaseName), helmErr
}

// imageName if the given string includes a fully qualified docker image name then lets trim just the tag part out
func extractTag(imageName string) string {
	idx := strings.LastIndex(imageName, "/")
	if idx < 0 {
		return imageName
	}
	tag := imageName[idx+1:]
	idx = strings.Index(tag, ":")
	if idx > 0 {
		return tag[idx+1:]
	}
	return tag
}

// packageChart packages the chart and returns path to the chart archive file.
// If this function returns an error, it will always be wrapped.
func (h *HelmDeployer) packageChart(ctx context.Context, r latest.HelmRelease) (string, error) {
	tmp := os.TempDir()
	packageArgs := []string{"package", r.ChartPath, "--destination", tmp}
	if r.Packaged.Version != "" {
		v, err := concretize(r.Packaged.Version)
		if err != nil {
			return "", errors.Wrap(err, `concretize "packaged.version" template`)
		}
		packageArgs = append(packageArgs, "--version", v)
	}
	if r.Packaged.AppVersion != "" {
		av, err := concretize(r.Packaged.AppVersion)
		if err != nil {
			return "", errors.Wrap(err, `concretize "packaged.appVersion" template`)
		}
		packageArgs = append(packageArgs, "--app-version", av)
	}

	buf := &bytes.Buffer{}
	err := h.helm(ctx, buf, packageArgs...)
	output := strings.TrimSpace(buf.String())
	if err != nil {
		return "", errors.Wrapf(err, "package chart into a .tgz archive (%s)", output)
	}

	fpath, err := extractChartFilename(output, tmp)
	if err != nil {
		return "", err
	}

	return filepath.Join(tmp, fpath), nil
}

func (h *HelmDeployer) getReleaseInfo(ctx context.Context, release string) (*bufio.Reader, error) {
	var releaseInfo bytes.Buffer
	if err := h.helm(ctx, &releaseInfo, "get", release); err != nil {
		return nil, fmt.Errorf("error retrieving helm deployment info: %s", releaseInfo.String())
	}
	return bufio.NewReader(&releaseInfo), nil
}

// Retrieve info about all releases using helm get
// Skaffold labels will be applied to each deployed k8s object
// Since helm isn't always consistent with retrieving results, don't return errors here
func (h *HelmDeployer) getDeployResults(ctx context.Context, namespace string, release string) []Artifact {
	b, err := h.getReleaseInfo(ctx, release)
	if err != nil {
		logrus.Warnf(err.Error())
		return nil
	}
	return parseReleaseInfo(namespace, b)
}

func (h *HelmDeployer) deleteRelease(ctx context.Context, out io.Writer, r latest.HelmRelease) error {
	releaseName, err := evaluateReleaseName(r.Name)
	if err != nil {
		return errors.Wrap(err, "cannot parse the release name template")
	}

	if err := h.helm(ctx, out, "delete", releaseName, "--purge"); err != nil {
		logrus.Debugf("deleting release %s: %v\n", releaseName, err)
	}

	return nil
}

func (h *HelmDeployer) joinTagsToBuildResult(builds []build.Artifact, params map[string]string) (map[string]build.Artifact, error) {
	imageToBuildResult := map[string]build.Artifact{}
	for _, build := range builds {
		imageToBuildResult[build.ImageName] = build
	}

	paramToBuildResult := map[string]build.Artifact{}
	for param, imageName := range params {
		newImageName := util.SubstituteDefaultRepoIntoImage(h.defaultRepo, imageName)
		build, ok := imageToBuildResult[newImageName]
		if !ok {
			return nil, fmt.Errorf("no build present for %s", imageName)
		}
		paramToBuildResult[param] = build
	}
	return paramToBuildResult, nil
}

func evaluateReleaseName(nameTemplate string) (string, error) {
	tmpl, err := util.ParseEnvTemplate(nameTemplate)
	if err != nil {
		return "", errors.Wrap(err, "parsing template")
	}

	return util.ExecuteEnvTemplate(tmpl, nil)
}

// concretize parses and executes template s with OS environment variables.
// If s is not a template but a simple string, returns unchanged s.
func concretize(s string) (string, error) {
	tmpl, err := util.ParseEnvTemplate(s)
	if err != nil {
		return "", errors.Wrap(err, "parsing template")
	}

	tmpl.Option("missingkey=error")
	return util.ExecuteEnvTemplate(tmpl, nil)
}

func extractChartFilename(s, tmp string) (string, error) {
	s = strings.TrimSpace(s)
	idx := strings.Index(s, tmp)
	if idx == -1 {
		return "", errors.New("cannot locate packaged chart archive")
	}

	return s[idx+len(tmp):], nil
}
<|endoftext|>


<commit_before>package storage

import (
	"fmt"
	"sort"
	"time"

	"github.com/HeavyHorst/knowledgebase/pkg/models"
	"github.com/HeavyHorst/knowledgebase/pkg/ulid"
	"github.com/blevesearch/bleve"
	"github.com/pkg/errors"
	"github.com/russross/blackfriday"
	"github.com/timshannon/bolthold"
)

var articleIndex bleve.Index

func init() {
	var err error
	amapping := bleve.NewIndexMapping()
	articleIndex, err = bleve.Open("data/article.bleve")
	if err != nil {
		if err == bleve.ErrorIndexPathDoesNotExist {
			articleIndex, err = bleve.New("data/article.bleve", amapping)
			if err != nil {
				panic(err)
			}
		} else {
			panic(err)
		}
	}
}

type ArticleStore struct {
	store     *bolthold.Store
	userStore *UserStore
}

func newArticleStore(store *bolthold.Store, userStore *UserStore) (*ArticleStore, error) {
	return &ArticleStore{
		store:     store,
		userStore: userStore,
	}, nil
}

func (b *ArticleStore) updateAllAuthors(author models.User) error {
	return b.store.UpdateMatching(&models.Article{}, bolthold.Where("Authors").MatchFunc(func(ra *bolthold.RecordAccess) (bool, error) {
		record := ra.Record()
		article, ok := record.(*models.Article)
		if ok {
			for _, v := range article.Authors {
				if v.Username == author.Username {
					return true, nil
				}
			}
		}
		return false, nil

	}), func(record interface{}) error {
		update, ok := record.(*models.Article) // record will always be a pointer
		if !ok {
			return fmt.Errorf("Record isn't the correct type!  Wanted &models.Article, got %T", record)
		}

		for k := range update.Authors {
			if update.Authors[k].Username == author.Username {
				update.Authors[k] = author.UserInfo
			}
		}

		return nil
	})
}

func (b *ArticleStore) GetArticle(id string) (models.Article, error) {
	var art models.Article
	if err := b.store.Get(id, &art); err != nil {
		return art, errors.Wrapf(err, "couldn't get article %s", id)
	}

	if art.Authors == nil {
		art.Authors = make([]models.UserInfo, 0)
	}

	return art, nil
}

func (b *ArticleStore) ListArticles(limit, offset int, sortBy string, reverse bool) ([]models.Article, int, error) {
	var result []models.Article

	err := b.store.Find(&result, nil)
	count := len(result)
	end := offset + limit
	if end >= count {
		end = count
	}

	switch sortBy {
	case "title":
		sort.Slice(result, func(i, j int) bool {
			return (result[i].Title < result[j].Title) && !reverse
		})
	case "description":
		sort.Slice(result, func(i, j int) bool {
			return (result[i].Short < result[j].Short) && !reverse
		})
	case "last_modified":
		sort.Slice(result, func(i, j int) bool {
			return (result[i].LastModified.After(result[j].LastModified)) && !reverse
		})
	}

	subRes := result[offset:end]
	// we don't want the complete article in the listing
	for k := range subRes {
		subRes[k].Article = ""
		if subRes[k].Authors == nil {
			subRes[k].Authors = make([]models.UserInfo, 0)
		}
	}

	return subRes, count, errors.Wrap(err, "couldn't get list of articles")
}

func (b *ArticleStore) ListArticlesForCategory(catID string) ([]models.Article, error) {
	var result []models.Article
	err := b.store.Find(&result, bolthold.Where("Category").Eq(catID))

	for k := range result {
		result[k].Article = ""
		if result[k].Authors == nil {
			result[k].Authors = make([]models.UserInfo, 0)
		}
	}

	return result, errors.Wrapf(err, "couldn't get articles for category %s", catID)
}

func (b *ArticleStore) GetArticleHistory(artID string) ([]models.ArticleHistoryEntry, error) {
	var result []models.ArticleHistoryEntry
	err := b.store.Find(&result, bolthold.Where("ArticleID").Eq(artID))
	return result, errors.Wrapf(err, "couldn't get history for article %s", artID)
}

func (b *ArticleStore) upsertArticle(art models.Article, typ insertType, author models.User) error {
	var err error
	art.LastModified = time.Now()

	b.store.Insert(ulid.GetULID(), models.ArticleHistoryEntry{
		Timestamp:  art.LastModified,
		ModifiedBy: author.Username,
		ArticleID:  art.ID,
	})

	art.Authors = append(art.Authors, author.UserInfo)
	if len(art.Authors) >= 3 {
		art.Authors = art.Authors[len(art.Authors)-3:]
	}

	if len(art.Authors) >= 2 {
		a := art.Authors[len(art.Authors)-1]
		b := art.Authors[len(art.Authors)-2]

		if a.FirstName == b.FirstName && a.LastName == b.LastName {
			art.Authors = art.Authors[:len(art.Authors)-1]
		}
	}

	switch typ {
	case insertTypeCreate:
		err = b.store.Insert(art.ID, art)
	case insertTypeUpdate:
		err = b.store.Update(art.ID, art)
	}

	if err != nil {
		return errors.Wrapf(err, "couldn't insert %s into the store", art.ID)
	}

	r := blackfriday.MarkdownCommon([]byte(art.Article))
	art.Article = htmlToText(r)

	err = articleIndex.Index(art.ID, art)
	if err != nil {
		return errors.Wrapf(err, "couldn't add %s to the fulltext index", art.ID)
	}

	return nil
}

func (b *ArticleStore) CreateArticle(art models.Article, author models.User) error {
	return b.upsertArticle(art, insertTypeCreate, author)
}

func (b *ArticleStore) UpdateArticle(art models.Article, author models.User) error {
	return b.upsertArticle(art, insertTypeUpdate, author)
}

func (b *ArticleStore) DeleteArticle(art models.Article) error {
	// delete from store
	err := b.store.Delete(art.ID, art)
	if err != nil {
		return errors.Wrapf(err, "couldn't delete %s from the store", art.ID)
	}

	// delete the article change history
	err = b.store.DeleteMatching(&models.ArticleHistoryEntry{}, bolthold.Where("ArticleID").Eq(art.ID))
	if err != nil {
		return errors.Wrapf(err, "couldn't delete the history for %s", art.ID)
	}

	// delete from index
	err = articleIndex.Delete(art.ID)
	if err != nil {
		return errors.Wrapf(err, "couldn't delete %s from the fulltext index", art.ID)
	}

	return nil
}

func (b *ArticleStore) SearchArticles(q string) ([]models.Article, error) {
	query := bleve.NewQueryStringQuery(q)
	search := bleve.NewSearchRequestOptions(query, 150, 0, false)
	search.Highlight = bleve.NewHighlightWithStyle("html")
	searchResults, err := articleIndex.Search(search)
	if err != nil {
		return nil, errors.Wrap(err, "bleve index search failed")
	}

	var lastErrs error
	arts := make([]models.Article, 0, len(searchResults.Hits))

	for _, v := range searchResults.Hits {
		art, err := b.GetArticle(v.ID)
		if err != nil {
			lastErrs = errors.Wrap(lastErrs, err.Error())
		} else {
			art.Article = ""
			art.Fragments = v.Fragments
			arts = append(arts, art)
		}
	}
	return arts, lastErrs
}
<commit_msg>Sort the articles by the last modification date<commit_after>package storage

import (
	"fmt"
	"sort"
	"time"

	"github.com/HeavyHorst/knowledgebase/pkg/models"
	"github.com/HeavyHorst/knowledgebase/pkg/ulid"
	"github.com/blevesearch/bleve"
	"github.com/pkg/errors"
	"github.com/russross/blackfriday"
	"github.com/timshannon/bolthold"
)

var articleIndex bleve.Index

func init() {
	var err error
	amapping := bleve.NewIndexMapping()
	articleIndex, err = bleve.Open("data/article.bleve")
	if err != nil {
		if err == bleve.ErrorIndexPathDoesNotExist {
			articleIndex, err = bleve.New("data/article.bleve", amapping)
			if err != nil {
				panic(err)
			}
		} else {
			panic(err)
		}
	}
}

type ArticleStore struct {
	store     *bolthold.Store
	userStore *UserStore
}

func newArticleStore(store *bolthold.Store, userStore *UserStore) (*ArticleStore, error) {
	return &ArticleStore{
		store:     store,
		userStore: userStore,
	}, nil
}

func (b *ArticleStore) updateAllAuthors(author models.User) error {
	return b.store.UpdateMatching(&models.Article{}, bolthold.Where("Authors").MatchFunc(func(ra *bolthold.RecordAccess) (bool, error) {
		record := ra.Record()
		article, ok := record.(*models.Article)
		if ok {
			for _, v := range article.Authors {
				if v.Username == author.Username {
					return true, nil
				}
			}
		}
		return false, nil

	}), func(record interface{}) error {
		update, ok := record.(*models.Article) // record will always be a pointer
		if !ok {
			return fmt.Errorf("Record isn't the correct type!  Wanted &models.Article, got %T", record)
		}

		for k := range update.Authors {
			if update.Authors[k].Username == author.Username {
				update.Authors[k] = author.UserInfo
			}
		}

		return nil
	})
}

func (b *ArticleStore) GetArticle(id string) (models.Article, error) {
	var art models.Article
	if err := b.store.Get(id, &art); err != nil {
		return art, errors.Wrapf(err, "couldn't get article %s", id)
	}

	if art.Authors == nil {
		art.Authors = make([]models.UserInfo, 0)
	}

	return art, nil
}

func (b *ArticleStore) ListArticles(limit, offset int, sortBy string, reverse bool) ([]models.Article, int, error) {
	var result []models.Article

	err := b.store.Find(&result, nil)
	count := len(result)
	end := offset + limit
	if end >= count {
		end = count
	}

	switch sortBy {
	case "title":
		sort.Slice(result, func(i, j int) bool {
			return (result[i].Title < result[j].Title) && !reverse
		})
	case "description":
		sort.Slice(result, func(i, j int) bool {
			return (result[i].Short < result[j].Short) && !reverse
		})
	case "last_modified":
		sort.Slice(result, func(i, j int) bool {
			return (result[i].LastModified.After(result[j].LastModified)) && !reverse
		})
	}

	subRes := result[offset:end]
	// we don't want the complete article in the listing
	for k := range subRes {
		subRes[k].Article = ""
		if subRes[k].Authors == nil {
			subRes[k].Authors = make([]models.UserInfo, 0)
		}
	}

	return subRes, count, errors.Wrap(err, "couldn't get list of articles")
}

func (b *ArticleStore) ListArticlesForCategory(catID string) ([]models.Article, error) {
	var result []models.Article
	err := b.store.Find(&result, bolthold.Where("Category").Eq(catID))

	sort.Slice(result, func(i, j int) bool {
		return result[i].LastModified.After(result[j].LastModified)
	})

	for k := range result {
		result[k].Article = ""
		if result[k].Authors == nil {
			result[k].Authors = make([]models.UserInfo, 0)
		}
	}

	return result, errors.Wrapf(err, "couldn't get articles for category %s", catID)
}

func (b *ArticleStore) GetArticleHistory(artID string) ([]models.ArticleHistoryEntry, error) {
	var result []models.ArticleHistoryEntry
	err := b.store.Find(&result, bolthold.Where("ArticleID").Eq(artID))
	return result, errors.Wrapf(err, "couldn't get history for article %s", artID)
}

func (b *ArticleStore) upsertArticle(art models.Article, typ insertType, author models.User) error {
	var err error
	art.LastModified = time.Now()

	b.store.Insert(ulid.GetULID(), models.ArticleHistoryEntry{
		Timestamp:  art.LastModified,
		ModifiedBy: author.Username,
		ArticleID:  art.ID,
	})

	art.Authors = append(art.Authors, author.UserInfo)
	if len(art.Authors) >= 3 {
		art.Authors = art.Authors[len(art.Authors)-3:]
	}

	if len(art.Authors) >= 2 {
		a := art.Authors[len(art.Authors)-1]
		b := art.Authors[len(art.Authors)-2]

		if a.FirstName == b.FirstName && a.LastName == b.LastName {
			art.Authors = art.Authors[:len(art.Authors)-1]
		}
	}

	switch typ {
	case insertTypeCreate:
		err = b.store.Insert(art.ID, art)
	case insertTypeUpdate:
		err = b.store.Update(art.ID, art)
	}

	if err != nil {
		return errors.Wrapf(err, "couldn't insert %s into the store", art.ID)
	}

	r := blackfriday.MarkdownCommon([]byte(art.Article))
	art.Article = htmlToText(r)

	err = articleIndex.Index(art.ID, art)
	if err != nil {
		return errors.Wrapf(err, "couldn't add %s to the fulltext index", art.ID)
	}

	return nil
}

func (b *ArticleStore) CreateArticle(art models.Article, author models.User) error {
	return b.upsertArticle(art, insertTypeCreate, author)
}

func (b *ArticleStore) UpdateArticle(art models.Article, author models.User) error {
	return b.upsertArticle(art, insertTypeUpdate, author)
}

func (b *ArticleStore) DeleteArticle(art models.Article) error {
	// delete from store
	err := b.store.Delete(art.ID, art)
	if err != nil {
		return errors.Wrapf(err, "couldn't delete %s from the store", art.ID)
	}

	// delete the article change history
	err = b.store.DeleteMatching(&models.ArticleHistoryEntry{}, bolthold.Where("ArticleID").Eq(art.ID))
	if err != nil {
		return errors.Wrapf(err, "couldn't delete the history for %s", art.ID)
	}

	// delete from index
	err = articleIndex.Delete(art.ID)
	if err != nil {
		return errors.Wrapf(err, "couldn't delete %s from the fulltext index", art.ID)
	}

	return nil
}

func (b *ArticleStore) SearchArticles(q string) ([]models.Article, error) {
	query := bleve.NewQueryStringQuery(q)
	search := bleve.NewSearchRequestOptions(query, 150, 0, false)
	search.Highlight = bleve.NewHighlightWithStyle("html")
	searchResults, err := articleIndex.Search(search)
	if err != nil {
		return nil, errors.Wrap(err, "bleve index search failed")
	}

	var lastErrs error
	arts := make([]models.Article, 0, len(searchResults.Hits))

	for _, v := range searchResults.Hits {
		art, err := b.GetArticle(v.ID)
		if err != nil {
			lastErrs = errors.Wrap(lastErrs, err.Error())
		} else {
			art.Article = ""
			art.Fragments = v.Fragments
			arts = append(arts, art)
		}
	}
	return arts, lastErrs
}
<|endoftext|>


<commit_before>package testutils

import (
	"sync"
	"sync/atomic"

	k8sv1 "k8s.io/api/core/v1"
	"k8s.io/api/policy/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/tools/cache"
	framework "k8s.io/client-go/tools/cache/testing"
	"k8s.io/client-go/util/workqueue"

	v1 "kubevirt.io/client-go/api/v1"
	cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1alpha1"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
)

/*
MockWorkQueue is a helper workqueue which can be wrapped around
any RateLimitingInterface implementing queue. This allows synchronous
testing of the controller. The typical pattern is:

    MockQueue.ExpectAdd(3)
    vmiSource.Add(vmi)
    vmiSource.Add(vmi1)
    vmiSource.Add(vmi2)
    MockQueue.Wait()

This ensures that Source callbacks which are listening on vmiSource
enqueued three times an object. Since enqueing is typically the last
action in listener callbacks, we can assume that the wanted scenario for
a controller is set up, and an execution will process this scenario.
*/
type MockWorkQueue struct {
	workqueue.RateLimitingInterface
	addWG            *sync.WaitGroup
	rateLimitedEnque int32
}

func (q *MockWorkQueue) Add(obj interface{}) {
	q.RateLimitingInterface.Add(obj)
	if q.addWG != nil {
		q.addWG.Done()
	}
}

func (q *MockWorkQueue) AddRateLimited(item interface{}) {
	q.RateLimitingInterface.AddRateLimited(item)
	atomic.AddInt32(&q.rateLimitedEnque, 1)
}

func (q *MockWorkQueue) GetRateLimitedEnqueueCount() int {
	return int(atomic.LoadInt32(&q.rateLimitedEnque))
}

// ExpectAdds allows setting the amount of expected enqueues.
func (q *MockWorkQueue) ExpectAdds(diff int) {
	q.addWG = &sync.WaitGroup{}
	q.addWG.Add(diff)
}

// Wait waits until the expected amount of ExpectedAdds has happened.
// It will not block if there were no expectations set.
func (q *MockWorkQueue) Wait() {
	if q.addWG != nil {
		q.addWG.Wait()
		q.addWG = nil
	}
}

func NewMockWorkQueue(queue workqueue.RateLimitingInterface) *MockWorkQueue {
	return &MockWorkQueue{queue, nil, 0}
}

func NewFakeInformerFor(obj runtime.Object) (cache.SharedIndexInformer, *framework.FakeControllerSource) {
	objSource := framework.NewFakeControllerSource()
	objInformer := cache.NewSharedIndexInformer(objSource, obj, 0, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
	return objInformer, objSource
}

func NewFakeInformerWithIndexersFor(obj runtime.Object, indexers cache.Indexers) (cache.SharedIndexInformer, *framework.FakeControllerSource) {
	objSource := framework.NewFakeControllerSource()
	objInformer := cache.NewSharedIndexInformer(objSource, obj, 0, indexers)
	return objInformer, objSource
}

type VirtualMachineFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *VirtualMachineFeeder) Add(vmi *v1.VirtualMachineInstance) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(vmi)
	v.MockQueue.Wait()
}

func (v *VirtualMachineFeeder) Modify(vmi *v1.VirtualMachineInstance) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(vmi)
	v.MockQueue.Wait()
}

func (v *VirtualMachineFeeder) Delete(vmi *v1.VirtualMachineInstance) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(vmi)
	v.MockQueue.Wait()
}

func NewVirtualMachineFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *VirtualMachineFeeder {
	return &VirtualMachineFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type PodFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *PodFeeder) Add(pod *k8sv1.Pod) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(pod)
	v.MockQueue.Wait()
}

func (v *PodFeeder) Modify(pod *k8sv1.Pod) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(pod)
	v.MockQueue.Wait()
}

func (v *PodFeeder) Delete(pod *k8sv1.Pod) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(pod)
	v.MockQueue.Wait()
}

func NewPodFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *PodFeeder {
	return &PodFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type PodDisruptionBudgetFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *PodDisruptionBudgetFeeder) Add(pdb *v1beta1.PodDisruptionBudget) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(pdb)
	v.MockQueue.Wait()
}

func (v *PodDisruptionBudgetFeeder) Modify(pdb *v1beta1.PodDisruptionBudget) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(pdb)
	v.MockQueue.Wait()
}

func (v *PodDisruptionBudgetFeeder) Delete(pdb *v1beta1.PodDisruptionBudget) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(pdb)
	v.MockQueue.Wait()
}

func NewPodDisruptionBudgetFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *PodDisruptionBudgetFeeder {
	return &PodDisruptionBudgetFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type MigrationFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *MigrationFeeder) Add(migration *v1.VirtualMachineInstanceMigration) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(migration)
	v.MockQueue.Wait()
}

func (v *MigrationFeeder) Modify(migration *v1.VirtualMachineInstanceMigration) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(migration)
	v.MockQueue.Wait()
}

func (v *MigrationFeeder) Delete(migration *v1.VirtualMachineInstanceMigration) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(migration)
	v.MockQueue.Wait()
}

func NewMigrationFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *MigrationFeeder {
	return &MigrationFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type DomainFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *DomainFeeder) Add(vmi *api.Domain) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(vmi)
	v.MockQueue.Wait()
}

func (v *DomainFeeder) Modify(vmi *api.Domain) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(vmi)
	v.MockQueue.Wait()
}

func (v *DomainFeeder) Delete(vmi *api.Domain) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(vmi)
	v.MockQueue.Wait()
}

func NewDomainFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *DomainFeeder {
	return &DomainFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type DataVolumeFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *DataVolumeFeeder) Add(dataVolume *cdiv1.DataVolume) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(dataVolume)
	v.MockQueue.Wait()
}

func (v *DataVolumeFeeder) Modify(dataVolume *cdiv1.DataVolume) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(dataVolume)
	v.MockQueue.Wait()
}

func (v *DataVolumeFeeder) Delete(dataVolume *cdiv1.DataVolume) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(dataVolume)
	v.MockQueue.Wait()
}

func NewDataVolumeFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *DataVolumeFeeder {
	return &DataVolumeFeeder{
		MockQueue: queue,
		Source:    source,
	}
}
<commit_msg>mock_queue: add a mutex to avoid concurrent access to the WaitGroup This is mostly to make the race detector happy.<commit_after>package testutils

import (
	"sync"
	"sync/atomic"

	k8sv1 "k8s.io/api/core/v1"
	"k8s.io/api/policy/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/tools/cache"
	framework "k8s.io/client-go/tools/cache/testing"
	"k8s.io/client-go/util/workqueue"

	v1 "kubevirt.io/client-go/api/v1"
	cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1alpha1"
	"kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
)

/*
MockWorkQueue is a helper workqueue which can be wrapped around
any RateLimitingInterface implementing queue. This allows synchronous
testing of the controller. The typical pattern is:

    MockQueue.ExpectAdd(3)
    vmiSource.Add(vmi)
    vmiSource.Add(vmi1)
    vmiSource.Add(vmi2)
    MockQueue.Wait()

This ensures that Source callbacks which are listening on vmiSource
enqueued three times an object. Since enqueing is typically the last
action in listener callbacks, we can assume that the wanted scenario for
a controller is set up, and an execution will process this scenario.
*/
type MockWorkQueue struct {
	workqueue.RateLimitingInterface
	addWG            *sync.WaitGroup
	rateLimitedEnque int32
	wgLock           sync.Mutex
}

func (q *MockWorkQueue) Add(obj interface{}) {
	q.RateLimitingInterface.Add(obj)
	q.wgLock.Lock()
	defer q.wgLock.Unlock()
	if q.addWG != nil {
		q.addWG.Done()
	}
}

func (q *MockWorkQueue) AddRateLimited(item interface{}) {
	q.RateLimitingInterface.AddRateLimited(item)
	atomic.AddInt32(&q.rateLimitedEnque, 1)
}

func (q *MockWorkQueue) GetRateLimitedEnqueueCount() int {
	return int(atomic.LoadInt32(&q.rateLimitedEnque))
}

// ExpectAdds allows setting the amount of expected enqueues.
func (q *MockWorkQueue) ExpectAdds(diff int) {
	q.wgLock.Lock()
	defer q.wgLock.Unlock()
	q.addWG = &sync.WaitGroup{}
	q.addWG.Add(diff)
}

// Wait waits until the expected amount of ExpectedAdds has happened.
// It will not block if there were no expectations set.
func (q *MockWorkQueue) Wait() {
	if q.addWG != nil {
		q.addWG.Wait()
		q.addWG = nil
	}
}

func NewMockWorkQueue(queue workqueue.RateLimitingInterface) *MockWorkQueue {
	return &MockWorkQueue{queue, nil, 0, sync.Mutex{}}
}

func NewFakeInformerFor(obj runtime.Object) (cache.SharedIndexInformer, *framework.FakeControllerSource) {
	objSource := framework.NewFakeControllerSource()
	objInformer := cache.NewSharedIndexInformer(objSource, obj, 0, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
	return objInformer, objSource
}

func NewFakeInformerWithIndexersFor(obj runtime.Object, indexers cache.Indexers) (cache.SharedIndexInformer, *framework.FakeControllerSource) {
	objSource := framework.NewFakeControllerSource()
	objInformer := cache.NewSharedIndexInformer(objSource, obj, 0, indexers)
	return objInformer, objSource
}

type VirtualMachineFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *VirtualMachineFeeder) Add(vmi *v1.VirtualMachineInstance) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(vmi)
	v.MockQueue.Wait()
}

func (v *VirtualMachineFeeder) Modify(vmi *v1.VirtualMachineInstance) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(vmi)
	v.MockQueue.Wait()
}

func (v *VirtualMachineFeeder) Delete(vmi *v1.VirtualMachineInstance) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(vmi)
	v.MockQueue.Wait()
}

func NewVirtualMachineFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *VirtualMachineFeeder {
	return &VirtualMachineFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type PodFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *PodFeeder) Add(pod *k8sv1.Pod) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(pod)
	v.MockQueue.Wait()
}

func (v *PodFeeder) Modify(pod *k8sv1.Pod) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(pod)
	v.MockQueue.Wait()
}

func (v *PodFeeder) Delete(pod *k8sv1.Pod) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(pod)
	v.MockQueue.Wait()
}

func NewPodFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *PodFeeder {
	return &PodFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type PodDisruptionBudgetFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *PodDisruptionBudgetFeeder) Add(pdb *v1beta1.PodDisruptionBudget) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(pdb)
	v.MockQueue.Wait()
}

func (v *PodDisruptionBudgetFeeder) Modify(pdb *v1beta1.PodDisruptionBudget) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(pdb)
	v.MockQueue.Wait()
}

func (v *PodDisruptionBudgetFeeder) Delete(pdb *v1beta1.PodDisruptionBudget) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(pdb)
	v.MockQueue.Wait()
}

func NewPodDisruptionBudgetFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *PodDisruptionBudgetFeeder {
	return &PodDisruptionBudgetFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type MigrationFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *MigrationFeeder) Add(migration *v1.VirtualMachineInstanceMigration) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(migration)
	v.MockQueue.Wait()
}

func (v *MigrationFeeder) Modify(migration *v1.VirtualMachineInstanceMigration) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(migration)
	v.MockQueue.Wait()
}

func (v *MigrationFeeder) Delete(migration *v1.VirtualMachineInstanceMigration) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(migration)
	v.MockQueue.Wait()
}

func NewMigrationFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *MigrationFeeder {
	return &MigrationFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type DomainFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *DomainFeeder) Add(vmi *api.Domain) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(vmi)
	v.MockQueue.Wait()
}

func (v *DomainFeeder) Modify(vmi *api.Domain) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(vmi)
	v.MockQueue.Wait()
}

func (v *DomainFeeder) Delete(vmi *api.Domain) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(vmi)
	v.MockQueue.Wait()
}

func NewDomainFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *DomainFeeder {
	return &DomainFeeder{
		MockQueue: queue,
		Source:    source,
	}
}

type DataVolumeFeeder struct {
	MockQueue *MockWorkQueue
	Source    *framework.FakeControllerSource
}

func (v *DataVolumeFeeder) Add(dataVolume *cdiv1.DataVolume) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Add(dataVolume)
	v.MockQueue.Wait()
}

func (v *DataVolumeFeeder) Modify(dataVolume *cdiv1.DataVolume) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Modify(dataVolume)
	v.MockQueue.Wait()
}

func (v *DataVolumeFeeder) Delete(dataVolume *cdiv1.DataVolume) {
	v.MockQueue.ExpectAdds(1)
	v.Source.Delete(dataVolume)
	v.MockQueue.Wait()
}

func NewDataVolumeFeeder(queue *MockWorkQueue, source *framework.FakeControllerSource) *DataVolumeFeeder {
	return &DataVolumeFeeder{
		MockQueue: queue,
		Source:    source,
	}
}
<|endoftext|>