<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net"

	"os"

	log "github.com/Sirupsen/logrus"

	"github.com/containernetworking/cni/pkg/skel"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/projectcalico/calico-cni/utils"
	"github.com/projectcalico/libcalico-go/lib/client"
	cnet "github.com/projectcalico/libcalico-go/lib/net"
)

func main() {
	skel.PluginMain(cmdAdd, cmdDel)
}

type ipamArgs struct {
	types.CommonArgs
	IP net.IP `json:"ip,omitempty"`
}

func cmdAdd(args *skel.CmdArgs) error {
	conf := utils.NetConf{}
	if err := json.Unmarshal(args.StdinData, &conf); err != nil {
		return fmt.Errorf("failed to load netconf: %v", err)
	}

	utils.ConfigureLogging(conf.LogLevel)

	calicoClient, err := utils.CreateClient(conf)
	if err != nil {
		return err
	}

	workloadID, _, err := utils.GetIdentifiers(args)
	if err != nil {
		return err
	}
	logger := utils.CreateContextLogger(workloadID)

	ipamArgs := ipamArgs{}
	if err = types.LoadArgs(args.Args, &ipamArgs); err != nil {
		return err
	}

	r := &types.Result{}
	if ipamArgs.IP != nil {
		fmt.Fprintf(os.Stderr, "Calico CNI IPAM request IP: %v\n", ipamArgs.IP)

		// The hostname will be defaulted to the actual hostname if cong.Hostname is empty
		assignArgs := client.AssignIPArgs{IP: cnet.IP{ipamArgs.IP}, HandleID: &workloadID, Hostname: conf.Hostname}
		logger.WithField("assignArgs", assignArgs).Info("Assigning provided IP")
		err := calicoClient.IPAM().AssignIP(assignArgs)
		if err != nil {
			return err
		}

		ipV4Network := net.IPNet{IP: ipamArgs.IP, Mask: net.CIDRMask(32, 32)}
		r.IP4 = &types.IPConfig{IP: ipV4Network}
		logger.WithField("result.IP4", r.IP4).Info("Result IPv4")
	} else {
		// Default to assigning an IPv4 address
		num4 := 1
		if conf.IPAM.AssignIpv4 != nil && *conf.IPAM.AssignIpv4 == "false" {
			num4 = 0
		}

		// Default to NOT assigning an IPv6 address
		num6 := 0
		if conf.IPAM.AssignIpv6 != nil && *conf.IPAM.AssignIpv6 == "true" {
			num6 = 1
		}

		fmt.Fprintf(os.Stderr, "Calico CNI IPAM request count IPv4=%d IPv6=%d\n", num4, num6)

		assignArgs := client.AutoAssignArgs{Num4: num4, Num6: num6, HandleID: &workloadID, Hostname: conf.Hostname}
		logger.WithField("assignArgs", assignArgs).Info("Auto assigning IP")
		assignedV4, assignedV6, err := calicoClient.IPAM().AutoAssign(assignArgs)
		fmt.Fprintf(os.Stderr, "Calico CNI IPAM assigned addresses IPv4=%v IPv6=%v\n", assignedV4, assignedV6)
		if err != nil {
			return err
		}

		if num4 == 1 {
			ipV4Network := net.IPNet{IP: assignedV4[0].IP, Mask: net.CIDRMask(32, 32)}
			r.IP4 = &types.IPConfig{IP: ipV4Network}
		}

		if num6 == 1 {
			ipV6Network := net.IPNet{IP: assignedV6[0].IP, Mask: net.CIDRMask(128, 128)}
			r.IP6 = &types.IPConfig{IP: ipV6Network}
		}
		logger.WithFields(log.Fields{"result.IP4": r.IP4, "result.IP6": r.IP6}).Info("IPAM Result")
	}

	return r.Print()
}

func cmdDel(args *skel.CmdArgs) error {
	conf := utils.NetConf{}
	if err := json.Unmarshal(args.StdinData, &conf); err != nil {
		return fmt.Errorf("failed to load netconf: %v", err)
	}

	utils.ConfigureLogging(conf.LogLevel)

	calicoClient, err := utils.CreateClient(conf)
	if err != nil {
		return err
	}

	// Release the IP address by using the handle - which is workloadID.
	workloadID, _, err := utils.GetIdentifiers(args)
	if err != nil {
		return err
	}

	logger := utils.CreateContextLogger(workloadID)

	logger.Info("Releasing address using workloadID")
	if err := calicoClient.IPAM().ReleaseByHandle(workloadID); err != nil {
		return err
	}

	logger.Info("Released address using workloadID")
	return nil

}
<commit_msg>Check for number of returned IPs from IPAM<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net"

	"os"

	log "github.com/Sirupsen/logrus"

	"github.com/containernetworking/cni/pkg/skel"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/projectcalico/calico-cni/utils"
	"github.com/projectcalico/libcalico-go/lib/client"
	cnet "github.com/projectcalico/libcalico-go/lib/net"
)

func main() {
	skel.PluginMain(cmdAdd, cmdDel)
}

type ipamArgs struct {
	types.CommonArgs
	IP net.IP `json:"ip,omitempty"`
}

func cmdAdd(args *skel.CmdArgs) error {
	conf := utils.NetConf{}
	if err := json.Unmarshal(args.StdinData, &conf); err != nil {
		return fmt.Errorf("failed to load netconf: %v", err)
	}

	utils.ConfigureLogging(conf.LogLevel)

	calicoClient, err := utils.CreateClient(conf)
	if err != nil {
		return err
	}

	workloadID, _, err := utils.GetIdentifiers(args)
	if err != nil {
		return err
	}
	logger := utils.CreateContextLogger(workloadID)

	ipamArgs := ipamArgs{}
	if err = types.LoadArgs(args.Args, &ipamArgs); err != nil {
		return err
	}

	r := &types.Result{}
	if ipamArgs.IP != nil {
		fmt.Fprintf(os.Stderr, "Calico CNI IPAM request IP: %v\n", ipamArgs.IP)

		// The hostname will be defaulted to the actual hostname if cong.Hostname is empty
		assignArgs := client.AssignIPArgs{IP: cnet.IP{ipamArgs.IP}, HandleID: &workloadID, Hostname: conf.Hostname}
		logger.WithField("assignArgs", assignArgs).Info("Assigning provided IP")
		err := calicoClient.IPAM().AssignIP(assignArgs)
		if err != nil {
			return err
		}

		ipV4Network := net.IPNet{IP: ipamArgs.IP, Mask: net.CIDRMask(32, 32)}
		r.IP4 = &types.IPConfig{IP: ipV4Network}
		logger.WithField("result.IP4", r.IP4).Info("Result IPv4")
	} else {
		// Default to assigning an IPv4 address
		num4 := 1
		if conf.IPAM.AssignIpv4 != nil && *conf.IPAM.AssignIpv4 == "false" {
			num4 = 0
		}

		// Default to NOT assigning an IPv6 address
		num6 := 0
		if conf.IPAM.AssignIpv6 != nil && *conf.IPAM.AssignIpv6 == "true" {
			num6 = 1
		}

		fmt.Fprintf(os.Stderr, "Calico CNI IPAM request count IPv4=%d IPv6=%d\n", num4, num6)

		assignArgs := client.AutoAssignArgs{Num4: num4, Num6: num6, HandleID: &workloadID, Hostname: conf.Hostname}
		logger.WithField("assignArgs", assignArgs).Info("Auto assigning IP")
		assignedV4, assignedV6, err := calicoClient.IPAM().AutoAssign(assignArgs)
		fmt.Fprintf(os.Stderr, "Calico CNI IPAM assigned addresses IPv4=%v IPv6=%v\n", assignedV4, assignedV6)
		if err != nil {
			return err
		}

		if num4 == 1 {
			if len(assignedV4) != num4 {
				return fmt.Errorf("Failed to request %d IPv4 addresses. IPAM allocated only %d.", num4, len(assignedV4))
			}
			ipV4Network := net.IPNet{IP: assignedV4[0].IP, Mask: net.CIDRMask(32, 32)}
			r.IP4 = &types.IPConfig{IP: ipV4Network}
		}

		if num6 == 1 {
			if len(assignedV6) != num6 {
				return fmt.Errorf("Failed to request %d IPv6 addresses. IPAM allocated only %d.", num6, len(assignedV6))
			}
			ipV6Network := net.IPNet{IP: assignedV6[0].IP, Mask: net.CIDRMask(128, 128)}
			r.IP6 = &types.IPConfig{IP: ipV6Network}
		}
		logger.WithFields(log.Fields{"result.IP4": r.IP4, "result.IP6": r.IP6}).Info("IPAM Result")
	}

	return r.Print()
}

func cmdDel(args *skel.CmdArgs) error {
	conf := utils.NetConf{}
	if err := json.Unmarshal(args.StdinData, &conf); err != nil {
		return fmt.Errorf("failed to load netconf: %v", err)
	}

	utils.ConfigureLogging(conf.LogLevel)

	calicoClient, err := utils.CreateClient(conf)
	if err != nil {
		return err
	}

	// Release the IP address by using the handle - which is workloadID.
	workloadID, _, err := utils.GetIdentifiers(args)
	if err != nil {
		return err
	}

	logger := utils.CreateContextLogger(workloadID)

	logger.Info("Releasing address using workloadID")
	if err := calicoClient.IPAM().ReleaseByHandle(workloadID); err != nil {
		return err
	}

	logger.Info("Released address using workloadID")
	return nil

}
<|endoftext|>


<commit_before>package bootkube

import (
	"fmt"
	"strings"
	"time"

	"k8s.io/kubernetes/pkg/api/v1"
	certificates "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1"
	"k8s.io/kubernetes/pkg/client/cache"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
	"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/certificates/v1alpha1"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/fields"
	"k8s.io/kubernetes/pkg/util/wait"
)

// ApproveKubeletCSRs indefinitely approves any CSRs submitted by Kubelet
// instances, that are in the process of bootstrapping their TLS assets, without
// making any kind of validation.
func ApproveKubeletCSRs() error {
	errCh := make(chan error)

	client, err := clientset.NewForConfig(
		&restclient.Config{Host: insecureAPIAddr},
	)
	if err != nil {
		return err
	}
	csrClient := client.CertificatesV1alpha1Client.CertificateSigningRequests()

	watchList := cache.NewListWatchFromClient(
		client.CertificatesV1alpha1().RESTClient(),
		"certificatesigningrequests",
		v1.NamespaceAll,
		fields.Everything(),
	)

	_, controller := cache.NewInformer(
		watchList,
		&certificates.CertificateSigningRequest{},
		time.Second*30,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				if request, ok := obj.(*certificates.CertificateSigningRequest); ok {
					approveCSR(csrClient, request, errCh)
				}
			},
			UpdateFunc: func(_, obj interface{}) {
				if request, ok := obj.(*certificates.CertificateSigningRequest); ok {
					approveCSR(csrClient, request, errCh)
				}
			},
		},
	)

	go controller.Run(wait.NeverStop)
	return <-errCh
}

func approveCSR(client v1alpha1.CertificateSigningRequestInterface, request *certificates.CertificateSigningRequest, errCh chan error) {
	condition := certificates.CertificateSigningRequestCondition{
		Type:    certificates.CertificateApproved,
		Reason:  "AutoApproved",
		Message: "Auto approving of all kubelet CSRs is enabled on bootkube",
	}

	for {
		// Verify that the CSR hasn't been approved or denied already.
		//
		// There are only two possible conditions (CertificateApproved and
		// CertificateDenied). Therefore if the CSR already has a condition,
		// it means that the request has already been approved or denied, and that
		// we should ignore the request.
		if len(request.Status.Conditions) > 0 {
			return
		}

		// Approve the CSR.
		request.Status.Conditions = append(request.Status.Conditions, condition)

		// Submit the updated CSR.
		if _, err := client.UpdateApproval(request); err != nil {
			if strings.Contains(err.Error(), "the object has been modified") {
				// The CSR might have been updated by a third-party, retry until we
				// succeed.
				request, err = client.Get(request.Name)
				if err != nil {
					errCh <- fmt.Errorf("Error retrieving Kubelet's CSR %q: %s", request.Name, err)
					return
				}
				continue
			}

			errCh <- fmt.Errorf("Error approving Kubelet's CSR %q: %s", request.Name, err)
			return
		}

		UserOutput("Approved Kubelet's CSR %q\n", request.Name)
		return
	}
}
<commit_msg>pkg/bootkube: Validate only CSRs submitted by bootstrap<commit_after>package bootkube

import (
	"fmt"
	"strings"
	"time"

	"k8s.io/kubernetes/pkg/api/v1"
	certificates "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1"
	"k8s.io/kubernetes/pkg/client/cache"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
	"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/certificates/v1alpha1"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/fields"
	"k8s.io/kubernetes/pkg/util/wait"
)

const (
	kubeletBootstrapUsername = "kubelet-bootstrap"
	kubeletBootstrapGroup    = "system:kubelet-bootstrap"
)

// ApproveKubeletCSRs indefinitely approves any CSRs submitted by Kubelet
// instances, that are in the process of bootstrapping their TLS assets, without
// making any kind of validation.
func ApproveKubeletCSRs() error {
	errCh := make(chan error)

	client, err := clientset.NewForConfig(
		&restclient.Config{Host: insecureAPIAddr},
	)
	if err != nil {
		return err
	}
	csrClient := client.CertificatesV1alpha1Client.CertificateSigningRequests()

	watchList := cache.NewListWatchFromClient(
		client.CertificatesV1alpha1().RESTClient(),
		"certificatesigningrequests",
		v1.NamespaceAll,
		fields.Everything(),
	)

	_, controller := cache.NewInformer(
		watchList,
		&certificates.CertificateSigningRequest{},
		time.Second*30,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				if request, ok := obj.(*certificates.CertificateSigningRequest); ok {
					approveCSR(csrClient, request, errCh)
				}
			},
			UpdateFunc: func(_, obj interface{}) {
				if request, ok := obj.(*certificates.CertificateSigningRequest); ok {
					approveCSR(csrClient, request, errCh)
				}
			},
		},
	)

	go controller.Run(wait.NeverStop)
	return <-errCh
}

func approveCSR(client v1alpha1.CertificateSigningRequestInterface, request *certificates.CertificateSigningRequest, errCh chan error) {
	condition := certificates.CertificateSigningRequestCondition{
		Type:    certificates.CertificateApproved,
		Reason:  "AutoApproved",
		Message: "Auto approving of all kubelet CSRs is enabled on bootkube",
	}

	for {
		// Verify that the CSR hasn't been approved or denied already.
		//
		// There are only two possible conditions (CertificateApproved and
		// CertificateDenied). Therefore if the CSR already has a condition,
		// it means that the request has already been approved or denied, and that
		// we should ignore the request.
		if len(request.Status.Conditions) > 0 {
			return
		}

		// Ensure the CSR has been submitted by a kubelet performing its TLS
		// bootstrapping by checking the username and the group.
		if request.Spec.Username != kubeletBootstrapUsername {
			return
		}

		isKubeletBootstrapGroup := false
		for _, group := range request.Spec.Groups {
			if group == kubeletBootstrapGroup {
				isKubeletBootstrapGroup = true
				break
			}
		}
		if !isKubeletBootstrapGroup {
			return
		}

		// Approve the CSR.
		request.Status.Conditions = append(request.Status.Conditions, condition)

		// Submit the updated CSR.
		if _, err := client.UpdateApproval(request); err != nil {
			if strings.Contains(err.Error(), "the object has been modified") {
				// The CSR might have been updated by a third-party, retry until we
				// succeed.
				request, err = client.Get(request.Name)
				if err != nil {
					errCh <- fmt.Errorf("Error retrieving Kubelet's CSR %q: %s", request.Name, err)
					return
				}
				continue
			}

			errCh <- fmt.Errorf("Error approving Kubelet's CSR %q: %s", request.Name, err)
			return
		}

		UserOutput("Approved Kubelet's CSR %q\n", request.Name)
		return
	}
}
<|endoftext|>


<commit_before>package util

import (
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"
	"path"
	"regexp"
	"runtime"

	"github.com/imdario/mergo"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	kuberuntime "k8s.io/kubernetes/pkg/runtime"
)

// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
// of resources and different API sets.
type Factory struct {
	KubeFactory   *cmdutil.Factory
	User          *UserMeta
	Ctrl          *Controller
	CrafterRemote string
	BearerToken   string
	Serializer    kuberuntime.NegotiatedSerializer
	flags         *pflag.FlagSet
}

// BindFlags adds any flags that are common to all kubectl sub commands.
func (f *Factory) BindFlags(flags *pflag.FlagSet) {
	// Merge factory's flags
	flags.AddFlagSet(f.flags)

	// Globally persistent flags across all subcommands.
	// TODO Change flag names to consts to allow safer lookup from subcommands.
	// TODO Add a verbose flag that turns on glog logging. Probably need a way
	// to do that automatically for every subcommand.
	//flags.BoolVar(&f.clients.matchVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")

	// Normalize all flags that are coming from other packages or pre-configurations
	// a.k.a. change all "_" to "-". e.g. glog package
	//flags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
}

// BindExternalFlags adds any flags defined by external projects (not part of pflags)
func (f *Factory) BindExternalFlags(flags *pflag.FlagSet) {
	// any flags defined by external projects (not part of pflags)
	flags.AddGoFlagSet(flag.CommandLine)
}

// DefaultNamespace filter a default namespace based on the label '<koliutil.PrefixLabel>/default=true'.
// Returns an error if none or more than one namespace is found.
func (f *Factory) DefaultNamespace(cmd *cobra.Command, isNamespaced bool) (string, error) {
	if !isNamespaced {
		return "", nil
	}
	flag := cmd.Flag("namespace")
	if flag.Value.String() != "" {
		return flag.Value.String(), nil
	}
	// The client doesn't provide any namespace, need to find a default one
	mapper, typer := f.KubeFactory.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd))
	selector := fmt.Sprintf("%s/id=%s,%s/default=true", PrefixLabel, f.User.ID, PrefixLabel)
	r := resource.NewBuilder(mapper, typer,
		resource.ClientMapperFunc(f.KubeFactory.ClientForMapping), f.KubeFactory.Decoder(true)).
		SelectorParam(selector).
		ResourceTypes([]string{"namespace"}...).
		Latest().
		Flatten().
		Do()

	infos, err := r.Infos()
	if err != nil {
		return "", err
	} else if len(infos) != 1 {
		return "", fmt.Errorf("Found (%d) namespaces.", len(infos))
	}
	return infos[0].Name, nil
}

// RepositoryExists verifies if a repository already exists on deployments
func (f *Factory) RepositoryExists(cmd *cobra.Command, repository, namespace string) (bool, error) {
	// The client doesn't provide any namespace, need to find a default one
	mapper, typer := f.KubeFactory.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd))
	selector := fmt.Sprintf("%s/repo=%s", PrefixLabel, repository)
	r := resource.NewBuilder(mapper, typer,
		resource.ClientMapperFunc(f.KubeFactory.ClientForMapping), f.KubeFactory.Decoder(true)).
		SelectorParam(selector).
		NamespaceParam(namespace).
		ResourceTypes([]string{"deployment"}...).
		Latest().
		Flatten().
		Do()

	infos, err := r.Infos()
	if err != nil {
		return false, fmt.Errorf("could not verify existent repositories (%s)", err)
	}

	return len(infos) >= 1, nil
}

// NewFactory creates a factory with the default Kubernetes resources defined
func NewFactory(optionalClientConfig clientcmd.ClientConfig) (*Factory, error) {
	flags := pflag.NewFlagSet("", pflag.ContinueOnError)
	// flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags

	clientConfig := optionalClientConfig
	if optionalClientConfig == nil {
		clientConfig = DefaultClientConfig(flags)
	}
	kfactory := cmdutil.NewFactory(clientConfig)

	cfg, err := clientConfig.ClientConfig()
	if cfg.BearerToken == "" {
		return nil, errors.New("bearer token is empty")
	}
	if err != nil {
		return nil, err
	}

	ipRegexp := "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
	proxyHost := regexp.MustCompile(ipRegexp).FindStringSubmatch(cfg.Host)[0]
	if proxyHost == "" {
		return nil, errors.New("host not found, check your config")
	}
	url := &url.URL{
		Scheme: "http",
		// Host:   host,
		Host: fmt.Sprintf("%s:%s", proxyHost, "30080"),
		Path: "/",
	}

	crafterRemote := "http://crafter-orion.kolibox.io:30080" // TODO: hard-coded
	controller := NewController(url, "")
	controller.Request.SetHeader("Authorization", fmt.Sprintf("Bearer %s", cfg.BearerToken))
	platform := path.Join(runtime.GOOS, runtime.GOARCH)
	userAgent := "koli/v0.1.0 (%s) [kubectl/v1.4.0]"
	controller.Request.SetHeader("User-Agent", fmt.Sprintf(userAgent, platform))

	return &Factory{
		KubeFactory:   kfactory,
		Ctrl:          controller,
		CrafterRemote: crafterRemote,
		BearerToken:   cfg.BearerToken,
		Serializer:    cfg.NegotiatedSerializer,
		flags:         flags,
	}, nil
}

// DefaultClientConfig from pkg/kubectl/cmd/util/factory.go:DefaultClientConfig:
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	flags.StringVar(&loadingRules.ExplicitPath, "koliconfig", "", "Path to the koliconfig file to use for CLI requests.")

	overrides := &clientcmd.ConfigOverrides{}
	// use the standard defaults for this client config
	mergo.Merge(&overrides.ClusterDefaults, clientcmd.DefaultCluster)

	flagNames := clientcmd.RecommendedConfigOverrideFlags("")
	// short flagnames are disabled by default.  These are here for compatibility with existing scripts
	flagNames.ClusterOverrideFlags.APIServer.ShortName = "s"

	// AuthInfo Flags
	// flagNames.AuthOverrideFlags
	// Context Flags
	flagNames.ContextOverrideFlags.Namespace.BindStringFlag(flags, &overrides.Context.Namespace)

	// Cluster Flags
	flagNames.ClusterOverrideFlags.APIServer.BindStringFlag(flags, &overrides.ClusterInfo.Server)
	//clientcmd.BindOverrideFlags(overrides, flags, flagNames)
	return clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
}
<commit_msg>feat(cache-tokens) pass *url.URL object instead of string host<commit_after>package util

import (
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"
	"path"
	"regexp"
	"runtime"

	"github.com/imdario/mergo"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	kuberuntime "k8s.io/kubernetes/pkg/runtime"
)

// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
// of resources and different API sets.
type Factory struct {
	KubeFactory   cmdutil.Factory
	User          *UserMeta
	Ctrl          *Controller
	CrafterRemote *url.URL
	BearerToken   string
	Serializer    kuberuntime.NegotiatedSerializer
	flags         *pflag.FlagSet
}

// BindFlags adds any flags that are common to all kubectl sub commands.
func (f *Factory) BindFlags(flags *pflag.FlagSet) {
	// Merge factory's flags
	flags.AddFlagSet(f.flags)

	// Globally persistent flags across all subcommands.
	// TODO Change flag names to consts to allow safer lookup from subcommands.
	// TODO Add a verbose flag that turns on glog logging. Probably need a way
	// to do that automatically for every subcommand.
	//flags.BoolVar(&f.clients.matchVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")

	// Normalize all flags that are coming from other packages or pre-configurations
	// a.k.a. change all "_" to "-". e.g. glog package
	//flags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
}

// BindExternalFlags adds any flags defined by external projects (not part of pflags)
func (f *Factory) BindExternalFlags(flags *pflag.FlagSet) {
	// any flags defined by external projects (not part of pflags)
	flags.AddGoFlagSet(flag.CommandLine)
}

// DefaultNamespace filter a default namespace based on the label '<koliutil.PrefixLabel>/default=true'.
// Returns an error if none or more than one namespace is found.
func (f *Factory) DefaultNamespace(cmd *cobra.Command, isNamespaced bool) (string, error) {
	if !isNamespaced {
		return "", nil
	}
	flag := cmd.Flag("namespace")
	if flag.Value.String() != "" {
		return flag.Value.String(), nil
	}
	// The client doesn't provide any namespace, need to find a default one
	// mapper, typer := f.KubeFactory.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd))
	mapper, typer := f.KubeFactory.Object()
	selector := fmt.Sprintf("%s/id=%s,%s/default=true", PrefixLabel, f.User.ID, PrefixLabel)
	r := resource.NewBuilder(mapper, typer,
		resource.ClientMapperFunc(f.KubeFactory.ClientForMapping), f.KubeFactory.Decoder(true)).
		SelectorParam(selector).
		ResourceTypes([]string{"namespace"}...).
		Latest().
		Flatten().
		Do()

	infos, err := r.Infos()
	if err != nil {
		return "", err
	} else if len(infos) != 1 {
		return "", fmt.Errorf("Found (%d) namespaces.", len(infos))
	}
	return infos[0].Name, nil
}

// RepositoryExists verifies if a repository already exists on deployments
func (f *Factory) RepositoryExists(cmd *cobra.Command, repository, namespace string) (bool, error) {
	// The client doesn't provide any namespace, need to find a default one
	mapper, typer := f.KubeFactory.Object()
	selector := fmt.Sprintf("%s/repo=%s", PrefixLabel, repository)
	r := resource.NewBuilder(mapper, typer,
		resource.ClientMapperFunc(f.KubeFactory.ClientForMapping), f.KubeFactory.Decoder(true)).
		SelectorParam(selector).
		NamespaceParam(namespace).
		ResourceTypes([]string{"deployment"}...).
		Latest().
		Flatten().
		Do()

	infos, err := r.Infos()
	if err != nil {
		return false, fmt.Errorf("could not verify existent repositories (%s)", err)
	}

	return len(infos) >= 1, nil
}

// NewFactory creates a factory with the default Kubernetes resources defined
func NewFactory(optionalClientConfig clientcmd.ClientConfig) (*Factory, error) {
	flags := pflag.NewFlagSet("", pflag.ContinueOnError)
	// flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags

	clientConfig := optionalClientConfig
	if optionalClientConfig == nil {
		clientConfig = DefaultClientConfig(flags)
	}
	kfactory := cmdutil.NewFactory(clientConfig)
	cfg, err := clientConfig.ClientConfig()
	// TODO: issue an warning if the cfg.BearerToken is empty
	if err != nil {
		return nil, err
	}

	ipRegexp := "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
	proxyHost := regexp.MustCompile(ipRegexp).FindStringSubmatch(cfg.Host)[0]
	if proxyHost == "" {
		return nil, errors.New("host not found, check your config")
	}
	ctrlURL := &url.URL{
		Scheme: "http",
		Host:   fmt.Sprintf("%s:%s", proxyHost, "30080"),
		Path:   "/",
	}

	controller := NewController(ctrlURL, "")
	controller.Request.SetHeader("Authorization", fmt.Sprintf("Bearer %s", cfg.BearerToken))
	platform := path.Join(runtime.GOOS, runtime.GOARCH)
	userAgent := "koli/v0.1.0 (%s) [kubectl/v1.4.0]"
	controller.Request.SetHeader("User-Agent", fmt.Sprintf(userAgent, platform))

	return &Factory{
		KubeFactory: kfactory,
		Ctrl:        controller,
		// TODO: hard-coded
		CrafterRemote: &url.URL{Scheme: "http", Host: "crafter-orion.kolibox.io:30080"},
		BearerToken:   cfg.BearerToken,
		Serializer:    cfg.NegotiatedSerializer,
		flags:         flags,
	}, nil
}

// DefaultClientConfig from pkg/kubectl/cmd/util/factory.go:DefaultClientConfig:
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	flags.StringVar(&loadingRules.ExplicitPath, "koliconfig", "", "Path to the koliconfig file to use for CLI requests.")

	overrides := &clientcmd.ConfigOverrides{}
	// use the standard defaults for this client config
	mergo.Merge(&overrides.ClusterDefaults, clientcmd.ClusterDefaults)

	flagNames := clientcmd.RecommendedConfigOverrideFlags("")
	// short flagnames are disabled by default.  These are here for compatibility with existing scripts
	flagNames.ClusterOverrideFlags.APIServer.ShortName = "s"

	// AuthInfo Flags
	// flagNames.AuthOverrideFlags
	// Context Flags
	flagNames.ContextOverrideFlags.Namespace.BindStringFlag(flags, &overrides.Context.Namespace)

	// Cluster Flags
	flagNames.ClusterOverrideFlags.APIServer.BindStringFlag(flags, &overrides.ClusterInfo.Server)
	//clientcmd.BindOverrideFlags(overrides, flags, flagNames)
	return clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
}
<|endoftext|>


<commit_before>package kates

import (
	"context"
	"errors"
	"strings"
	"sync"

	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	"k8s.io/apiextensions-apiserver/pkg/apiserver/validation"

	"github.com/go-openapi/validate"
)

type Validator struct {
	client     *Client
	mutex      sync.Mutex
	validators map[string]*validate.SchemaValidator
}

func NewValidator(client *Client) *Validator {
	return &Validator{client: client, validators: make(map[string]*validate.SchemaValidator)}
}

func (v *Validator) getValidator(ctx context.Context, crd string) (*validate.SchemaValidator, error) {
	v.mutex.Lock()
	defer v.mutex.Unlock()
	validator, ok := v.validators[crd]
	if !ok {
		obj := &CustomResourceDefinition{
			TypeMeta: TypeMeta{
				Kind: "CustomResourceDefinition",
			},
			ObjectMeta: ObjectMeta{
				Name: crd,
			},
		}
		err := v.client.Get(ctx, obj, obj)
		if err != nil {
			if IsNotFound(err) {
				v.validators[crd] = nil
				return nil, nil
			}

			return nil, err
		}

		extcrd := &apiextensions.CustomResourceDefinition{}
		err = v1.Convert_v1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(obj, extcrd, nil)
		if err != nil {
			return nil, err
		}

		validator, _, err = validation.NewSchemaValidator(extcrd.Spec.Validation)
		if err != nil {
			return nil, err
		}

		v.validators[crd] = validator
	}
	return validator, nil
}

func (v *Validator) Validate(ctx context.Context, resource interface{}) error {
	var tm TypeMeta
	err := convert(resource, &tm)
	if err != nil {
		return err
	}

	mapping, err := v.client.mappingFor(tm.GroupVersionKind().GroupKind().String())
	if err != nil {
		return err
	}

	crd := mapping.Resource.GroupResource().String()

	validator, err := v.getValidator(ctx, crd)
	if err != nil {
		return err
	}

	result := validator.Validate(resource)

	var errs []error
	for _, e := range result.Errors {
		errs = append(errs, e)
	}

	for _, w := range result.Warnings {
		errs = append(errs, w)
	}

	if len(errs) > 0 {
		msg := strings.Builder{}
		for _, e := range errs {
			msg.WriteString(e.Error() + "\n")
		}
		return errors.New(msg.String())
	}

	return nil
}
<commit_msg>(from AES) added docs to validation.go<commit_after>package kates

import (
	"context"
	"errors"
	"strings"
	"sync"

	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	"k8s.io/apiextensions-apiserver/pkg/apiserver/validation"

	"github.com/go-openapi/validate"
)

// A Validator may be used in concert with a Client to perform
// validate of freeform jsonish data structures as kubernetes CRDs.
type Validator struct {
	client     *Client
	mutex      sync.Mutex
	validators map[string]*validate.SchemaValidator
}

// The NewValidator constructor returns a *Validator that uses the
// provided *Client to fetch CustomResourceDefinitions from kubernetes
// on demand as needed to validate data passed to the Validator.Validate()
// method.
func NewValidator(client *Client) *Validator {
	return &Validator{client: client, validators: make(map[string]*validate.SchemaValidator)}
}

func (v *Validator) getValidator(ctx context.Context, crd string) (*validate.SchemaValidator, error) {
	v.mutex.Lock()
	defer v.mutex.Unlock()
	validator, ok := v.validators[crd]
	if !ok {
		obj := &CustomResourceDefinition{
			TypeMeta: TypeMeta{
				Kind: "CustomResourceDefinition",
			},
			ObjectMeta: ObjectMeta{
				Name: crd,
			},
		}
		err := v.client.Get(ctx, obj, obj)
		if err != nil {
			if IsNotFound(err) {
				v.validators[crd] = nil
				return nil, nil
			}

			return nil, err
		}

		extcrd := &apiextensions.CustomResourceDefinition{}
		err = v1.Convert_v1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(obj, extcrd, nil)
		if err != nil {
			return nil, err
		}

		validator, _, err = validation.NewSchemaValidator(extcrd.Spec.Validation)
		if err != nil {
			return nil, err
		}

		v.validators[crd] = validator
	}
	return validator, nil
}

// The Validate method validates the supplied jsonish object as a
// kubernetes CRD instance.
//
// If the supplied object is *not* a CRD instance but instead a
// regular kubernetes instance, the Validate method will assume that
// the supplied object is valid.
//
// If the supplied object is not a valid kubernetes resource at all,
// the Validate method will return an error.
//
// Typically the Validate method will perform only local operations,
// however the first time an instance of a given Kind is supplied, the
// Validator needs to query the cluster to figure out if it is a CRD
// and if so to fetch the schema needed to perform validation. All
// subsequent Validate() calls for that Kind will be local.
func (v *Validator) Validate(ctx context.Context, resource interface{}) error {
	var tm TypeMeta
	err := convert(resource, &tm)
	if err != nil {
		return err
	}

	mapping, err := v.client.mappingFor(tm.GroupVersionKind().GroupKind().String())
	if err != nil {
		return err
	}

	crd := mapping.Resource.GroupResource().String()

	validator, err := v.getValidator(ctx, crd)
	if err != nil {
		return err
	}

	result := validator.Validate(resource)

	var errs []error
	for _, e := range result.Errors {
		errs = append(errs, e)
	}

	for _, w := range result.Warnings {
		errs = append(errs, w)
	}

	if len(errs) > 0 {
		msg := strings.Builder{}
		for _, e := range errs {
			msg.WriteString(e.Error() + "\n")
		}
		return errors.New(msg.String())
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016-2019 Authors of Cilium
//
// 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 gc

import (
	"fmt"
	"os"
	"time"

	"github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/endpoint"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/maps/ctmap"
	"github.com/sirupsen/logrus"
)

var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "ct-gc")

// EndpointManager is any type which returns the list of Endpoints which are
// globally exposed on the current node.
type EndpointManager interface {
	GetEndpoints() []*endpoint.Endpoint
}

// Enable enables the connection tracking garbage collection.
func Enable(ipv4, ipv6 bool, restoredEndpoints []*endpoint.Endpoint, mgr EndpointManager) {
	var (
		initialScan         = true
		initialScanComplete = make(chan struct{})
		mapType             bpf.MapType
	)

	go func() {
		for {
			var maxDeleteRatio float64
			eps := mgr.GetEndpoints()
			if len(eps) > 0 || initialScan {
				mapType, maxDeleteRatio = runGC(nil, ipv4, ipv6, createGCFilter(initialScan, restoredEndpoints))
			}
			for _, e := range eps {
				if !e.ConntrackLocal() {
					// Skip because GC was handled above.
					continue
				}
				runGC(e, ipv4, ipv6, &ctmap.GCFilter{RemoveExpired: true})
			}

			if initialScan {
				close(initialScanComplete)
				initialScan = false
			}

			time.Sleep(ctmap.GetInterval(mapType, maxDeleteRatio))
		}
	}()

	select {
	case <-initialScanComplete:
		log.Info("Initial scan of connection tracking completed")
	case <-time.After(30 * time.Second):
		log.Fatal("Timeout while waiting for initial conntrack scan")
	}
}

// runGC run CT's garbage collector for the given endpoint. `isLocal` refers if
// the CT map is set to local. If `isIPv6` is set specifies that is the IPv6
// map. `filter` represents the filter type to be used while looping all CT
// entries.
//
// The provided endpoint is optional; if it is provided, then its map will be
// garbage collected and any failures will be logged to the endpoint log.
// Otherwise it will garbage-collect the global map and use the global log.
func runGC(e *endpoint.Endpoint, ipv4, ipv6 bool, filter *ctmap.GCFilter) (mapType bpf.MapType, maxDeleteRatio float64) {
	var maps []*ctmap.Map

	if e == nil {
		maps = ctmap.GlobalMaps(ipv4, ipv6)
	} else {
		maps = ctmap.LocalMaps(e, ipv4, ipv6)
	}
	for _, m := range maps {
		path, err := m.Path()
		if err == nil {
			err = m.Open()
		}
		if err != nil {
			msg := "Skipping CT garbage collection"
			scopedLog := log.WithError(err).WithField(logfields.Path, path)
			if os.IsNotExist(err) {
				scopedLog.Debug(msg)
			} else {
				scopedLog.Warn(msg)
			}
			if e != nil {
				e.LogStatus(endpoint.BPF, endpoint.Warning, fmt.Sprintf("%s: %s", msg, err))
			}
			continue
		}
		defer m.Close()

		mapType = m.MapInfo.MapType

		deleted := ctmap.GC(m, filter)

		if deleted > 0 {
			ratio := float64(deleted) / float64(m.MapInfo.MaxEntries)
			if ratio > maxDeleteRatio {
				maxDeleteRatio = ratio
			}
			log.WithFields(logrus.Fields{
				logfields.Path: path,
				"count":        deleted,
			}).Debug("Deleted filtered entries from map")
		}
	}

	return
}

func createGCFilter(initialScan bool, restoredEndpoints []*endpoint.Endpoint) *ctmap.GCFilter {
	filter := &ctmap.GCFilter{
		RemoveExpired: true,
	}

	// On the initial scan, scrub all IPs from the conntrack table which do
	// not belong to IPs of any endpoint that has been restored. No new
	// endpoints can appear yet so we can assume that any other entry not
	// belonging to a restored endpoint has become stale.
	if initialScan {
		filter.ValidIPs = map[string]struct{}{}
		for _, ep := range restoredEndpoints {
			filter.ValidIPs[ep.IPv6.String()] = struct{}{}
			filter.ValidIPs[ep.IPv4.String()] = struct{}{}
		}
	}

	return filter
}
<commit_msg>cilium: change CT GC sleep into a wakeup from select timeout<commit_after>// Copyright 2016-2019 Authors of Cilium
//
// 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 gc

import (
	"fmt"
	"os"
	"time"

	"github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/endpoint"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/maps/ctmap"
	"github.com/sirupsen/logrus"
)

var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "ct-gc")

// EndpointManager is any type which returns the list of Endpoints which are
// globally exposed on the current node.
type EndpointManager interface {
	GetEndpoints() []*endpoint.Endpoint
}

// Enable enables the connection tracking garbage collection.
func Enable(ipv4, ipv6 bool, restoredEndpoints []*endpoint.Endpoint, mgr EndpointManager) {
	var (
		initialScan         = true
		initialScanComplete = make(chan struct{})
		mapType             bpf.MapType
	)

	go func() {
		for {
			var maxDeleteRatio float64
			eps := mgr.GetEndpoints()
			if len(eps) > 0 || initialScan {
				mapType, maxDeleteRatio = runGC(nil, ipv4, ipv6, createGCFilter(initialScan, restoredEndpoints))
			}
			for _, e := range eps {
				if !e.ConntrackLocal() {
					// Skip because GC was handled above.
					continue
				}
				runGC(e, ipv4, ipv6, &ctmap.GCFilter{RemoveExpired: true})
			}

			if initialScan {
				close(initialScanComplete)
				initialScan = false
			}

			select {
			case <-time.After(ctmap.GetInterval(mapType, maxDeleteRatio)):
			}
		}
	}()

	select {
	case <-initialScanComplete:
		log.Info("Initial scan of connection tracking completed")
	case <-time.After(30 * time.Second):
		log.Fatal("Timeout while waiting for initial conntrack scan")
	}
}

// runGC run CT's garbage collector for the given endpoint. `isLocal` refers if
// the CT map is set to local. If `isIPv6` is set specifies that is the IPv6
// map. `filter` represents the filter type to be used while looping all CT
// entries.
//
// The provided endpoint is optional; if it is provided, then its map will be
// garbage collected and any failures will be logged to the endpoint log.
// Otherwise it will garbage-collect the global map and use the global log.
func runGC(e *endpoint.Endpoint, ipv4, ipv6 bool, filter *ctmap.GCFilter) (mapType bpf.MapType, maxDeleteRatio float64) {
	var maps []*ctmap.Map

	if e == nil {
		maps = ctmap.GlobalMaps(ipv4, ipv6)
	} else {
		maps = ctmap.LocalMaps(e, ipv4, ipv6)
	}
	for _, m := range maps {
		path, err := m.Path()
		if err == nil {
			err = m.Open()
		}
		if err != nil {
			msg := "Skipping CT garbage collection"
			scopedLog := log.WithError(err).WithField(logfields.Path, path)
			if os.IsNotExist(err) {
				scopedLog.Debug(msg)
			} else {
				scopedLog.Warn(msg)
			}
			if e != nil {
				e.LogStatus(endpoint.BPF, endpoint.Warning, fmt.Sprintf("%s: %s", msg, err))
			}
			continue
		}
		defer m.Close()

		mapType = m.MapInfo.MapType

		deleted := ctmap.GC(m, filter)

		if deleted > 0 {
			ratio := float64(deleted) / float64(m.MapInfo.MaxEntries)
			if ratio > maxDeleteRatio {
				maxDeleteRatio = ratio
			}
			log.WithFields(logrus.Fields{
				logfields.Path: path,
				"count":        deleted,
			}).Debug("Deleted filtered entries from map")
		}
	}

	return
}

func createGCFilter(initialScan bool, restoredEndpoints []*endpoint.Endpoint) *ctmap.GCFilter {
	filter := &ctmap.GCFilter{
		RemoveExpired: true,
	}

	// On the initial scan, scrub all IPs from the conntrack table which do
	// not belong to IPs of any endpoint that has been restored. No new
	// endpoints can appear yet so we can assume that any other entry not
	// belonging to a restored endpoint has become stale.
	if initialScan {
		filter.ValidIPs = map[string]struct{}{}
		for _, ep := range restoredEndpoints {
			filter.ValidIPs[ep.IPv6.String()] = struct{}{}
			filter.ValidIPs[ep.IPv4.String()] = struct{}{}
		}
	}

	return filter
}
<|endoftext|>


<commit_before>package reactors

import (
	"strings"

	"github.com/disiqueira/MySlackBot/pkg/listener"
	"github.com/disiqueira/MySlackBot/pkg/provider"
	"github.com/disiqueira/MySlackBot/pkg/slack"
)

type (
	Wolfram interface {
		listener.Reactor
	}

	wolfram struct {
		prefix   string
		provider provider.Wolfram
	}
)

func NewWolfram(provider provider.Wolfram, prefix string) Wolfram {
	return &wolfram{
		prefix:   prefix,
		provider: provider,
	}
}

func (w *wolfram) Usage() string {
	return w.prefix + " {question} - Answer any question in natural language"
}

func (w *wolfram) Execute(agent slack.Agent, message slack.Message) error {
	if !strings.HasPrefix(message.Text, w.prefix) {
		return nil
	}
	text := strings.Replace(message.Text, w.prefix, "", 1)
	text = strings.Trim(text, " ")

	wolframAnswer, err := w.provider.Ask(text)
	if err != nil {
		return err
	}
	answer := message
	answer.Text = wolframAnswer

	agent.SendMessage(answer)
	return nil
}
<commit_msg>update wolfram reactor<commit_after>package reactors

import (
	"strings"

	"github.com/disiqueira/MySlackBot/pkg/listener"
	"github.com/disiqueira/MySlackBot/pkg/provider"
	"github.com/disiqueira/MySlackBot/pkg/slack"
	"fmt"
)

type (
	Wolfram interface {
		listener.Reactor
	}

	wolfram struct {
		prefix   string
		provider provider.Wolfram
	}
)

const (
	multivac = "INSUFFICIENT DATA FOR A MEANINGFUL ANSWER"
)

func NewWolfram(provider provider.Wolfram, prefix string) Wolfram {
	return &wolfram{
		prefix:   prefix,
		provider: provider,
	}
}

func (w *wolfram) Usage() string {
	return w.prefix + " {question} - Answer any question in natural language"
}

func (w *wolfram) Execute(agent slack.Agent, message slack.Message) error {
	if !strings.HasPrefix(message.Text, w.prefix) {
		return nil
	}
	text := strings.Replace(message.Text, w.prefix, "", 1)
	text = strings.Trim(text, " ")

	wolframAnswer, err := w.provider.Ask(text)
	if err != nil {
		return err
	}
	answer := message

	var plainTextList []string
	for _, pod := range wolframAnswer.Queryresult.Pods {
		plainTextList = filterAndAddToList(plainTextList,fmt.Sprintf("*%s:*", pod.Title))
		for _, subPod := range pod.Subpods {
			plainTextList = filterAndAddToList(plainTextList, fmt.Sprintf("*%s:*", subPod.Title))
			plainTextList = filterAndAddToList(plainTextList, subPod.Plaintext)
		}
	}

	for _, mean := range wolframAnswer.Queryresult.Didyoumeans {
		plainTextList = append(plainTextList,fmt.Sprintf("You should try: %s (%s)", mean.Val, mean.Score))
	}

	if len(plainTextList) == 0 {
		answer.Text = multivac

		agent.SendMessage(answer)
		return nil
	}

	answer.Text = strings.Join(plainTextList, "\n")

	agent.SendMessage(answer)
	return nil
}

func filterAndAddToList(list []string, item string) []string {
	item = strings.Replace(item, "|", ":", -1)
	item = strings.TrimSpace(item)
	if item != "" && len(item) > 3 {
		return append(list, item)
	}
	return list
}
<|endoftext|>


<commit_before>// Copyright © 2018 Banzai Cloud
//
// 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 vault

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/hashicorp/vault/api"
	vaultapi "github.com/hashicorp/vault/api"
	log "github.com/sirupsen/logrus"
	"k8s.io/client-go/rest"
)

const (
	serviceAccountFile  = "/var/run/secrets/kubernetes.io/serviceaccount/token"
	initialTokenTimeout = 10 * time.Second
)

var logger *log.Logger

// NewData is a helper function for Vault KV Version two secret data creation
func NewData(cas int, data map[string]interface{}) map[string]interface{} {
	return map[string]interface{}{
		"options": map[string]interface{}{"cas": cas},
		"data":    data,
	}
}

type clientOptions struct {
	url       string
	role      string
	authPath  string
	tokenPath string
	token     string
}

// ClientOption configures a Vault client using the functional options paradigm popularized by Rob Pike and Dave Cheney.
// If you're unfamiliar with this style,
// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and
// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis.
type ClientOption interface {
	apply(o *clientOptions)
}

// ClientURL is the vault url EX: https://my-vault.vault.org
type ClientURL string

func (co ClientURL) apply(o *clientOptions) {
	o.url = string(co)
}

// ClientRole is the vault role which the client would like to receive
type ClientRole string

func (co ClientRole) apply(o *clientOptions) {
	o.role = string(co)
}

// ClientAuthPath is the mount path where the auth method is enabled.
type ClientAuthPath string

func (co ClientAuthPath) apply(o *clientOptions) {
	o.authPath = string(co)
}

// ClientTokenPath file where the Vault token can be found.
type ClientTokenPath string

func (co ClientTokenPath) apply(o *clientOptions) {
	o.tokenPath = string(co)
}

// ClientToken is a Vault token.
type ClientToken string

func (co ClientToken) apply(o *clientOptions) {
	o.token = string(co)
}

// Client is a Vault client with Kubernetes support and token automatic renewing
type Client struct {
	client       *vaultapi.Client
	logical      *vaultapi.Logical
	tokenRenewer *vaultapi.Renewer
	closed       bool
	watch        *fsnotify.Watcher
	mu           sync.Mutex
}

// NewClient creates a new Vault client.
func NewClient(role string) (*Client, error) {
	return NewClientWithOptions(ClientRole(role))
}

// NewClientWithOptions creates a new Vault client with custom options.
func NewClientWithOptions(opts ...ClientOption) (*Client, error) {
	return NewClientFromConfig(vaultapi.DefaultConfig(), opts...)
}

// NewClientWithConfig creates a new Vault client with custom configuration.
// Deprecated: use NewClientFromConfig instead.
func NewClientWithConfig(config *vaultapi.Config, role, path string) (*Client, error) {
	return NewClientFromConfig(config, ClientRole(role), ClientAuthPath(path))
}

// NewClientFromConfig creates a new Vault client from custom configuration.
func NewClientFromConfig(config *vaultapi.Config, opts ...ClientOption) (*Client, error) {
	enableJSONLog := os.Getenv("VAULT_JSON_LOG")

	logger = log.New()
	if enableJSONLog == "true" {
		logger.SetFormatter(&log.JSONFormatter{})
	}

	rawClient, err := vaultapi.NewClient(config)
	if err != nil {
		return nil, err
	}

	client, err := NewClientFromRawClient(rawClient, opts...)
	if err != nil {
		return nil, err
	}

	caCertPath := os.Getenv(vaultapi.EnvVaultCACert)
	caCertReload := os.Getenv("VAULT_CACERT_RELOAD") != "false"

	if caCertPath != "" && caCertReload {
		watch, err := fsnotify.NewWatcher()
		if err != nil {
			return nil, err
		}

		caCertFile := filepath.Clean(caCertPath)
		configDir, _ := filepath.Split(caCertFile)

		_ = watch.Add(configDir)

		go func() {
			for {
				client.mu.Lock()
				if client.closed {
					client.mu.Unlock()
					break
				}
				client.mu.Unlock()

				select {
				case event := <-watch.Events:
					// we only care about the CA cert file or the Secret mount directory (if in Kubernetes)
					if filepath.Clean(event.Name) == caCertFile || filepath.Base(event.Name) == "..data" {
						if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
							err := config.ReadEnvironment()
							if err != nil {
								logger.Println("failed to reload Vault config:", err)
							} else {
								logger.Println("CA certificate reloaded")
							}
						}
					}
				case err := <-watch.Errors:
					logger.Println("watcher error:", err)
				}
			}
		}()

		client.watch = watch
	}

	return client, nil
}

// NewClientFromRawClient creates a new Vault client from custom raw client.
func NewClientFromRawClient(rawClient *vaultapi.Client, opts ...ClientOption) (*Client, error) {
	logical := rawClient.Logical()
	client := &Client{client: rawClient, logical: logical}

	var tokenRenewer *vaultapi.Renewer

	o := &clientOptions{}

	for _, opt := range opts {
		opt.apply(o)
	}

	// Default URL
	if o.url == "" {
		o.url = vaultapi.DefaultConfig().Address
	}

	// Default role
	if o.role == "" {
		o.role = "default"
	}

	// Default auth path
	if o.authPath == "" {
		o.authPath = "kubernetes"
	}

	// Default token path
	if o.tokenPath == "" {
		o.tokenPath = os.Getenv("HOME") + "/.vault-token"
		if env, ok := os.LookupEnv("VAULT_TOKEN_PATH"); ok {
			o.tokenPath = env
		}
	}

	rawClient.SetAddress(o.url)

	// Add token if set
	if o.token != "" {
		rawClient.SetToken(o.token)

	} else if rawClient.Token() == "" {
		token, err := ioutil.ReadFile(o.tokenPath)
		if err == nil {
			rawClient.SetToken(string(token))
		} else {
			// If VAULT_TOKEN, VAULT_TOKEN_PATH or ~/.vault-token wasn't provided let's
			// suppose we are in Kubernetes and try to get one with the ServiceAccount token.

			// Check that we are in Kubernetes
			_, err := rest.InClusterConfig()
			if err != nil {
				return nil, err
			}

			jwt, err := ioutil.ReadFile(serviceAccountFile)
			if err != nil {
				return nil, err
			}

			initialTokenArrived := make(chan string, 1)
			initialTokenSent := false

			go func() {
				for {
					client.mu.Lock()
					if client.closed {
						client.mu.Unlock()
						break
					}
					client.mu.Unlock()

					data := map[string]interface{}{"jwt": string(jwt), "role": o.role}

					secret, err := logical.Write(fmt.Sprintf("auth/%s/login", o.authPath), data)
					if err != nil {
						logger.Println("Failed to request new Vault token", err.Error())
						time.Sleep(1 * time.Second)
						continue
					}

					if secret == nil {
						logger.Println("Received empty answer from Vault, retrying")
						time.Sleep(1 * time.Second)
						continue
					}

					logger.Println("Received new Vault token")

					// Set the first token from the response
					rawClient.SetToken(secret.Auth.ClientToken)

					if !initialTokenSent {
						initialTokenArrived <- secret.LeaseID
						initialTokenSent = true
					}

					// Start the renewing process
					tokenRenewer, err = rawClient.NewRenewer(&vaultapi.RenewerInput{Secret: secret})
					if err != nil {
						logger.Println("Failed to renew Vault token", err.Error())
						continue
					}

					client.mu.Lock()
					client.tokenRenewer = tokenRenewer
					client.mu.Unlock()

					go tokenRenewer.Renew()

					runRenewChecker(tokenRenewer)
				}
				logger.Println("Vault token renewal closed")
			}()

			select {
			case <-initialTokenArrived:
				logger.Println("Initial Vault token arrived")

			case <-time.After(initialTokenTimeout):
				client.Close()
				return nil, fmt.Errorf("timeout [%s] during waiting for Vault token", initialTokenTimeout)
			}
		}
	}

	return client, nil
}

func runRenewChecker(tokenRenewer *vaultapi.Renewer) {
	for {
		select {
		case err := <-tokenRenewer.DoneCh():
			if err != nil {
				logger.Println("Vault token renewal error:", err.Error())
			}
			return
		case <-tokenRenewer.RenewCh():
			logger.Printf("Renewed Vault Token")
		}
	}
}

// Vault returns the underlying hashicorp Vault client.
// Deprecated: use RawClient instead.
func (client *Client) Vault() *vaultapi.Client {
	return client.RawClient()
}

// RawClient returns the underlying raw Vault client.
func (client *Client) RawClient() *vaultapi.Client {
	return client.client
}

// Close stops the token renewing process of this client
func (client *Client) Close() {
	client.mu.Lock()
	defer client.mu.Unlock()

	if client.tokenRenewer != nil {
		client.closed = true
		client.tokenRenewer.Stop()
	}

	if client.watch != nil {
		_ = client.watch.Close()
	}
}

// NewRawClient creates a new raw Vault client.
func NewRawClient() (*api.Client, error) {
	config := vaultapi.DefaultConfig()
	if config.Error != nil {
		return nil, config.Error
	}

	config.HttpClient.Transport.(*http.Transport).TLSHandshakeTimeout = 5 * time.Second

	return vaultapi.NewClient(config)
}
<commit_msg>fix SetAddress<commit_after>// Copyright © 2018 Banzai Cloud
//
// 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 vault

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/hashicorp/vault/api"
	vaultapi "github.com/hashicorp/vault/api"
	log "github.com/sirupsen/logrus"
	"k8s.io/client-go/rest"
)

const (
	serviceAccountFile  = "/var/run/secrets/kubernetes.io/serviceaccount/token"
	initialTokenTimeout = 10 * time.Second
)

var logger *log.Logger

// NewData is a helper function for Vault KV Version two secret data creation
func NewData(cas int, data map[string]interface{}) map[string]interface{} {
	return map[string]interface{}{
		"options": map[string]interface{}{"cas": cas},
		"data":    data,
	}
}

type clientOptions struct {
	url       string
	role      string
	authPath  string
	tokenPath string
	token     string
}

// ClientOption configures a Vault client using the functional options paradigm popularized by Rob Pike and Dave Cheney.
// If you're unfamiliar with this style,
// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and
// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis.
type ClientOption interface {
	apply(o *clientOptions)
}

// ClientURL is the vault url EX: https://my-vault.vault.org
type ClientURL string

func (co ClientURL) apply(o *clientOptions) {
	o.url = string(co)
}

// ClientRole is the vault role which the client would like to receive
type ClientRole string

func (co ClientRole) apply(o *clientOptions) {
	o.role = string(co)
}

// ClientAuthPath is the mount path where the auth method is enabled.
type ClientAuthPath string

func (co ClientAuthPath) apply(o *clientOptions) {
	o.authPath = string(co)
}

// ClientTokenPath file where the Vault token can be found.
type ClientTokenPath string

func (co ClientTokenPath) apply(o *clientOptions) {
	o.tokenPath = string(co)
}

// ClientToken is a Vault token.
type ClientToken string

func (co ClientToken) apply(o *clientOptions) {
	o.token = string(co)
}

// Client is a Vault client with Kubernetes support and token automatic renewing
type Client struct {
	client       *vaultapi.Client
	logical      *vaultapi.Logical
	tokenRenewer *vaultapi.Renewer
	closed       bool
	watch        *fsnotify.Watcher
	mu           sync.Mutex
}

// NewClient creates a new Vault client.
func NewClient(role string) (*Client, error) {
	return NewClientWithOptions(ClientRole(role))
}

// NewClientWithOptions creates a new Vault client with custom options.
func NewClientWithOptions(opts ...ClientOption) (*Client, error) {
	return NewClientFromConfig(vaultapi.DefaultConfig(), opts...)
}

// NewClientWithConfig creates a new Vault client with custom configuration.
// Deprecated: use NewClientFromConfig instead.
func NewClientWithConfig(config *vaultapi.Config, role, path string) (*Client, error) {
	return NewClientFromConfig(config, ClientRole(role), ClientAuthPath(path))
}

// NewClientFromConfig creates a new Vault client from custom configuration.
func NewClientFromConfig(config *vaultapi.Config, opts ...ClientOption) (*Client, error) {
	enableJSONLog := os.Getenv("VAULT_JSON_LOG")

	logger = log.New()
	if enableJSONLog == "true" {
		logger.SetFormatter(&log.JSONFormatter{})
	}

	rawClient, err := vaultapi.NewClient(config)
	if err != nil {
		return nil, err
	}

	client, err := NewClientFromRawClient(rawClient, opts...)
	if err != nil {
		return nil, err
	}

	caCertPath := os.Getenv(vaultapi.EnvVaultCACert)
	caCertReload := os.Getenv("VAULT_CACERT_RELOAD") != "false"

	if caCertPath != "" && caCertReload {
		watch, err := fsnotify.NewWatcher()
		if err != nil {
			return nil, err
		}

		caCertFile := filepath.Clean(caCertPath)
		configDir, _ := filepath.Split(caCertFile)

		_ = watch.Add(configDir)

		go func() {
			for {
				client.mu.Lock()
				if client.closed {
					client.mu.Unlock()
					break
				}
				client.mu.Unlock()

				select {
				case event := <-watch.Events:
					// we only care about the CA cert file or the Secret mount directory (if in Kubernetes)
					if filepath.Clean(event.Name) == caCertFile || filepath.Base(event.Name) == "..data" {
						if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
							err := config.ReadEnvironment()
							if err != nil {
								logger.Println("failed to reload Vault config:", err)
							} else {
								logger.Println("CA certificate reloaded")
							}
						}
					}
				case err := <-watch.Errors:
					logger.Println("watcher error:", err)
				}
			}
		}()

		client.watch = watch
	}

	return client, nil
}

// NewClientFromRawClient creates a new Vault client from custom raw client.
func NewClientFromRawClient(rawClient *vaultapi.Client, opts ...ClientOption) (*Client, error) {
	logical := rawClient.Logical()
	client := &Client{client: rawClient, logical: logical}

	var tokenRenewer *vaultapi.Renewer

	o := &clientOptions{}

	for _, opt := range opts {
		opt.apply(o)
	}

	// Set URL if defined
	if o.url != "" {
		rawClient.SetAddress(o.url)
	}

	// Default role
	if o.role == "" {
		o.role = "default"
	}

	// Default auth path
	if o.authPath == "" {
		o.authPath = "kubernetes"
	}

	// Default token path
	if o.tokenPath == "" {
		o.tokenPath = os.Getenv("HOME") + "/.vault-token"
		if env, ok := os.LookupEnv("VAULT_TOKEN_PATH"); ok {
			o.tokenPath = env
		}
	}

	// Add token if set
	if o.token != "" {
		rawClient.SetToken(o.token)

	} else if rawClient.Token() == "" {
		token, err := ioutil.ReadFile(o.tokenPath)
		if err == nil {
			rawClient.SetToken(string(token))
		} else {
			// If VAULT_TOKEN, VAULT_TOKEN_PATH or ~/.vault-token wasn't provided let's
			// suppose we are in Kubernetes and try to get one with the ServiceAccount token.

			// Check that we are in Kubernetes
			_, err := rest.InClusterConfig()
			if err != nil {
				return nil, err
			}

			jwt, err := ioutil.ReadFile(serviceAccountFile)
			if err != nil {
				return nil, err
			}

			initialTokenArrived := make(chan string, 1)
			initialTokenSent := false

			go func() {
				for {
					client.mu.Lock()
					if client.closed {
						client.mu.Unlock()
						break
					}
					client.mu.Unlock()

					data := map[string]interface{}{"jwt": string(jwt), "role": o.role}

					secret, err := logical.Write(fmt.Sprintf("auth/%s/login", o.authPath), data)
					if err != nil {
						logger.Println("Failed to request new Vault token", err.Error())
						time.Sleep(1 * time.Second)
						continue
					}

					if secret == nil {
						logger.Println("Received empty answer from Vault, retrying")
						time.Sleep(1 * time.Second)
						continue
					}

					logger.Println("Received new Vault token")

					// Set the first token from the response
					rawClient.SetToken(secret.Auth.ClientToken)

					if !initialTokenSent {
						initialTokenArrived <- secret.LeaseID
						initialTokenSent = true
					}

					// Start the renewing process
					tokenRenewer, err = rawClient.NewRenewer(&vaultapi.RenewerInput{Secret: secret})
					if err != nil {
						logger.Println("Failed to renew Vault token", err.Error())
						continue
					}

					client.mu.Lock()
					client.tokenRenewer = tokenRenewer
					client.mu.Unlock()

					go tokenRenewer.Renew()

					runRenewChecker(tokenRenewer)
				}
				logger.Println("Vault token renewal closed")
			}()

			select {
			case <-initialTokenArrived:
				logger.Println("Initial Vault token arrived")

			case <-time.After(initialTokenTimeout):
				client.Close()
				return nil, fmt.Errorf("timeout [%s] during waiting for Vault token", initialTokenTimeout)
			}
		}
	}

	return client, nil
}

func runRenewChecker(tokenRenewer *vaultapi.Renewer) {
	for {
		select {
		case err := <-tokenRenewer.DoneCh():
			if err != nil {
				logger.Println("Vault token renewal error:", err.Error())
			}
			return
		case <-tokenRenewer.RenewCh():
			logger.Printf("Renewed Vault Token")
		}
	}
}

// Vault returns the underlying hashicorp Vault client.
// Deprecated: use RawClient instead.
func (client *Client) Vault() *vaultapi.Client {
	return client.RawClient()
}

// RawClient returns the underlying raw Vault client.
func (client *Client) RawClient() *vaultapi.Client {
	return client.client
}

// Close stops the token renewing process of this client
func (client *Client) Close() {
	client.mu.Lock()
	defer client.mu.Unlock()

	if client.tokenRenewer != nil {
		client.closed = true
		client.tokenRenewer.Stop()
	}

	if client.watch != nil {
		_ = client.watch.Close()
	}
}

// NewRawClient creates a new raw Vault client.
func NewRawClient() (*api.Client, error) {
	config := vaultapi.DefaultConfig()
	if config.Error != nil {
		return nil, config.Error
	}

	config.HttpClient.Transport.(*http.Transport).TLSHandshakeTimeout = 5 * time.Second

	return vaultapi.NewClient(config)
}
<|endoftext|>


<commit_before>// Copyright Istio 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 sets

import "sort"

type Set map[string]struct{}

// NewWithLength returns an empty Set with the given capacity.
// It's only a hint, not a limitation.
func NewWithLength(l int) Set {
	return make(Set, l)
}

// New creates a new Set with the given items.
func New(items ...string) Set {
	s := NewWithLength(len(items))
	return s.InsertAll(items...)
}

// Insert a single item to this Set.
func (s Set) Insert(item string) Set {
	s[item] = struct{}{}
	return s
}

// InsertAll adds the items to this Set.
func (s Set) InsertAll(items ...string) Set {
	for _, item := range items {
		s[item] = struct{}{}
	}
	return s
}

// Delete removes items from the set.
func (s Set) Delete(items ...string) Set {
	for _, item := range items {
		delete(s, item)
	}
	return s
}

// Merge a set of objects that are in s2 into s
// For example:
// s = {a1, a2, a3}
// s2 = {a3, a4, a5}
// s.Merge(s2) = {a1, a2, a3, a4, a5}
func (s Set) Merge(s2 Set) Set {
	for item := range s2 {
		s[item] = struct{}{}
	}

	return s
}

// Copy this set.
func (s Set) Copy() Set {
	result := New()
	for key := range s {
		result.Insert(key)
	}
	return result
}

// Union returns a set of objects that are in s or s2
// For example:
// s = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s.Union(s2) = s2.Union(s) = {a1, a2, a3, a4, a5}
func (s Set) Union(s2 Set) Set {
	result := s.Copy()
	for key := range s2 {
		result.Insert(key)
	}
	return result
}

// Difference returns a set of objects that are not in s2
// For example:
// s = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s.Difference(s2) = {a3}
// s2.Difference(s) = {a4, a5}
func (s Set) Difference(s2 Set) Set {
	result := New()
	for key := range s {
		if !s2.Contains(key) {
			result.Insert(key)
		}
	}
	return result
}

// Intersection returns a set of objects that are common between s and s2
// For example:
// s = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s.Intersection(s2) = {a1, a2}
func (s Set) Intersection(s2 Set) Set {
	result := New()
	for key := range s {
		if s2.Contains(key) {
			result.Insert(key)
		}
	}
	return result
}

// SupersetOf returns true if s contains all elements of s2
// For example:
// s = {a1, a2, a3}
// s2 = {a1, a2, a3, a4, a5}
// s.SupersetOf(s2) = false
// s2.SupersetOf(s) = true
func (s Set) SupersetOf(s2 Set) bool {
	return s2.Difference(s).IsEmpty()
}

// UnsortedList returns the slice with contents in random order.
func (s Set) UnsortedList() []string {
	res := make([]string, 0, s.Len())
	for key := range s {
		res = append(res, key)
	}
	return res
}

// SortedList returns the slice with contents sorted.
func (s Set) SortedList() []string {
	res := s.UnsortedList()
	sort.Strings(res)
	return res
}

// Contains returns whether the given item is in the set.
func (s Set) Contains(item string) bool {
	_, ok := s[item]
	return ok
}

// Equals checks whether the given set is equal to the current set.
func (s Set) Equals(other Set) bool {
	if s.Len() != other.Len() {
		return false
	}

	for key := range s {
		if !other.Contains(key) {
			return false
		}
	}

	return true
}

// Len returns the number of elements in this Set.
func (s Set) Len() int {
	return len(s)
}

// IsEmpty indicates whether the set is the empty set.
func (s Set) IsEmpty() bool {
	return len(s) == 0
}

// Diff takes a pair of Sets, and returns the elements that occur only on the left and right set.
func (s Set) Diff(other Set) (left []string, right []string) {
	for k := range s {
		if _, f := other[k]; !f {
			left = append(left, k)
		}
	}
	for k := range other {
		if _, f := s[k]; !f {
			right = append(right, k)
		}
	}
	return
}
<commit_msg>remove unused code (#38848)<commit_after>// Copyright Istio 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 sets

import "sort"

type Set map[string]struct{}

// NewWithLength returns an empty Set with the given capacity.
// It's only a hint, not a limitation.
func NewWithLength(l int) Set {
	return make(Set, l)
}

// New creates a new Set with the given items.
func New(items ...string) Set {
	s := NewWithLength(len(items))
	return s.InsertAll(items...)
}

// Insert a single item to this Set.
func (s Set) Insert(item string) Set {
	s[item] = struct{}{}
	return s
}

// InsertAll adds the items to this Set.
func (s Set) InsertAll(items ...string) Set {
	for _, item := range items {
		s[item] = struct{}{}
	}
	return s
}

// Delete removes items from the set.
func (s Set) Delete(items ...string) Set {
	for _, item := range items {
		delete(s, item)
	}
	return s
}

// Merge a set of objects that are in s2 into s
// For example:
// s = {a1, a2, a3}
// s2 = {a3, a4, a5}
// s.Merge(s2) = {a1, a2, a3, a4, a5}
func (s Set) Merge(s2 Set) Set {
	for item := range s2 {
		s[item] = struct{}{}
	}

	return s
}

// Copy this set.
func (s Set) Copy() Set {
	result := New()
	for key := range s {
		result.Insert(key)
	}
	return result
}

// Union returns a set of objects that are in s or s2
// For example:
// s = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s.Union(s2) = s2.Union(s) = {a1, a2, a3, a4, a5}
func (s Set) Union(s2 Set) Set {
	result := s.Copy()
	for key := range s2 {
		result.Insert(key)
	}
	return result
}

// Difference returns a set of objects that are not in s2
// For example:
// s = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s.Difference(s2) = {a3}
// s2.Difference(s) = {a4, a5}
func (s Set) Difference(s2 Set) Set {
	result := New()
	for key := range s {
		if !s2.Contains(key) {
			result.Insert(key)
		}
	}
	return result
}

// Intersection returns a set of objects that are common between s and s2
// For example:
// s = {a1, a2, a3}
// s2 = {a1, a2, a4, a5}
// s.Intersection(s2) = {a1, a2}
func (s Set) Intersection(s2 Set) Set {
	result := New()
	for key := range s {
		if s2.Contains(key) {
			result.Insert(key)
		}
	}
	return result
}

// SupersetOf returns true if s contains all elements of s2
// For example:
// s = {a1, a2, a3}
// s2 = {a1, a2, a3, a4, a5}
// s.SupersetOf(s2) = false
// s2.SupersetOf(s) = true
func (s Set) SupersetOf(s2 Set) bool {
	return s2.Difference(s).IsEmpty()
}

// UnsortedList returns the slice with contents in random order.
func (s Set) UnsortedList() []string {
	res := make([]string, 0, s.Len())
	for key := range s {
		res = append(res, key)
	}
	return res
}

// SortedList returns the slice with contents sorted.
func (s Set) SortedList() []string {
	res := s.UnsortedList()
	sort.Strings(res)
	return res
}

// Contains returns whether the given item is in the set.
func (s Set) Contains(item string) bool {
	_, ok := s[item]
	return ok
}

// Equals checks whether the given set is equal to the current set.
func (s Set) Equals(other Set) bool {
	if s.Len() != other.Len() {
		return false
	}

	for key := range s {
		if !other.Contains(key) {
			return false
		}
	}

	return true
}

// Len returns the number of elements in this Set.
func (s Set) Len() int {
	return len(s)
}

// IsEmpty indicates whether the set is the empty set.
func (s Set) IsEmpty() bool {
	return len(s) == 0
}
<|endoftext|>


<commit_before>// Copyright 2016-2018 Authors of Cilium
//
// 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 workloads

import (
	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/pkg/endpoint"
)

var (
	// defaultClient is the default client initialized by initClient
	defaultClient WorkloadRuntime
)

func initClient(module workloadModule) error {
	c, err := module.newClient()
	if err != nil {
		return err
	}

	defaultClient = c

	return nil
}

// Client returns the global WorkloadRuntime being used.
func Client() WorkloadRuntime {
	return defaultClient
}

// IsRunning returns false if the provided endpoint cannot be associated with a
// running workload. The runtime must be reachable to make this decision.
func IsRunning(ep *endpoint.Endpoint) bool {
	if Client() == nil {
		return false
	}

	return Client().IsRunning(ep)
}

// Status returns the status of the workload runtime
func Status() *models.Status {
	return Client().Status()
}

// EnableEventListener watches for docker events. Performs the plumbing for the
// containers started or dead.
func EnableEventListener() (eventsCh chan<- *EventMessage, err error) {
	return Client().EnableEventListener()
}

// IgnoreRunningWorkloads checks for already running containers and checks
// their IP address, then adds the containers to the list of ignored containers
// and allocates the IPs they are using to prevent future collisions.
func IgnoreRunningWorkloads() {
	Client().IgnoreRunningWorkloads()
}
<commit_msg>pkg/workloads: check if Client() is nil to avoid nil pointer dereference<commit_after>// Copyright 2016-2018 Authors of Cilium
//
// 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 workloads

import (
	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/pkg/endpoint"
)

var (
	// defaultClient is the default client initialized by initClient
	defaultClient WorkloadRuntime
)

func initClient(module workloadModule) error {
	c, err := module.newClient()
	if err != nil {
		return err
	}

	defaultClient = c

	return nil
}

// Client returns the global WorkloadRuntime being used.
func Client() WorkloadRuntime {
	return defaultClient
}

// IsRunning returns false if the provided endpoint cannot be associated with a
// running workload. The runtime must be reachable to make this decision.
func IsRunning(ep *endpoint.Endpoint) bool {
	if Client() == nil {
		return false
	}

	return Client().IsRunning(ep)
}

// Status returns the status of the workload runtime
func Status() *models.Status {
	if Client() == nil {
		return &models.Status{State: models.StatusStateDisabled}
	}
	return Client().Status()
}

// EnableEventListener watches for docker events. Performs the plumbing for the
// containers started or dead.
func EnableEventListener() (eventsCh chan<- *EventMessage, err error) {
	if Client() == nil {
		return nil, nil
	}
	return Client().EnableEventListener()
}

// IgnoreRunningWorkloads checks for already running containers and checks
// their IP address, then adds the containers to the list of ignored containers
// and allocates the IPs they are using to prevent future collisions.
func IgnoreRunningWorkloads() {
	if Client() == nil {
		return
	}
	Client().IgnoreRunningWorkloads()
}
<|endoftext|>


<commit_before>package forward

import (
	"crypto/tls"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/coredns/coredns/plugin/pkg/up"

	"github.com/miekg/dns"
)

// Proxy defines an upstream host.
type Proxy struct {
	addr   string
	client *dns.Client

	// Connection caching
	expire    time.Duration
	transport *transport

	// health checking
	probe *up.Probe
	fails uint32

	avgRtt int64
}

// NewProxy returns a new proxy.
func NewProxy(addr string, tlsConfig *tls.Config) *Proxy {
	p := &Proxy{
		addr:      addr,
		fails:     0,
		probe:     up.New(),
		transport: newTransport(addr, tlsConfig),
		avgRtt:    int64(timeout / 2),
	}
	p.client = dnsClient(tlsConfig)
	runtime.SetFinalizer(p, (*Proxy).finalizer)
	return p
}

// Addr returns the address to forward to.
func (p *Proxy) Addr() (addr string) { return p.addr }

// dnsClient returns a client used for health checking.
func dnsClient(tlsConfig *tls.Config) *dns.Client {
	c := new(dns.Client)
	c.Net = "udp"
	// TODO(miek): this should be half of hcDuration?
	c.ReadTimeout = 1 * time.Second
	c.WriteTimeout = 1 * time.Second

	if tlsConfig != nil {
		c.Net = "tcp-tls"
		c.TLSConfig = tlsConfig
	}
	return c
}

// SetTLSConfig sets the TLS config in the lower p.transport and in the healthchecking client.
func (p *Proxy) SetTLSConfig(cfg *tls.Config) {
	p.transport.SetTLSConfig(cfg)
	p.client = dnsClient(cfg)
}

// IsTLS returns true if proxy uses tls.
func (p *Proxy) IsTLS() bool { return p.transport.tlsConfig != nil }

// SetExpire sets the expire duration in the lower p.transport.
func (p *Proxy) SetExpire(expire time.Duration) { p.transport.SetExpire(expire) }

// Dial connects to the host in p with the configured transport.
func (p *Proxy) Dial(proto string) (*dns.Conn, bool, error) { return p.transport.Dial(proto) }

// Yield returns the connection to the pool.
func (p *Proxy) Yield(c *dns.Conn) { p.transport.Yield(c) }

// Healthcheck kicks of a round of health checks for this proxy.
func (p *Proxy) Healthcheck() { p.probe.Do(p.Check) }

// Down returns true if this proxy is down, i.e. has *more* fails than maxfails.
func (p *Proxy) Down(maxfails uint32) bool {
	if maxfails == 0 {
		return false
	}

	fails := atomic.LoadUint32(&p.fails)
	return fails > maxfails
}

// close stops the health checking goroutine.
func (p *Proxy) close() {
	p.probe.Stop()
}

func (p *Proxy) finalizer() {
	p.transport.Stop()
}

// start starts the proxy's healthchecking.
func (p *Proxy) start(duration time.Duration) {
	p.probe.Start(duration)
	p.transport.Start()
}

const (
	dialTimeout = 4 * time.Second
	timeout     = 2 * time.Second
	maxTimeout  = 2 * time.Second
	minTimeout  = 10 * time.Millisecond
	hcDuration  = 500 * time.Millisecond
)
<commit_msg>plugin/forward: fix alignment for sync.Atomic (#1855)<commit_after>package forward

import (
	"crypto/tls"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/coredns/coredns/plugin/pkg/up"

	"github.com/miekg/dns"
)

// Proxy defines an upstream host.
type Proxy struct {
	avgRtt int64
	fails  uint32

	addr   string
	client *dns.Client

	// Connection caching
	expire    time.Duration
	transport *transport

	// health checking
	probe *up.Probe
}

// NewProxy returns a new proxy.
func NewProxy(addr string, tlsConfig *tls.Config) *Proxy {
	p := &Proxy{
		addr:      addr,
		fails:     0,
		probe:     up.New(),
		transport: newTransport(addr, tlsConfig),
		avgRtt:    int64(timeout / 2),
	}
	p.client = dnsClient(tlsConfig)
	runtime.SetFinalizer(p, (*Proxy).finalizer)
	return p
}

// Addr returns the address to forward to.
func (p *Proxy) Addr() (addr string) { return p.addr }

// dnsClient returns a client used for health checking.
func dnsClient(tlsConfig *tls.Config) *dns.Client {
	c := new(dns.Client)
	c.Net = "udp"
	// TODO(miek): this should be half of hcDuration?
	c.ReadTimeout = 1 * time.Second
	c.WriteTimeout = 1 * time.Second

	if tlsConfig != nil {
		c.Net = "tcp-tls"
		c.TLSConfig = tlsConfig
	}
	return c
}

// SetTLSConfig sets the TLS config in the lower p.transport and in the healthchecking client.
func (p *Proxy) SetTLSConfig(cfg *tls.Config) {
	p.transport.SetTLSConfig(cfg)
	p.client = dnsClient(cfg)
}

// IsTLS returns true if proxy uses tls.
func (p *Proxy) IsTLS() bool { return p.transport.tlsConfig != nil }

// SetExpire sets the expire duration in the lower p.transport.
func (p *Proxy) SetExpire(expire time.Duration) { p.transport.SetExpire(expire) }

// Dial connects to the host in p with the configured transport.
func (p *Proxy) Dial(proto string) (*dns.Conn, bool, error) { return p.transport.Dial(proto) }

// Yield returns the connection to the pool.
func (p *Proxy) Yield(c *dns.Conn) { p.transport.Yield(c) }

// Healthcheck kicks of a round of health checks for this proxy.
func (p *Proxy) Healthcheck() { p.probe.Do(p.Check) }

// Down returns true if this proxy is down, i.e. has *more* fails than maxfails.
func (p *Proxy) Down(maxfails uint32) bool {
	if maxfails == 0 {
		return false
	}

	fails := atomic.LoadUint32(&p.fails)
	return fails > maxfails
}

// close stops the health checking goroutine.
func (p *Proxy) close() {
	p.probe.Stop()
}

func (p *Proxy) finalizer() {
	p.transport.Stop()
}

// start starts the proxy's healthchecking.
func (p *Proxy) start(duration time.Duration) {
	p.probe.Start(duration)
	p.transport.Start()
}

const (
	dialTimeout = 4 * time.Second
	timeout     = 2 * time.Second
	maxTimeout  = 2 * time.Second
	minTimeout  = 10 * time.Millisecond
	hcDuration  = 500 * time.Millisecond
)
<|endoftext|>


<commit_before>package extra

import (
	"errors"
	"regexp"
	"strings"
	"sync"
	"time"

	seabird "github.com/belak/go-seabird"
	"github.com/go-xorm/xorm"
	irc "gopkg.in/irc.v3"
)

func init() {
	seabird.RegisterPlugin("remind", newReminderPlugin)
}

var timeRegexp = regexp.MustCompile(`\d+[smhd]`)

type reminderPlugin struct {
	db *xorm.Engine

	roomLock *sync.Mutex
	rooms    map[string]bool

	// Singly buffered channel
	updateChan chan struct{}
}

type targetType int

const (
	channelTarget targetType = iota
	privateTarget
)

// Reminder represents the xorm model for the reminder plugin
type Reminder struct {
	ID           int64
	Target       string
	TargetType   targetType
	Content      string
	ReminderTime time.Time
}

func newReminderPlugin(b *seabird.Bot) error {
	bm := b.BasicMux()
	cm := b.CommandMux()

	if err := b.EnsurePlugin("db"); err != nil {
		return err
	}

	p := &reminderPlugin{
		roomLock:   &sync.Mutex{},
		rooms:      make(map[string]bool),
		updateChan: make(chan struct{}, 1),

		db: CtxDB(b.Context()),
	}

	err := p.db.Sync(Reminder{})
	if err != nil {
		return err
	}

	bm.Event("001", p.InitialDispatch)
	bm.Event("JOIN", p.joinHandler)
	bm.Event("PART", p.partHandler)
	bm.Event("KICK", p.kickHandler)

	cm.Event("remind", p.RemindCommand, &seabird.HelpInfo{
		Usage:       "<duration> <message>",
		Description: "Remind yourself to do something.",
	})

	return nil
}

func (p *reminderPlugin) joinHandler(r *seabird.Request) {
	if r.Message.Prefix.Name != r.CurrentNick() {
		return
	}

	p.roomLock.Lock()
	defer p.roomLock.Unlock()
	p.rooms[r.Message.Params[0]] = true

	p.updateChan <- struct{}{}
}

func (p *reminderPlugin) partHandler(r *seabird.Request) {
	if r.Message.Prefix.Name != r.CurrentNick() {
		return
	}

	p.roomLock.Lock()
	defer p.roomLock.Unlock()
	delete(p.rooms, r.Message.Params[0])

	p.updateChan <- struct{}{}
}

func (p *reminderPlugin) kickHandler(r *seabird.Request) {
	if r.Message.Params[1] != r.CurrentNick() {
		return
	}

	p.roomLock.Lock()
	defer p.roomLock.Unlock()
	delete(p.rooms, r.Message.Params[0])

	p.updateChan <- struct{}{}
}

func (p *reminderPlugin) nextReminder() (*Reminder, error) {
	// Find the next reminder we'll have to send
	r := &Reminder{}
	_, err := p.db.OrderBy("reminder_time ASC").Get(r)

	if r.ID == 0 {
		r = nil
	}

	return r, err
}

func (p *reminderPlugin) remindLoop(r *seabird.Request) {
	logger := r.GetLogger("remind")

	logger.Info("Starting reminder loop")

	// TODO: this should use the bot, not the request, as they're scoped
	// differently.

	for {
		reminder, err := p.nextReminder()
		if err != nil {
			logger.WithError(err).Error("Transaction failure. Exiting loop.")
			return
		}

		var timer <-chan time.Time

		if reminder != nil {
			logger.WithField("reminder", reminder).Debug("Next reminder")

			waitDur := time.Until(reminder.ReminderTime)
			if waitDur <= 0 {
				p.dispatch(r, reminder)
				continue
			}

			timer = time.After(waitDur)
		}

		select {
		case <-timer:
			p.dispatch(r, reminder)
		case <-p.updateChan:
			continue
		}
	}
}

func (p *reminderPlugin) dispatch(r *seabird.Request, reminder *Reminder) {
	logger := r.GetLogger("remind").WithField("reminder", r)

	// Send the message
	r.WriteMessage(&irc.Message{
		Prefix:  &irc.Prefix{},
		Command: "PRIVMSG",
		Params:  []string{reminder.Target, reminder.Content},
	})

	// Nuke the reminder now that it's been sent
	_, err := p.db.Delete(r)
	if err != nil {
		logger.WithError(err).Error("Failed to remove reminder")
	}

	logger.Debug("Dispatched reminder")
}

// InitialDispatch is used to send private messages to users on connection. We
// can't queue up the channels yet because we haven't joined them.
func (p *reminderPlugin) InitialDispatch(r *seabird.Request) {
	go p.remindLoop(r)
}

// ParseTime parses the text string and turns it into a time.Duration
func (p *reminderPlugin) ParseTime(timeStr string) (time.Duration, error) {
	var ret time.Duration

	for _, match := range timeRegexp.FindAllString(timeStr, -1) {
		switch match[len(match)-1] {
		case 's', 'm', 'h':
			tmp, err := time.ParseDuration(match)
			if err != nil {
				return ret, err
			}

			ret += tmp
		case 'd':
			// We can parse days as if they were hours then just
			// multiply the result by 24. This will result in some
			// loss of precision, but it should otherwise be fine.
			tmp, err := time.ParseDuration(match[:len(match)-1] + "h")
			if err != nil {
				return ret, err
			}

			ret += tmp * 24
		default:
			return ret, errors.New("Unknown time type")
		}
	}

	return ret, nil
}

func (p *reminderPlugin) RemindCommand(r *seabird.Request) {
	split := strings.SplitN(r.Message.Trailing(), " ", 2)
	if len(split) != 2 {
		r.MentionReply("Not enough args")
		return
	}

	dur, err := p.ParseTime(split[0])
	if err != nil {
		r.MentionReply("Invalid duration: %s", err)
		return
	}

	rem := &Reminder{
		Target:       r.Message.Prefix.Name,
		TargetType:   privateTarget,
		Content:      split[1],
		ReminderTime: time.Now().Add(dur),
	}

	if r.FromChannel() {
		// If it was from a channel, we need to prepend the user's name.
		rem.Target = r.Message.Params[0]
		rem.TargetType = channelTarget
		rem.Content = r.Message.Prefix.Name + ": " + rem.Content
	}

	_, err = p.db.Insert(rem)
	if err != nil {
		r.MentionReply("Failed to store reminder: %s", err)
		return
	}

	r.MentionReply("Event stored")

	logger := r.GetLogger("remind")
	logger.WithField("reminder", rem).Debug("Stored reminder")

	p.updateChan <- struct{}{}
}
<commit_msg>remind: fix crash loop<commit_after>package extra

import (
	"errors"
	"regexp"
	"strings"
	"sync"
	"time"

	seabird "github.com/belak/go-seabird"
	"github.com/go-xorm/xorm"
	irc "gopkg.in/irc.v3"
)

func init() {
	seabird.RegisterPlugin("remind", newReminderPlugin)
}

var timeRegexp = regexp.MustCompile(`\d+[smhd]`)

type reminderPlugin struct {
	db *xorm.Engine

	roomLock *sync.Mutex
	rooms    map[string]bool

	// Singly buffered channel
	updateChan chan struct{}
}

type targetType int

const (
	channelTarget targetType = iota
	privateTarget
)

// Reminder represents the xorm model for the reminder plugin
type Reminder struct {
	ID           int64
	Target       string
	TargetType   targetType
	Content      string
	ReminderTime time.Time
}

func newReminderPlugin(b *seabird.Bot) error {
	bm := b.BasicMux()
	cm := b.CommandMux()

	if err := b.EnsurePlugin("db"); err != nil {
		return err
	}

	p := &reminderPlugin{
		roomLock:   &sync.Mutex{},
		rooms:      make(map[string]bool),
		updateChan: make(chan struct{}, 1),

		db: CtxDB(b.Context()),
	}

	err := p.db.Sync(Reminder{})
	if err != nil {
		return err
	}

	bm.Event("001", p.InitialDispatch)
	bm.Event("JOIN", p.joinHandler)
	bm.Event("PART", p.partHandler)
	bm.Event("KICK", p.kickHandler)

	cm.Event("remind", p.RemindCommand, &seabird.HelpInfo{
		Usage:       "<duration> <message>",
		Description: "Remind yourself to do something.",
	})

	return nil
}

func (p *reminderPlugin) joinHandler(r *seabird.Request) {
	if r.Message.Prefix.Name != r.CurrentNick() {
		return
	}

	p.roomLock.Lock()
	defer p.roomLock.Unlock()
	p.rooms[r.Message.Params[0]] = true

	p.updateChan <- struct{}{}
}

func (p *reminderPlugin) partHandler(r *seabird.Request) {
	if r.Message.Prefix.Name != r.CurrentNick() {
		return
	}

	p.roomLock.Lock()
	defer p.roomLock.Unlock()
	delete(p.rooms, r.Message.Params[0])

	p.updateChan <- struct{}{}
}

func (p *reminderPlugin) kickHandler(r *seabird.Request) {
	if r.Message.Params[1] != r.CurrentNick() {
		return
	}

	p.roomLock.Lock()
	defer p.roomLock.Unlock()
	delete(p.rooms, r.Message.Params[0])

	p.updateChan <- struct{}{}
}

func (p *reminderPlugin) nextReminder() (*Reminder, error) {
	// Find the next reminder we'll have to send
	r := &Reminder{}
	_, err := p.db.OrderBy("reminder_time ASC").Get(r)

	if r.ID == 0 {
		r = nil
	}

	return r, err
}

func (p *reminderPlugin) remindLoop(r *seabird.Request) {
	logger := r.GetLogger("remind")

	logger.Info("Starting reminder loop")

	// TODO: this should use the bot, not the request, as they're scoped
	// differently.

	for {
		reminder, err := p.nextReminder()
		if err != nil {
			logger.WithError(err).Error("Transaction failure. Exiting loop.")
			return
		}

		var timer <-chan time.Time

		if reminder != nil {
			logger.WithField("reminder", reminder).Debug("Next reminder")

			waitDur := time.Until(reminder.ReminderTime)
			if waitDur <= 0 {
				p.dispatch(r, reminder)
				continue
			}

			timer = time.After(waitDur)
		}

		select {
		case <-timer:
			p.dispatch(r, reminder)
		case <-p.updateChan:
			continue
		}
	}
}

func (p *reminderPlugin) dispatch(r *seabird.Request, reminder *Reminder) {
	logger := r.GetLogger("remind").WithField("reminder", r)

	// Send the message
	r.WriteMessage(&irc.Message{
		Prefix:  &irc.Prefix{},
		Command: "PRIVMSG",
		Params:  []string{reminder.Target, reminder.Content},
	})

	// Nuke the reminder now that it's been sent
	_, err := p.db.Delete(reminder)
	if err != nil {
		logger.WithError(err).Error("Failed to remove reminder")
	}

	logger.Debug("Dispatched reminder")
}

// InitialDispatch is used to send private messages to users on connection. We
// can't queue up the channels yet because we haven't joined them.
func (p *reminderPlugin) InitialDispatch(r *seabird.Request) {
	go p.remindLoop(r)
}

// ParseTime parses the text string and turns it into a time.Duration
func (p *reminderPlugin) ParseTime(timeStr string) (time.Duration, error) {
	var ret time.Duration

	for _, match := range timeRegexp.FindAllString(timeStr, -1) {
		switch match[len(match)-1] {
		case 's', 'm', 'h':
			tmp, err := time.ParseDuration(match)
			if err != nil {
				return ret, err
			}

			ret += tmp
		case 'd':
			// We can parse days as if they were hours then just
			// multiply the result by 24. This will result in some
			// loss of precision, but it should otherwise be fine.
			tmp, err := time.ParseDuration(match[:len(match)-1] + "h")
			if err != nil {
				return ret, err
			}

			ret += tmp * 24
		default:
			return ret, errors.New("Unknown time type")
		}
	}

	return ret, nil
}

func (p *reminderPlugin) RemindCommand(r *seabird.Request) {
	split := strings.SplitN(r.Message.Trailing(), " ", 2)
	if len(split) != 2 {
		r.MentionReply("Not enough args")
		return
	}

	dur, err := p.ParseTime(split[0])
	if err != nil {
		r.MentionReply("Invalid duration: %s", err)
		return
	}

	rem := &Reminder{
		Target:       r.Message.Prefix.Name,
		TargetType:   privateTarget,
		Content:      split[1],
		ReminderTime: time.Now().Add(dur),
	}

	if r.FromChannel() {
		// If it was from a channel, we need to prepend the user's name.
		rem.Target = r.Message.Params[0]
		rem.TargetType = channelTarget
		rem.Content = r.Message.Prefix.Name + ": " + rem.Content
	}

	_, err = p.db.Insert(rem)
	if err != nil {
		r.MentionReply("Failed to store reminder: %s", err)
		return
	}

	r.MentionReply("Event stored")

	logger := r.GetLogger("remind")
	logger.WithField("reminder", rem).Debug("Stored reminder")

	p.updateChan <- struct{}{}
}
<|endoftext|>


<commit_before>package tests

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func fibo(rank uint64) uint64 {
	if rank == 0 {
		return 0
	} else if rank <= 2 {
		return 1
	} else {
		return fibo(rank-1) + fibo(rank-2)
	}
}

func TestMain(t *testing.T) {
	Convey("Testing random things", t, func() {
		Convey("Testing 1+1", func() {
			res := 1 + 1
			Convey("Should equal 2", func() {
				So(res, ShouldEqual, 2)
			})
		})
		Convey("Testing fibonacci", func() {
			Convey("Fibo(0) should return 0", func() {
				So(fibo(0), ShouldEqual, 0)
			})
			Convey("Fibo(1) and fibo(2) should return 1", func() {
				So(fibo(1), ShouldEqual, 1)
				So(fibo(2), ShouldEqual, 1)
			})
			Convey("Fibo(8) should return 21", func() {
				So(fibo(8), ShouldEqual, 21)
			})
			Convey("Fibo(25) should return 75025", func() {
				So(fibo(25), ShouldEqual, 75025)
			})
		})
	})
}
<commit_msg>Tests: Added **1*1** test<commit_after>package tests

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func fibo(rank uint64) uint64 {
	if rank == 0 {
		return 0
	} else if rank <= 2 {
		return 1
	} else {
		return fibo(rank-1) + fibo(rank-2)
	}
}

func TestMain(t *testing.T) {
	Convey("Testing random things", t, func() {
		Convey("Testing 1+1", func() {
			res := 1 + 1
			Convey("Should equal 2", func() {
				So(res, ShouldEqual, 2)
			})
		})
		Convey("Testing 1*1", func() {
			res := 1 * 1
			Convey("Should equal 1", func() {
				So(res, ShouldEqual, 1)
			})
		})
		Convey("Testing fibonacci", func() {
			Convey("Fibo(0) should return 0", func() {
				So(fibo(0), ShouldEqual, 0)
			})
			Convey("Fibo(1) and fibo(2) should return 1", func() {
				So(fibo(1), ShouldEqual, 1)
				So(fibo(2), ShouldEqual, 1)
			})
			Convey("Fibo(8) should return 21", func() {
				So(fibo(8), ShouldEqual, 21)
			})
			Convey("Fibo(25) should return 75025", func() {
				So(fibo(25), ShouldEqual, 75025)
			})
		})
	})
}
<|endoftext|>