<commit_before>/*
Copyright 2016 The Kubernetes 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 taint

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/spf13/cobra"
	"k8s.io/klog"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/apimachinery/pkg/util/validation"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/cli-runtime/pkg/resource"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/scheme"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
)

// TaintOptions have the data required to perform the taint operation
type TaintOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	ToPrinter  func(string) (printers.ResourcePrinter, error)

	DryRunStrategy cmdutil.DryRunStrategy
	DryRunVerifier *resource.DryRunVerifier

	resources      []string
	taintsToAdd    []v1.Taint
	taintsToRemove []v1.Taint
	builder        *resource.Builder
	selector       string
	overwrite      bool
	all            bool

	ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error)

	genericclioptions.IOStreams
}

var (
	taintLong = templates.LongDesc(i18n.T(`
		Update the taints on one or more nodes.

		* A taint consists of a key, value, and effect. As an argument here, it is expressed as key=value:effect.
		* The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
		* Optionally, the key can begin with a DNS subdomain prefix and a single '/', like example.com/my-app
		* The value is optional. If given, it must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[2]d characters.
		* The effect must be NoSchedule, PreferNoSchedule or NoExecute.
		* Currently taint can only apply to node.`))

	taintExample = templates.Examples(i18n.T(`
		# Update node 'foo' with a taint with key 'dedicated' and value 'special-user' and effect 'NoSchedule'.
		# If a taint with that key and effect already exists, its value is replaced as specified.
		kubectl taint nodes foo dedicated=special-user:NoSchedule

		# Remove from node 'foo' the taint with key 'dedicated' and effect 'NoSchedule' if one exists.
		kubectl taint nodes foo dedicated:NoSchedule-

		# Remove from node 'foo' all the taints with key 'dedicated'
		kubectl taint nodes foo dedicated-

		# Add a taint with key 'dedicated' on nodes having label mylabel=X
		kubectl taint node -l myLabel=X  dedicated=foo:PreferNoSchedule

		# Add to node 'foo' a taint with key 'bar' and no value
		kubectl taint nodes foo bar:NoSchedule`))
)

func NewCmdTaint(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
	options := &TaintOptions{
		PrintFlags: genericclioptions.NewPrintFlags("tainted").WithTypeSetter(scheme.Scheme),
		IOStreams:  streams,
	}

	validArgs := []string{"node"}

	cmd := &cobra.Command{
		Use:                   "taint NODE NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N",
		DisableFlagsInUseLine: true,
		Short:                 i18n.T("Update the taints on one or more nodes"),
		Long:                  fmt.Sprintf(taintLong, validation.DNS1123SubdomainMaxLength, validation.LabelValueMaxLength),
		Example:               taintExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(options.Complete(f, cmd, args))
			cmdutil.CheckErr(options.Validate())
			cmdutil.CheckErr(options.RunTaint())
		},
		ValidArgs: validArgs,
	}

	options.PrintFlags.AddFlags(cmd)
	cmdutil.AddDryRunFlag(cmd)

	cmdutil.AddValidateFlags(cmd)
	cmd.Flags().StringVarP(&options.selector, "selector", "l", options.selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
	cmd.Flags().BoolVar(&options.overwrite, "overwrite", options.overwrite, "If true, allow taints to be overwritten, otherwise reject taint updates that overwrite existing taints.")
	cmd.Flags().BoolVar(&options.all, "all", options.all, "Select all nodes in the cluster")
	return cmd
}

// Complete adapts from the command line args and factory to the data required.
func (o *TaintOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) (err error) {
	namespace, _, err := f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}

	o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
	if err != nil {
		return err
	}
	dynamicClient, err := f.DynamicClient()
	if err != nil {
		return err
	}
	discoveryClient, err := f.ToDiscoveryClient()
	if err != nil {
		return err
	}
	o.DryRunVerifier = resource.NewDryRunVerifier(dynamicClient, discoveryClient)
	cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)

	// retrieves resource and taint args from args
	// also checks args to verify that all resources are specified before taints
	taintArgs := []string{}
	metTaintArg := false
	for _, s := range args {
		isTaint := strings.Contains(s, "=") || strings.Contains(s, ":") || strings.HasSuffix(s, "-")
		switch {
		case !metTaintArg && isTaint:
			metTaintArg = true
			fallthrough
		case metTaintArg && isTaint:
			taintArgs = append(taintArgs, s)
		case !metTaintArg && !isTaint:
			o.resources = append(o.resources, s)
		case metTaintArg && !isTaint:
			return fmt.Errorf("all resources must be specified before taint changes: %s", s)
		}
	}

	o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
		o.PrintFlags.NamePrintFlags.Operation = operation
		return o.PrintFlags.ToPrinter()
	}

	if len(o.resources) < 1 {
		return fmt.Errorf("one or more resources must be specified as <resource> <name>")
	}
	if len(taintArgs) < 1 {
		return fmt.Errorf("at least one taint update is required")
	}

	if o.taintsToAdd, o.taintsToRemove, err = parseTaints(taintArgs); err != nil {
		return cmdutil.UsageErrorf(cmd, err.Error())
	}
	o.builder = f.NewBuilder().
		WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
		ContinueOnError().
		NamespaceParam(namespace).DefaultNamespace()
	if o.selector != "" {
		o.builder = o.builder.LabelSelectorParam(o.selector).ResourceTypes("node")
	}
	if o.all {
		o.builder = o.builder.SelectAllParam(o.all).ResourceTypes("node").Flatten().Latest()
	}
	if !o.all && o.selector == "" && len(o.resources) >= 2 {
		o.builder = o.builder.ResourceNames("node", o.resources[1:]...)
	}
	o.builder = o.builder.LabelSelectorParam(o.selector).
		Flatten().
		Latest()

	o.ClientForMapping = f.ClientForMapping
	return nil
}

// validateFlags checks for the validation of flags for kubectl taints.
func (o TaintOptions) validateFlags() error {
	// Cannot have a non-empty selector and all flag set. They are mutually exclusive.
	if o.all && o.selector != "" {
		return fmt.Errorf("setting 'all' parameter with a non empty selector is prohibited")
	}
	// If both selector and all are not set.
	if !o.all && o.selector == "" {
		if len(o.resources) < 2 {
			return fmt.Errorf("at least one resource name must be specified since 'all' parameter is not set")
		} else {
			return nil
		}
	}
	return nil
}

// Validate checks to the TaintOptions to see if there is sufficient information run the command.
func (o TaintOptions) Validate() error {
	resourceType := strings.ToLower(o.resources[0])
	validResources, isValidResource := []string{"node", "nodes"}, false
	for _, validResource := range validResources {
		if resourceType == validResource {
			isValidResource = true
			break
		}
	}
	if !isValidResource {
		return fmt.Errorf("invalid resource type %s, only %q are supported", o.resources[0], validResources)
	}

	// check the format of taint args and checks removed taints aren't in the new taints list
	var conflictTaints []string
	for _, taintAdd := range o.taintsToAdd {
		for _, taintRemove := range o.taintsToRemove {
			if taintAdd.Key != taintRemove.Key {
				continue
			}
			if len(taintRemove.Effect) == 0 || taintAdd.Effect == taintRemove.Effect {
				conflictTaint := fmt.Sprintf("{\"%s\":\"%s\"}", taintRemove.Key, taintRemove.Effect)
				conflictTaints = append(conflictTaints, conflictTaint)
			}
		}
	}
	if len(conflictTaints) > 0 {
		return fmt.Errorf("can not both modify and remove the following taint(s) in the same command: %s", strings.Join(conflictTaints, ", "))
	}
	return o.validateFlags()
}

// RunTaint does the work
func (o TaintOptions) RunTaint() error {
	r := o.builder.Do()
	if err := r.Err(); err != nil {
		return err
	}

	return r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}

		obj := info.Object
		name, namespace := info.Name, info.Namespace
		oldData, err := json.Marshal(obj)
		if err != nil {
			return err
		}
		operation, err := o.updateTaints(obj)
		if err != nil {
			return err
		}
		newData, err := json.Marshal(obj)
		if err != nil {
			return err
		}
		patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj)
		createdPatch := err == nil
		if err != nil {
			klog.V(2).Infof("couldn't compute patch: %v", err)
		}

		printer, err := o.ToPrinter(operation)
		if err != nil {
			return err
		}
		if o.DryRunStrategy == cmdutil.DryRunClient {
			if createdPatch {
				typedObj, err := scheme.Scheme.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
				if err != nil {
					return err
				}

				nodeObj, ok := typedObj.(*v1.Node)
				if !ok {
					return fmt.Errorf("unexpected type %T", typedObj)
				}

				originalObjJS, err := json.Marshal(nodeObj)
				if err != nil {
					return err
				}

				originalPatchedObjJS, err := strategicpatch.StrategicMergePatch(originalObjJS, patchBytes, nodeObj)
				if err != nil {
					return err
				}

				targetObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, originalPatchedObjJS)
				if err != nil {
					return err
				}
				return printer.PrintObj(targetObj, o.Out)
			}
			return printer.PrintObj(obj, o.Out)
		}

		mapping := info.ResourceMapping()
		if o.DryRunStrategy == cmdutil.DryRunServer {
			if err := o.DryRunVerifier.HasSupport(mapping.GroupVersionKind); err != nil {
				return err
			}
		}
		client, err := o.ClientForMapping(mapping)
		if err != nil {
			return err
		}
		helper := resource.
			NewHelper(client, mapping).
			DryRun(o.DryRunStrategy == cmdutil.DryRunServer)

		var outputObj runtime.Object
		if createdPatch {
			outputObj, err = helper.Patch(namespace, name, types.StrategicMergePatchType, patchBytes, nil)
		} else {
			outputObj, err = helper.Replace(namespace, name, false, obj)
		}
		if err != nil {
			return err
		}

		return printer.PrintObj(outputObj, o.Out)
	})
}

// updateTaints applies a taint option(o) to a node in cluster after computing the net effect of operation(i.e. does it result in an overwrite?), it reports back the end result in a way that user can easily interpret.
func (o TaintOptions) updateTaints(obj runtime.Object) (string, error) {
	node, ok := obj.(*v1.Node)
	if !ok {
		return "", fmt.Errorf("unexpected type %T, expected Node", obj)
	}
	if !o.overwrite {
		if exists := checkIfTaintsAlreadyExists(node.Spec.Taints, o.taintsToAdd); len(exists) != 0 {
			return "", fmt.Errorf("node %s already has %v taint(s) with same effect(s) and --overwrite is false", node.Name, exists)
		}
	}
	operation, newTaints, err := reorganizeTaints(node, o.overwrite, o.taintsToAdd, o.taintsToRemove)
	if err != nil {
		return "", err
	}
	node.Spec.Taints = newTaints
	return operation, nil
}
<commit_msg>Kubectl Taint now also accepts short forms for "node","nodes","no"<commit_after>/*
Copyright 2016 The Kubernetes 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 taint

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/spf13/cobra"
	"k8s.io/klog"
	"k8s.io/kubectl/pkg/explain"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/apimachinery/pkg/util/validation"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/cli-runtime/pkg/resource"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/scheme"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
)

// TaintOptions have the data required to perform the taint operation
type TaintOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	ToPrinter  func(string) (printers.ResourcePrinter, error)

	DryRunStrategy cmdutil.DryRunStrategy
	DryRunVerifier *resource.DryRunVerifier

	resources      []string
	taintsToAdd    []v1.Taint
	taintsToRemove []v1.Taint
	builder        *resource.Builder
	selector       string
	overwrite      bool
	all            bool

	ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error)

	genericclioptions.IOStreams

	Mapper meta.RESTMapper
}

var (
	taintLong = templates.LongDesc(i18n.T(`
		Update the taints on one or more nodes.

		* A taint consists of a key, value, and effect. As an argument here, it is expressed as key=value:effect.
		* The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
		* Optionally, the key can begin with a DNS subdomain prefix and a single '/', like example.com/my-app
		* The value is optional. If given, it must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[2]d characters.
		* The effect must be NoSchedule, PreferNoSchedule or NoExecute.
		* Currently taint can only apply to node.`))

	taintExample = templates.Examples(i18n.T(`
		# Update node 'foo' with a taint with key 'dedicated' and value 'special-user' and effect 'NoSchedule'.
		# If a taint with that key and effect already exists, its value is replaced as specified.
		kubectl taint nodes foo dedicated=special-user:NoSchedule

		# Remove from node 'foo' the taint with key 'dedicated' and effect 'NoSchedule' if one exists.
		kubectl taint nodes foo dedicated:NoSchedule-

		# Remove from node 'foo' all the taints with key 'dedicated'
		kubectl taint nodes foo dedicated-

		# Add a taint with key 'dedicated' on nodes having label mylabel=X
		kubectl taint node -l myLabel=X  dedicated=foo:PreferNoSchedule

		# Add to node 'foo' a taint with key 'bar' and no value
		kubectl taint nodes foo bar:NoSchedule`))
)

func NewCmdTaint(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
	options := &TaintOptions{
		PrintFlags: genericclioptions.NewPrintFlags("tainted").WithTypeSetter(scheme.Scheme),
		IOStreams:  streams,
	}

	validArgs := []string{"node"}

	cmd := &cobra.Command{
		Use:                   "taint NODE NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N",
		DisableFlagsInUseLine: true,
		Short:                 i18n.T("Update the taints on one or more nodes"),
		Long:                  fmt.Sprintf(taintLong, validation.DNS1123SubdomainMaxLength, validation.LabelValueMaxLength),
		Example:               taintExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(options.Complete(f, cmd, args))
			cmdutil.CheckErr(options.Validate())
			cmdutil.CheckErr(options.RunTaint())
		},
		ValidArgs: validArgs,
	}

	options.PrintFlags.AddFlags(cmd)
	cmdutil.AddDryRunFlag(cmd)

	cmdutil.AddValidateFlags(cmd)
	cmd.Flags().StringVarP(&options.selector, "selector", "l", options.selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
	cmd.Flags().BoolVar(&options.overwrite, "overwrite", options.overwrite, "If true, allow taints to be overwritten, otherwise reject taint updates that overwrite existing taints.")
	cmd.Flags().BoolVar(&options.all, "all", options.all, "Select all nodes in the cluster")
	return cmd
}

// Complete adapts from the command line args and factory to the data required.
func (o *TaintOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) (err error) {
	namespace, _, err := f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}

	o.Mapper, err = f.ToRESTMapper()
	if err != nil {
		return err
	}

	o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
	if err != nil {
		return err
	}
	dynamicClient, err := f.DynamicClient()
	if err != nil {
		return err
	}
	discoveryClient, err := f.ToDiscoveryClient()
	if err != nil {
		return err
	}
	o.DryRunVerifier = resource.NewDryRunVerifier(dynamicClient, discoveryClient)
	cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)

	// retrieves resource and taint args from args
	// also checks args to verify that all resources are specified before taints
	taintArgs := []string{}
	metTaintArg := false
	for _, s := range args {
		isTaint := strings.Contains(s, "=") || strings.Contains(s, ":") || strings.HasSuffix(s, "-")
		switch {
		case !metTaintArg && isTaint:
			metTaintArg = true
			fallthrough
		case metTaintArg && isTaint:
			taintArgs = append(taintArgs, s)
		case !metTaintArg && !isTaint:
			o.resources = append(o.resources, s)
		case metTaintArg && !isTaint:
			return fmt.Errorf("all resources must be specified before taint changes: %s", s)
		}
	}

	o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
		o.PrintFlags.NamePrintFlags.Operation = operation
		return o.PrintFlags.ToPrinter()
	}

	if len(o.resources) < 1 {
		return fmt.Errorf("one or more resources must be specified as <resource> <name>")
	}
	if len(taintArgs) < 1 {
		return fmt.Errorf("at least one taint update is required")
	}

	if o.taintsToAdd, o.taintsToRemove, err = parseTaints(taintArgs); err != nil {
		return cmdutil.UsageErrorf(cmd, err.Error())
	}
	o.builder = f.NewBuilder().
		WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
		ContinueOnError().
		NamespaceParam(namespace).DefaultNamespace()
	if o.selector != "" {
		o.builder = o.builder.LabelSelectorParam(o.selector).ResourceTypes("node")
	}
	if o.all {
		o.builder = o.builder.SelectAllParam(o.all).ResourceTypes("node").Flatten().Latest()
	}
	if !o.all && o.selector == "" && len(o.resources) >= 2 {
		o.builder = o.builder.ResourceNames("node", o.resources[1:]...)
	}
	o.builder = o.builder.LabelSelectorParam(o.selector).
		Flatten().
		Latest()

	o.ClientForMapping = f.ClientForMapping
	return nil
}

// validateFlags checks for the validation of flags for kubectl taints.
func (o TaintOptions) validateFlags() error {
	// Cannot have a non-empty selector and all flag set. They are mutually exclusive.
	if o.all && o.selector != "" {
		return fmt.Errorf("setting 'all' parameter with a non empty selector is prohibited")
	}
	// If both selector and all are not set.
	if !o.all && o.selector == "" {
		if len(o.resources) < 2 {
			return fmt.Errorf("at least one resource name must be specified since 'all' parameter is not set")
		} else {
			return nil
		}
	}
	return nil
}

// Validate checks to the TaintOptions to see if there is sufficient information run the command.
func (o TaintOptions) Validate() error {
	resourceType := strings.ToLower(o.resources[0])
	fullySpecifiedGVR, _, err := explain.SplitAndParseResourceRequest(resourceType, o.Mapper)
	if err != nil {
		return err
	}

	gvk, err := o.Mapper.KindFor(fullySpecifiedGVR)
	if err != nil {
		return err
	}

	if gvk.Kind != "Node" {
		return fmt.Errorf("invalid resource type %s, only node types are supported", resourceType)
	}

	// check the format of taint args and checks removed taints aren't in the new taints list
	var conflictTaints []string
	for _, taintAdd := range o.taintsToAdd {
		for _, taintRemove := range o.taintsToRemove {
			if taintAdd.Key != taintRemove.Key {
				continue
			}
			if len(taintRemove.Effect) == 0 || taintAdd.Effect == taintRemove.Effect {
				conflictTaint := fmt.Sprintf("{\"%s\":\"%s\"}", taintRemove.Key, taintRemove.Effect)
				conflictTaints = append(conflictTaints, conflictTaint)
			}
		}
	}
	if len(conflictTaints) > 0 {
		return fmt.Errorf("can not both modify and remove the following taint(s) in the same command: %s", strings.Join(conflictTaints, ", "))
	}
	return o.validateFlags()
}

// RunTaint does the work
func (o TaintOptions) RunTaint() error {
	r := o.builder.Do()
	if err := r.Err(); err != nil {
		return err
	}

	return r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}

		obj := info.Object
		name, namespace := info.Name, info.Namespace
		oldData, err := json.Marshal(obj)
		if err != nil {
			return err
		}
		operation, err := o.updateTaints(obj)
		if err != nil {
			return err
		}
		newData, err := json.Marshal(obj)
		if err != nil {
			return err
		}
		patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj)
		createdPatch := err == nil
		if err != nil {
			klog.V(2).Infof("couldn't compute patch: %v", err)
		}

		printer, err := o.ToPrinter(operation)
		if err != nil {
			return err
		}
		if o.DryRunStrategy == cmdutil.DryRunClient {
			if createdPatch {
				typedObj, err := scheme.Scheme.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
				if err != nil {
					return err
				}

				nodeObj, ok := typedObj.(*v1.Node)
				if !ok {
					return fmt.Errorf("unexpected type %T", typedObj)
				}

				originalObjJS, err := json.Marshal(nodeObj)
				if err != nil {
					return err
				}

				originalPatchedObjJS, err := strategicpatch.StrategicMergePatch(originalObjJS, patchBytes, nodeObj)
				if err != nil {
					return err
				}

				targetObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, originalPatchedObjJS)
				if err != nil {
					return err
				}
				return printer.PrintObj(targetObj, o.Out)
			}
			return printer.PrintObj(obj, o.Out)
		}

		mapping := info.ResourceMapping()
		if o.DryRunStrategy == cmdutil.DryRunServer {
			if err := o.DryRunVerifier.HasSupport(mapping.GroupVersionKind); err != nil {
				return err
			}
		}
		client, err := o.ClientForMapping(mapping)
		if err != nil {
			return err
		}
		helper := resource.
			NewHelper(client, mapping).
			DryRun(o.DryRunStrategy == cmdutil.DryRunServer)

		var outputObj runtime.Object
		if createdPatch {
			outputObj, err = helper.Patch(namespace, name, types.StrategicMergePatchType, patchBytes, nil)
		} else {
			outputObj, err = helper.Replace(namespace, name, false, obj)
		}
		if err != nil {
			return err
		}

		return printer.PrintObj(outputObj, o.Out)
	})
}

// updateTaints applies a taint option(o) to a node in cluster after computing the net effect of operation(i.e. does it result in an overwrite?), it reports back the end result in a way that user can easily interpret.
func (o TaintOptions) updateTaints(obj runtime.Object) (string, error) {
	node, ok := obj.(*v1.Node)
	if !ok {
		return "", fmt.Errorf("unexpected type %T, expected Node", obj)
	}
	if !o.overwrite {
		if exists := checkIfTaintsAlreadyExists(node.Spec.Taints, o.taintsToAdd); len(exists) != 0 {
			return "", fmt.Errorf("node %s already has %v taint(s) with same effect(s) and --overwrite is false", node.Name, exists)
		}
	}
	operation, newTaints, err := reorganizeTaints(node, o.overwrite, o.taintsToAdd, o.taintsToRemove)
	if err != nil {
		return "", err
	}
	node.Spec.Taints = newTaints
	return operation, nil
}
<|endoftext|>


<commit_before>package expr

import (
	"encoding/json"
	"testing"

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

func TestServicebuildPipeLine(t *testing.T) {
	var tests = []struct {
		name              string
		req               *Request
		expectedOrder     []string
		expectErrContains string
	}{
		{
			name: "simple: a requires b",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"expression": "B",
							"reducer": "mean",
							"type": "reduce"
						}`),
					},
					{
						RefID:         "B",
						DatasourceUID: "Fake",
					},
				},
			},
			expectedOrder: []string{"B", "A"},
		},
		{
			name: "cycle will error",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
								"expression": "$B",
								"type": "math"
							}`),
					},
					{
						RefID:         "B",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
								"expression": "$A",
								"type": "math"
							}`),
					},
				},
			},
			expectErrContains: "cyclic components",
		},
		{
			name: "self reference will error",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
								"expression": "$A",
								"type": "math"
							}`),
					},
				},
			},
			expectErrContains: "self referencing node",
		},
		{
			name: "missing dependency will error",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
								"expression": "$B",
								"type": "math"
							}`),
					},
				},
			},
			expectErrContains: "find dependent",
		},
		{
			name: "classic can not take input from another expression",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"type": "classic_conditions",
							"conditions": [
								{
									"evaluator": {
									"params": [
										2,
										3
									],
									"type": "within_range"
									},
									"operator": {
									"type": "or"
									},
									"query": {
									"params": [
										"B"
									]
									},
									"reducer": {
									"params": [],
									"type": "diff"
									},
									"type": "query"
								}
							]
						}`),
					},
					{
						RefID:         "B",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"expression": "C",
							"reducer": "mean",
							"type": "reduce"
						}`),
					},
					{
						RefID:         "C",
						DatasourceUID: "Fake",
					},
				},
			},
			expectErrContains: "only data source queries may be inputs to a classic condition",
		},
		{
			name: "classic can not output to another expression",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"type": "classic_conditions",
							"conditions": [
								{
									"evaluator": {
									"params": [
										2,
										3
									],
									"type": "within_range"
									},
									"operator": {
									"type": "or"
									},
									"query": {
									"params": [
										"C"
									]
									},
									"reducer": {
									"params": [],
									"type": "diff"
									},
									"type": "query"
								}
							]
						}`),
					},
					{
						RefID:         "B",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"expression": "A",
							"reducer": "mean",
							"type": "reduce"
						}`),
					},
					{
						RefID:         "C",
						DatasourceUID: "Fake",
					},
				},
			},
			expectErrContains: "classic conditions may not be the input for other expressions",
		},
		{
			name: "Queries with new datasource ref object",
			req: &Request{
				Queries: []Query{
					{
						RefID: "A",
						JSON: json.RawMessage(`{
							"datasource":  {
										"uid": "MyDS"
									}
							}`),
					},
					{
						RefID: "B",
						JSON: json.RawMessage(`{
							"datasource":  {
								"uid": "MyDS"
							},
							"expression": "A",
							"reducer": "mean",
							"type": "reduce"
						}`),
					},
				},
			},
			expectedOrder: []string{"B", "A"},
		},
	}
	s := Service{}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			nodes, err := s.buildPipeline(tt.req)
			if tt.expectErrContains != "" {
				require.Error(t, err)
				require.Contains(t, err.Error(), tt.expectErrContains)
			} else {
				require.NoError(t, err)
				require.Equal(t, tt.expectedOrder, getRefIDOrder(nodes))
			}
		})
	}
}

func getRefIDOrder(nodes []Node) []string {
	ids := make([]string, 0, len(nodes))
	for _, n := range nodes {
		ids = append(ids, n.RefID())
	}
	return ids
}
<commit_msg>Comment out "Queries with new datasource ref object" test (#41187)<commit_after>package expr

import (
	"encoding/json"
	"testing"

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

func TestServicebuildPipeLine(t *testing.T) {
	var tests = []struct {
		name              string
		req               *Request
		expectedOrder     []string
		expectErrContains string
	}{
		{
			name: "simple: a requires b",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"expression": "B",
							"reducer": "mean",
							"type": "reduce"
						}`),
					},
					{
						RefID:         "B",
						DatasourceUID: "Fake",
					},
				},
			},
			expectedOrder: []string{"B", "A"},
		},
		{
			name: "cycle will error",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
								"expression": "$B",
								"type": "math"
							}`),
					},
					{
						RefID:         "B",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
								"expression": "$A",
								"type": "math"
							}`),
					},
				},
			},
			expectErrContains: "cyclic components",
		},
		{
			name: "self reference will error",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
								"expression": "$A",
								"type": "math"
							}`),
					},
				},
			},
			expectErrContains: "self referencing node",
		},
		{
			name: "missing dependency will error",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
								"expression": "$B",
								"type": "math"
							}`),
					},
				},
			},
			expectErrContains: "find dependent",
		},
		{
			name: "classic can not take input from another expression",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"type": "classic_conditions",
							"conditions": [
								{
									"evaluator": {
									"params": [
										2,
										3
									],
									"type": "within_range"
									},
									"operator": {
									"type": "or"
									},
									"query": {
									"params": [
										"B"
									]
									},
									"reducer": {
									"params": [],
									"type": "diff"
									},
									"type": "query"
								}
							]
						}`),
					},
					{
						RefID:         "B",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"expression": "C",
							"reducer": "mean",
							"type": "reduce"
						}`),
					},
					{
						RefID:         "C",
						DatasourceUID: "Fake",
					},
				},
			},
			expectErrContains: "only data source queries may be inputs to a classic condition",
		},
		{
			name: "classic can not output to another expression",
			req: &Request{
				Queries: []Query{
					{
						RefID:         "A",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"type": "classic_conditions",
							"conditions": [
								{
									"evaluator": {
									"params": [
										2,
										3
									],
									"type": "within_range"
									},
									"operator": {
									"type": "or"
									},
									"query": {
									"params": [
										"C"
									]
									},
									"reducer": {
									"params": [],
									"type": "diff"
									},
									"type": "query"
								}
							]
						}`),
					},
					{
						RefID:         "B",
						DatasourceUID: DatasourceUID,
						JSON: json.RawMessage(`{
							"expression": "A",
							"reducer": "mean",
							"type": "reduce"
						}`),
					},
					{
						RefID:         "C",
						DatasourceUID: "Fake",
					},
				},
			},
			expectErrContains: "classic conditions may not be the input for other expressions",
		},
		//{
		//	name: "Queries with new datasource ref object",
		//	req: &Request{
		//		Queries: []Query{
		//			{
		//				RefID: "A",
		//				JSON: json.RawMessage(`{
		//					"datasource":  {
		//								"uid": "MyDS"
		//							}
		//					}`),
		//			},
		//			{
		//				RefID: "B",
		//				JSON: json.RawMessage(`{
		//					"datasource":  {
		//						"uid": "MyDS"
		//					},
		//					"expression": "A",
		//					"reducer": "mean",
		//					"type": "reduce"
		//				}`),
		//			},
		//		},
		//	},
		//	expectedOrder: []string{"B", "A"},
		//},
	}
	s := Service{}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			nodes, err := s.buildPipeline(tt.req)
			if tt.expectErrContains != "" {
				require.Error(t, err)
				require.Contains(t, err.Error(), tt.expectErrContains)
			} else {
				require.NoError(t, err)
				require.Equal(t, tt.expectedOrder, getRefIDOrder(nodes))
			}
		})
	}
}

func getRefIDOrder(nodes []Node) []string {
	ids := make([]string, 0, len(nodes))
	for _, n := range nodes {
		ids = append(ids, n.RefID())
	}
	return ids
}
<|endoftext|>


<commit_before>package imexport

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"io"
	"time"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/vfs"
)

func writeFile(fs vfs.VFS, name string, tw *tar.Writer, doc *vfs.FileDoc) error {
	file, err := fs.OpenFile(doc)
	if err != nil {
		return err
	}

	hdr := &tar.Header{
		Name:       name,
		Mode:       0644,
		Size:       doc.Size(),
		ModTime:    doc.ModTime(),
		AccessTime: doc.CreatedAt,
		ChangeTime: doc.UpdatedAt,
		Typeflag:   tar.TypeReg,
	}
	if doc.Executable {
		hdr.Mode = 0755
	}

	if err := tw.WriteHeader(hdr); err != nil {
		return err
	}
	if _, err := io.Copy(tw, file); err != nil {
		return err
	}
	return nil
}

func createDir(name string, tw *tar.Writer, dir *vfs.DirDoc) error {

	hdr := &tar.Header{
		Name:     name,
		Mode:     0755,
		Size:     dir.Size(),
		ModTime:  dir.ModTime(),
		Typeflag: tar.TypeDir,
	}
	err := tw.WriteHeader(hdr)

	return err
}

func metadata(tw *tar.Writer, fs vfs.VFS, domain string) error {
	db := couchdb.SimpleDatabasePrefix(domain)
	doctype := "io.cozy.files"
	req := &couchdb.AllDocsRequest{}
	var results []map[string]interface{}
	if err := couchdb.GetAllDocs(db, doctype, req, &results); err != nil {
		return err
	}

	metaDir := "Metadata/"
	hdrDir := &tar.Header{
		Name:     metaDir,
		Mode:     0755,
		Typeflag: tar.TypeDir,
	}
	if err := tw.WriteHeader(hdrDir); err != nil {
		return err
	}

	hdr := &tar.Header{
		Name:       metaDir + "album.json",
		Size:       int64(0),
		Mode:       0644,
		AccessTime: time.Now(),
		ChangeTime: time.Now(),
		Typeflag:   tar.TypeReg,
	}

	var content [][]byte
	var size int64

	for _, val := range results {
		if val["referenced_by"] != nil {

			id := val["_id"].(string)
			doc, err := fs.FileByID(id)
			if err != nil {
				return err
			}

			path, err := fs.FilePath(doc)
			if err != nil {
				return err
			}
			val["album_filepath"] = path

			ref := doc.ReferencedBy
			for _, v := range ref {
				out := &couchdb.JSONDoc{}
				if err = couchdb.GetDoc(db, v.Type, v.ID, out); err != nil {
					return err
				}
				m := out.ToMapWithType()

				albumName := m["name"]
				val["album_name"] = albumName
			}

			jsonString, err := json.Marshal(val)
			if err != nil {
				return err
			}

			size += int64(len(jsonString) + 1) // len([]byte("\n"))

			content = append(content, jsonString)
			content = append(content, []byte("\n"))

		}

	}

	hdr.Size = size
	if err := tw.WriteHeader(hdr); err != nil {
		return err
	}

	for _, val := range content {
		if _, err := io.Copy(tw, bytes.NewReader(val)); err != nil {
			return err
		}
	}
	return nil
}

func export(tw *tar.Writer, fs vfs.VFS, domain string) error {
	root := "/"

	err := vfs.Walk(fs, root, func(name string, dir *vfs.DirDoc, file *vfs.FileDoc, err error) error {
		if err != nil {
			return err
		}

		if dir != nil {
			if err := createDir(name, tw, dir); err != nil {
				return err
			}
		}

		if file != nil {
			if err := writeFile(fs, name, tw, file); err != nil {
				return err
			}

		}

		return nil
	})
	if err != nil {
		return err
	}
	err = metadata(tw, fs, domain)
	return err
}

// Tardir tar doc directory
func Tardir(w io.Writer, fs vfs.VFS, domain string) error {
	//gzip writer
	gw := gzip.NewWriter(w)
	defer gw.Close()

	//tar writer
	tw := tar.NewWriter(gw)
	defer tw.Close()

	err := export(tw, fs, domain)

	return err

}
<commit_msg>correction and simplification<commit_after>package imexport

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"io"
	"time"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/vfs"
)

func writeFile(fs vfs.VFS, name string, tw *tar.Writer, doc *vfs.FileDoc) error {
	file, err := fs.OpenFile(doc)
	if err != nil {
		return err
	}

	hdr := &tar.Header{
		Name:       name,
		Mode:       0644,
		Size:       doc.Size(),
		ModTime:    doc.ModTime(),
		AccessTime: doc.CreatedAt,
		ChangeTime: doc.UpdatedAt,
		Typeflag:   tar.TypeReg,
	}
	if doc.Executable {
		hdr.Mode = 0755
	}

	if err := tw.WriteHeader(hdr); err != nil {
		return err
	}
	if _, err := io.Copy(tw, file); err != nil {
		return err
	}
	return nil
}

func createDir(name string, tw *tar.Writer, dir *vfs.DirDoc) error {

	hdr := &tar.Header{
		Name:     name,
		Mode:     0755,
		Size:     dir.Size(),
		ModTime:  dir.ModTime(),
		Typeflag: tar.TypeDir,
	}
	err := tw.WriteHeader(hdr)

	return err
}

func metadata(tw *tar.Writer, fs vfs.VFS, domain string) error {
	db := couchdb.SimpleDatabasePrefix(domain)
	doctype := "io.cozy.files"
	req := &couchdb.AllDocsRequest{}
	var results []map[string]interface{}
	if err := couchdb.GetAllDocs(db, doctype, req, &results); err != nil {
		return err
	}

	metaDir := "metadata/album/"
	hdrDir := &tar.Header{
		Name:     metaDir,
		Mode:     0755,
		Typeflag: tar.TypeDir,
	}
	if err := tw.WriteHeader(hdrDir); err != nil {
		return err
	}

	hdr := &tar.Header{
		Name:       metaDir + "album.json",
		Mode:       0644,
		AccessTime: time.Now(),
		ChangeTime: time.Now(),
		Typeflag:   tar.TypeReg,
	}

	var content bytes.Buffer
	var size int64

	for _, val := range results {
		if val["referenced_by"] != nil {

			id := val["_id"].(string)
			doc, err := fs.FileByID(id)
			if err != nil {
				return err
			}

			path, err := fs.FilePath(doc)
			if err != nil {
				return err
			}
			val["album_filepath"] = path

			ref := doc.ReferencedBy
			for _, v := range ref {
				out := &couchdb.JSONDoc{}
				if err = couchdb.GetDoc(db, v.Type, v.ID, out); err != nil {
					return err
				}
				m := out.ToMapWithType()

				albumName := m["name"]
				val["album_name"] = albumName
			}

			jsonString, err := json.Marshal(val)
			if err != nil {
				return err
			}

			size += int64(len(jsonString) + 1) // len([]byte("\n"))

			content.Write(jsonString)
			content.Write([]byte("\n"))

		}

	}

	hdr.Size = size
	if err := tw.WriteHeader(hdr); err != nil {
		return err
	}

	if _, err := content.WriteTo(tw); err != nil {
		return err
	}

	return nil
}

func export(tw *tar.Writer, fs vfs.VFS, domain string) error {
	root := "/"

	err := vfs.Walk(fs, root, func(name string, dir *vfs.DirDoc, file *vfs.FileDoc, err error) error {
		if err != nil {
			return err
		}

		if dir != nil {
			if err := createDir(name, tw, dir); err != nil {
				return err
			}
		}

		if file != nil {
			if err := writeFile(fs, name, tw, file); err != nil {
				return err
			}

		}

		return nil
	})
	if err != nil {
		return err
	}
	return metadata(tw, fs, domain)
}

// Tardir tar doc directory
func Tardir(w io.Writer, fs vfs.VFS, domain string) error {
	//gzip writer
	gw := gzip.NewWriter(w)
	defer gw.Close()

	//tar writer
	tw := tar.NewWriter(gw)
	defer tw.Close()

	err := export(tw, fs, domain)

	return err

}
<|endoftext|>


<commit_before>package knative

import (
	"knative.dev/pkg/network"
	"knative.dev/pkg/system"
	networkingv1alpha1 "knative.dev/serving/pkg/apis/networking/v1alpha1"
	"knative.dev/serving/pkg/client/clientset/versioned"
)

const (
	// These 2 are defined in the deployment yaml
	internalServiceName = "kourier-internal"
	externalServiceName = "kourier"
)

func MarkIngressReady(knativeClient versioned.Interface, ingress *networkingv1alpha1.Ingress) error {
	// TODO: Improve. Currently once we go trough the generation of the envoy cache, we mark the objects as Ready,
	//  but that is not exactly true, it can take a while until envoy exposes the routes. Is there a way to get a "callback" from envoy?
	var err error
	status := ingress.GetStatus()
	if ingress.GetGeneration() != status.ObservedGeneration || !status.IsReady() {
		internalDomain := domainForServiceName(internalServiceName)
		externalDomain := domainForServiceName(externalServiceName)

		status.InitializeConditions()
		var domain string

		if ingress.GetSpec().Visibility == networkingv1alpha1.IngressVisibilityClusterLocal {
			domain = internalDomain
		} else {
			domain = externalDomain
		}

		status.MarkLoadBalancerReady(
			[]networkingv1alpha1.LoadBalancerIngressStatus{
				{
					DomainInternal: domain,
				},
			},
			[]networkingv1alpha1.LoadBalancerIngressStatus{
				{
					DomainInternal: externalDomain,
				},
			},
			[]networkingv1alpha1.LoadBalancerIngressStatus{
				{
					DomainInternal: internalDomain,
				},
			})
		status.MarkNetworkConfigured()
		status.ObservedGeneration = ingress.GetGeneration()
		ingress.SetStatus(*status)

		_, err = knativeClient.NetworkingV1alpha1().Ingresses(ingress.GetNamespace()).UpdateStatus(ingress)
		return err
	}
	return nil
}

func domainForServiceName(serviceName string) string {
	return serviceName + "." + system.Namespace() + ".svc." + network.GetClusterDomainName()
}
<commit_msg>ingress: delete old TODO<commit_after>package knative

import (
	"knative.dev/pkg/network"
	"knative.dev/pkg/system"
	networkingv1alpha1 "knative.dev/serving/pkg/apis/networking/v1alpha1"
	"knative.dev/serving/pkg/client/clientset/versioned"
)

const (
	// These 2 are defined in the deployment yaml
	internalServiceName = "kourier-internal"
	externalServiceName = "kourier"
)

func MarkIngressReady(knativeClient versioned.Interface, ingress *networkingv1alpha1.Ingress) error {
	var err error
	status := ingress.GetStatus()
	if ingress.GetGeneration() != status.ObservedGeneration || !status.IsReady() {
		internalDomain := domainForServiceName(internalServiceName)
		externalDomain := domainForServiceName(externalServiceName)

		status.InitializeConditions()
		var domain string

		if ingress.GetSpec().Visibility == networkingv1alpha1.IngressVisibilityClusterLocal {
			domain = internalDomain
		} else {
			domain = externalDomain
		}

		status.MarkLoadBalancerReady(
			[]networkingv1alpha1.LoadBalancerIngressStatus{
				{
					DomainInternal: domain,
				},
			},
			[]networkingv1alpha1.LoadBalancerIngressStatus{
				{
					DomainInternal: externalDomain,
				},
			},
			[]networkingv1alpha1.LoadBalancerIngressStatus{
				{
					DomainInternal: internalDomain,
				},
			})
		status.MarkNetworkConfigured()
		status.ObservedGeneration = ingress.GetGeneration()
		ingress.SetStatus(*status)

		_, err = knativeClient.NetworkingV1alpha1().Ingresses(ingress.GetNamespace()).UpdateStatus(ingress)
		return err
	}
	return nil
}

func domainForServiceName(serviceName string) string {
	return serviceName + "." + system.Namespace() + ".svc." + network.GetClusterDomainName()
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018 the original author or 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 kubectl

import (
	"time"

	"github.com/projectriff/riff-cli/pkg/osutils"
)

type KubeCtl interface {
	Exec(cmdArgs []string) (string, error)
	ExecStdin(cmdArgs []string, stdin *[]byte) (string, error)
}

// processKubeCtl interacts with kubernetes by spawning a process and running the kubectl
// command line tool.
type processKubeCtl struct {
}

func (kc *processKubeCtl) Exec(cmdArgs []string) (string, error) {
	out, err := osutils.Exec("kubectl", cmdArgs, 60*time.Second)
	return string(out), err
}

func (kc *processKubeCtl) ExecStdin(cmdArgs []string, stdin *[]byte) (string, error) {
	out, err := osutils.ExecStdin("kubectl", cmdArgs, stdin, 20*time.Second)
	return string(out), err
}

func RealKubeCtl() KubeCtl {
	return &processKubeCtl{}
}
<commit_msg>Increase timeout for kubectl commands<commit_after>/*
 * Copyright 2018 the original author or 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 kubectl

import (
	"time"

	"github.com/projectriff/riff-cli/pkg/osutils"
)

type KubeCtl interface {
	Exec(cmdArgs []string) (string, error)
	ExecStdin(cmdArgs []string, stdin *[]byte) (string, error)
}

// processKubeCtl interacts with kubernetes by spawning a process and running the kubectl
// command line tool.
type processKubeCtl struct {
}

func (kc *processKubeCtl) Exec(cmdArgs []string) (string, error) {
	out, err := osutils.Exec("kubectl", cmdArgs, 60*time.Second)
	return string(out), err
}

func (kc *processKubeCtl) ExecStdin(cmdArgs []string, stdin *[]byte) (string, error) {
	out, err := osutils.ExecStdin("kubectl", cmdArgs, stdin, 60*time.Second)
	return string(out), err
}

func RealKubeCtl() KubeCtl {
	return &processKubeCtl{}
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2012 Matthias S. Benkmann

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA  02110-1301, USA.
*/

// Various re-usable utility functions.
package util

import (
         "io"
         "fmt"
         "net"
         "time"
         "bytes"
         "regexp"
         "strings"
         "crypto/md5"
         "runtime/debug"
       )

// Returns the md5sum of its argument as a string of hex digits.
func Md5sum(s string) string {
  md5 := md5.New()
  io.WriteString(md5, s)
  return fmt.Sprintf("%x",md5.Sum(nil))
}

// Number of consecutive short writes before WriteAll() will give up
// ATTENTION! The wait time between tries increases exponetially, so don't
// blindly increase this number.
const write_all_max_tries = 8

// Calls g wrapped in a panic handler that logs the panic and recovers from it.
// Example:
//   go util.WithPanicHandler(foobar)
//   go util.WithPanicHandler(func(){ Send_foreign_job_updates(server, jobs) })
func WithPanicHandler(g func()) {
  defer func() {
    if x := recover(); x != nil {
      Log(0, "PANIC! %v\n%v", x, string(debug.Stack()))
    }
  }()
  g()
}

// Returns a new channel that will return start as first item and then
// always the next number.
func Counter(start uint64) chan uint64 {
  c := make(chan uint64)
  go func() {
    for {
      c<-start
      start++
    }
  }()
  return c
}

// Sleeps until time t. If clock is adjusted during the sleep, this will not
// cause more than 10 minutes extra sleep. In no case will this function return
// until time.Now() >= t.
func WaitUntil(t time.Time) {
  // Wait in a for loop because clock might be adjusted while we're sleeping.
  // ATTENTION! The check t.After(time.Now()) is NOT REDUNDANT with the check
  // if dur <= 0 { return }
  // A time.Duration has a limited range that can not express all differences
  // between timestamps. Without the t.After(time.Now()) check, extreme timestamps
  // in the past can cause large wait times due to overflow.
  for ; t.After(time.Now()); {
    dur := t.Sub(time.Now())
    if dur <= 0 { return }
    // wake up every 10 minutes to deal with clock adjustments (DST etc.)
    if dur > 10*time.Minute { dur = 10*time.Minute }
    time.Sleep(dur)
  }
}

// Writes data to w, with automatic handling of short writes.
// A short write error will only be returned if multiple attempts
// failed in a row.
func WriteAll(w io.Writer, data []byte) (n int, err error) {
  // Yeah, I know. Other people just ignore the issue of
  // short writes. That's why their code fails more often than mine :-P
  tries := write_all_max_tries
  var bytes_written int
  for n = 0; n < len(data); {
    bytes_written, err = w.Write(data[n:])
    n += bytes_written

    if err != nil && err != io.ErrShortWrite {
      return n, err
    }

    if bytes_written == 0 {
      tries--
      if tries <= 0 {
        if err == nil {
          err = io.ErrShortWrite
        }
        return n, err
      }

      // The first time we don't sleep. The 2nd time we sleep 1ms. The 3rd time 2ms.
      // The 4th time 4ms. Then 8ms, 16ms, 32ms, 64ms,...
      var wait time.Duration = (1 << (write_all_max_tries-2)) >> uint(tries)
      time.Sleep(wait * time.Millisecond)

    } else {
      tries = write_all_max_tries  // every time we succeed at writing we start tries again
    }
  }

  return n, nil
}

// Opens a connection to target (e.g. "foo.example.com:20081"), sends msg followed
// by \r\n and then closes the connection.
// If timeout >= 0, then the connection will be terminated after at most this duration.
func SendLnTo(target string, msg string, timeout time.Duration) {
  conn, err := net.Dial("tcp", target)
  if err != nil {
    Log(0, "ERROR! Dial: %v", err)
    return
  }
  defer conn.Close()
  SendLn(conn, msg, timeout)
}

// Sends strings via connection conn, followed by "\r\n"
// If timeout >= 0, then the send attempt will be aborted after at most this duration.
// Returns nil if sending was successful, an error otherwise. The error will also
// be sent to the log, so the caller does not have to log it.
func SendLn(conn net.Conn, s string, timeout time.Duration) error {
  sendbuf := make([]byte, len(s)+2)
  copy(sendbuf, s)
  sendbuf[len(s)]='\r'
  sendbuf[len(s)+1]='\n'

  var deadline time.Time // zero value means "no deadline"
  if timeout >= 0 { deadline = time.Now().Add(timeout) }
  conn.SetWriteDeadline(deadline)

  _, err := WriteAll(conn, sendbuf)
  if err != nil {
    Log(0, "ERROR! WriteAll: %v", err)
  }
  return err
}

// Reads from the connection until \n is seen (or timeout or error) and
// returns the first line with trailing \n and \r removed.
// If timeout > 0, then the connection will be terminated after at most this duration.
func ReadLn(conn net.Conn, timeout time.Duration) string {
  var buf = make([]byte, 65536)
  i := 0
  n := 1

  var deadline time.Time // zero value means "no deadline"
  if timeout > 0 { deadline = time.Now().Add(timeout) }
  conn.SetReadDeadline(deadline)

  var err error
  for n != 0 {
    n, err = conn.Read(buf[i:])
    if err != nil && err != io.EOF {
      Log(0, "ERROR! Read: %v", err)
    }
    if err == io.EOF && i != 0 {
      Log(0, "ERROR! Incomplete message (i.e. not terminated by \"\\n\") of %v bytes", i)
    }

    i += n

    if i == len(buf) {
      buf_new := make([]byte, len(buf)+65536)
      copy(buf_new, buf)
      buf = buf_new
    }

    // Find complete line terminated by '\n' and return it
    eol := bytes.IndexByte(buf[0:i], '\n')

    if eol >= 0 {
      for ; eol >= 0 && (buf[eol] == '\n' || buf[eol] == '\r') ; { eol-- }
      return string(buf[0:eol+1])
    }
  }

  return ""
}

// Sends a UDP wake-on-lan packet coded for the given macaddress to the
// broadcast address for the given host or network.
//  host_or_net can be:
//    a) a host name (with or without ":<port>")
//    b) a host's IP address from which the broadcast address will be
//       derived by setting final byte to 255
//    c) a network's broadcast IP address
//  If host_or_net includes a port that port will be used, otherwise the
//  default port 40000 is used.
func Wake(macaddress string, host_or_net string) error {
  hwaddr, err := net.ParseMAC(macaddress)
  if err != nil { return err }

  if strings.Index(host_or_net,":") < 0 { host_or_net += ":40000" }

  udpaddr,err := net.ResolveUDPAddr("udp", host_or_net)
  if err != nil { return err }

  udpaddr.IP[len(udpaddr.IP)-1] = 255
  udpconn,err := net.DialUDP("udp", nil, udpaddr)
  if err != nil { return err }

  payload := []byte{0xff,0xff,0xff,0xff,0xff,0xff}
  for i := 0; i < 16; i++ { payload = append(payload, hwaddr...) }
  _, err = udpconn.Write(payload)
  return err
}

const re_1xx = "(1([0-9]?[0-9]?))"
const re_2xx = "(2([6-9]|([0-4][0-9]?)|(5[0-5]?))?)"
const re_xx  = "([3-9][0-9]?)"
const re_port = "(0|([1-6][0-9]{0,4})|([7-9][0-9]{0,3}))"
const ip_part = "(0|"+re_1xx+"|"+re_2xx+"|"+re_xx+")"
var startsWithIPv4Regexp = regexp.MustCompile("^"+ip_part+"([.]"+ip_part+"){3}")
var endsWithPort = regexp.MustCompile(":"+re_port+"$")

// Takes either a HOST or a HOST:IP pair and replaces the HOST part with
// that machine's IP address (preferring an IPv4 address if there is one).
// If HOST is "localhost" or "::1", it will be replaced with "127.0.0.1".
// If HOST is already an IPv4 address, it will be kept.
// If there is only an IPv6 address available, it will be enclosed in "[...]"
// in the result (even if there is no port).
//
// Returns the modified address or the original address with an error.
func Resolve(addr string) (string, error) {
  if startsWithIPv4Regexp.MatchString(addr) { return addr, nil }

  host := addr
  port := ""
  var err error

  // the net.ParseIP() check tries to prevent confusing an IPv6 address for a port
  if endsWithPort.MatchString(addr) && net.ParseIP(addr) == nil {
    host, port, err = net.SplitHostPort(addr)
    if err != nil { return addr, err }
    port = ":" + port
  }

  if host == "localhost" || host == "::1" || host == "[::1]" {
    return "127.0.0.1" + port, nil
  }

  addrs, err := net.LookupIP(host)
  if err != nil {
    return addr, err
  }

  if len(addrs) == 0 { // I don't think this is possible but just in case...
    return addr, fmt.Errorf("No IP address for %v", host)
  }

  // try to find an IPv4 non-loopback address
  for _, a := range addrs {
    if !a.IsLoopback() && a.To4() != nil { return a.String() + port, nil }
  }

  // try to find an IPv4 address (possibly loopback)
  for _, a := range addrs {
    if a.To4() != nil { return a.String() + port, nil }
  }

  // take the first address (which is IPv6)
  return "[" + addrs[0].String() + "]" + port, nil
}

<commit_msg>util.Resolve() now ensures consistent use of config.IP instead of 127.0.0.1<commit_after>/*
Copyright (c) 2012 Matthias S. Benkmann

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA  02110-1301, USA.
*/

// Various re-usable utility functions.
package util

import (
         "io"
         "fmt"
         "net"
         "time"
         "bytes"
         "regexp"
         "strings"
         "crypto/md5"
         "runtime/debug"
       )

// Returns the md5sum of its argument as a string of hex digits.
func Md5sum(s string) string {
  md5 := md5.New()
  io.WriteString(md5, s)
  return fmt.Sprintf("%x",md5.Sum(nil))
}

// Number of consecutive short writes before WriteAll() will give up
// ATTENTION! The wait time between tries increases exponetially, so don't
// blindly increase this number.
const write_all_max_tries = 8

// Calls g wrapped in a panic handler that logs the panic and recovers from it.
// Example:
//   go util.WithPanicHandler(foobar)
//   go util.WithPanicHandler(func(){ Send_foreign_job_updates(server, jobs) })
func WithPanicHandler(g func()) {
  defer func() {
    if x := recover(); x != nil {
      Log(0, "PANIC! %v\n%v", x, string(debug.Stack()))
    }
  }()
  g()
}

// Returns a new channel that will return start as first item and then
// always the next number.
func Counter(start uint64) chan uint64 {
  c := make(chan uint64)
  go func() {
    for {
      c<-start
      start++
    }
  }()
  return c
}

// Sleeps until time t. If clock is adjusted during the sleep, this will not
// cause more than 10 minutes extra sleep. In no case will this function return
// until time.Now() >= t.
func WaitUntil(t time.Time) {
  // Wait in a for loop because clock might be adjusted while we're sleeping.
  // ATTENTION! The check t.After(time.Now()) is NOT REDUNDANT with the check
  // if dur <= 0 { return }
  // A time.Duration has a limited range that can not express all differences
  // between timestamps. Without the t.After(time.Now()) check, extreme timestamps
  // in the past can cause large wait times due to overflow.
  for ; t.After(time.Now()); {
    dur := t.Sub(time.Now())
    if dur <= 0 { return }
    // wake up every 10 minutes to deal with clock adjustments (DST etc.)
    if dur > 10*time.Minute { dur = 10*time.Minute }
    time.Sleep(dur)
  }
}

// Writes data to w, with automatic handling of short writes.
// A short write error will only be returned if multiple attempts
// failed in a row.
func WriteAll(w io.Writer, data []byte) (n int, err error) {
  // Yeah, I know. Other people just ignore the issue of
  // short writes. That's why their code fails more often than mine :-P
  tries := write_all_max_tries
  var bytes_written int
  for n = 0; n < len(data); {
    bytes_written, err = w.Write(data[n:])
    n += bytes_written

    if err != nil && err != io.ErrShortWrite {
      return n, err
    }

    if bytes_written == 0 {
      tries--
      if tries <= 0 {
        if err == nil {
          err = io.ErrShortWrite
        }
        return n, err
      }

      // The first time we don't sleep. The 2nd time we sleep 1ms. The 3rd time 2ms.
      // The 4th time 4ms. Then 8ms, 16ms, 32ms, 64ms,...
      var wait time.Duration = (1 << (write_all_max_tries-2)) >> uint(tries)
      time.Sleep(wait * time.Millisecond)

    } else {
      tries = write_all_max_tries  // every time we succeed at writing we start tries again
    }
  }

  return n, nil
}

// Opens a connection to target (e.g. "foo.example.com:20081"), sends msg followed
// by \r\n and then closes the connection.
// If timeout >= 0, then the connection will be terminated after at most this duration.
func SendLnTo(target string, msg string, timeout time.Duration) {
  conn, err := net.Dial("tcp", target)
  if err != nil {
    Log(0, "ERROR! Dial: %v", err)
    return
  }
  defer conn.Close()
  SendLn(conn, msg, timeout)
}

// Sends strings via connection conn, followed by "\r\n"
// If timeout >= 0, then the send attempt will be aborted after at most this duration.
// Returns nil if sending was successful, an error otherwise. The error will also
// be sent to the log, so the caller does not have to log it.
func SendLn(conn net.Conn, s string, timeout time.Duration) error {
  sendbuf := make([]byte, len(s)+2)
  copy(sendbuf, s)
  sendbuf[len(s)]='\r'
  sendbuf[len(s)+1]='\n'

  var deadline time.Time // zero value means "no deadline"
  if timeout >= 0 { deadline = time.Now().Add(timeout) }
  conn.SetWriteDeadline(deadline)

  _, err := WriteAll(conn, sendbuf)
  if err != nil {
    Log(0, "ERROR! WriteAll: %v", err)
  }
  return err
}

// Reads from the connection until \n is seen (or timeout or error) and
// returns the first line with trailing \n and \r removed.
// If timeout > 0, then the connection will be terminated after at most this duration.
func ReadLn(conn net.Conn, timeout time.Duration) string {
  var buf = make([]byte, 65536)
  i := 0
  n := 1

  var deadline time.Time // zero value means "no deadline"
  if timeout > 0 { deadline = time.Now().Add(timeout) }
  conn.SetReadDeadline(deadline)

  var err error
  for n != 0 {
    n, err = conn.Read(buf[i:])
    if err != nil && err != io.EOF {
      Log(0, "ERROR! Read: %v", err)
    }
    if err == io.EOF && i != 0 {
      Log(0, "ERROR! Incomplete message (i.e. not terminated by \"\\n\") of %v bytes", i)
    }

    i += n

    if i == len(buf) {
      buf_new := make([]byte, len(buf)+65536)
      copy(buf_new, buf)
      buf = buf_new
    }

    // Find complete line terminated by '\n' and return it
    eol := bytes.IndexByte(buf[0:i], '\n')

    if eol >= 0 {
      for ; eol >= 0 && (buf[eol] == '\n' || buf[eol] == '\r') ; { eol-- }
      return string(buf[0:eol+1])
    }
  }

  return ""
}

// Sends a UDP wake-on-lan packet coded for the given macaddress to the
// broadcast address for the given host or network.
//  host_or_net can be:
//    a) a host name (with or without ":<port>")
//    b) a host's IP address from which the broadcast address will be
//       derived by setting final byte to 255
//    c) a network's broadcast IP address
//  If host_or_net includes a port that port will be used, otherwise the
//  default port 40000 is used.
func Wake(macaddress string, host_or_net string) error {
  hwaddr, err := net.ParseMAC(macaddress)
  if err != nil { return err }

  if strings.Index(host_or_net,":") < 0 { host_or_net += ":40000" }

  udpaddr,err := net.ResolveUDPAddr("udp", host_or_net)
  if err != nil { return err }

  udpaddr.IP[len(udpaddr.IP)-1] = 255
  udpconn,err := net.DialUDP("udp", nil, udpaddr)
  if err != nil { return err }

  payload := []byte{0xff,0xff,0xff,0xff,0xff,0xff}
  for i := 0; i < 16; i++ { payload = append(payload, hwaddr...) }
  _, err = udpconn.Write(payload)
  return err
}

const re_1xx = "(1([0-9]?[0-9]?))"
const re_2xx = "(2([6-9]|([0-4][0-9]?)|(5[0-5]?))?)"
const re_xx  = "([3-9][0-9]?)"
const re_port = "(0|([1-6][0-9]{0,4})|([7-9][0-9]{0,3}))"
const ip_part = "(0|"+re_1xx+"|"+re_2xx+"|"+re_xx+")"
var startsWithIPv4Regexp = regexp.MustCompile("^"+ip_part+"([.]"+ip_part+"){3}")
var endsWithPort = regexp.MustCompile(":"+re_port+"$")

// Takes either a HOST or a HOST:IP pair and replaces the HOST part with
// that machine's IP address (preferring an IPv4 address if there is one).
// If HOST is "localhost" or "::1", it will be replaced with "127.0.0.1".
// If HOST is already an IPv4 address, it will be kept.
// If there is only an IPv6 address available, it will be enclosed in "[...]"
// in the result (even if there is no port).
//
// If localip != "" in the result "127.0.0.1" will be replaced with localip.
//
// Returns the modified address or the original address with an error.
func Resolve(addr string, localip string) (string, error) {
  if localip == "" { localip = "127.0.0.1" }
  addr = strings.Replace(addr, "127.0.0.1", localip, 1)

  if startsWithIPv4Regexp.MatchString(addr) { return addr, nil }

  host := addr
  port := ""
  var err error

  // the net.ParseIP() check tries to prevent confusing an IPv6 address for a port
  if endsWithPort.MatchString(addr) && net.ParseIP(addr) == nil {
    host, port, err = net.SplitHostPort(addr)
    if err != nil { return addr, err }
    port = ":" + port
  }

  if host == "localhost" || host == "::1" || host == "[::1]" {
    return localip + port, nil
  }

  addrs, err := net.LookupIP(host)
  if err != nil {
    return addr, err
  }

  if len(addrs) == 0 { // I don't think this is possible but just in case...
    return addr, fmt.Errorf("No IP address for %v", host)
  }

  // try to find an IPv4 non-loopback address
  for _, a := range addrs {
    if !a.IsLoopback() && a.To4() != nil { return a.String() + port, nil }
  }

  // try to find an IPv4 address (possibly loopback)
  for _, a := range addrs {
    if a.To4() != nil { return strings.Replace(a.String(), "127.0.0.1", localip, 1) + port, nil }
  }

  // take the first address (which is IPv6)
  return "[" + addrs[0].String() + "]" + port, nil
}

<|endoftext|>


<commit_before>package manta

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"runtime"
	"strconv"
	"strings"

	"github.com/davecgh/go-spew/spew"
	"github.com/golang/protobuf/proto"
)

var debugMode, traceMode bool

func init() {
	if os.Getenv("DEBUG") != "" {
		debugMode = true
	}
}

var (
	_sprintf = fmt.Sprintf
	_sdump   = spew.Sdump
)

// Convert a string to an int32
func atoi32(s string) (int32, error) {
	n, err := strconv.Atoi(s)
	if err != nil {
		return 0, err
	}
	return int32(n), nil
}

// printf only if debugging
func _debugf(format string, args ...interface{}) {
	if debugMode {
		args = append([]interface{}{_caller(2)}, args...)
		fmt.Printf("%s: "+format+"\n", args...)
	}
}

// printf only if tracing
func _tracef(format string, args ...interface{}) {
	if traceMode {
		args = append([]interface{}{_caller(2)}, args...)
		fmt.Printf("%s: "+format+"\n", args...)
	}
}

// error with printf syntax
func _errorf(format string, args ...interface{}) error {
	return fmt.Errorf(format, args...)
}

// panic with printf syntax
func _panicf(format string, args ...interface{}) {
	panic(fmt.Errorf(format, args...))
}

// dump named object only if debugging
func _dump(label string, args ...interface{}) {
	if debugMode {
		fmt.Printf("%s: %s", _caller(2), label)
		spew.Dump(args...)
	}
}

// dumps a given byte buffer to the given fixture filename
func _dump_fixture(filename string, buf []byte) {
	if err := ioutil.WriteFile("./fixtures/"+filename, buf, 0644); err != nil {
		panic(err)
	}
}

// reads a byte buffer from the given fixture filename
func _read_fixture(filename string) []byte {
	buf, err := ioutil.ReadFile("./fixtures/" + filename)
	if err != nil {
		panic(err)
	}
	return buf
}

// marshal a proto.Message to bytes
func _proto_marshal(obj proto.Message) []byte {
	buf, err := proto.Marshal(obj)
	if err != nil {
		panic(err)
	}
	return buf
}

// marshal an interface{} to JSON bytes
func _json_marshal(obj interface{}) []byte {
	buf, err := json.MarshalIndent(obj, "", "  ")
	if err != nil {
		panic(err)
	}
	return buf
}

// Returns the name of the calling function
func _caller(n int) string {
	if pc, _, _, ok := runtime.Caller(n); ok {
		fns := strings.Split(runtime.FuncForPC(pc).Name(), "/")
		return fns[len(fns)-1]
	}

	return "unknown"
}

func log2(n int) int {
	return int(math.Log(float64(n))/math.Log(2)) + 1
}
<commit_msg>Log when writing fixtures<commit_after>package manta

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"runtime"
	"strconv"
	"strings"

	"github.com/davecgh/go-spew/spew"
	"github.com/golang/protobuf/proto"
)

var debugMode, traceMode bool

func init() {
	if os.Getenv("DEBUG") != "" {
		debugMode = true
	}
}

var (
	_sprintf = fmt.Sprintf
	_sdump   = spew.Sdump
)

// Convert a string to an int32
func atoi32(s string) (int32, error) {
	n, err := strconv.Atoi(s)
	if err != nil {
		return 0, err
	}
	return int32(n), nil
}

// printf only if debugging
func _debugf(format string, args ...interface{}) {
	if debugMode {
		args = append([]interface{}{_caller(2)}, args...)
		fmt.Printf("%s: "+format+"\n", args...)
	}
}

// printf only if tracing
func _tracef(format string, args ...interface{}) {
	if traceMode {
		args = append([]interface{}{_caller(2)}, args...)
		fmt.Printf("%s: "+format+"\n", args...)
	}
}

// error with printf syntax
func _errorf(format string, args ...interface{}) error {
	return fmt.Errorf(format, args...)
}

// panic with printf syntax
func _panicf(format string, args ...interface{}) {
	panic(fmt.Errorf(format, args...))
}

// dump named object only if debugging
func _dump(label string, args ...interface{}) {
	if debugMode {
		fmt.Printf("%s: %s", _caller(2), label)
		spew.Dump(args...)
	}
}

// dumps a given byte buffer to the given fixture filename
func _dump_fixture(filename string, buf []byte) {
	fmt.Printf("writing fixture %s...\n", filename)
	if err := ioutil.WriteFile("./fixtures/"+filename, buf, 0644); err != nil {
		panic(err)
	}
}

// reads a byte buffer from the given fixture filename
func _read_fixture(filename string) []byte {
	buf, err := ioutil.ReadFile("./fixtures/" + filename)
	if err != nil {
		panic(err)
	}
	return buf
}

// marshal a proto.Message to bytes
func _proto_marshal(obj proto.Message) []byte {
	buf, err := proto.Marshal(obj)
	if err != nil {
		panic(err)
	}
	return buf
}

// marshal an interface{} to JSON bytes
func _json_marshal(obj interface{}) []byte {
	buf, err := json.MarshalIndent(obj, "", "  ")
	if err != nil {
		panic(err)
	}
	return buf
}

// Returns the name of the calling function
func _caller(n int) string {
	if pc, _, _, ok := runtime.Caller(n); ok {
		fns := strings.Split(runtime.FuncForPC(pc).Name(), "/")
		return fns[len(fns)-1]
	}

	return "unknown"
}

func log2(n int) int {
	return int(math.Log(float64(n))/math.Log(2)) + 1
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2017 Red Hat, Inc.
//
// 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 ovirtsdk4

type IService interface {}

// This is the base for all the services of the SDK. It contains the
// utility methods used by all of them.
type Service struct {
	Connection *Connection
	Path string
}

func (ser *Service) internalGet(headers, query map[string]string) (*OvResponse, error) {
	req := NewOvRequest("GET", ser.Path, headers, query, nil)
	res, err := ser.Connection.Send(req)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func (ser *Service) internalPost()<commit_msg>complete service internal functions.<commit_after>//
// Copyright (c) 2017 Red Hat, Inc.
//
// 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 ovirtsdk4

import {
	"encoding/xml"
}

type IService interface{}

// This is the base for all the services of the SDK. It contains the
// utility methods used by all of them.
type Service struct {
	Connection *Connection
	Path string
}

func (service *Service) internalGet(headers, query map[string]string) (*OvResponse, error) {
	req := NewOvRequest("GET", service.Path, headers, query, nil)
	res, err := service.Connection.Send(req)
	if err != nil {
		return nil, err
	}
	return res, nil
}

// Executes an `add` method.
func (service *Service) internalAdd(object interface{}, headers, query map[string]string) (*OvResponse, error) {
	req := NewOvRequest("POST", service.Path, headers, query, nil)
	xmlBytes, err := xml.Marshal(object)
	if err != nil {
		return nil, err
	}
	req.Body = string(xmlBytes)
	res, err := service.Connection.Send(req)
	if err != nil {
		return nil, err
	}
	return res, nil
}

// Executes a `update` method
func (service *Service) internalUpdate(object interface{}, headers, query map[string]string) (*OvResponse, error) {
	req := NewOvRequest("PUT", service.Path, headers, query, nil)
	xmlBytes, err := xml.Marshal(object)
	if err != nil {
		return nil, err
	}
	req.Body = string(xmlBytes)
	res, err := service.Connection.Send(req)
	if err != nil {
		return nil, err
	}
	return res, nil
}

// Executes a `remove` method
func (service *Service) internalRemove(headers, query map[string]string) (*OvResponse, error) {
	req := NewOvRequest("DELETE", service.Path, headers, query, nil)
	res, err := service.Connection.Send(req)
	if err != nil {
		return nil, err
	}
	return res, nil
}

// Executes an `action` method
func (service *Service) internalAction(action *Action, path string, headers, query map[string]string) (*OvResponse, error) {
	req := NewOvRequest("POST", fmt.Sprintf("%s/%s", service.Path, path), headers, query, nil)
	res, err := service.Connection.Send(req)
	if err != nil {
		return nil, err
	}
	return res, nil
}
<|endoftext|>


<commit_before>package lm

import (
	"fmt"
	"io"
	"sort"

	"github.com/suggest-go/suggest/pkg/store"
	"github.com/suggest-go/suggest/pkg/utils"
)

type packedArray struct {
	containers []rangeContainer
	values     []packedValue
	total      WordCount
}

type packedValue = uint64

func newPackedValue(wordID WordID, wordCount WordCount) packedValue {
	return utils.Pack(wordID, wordCount)
}

func getWordID(p packedValue) WordID {
	return utils.UnpackLeft(p)
}

func getWordCount(p packedValue) WordCount {
	return utils.UnpackRight(p)
}

type rangeContainer = uint64

func newRangeContainer(context ContextOffset, from uint32) rangeContainer {
	return utils.Pack(context, from)
}

func getContext(rc rangeContainer) ContextOffset {
	return utils.UnpackLeft(rc)
}

func getFrom(rc rangeContainer) uint32 {
	return utils.UnpackRight(rc)
}

// NewNGramVector creates a new instance of NGramVector.
func NewNGramVector() NGramVector {
	return &packedArray{}
}

// GetCount returns WordCount and Node ContextOffset for the given pair (word, context)
func (s *packedArray) GetCount(word WordID, context ContextOffset) (WordCount, ContextOffset) {
	value, i := s.find(word, context)

	if InvalidContextOffset == i {
		return 0, InvalidContextOffset
	}

	return getWordCount(value), i
}

// GetContextOffset returns the given node context offset
func (s *packedArray) GetContextOffset(word WordID, context ContextOffset) ContextOffset {
	_, i := s.find(word, context)

	return i
}

// CorpusCount returns size of all counts in the collection
func (s *packedArray) CorpusCount() WordCount {
	return s.total
}

// SubVector returns NGramVector for the given context
func (s *packedArray) SubVector(context ContextOffset) NGramVector {
	i := s.findContainerPos(context)

	if i == -1 {
		return nil
	}

	containers := []rangeContainer{s.containers[i]}

	if i < len(s.containers)-1 {
		containers = append(containers, s.containers[i+1])
	}

	return &packedArray{
		containers: containers,
		values:     s.values,
		total:      s.total,
	}
}

func (s *packedArray) Store(out store.Output) (int, error) {
	// write header
	containerSize := uint32(8 * len(s.containers))
	valuesSize := uint32(8 * len(s.values))
	n, err := fmt.Fprintln(out, containerSize, valuesSize, s.total)
	p := n

	if err != nil {
		return p, err
	}

	// write values
	if n, err = out.Write(uint64SliceAsByteSlice(s.containers)); err != nil {
		return p + n, err
	}

	p += n

	if n, err = out.Write(uint64SliceAsByteSlice(s.values)); err != nil {
		return p + n, err
	}

	return p + n, nil
}

func (s *packedArray) Load(in store.Input) (int, error) {
	containersSize, valuesSize := uint32(0), uint32(0)
	n, err := fmt.Fscanln(in, &containersSize, &valuesSize, &s.total)

	if err != nil {
		return 0, err
	}

	offset, err := in.Seek(0, io.SeekCurrent)
	p := int(offset)

	if err != nil {
		return p, err
	}

	var data []byte

	if accessable, ok := in.(store.SliceAccessible); ok {
		n := int(containersSize + valuesSize)
		data = accessable.Data()[p : p+n]

		if _, err = in.Seek(offset+int64(n), io.SeekStart); err != nil {
			return p, err
		}

		p += n
	} else {
		data = make([]byte, containersSize+valuesSize)

		if n, err = in.Read(data); err != nil {
			return p + n, err
		}

		p += n
	}

	s.containers = byteSliceAsUint64Slice(data[:containersSize])
	s.values = byteSliceAsUint64Slice(data[containersSize:])

	return p, nil
}

// find finds the given key in the collection. Returns ContextOffset if the key exists, otherwise returns InvalidContextOffset
func (s *packedArray) find(wordID WordID, context ContextOffset) (packedValue, ContextOffset) {
	i := s.findContainerPos(context)

	if i == -1 {
		return 0, InvalidContextOffset
	}

	values := []packedValue{}
	container := s.containers[i]
	from := getFrom(container)

	if i == len(s.containers)-1 {
		values = s.values[from:]
	} else {
		to := getFrom(s.containers[i+1])
		values = s.values[from:to]
	}

	target := newPackedValue(wordID, 0)
	j := sort.Search(len(values), func(i int) bool { return values[i] >= target })

	if j < 0 || j >= len(values) || getWordID(values[j]) != wordID {
		return 0, InvalidContextOffset
	}

	return values[j], from + ContextOffset(j)
}

// findContainerPos searches for a position of a range container with the given context.
// Returns -1 if container does not exist.
func (s *packedArray) findContainerPos(context ContextOffset) int {
	if len(s.containers) == 0 || getContext(s.containers[0]) > context || getContext(s.containers[len(s.containers)-1]) < context {
		return -1
	}

	target := newRangeContainer(context, 0)
	i := sort.Search(len(s.containers), func(i int) bool { return s.containers[i] >= target })

	if i < 0 || i >= len(s.containers) || getContext(s.containers[i]) != context {
		return -1
	}

	return i
}

// CreatePackedArray creates a NGramVector form the channel of NGramNodes.
func CreatePackedArray(ch <-chan NGramNode) NGramVector {
	pa := &packedArray{
		containers: []rangeContainer{},
		values:     []packedValue{},
		total:      0,
	}

	context := InvalidContextOffset
	from := uint32(0)

	for node := range ch {
		pa.total += node.Count
		currContext := node.Key.GetContext()

		if context != currContext || len(pa.containers) == 0 {
			pa.containers = append(pa.containers, newRangeContainer(currContext, from))
			context = currContext
		}

		pa.values = append(pa.values, newPackedValue(node.Key.GetWordID(), node.Count))
		from++
	}

	return pa
}
<commit_msg>packedArray check if wordId belongs to valid boundaries of values<commit_after>package lm

import (
	"fmt"
	"io"
	"sort"

	"github.com/suggest-go/suggest/pkg/store"
	"github.com/suggest-go/suggest/pkg/utils"
)

type packedArray struct {
	containers []rangeContainer
	values     []packedValue
	total      WordCount
}

type packedValue = uint64

func newPackedValue(wordID WordID, wordCount WordCount) packedValue {
	return utils.Pack(wordID, wordCount)
}

func getWordID(p packedValue) WordID {
	return utils.UnpackLeft(p)
}

func getWordCount(p packedValue) WordCount {
	return utils.UnpackRight(p)
}

type rangeContainer = uint64

func newRangeContainer(context ContextOffset, from uint32) rangeContainer {
	return utils.Pack(context, from)
}

func getContext(rc rangeContainer) ContextOffset {
	return utils.UnpackLeft(rc)
}

func getFrom(rc rangeContainer) uint32 {
	return utils.UnpackRight(rc)
}

// NewNGramVector creates a new instance of NGramVector.
func NewNGramVector() NGramVector {
	return &packedArray{}
}

// GetCount returns WordCount and Node ContextOffset for the given pair (word, context)
func (s *packedArray) GetCount(word WordID, context ContextOffset) (WordCount, ContextOffset) {
	value, i := s.find(word, context)

	if InvalidContextOffset == i {
		return 0, InvalidContextOffset
	}

	return getWordCount(value), i
}

// GetContextOffset returns the given node context offset
func (s *packedArray) GetContextOffset(word WordID, context ContextOffset) ContextOffset {
	_, i := s.find(word, context)

	return i
}

// CorpusCount returns size of all counts in the collection
func (s *packedArray) CorpusCount() WordCount {
	return s.total
}

// SubVector returns NGramVector for the given context
func (s *packedArray) SubVector(context ContextOffset) NGramVector {
	i := s.findContainerPos(context)

	if i == -1 {
		return nil
	}

	containers := []rangeContainer{s.containers[i]}

	if i < len(s.containers)-1 {
		containers = append(containers, s.containers[i+1])
	}

	return &packedArray{
		containers: containers,
		values:     s.values,
		total:      s.total,
	}
}

func (s *packedArray) Store(out store.Output) (int, error) {
	// write header
	containerSize := uint32(8 * len(s.containers))
	valuesSize := uint32(8 * len(s.values))
	n, err := fmt.Fprintln(out, containerSize, valuesSize, s.total)
	p := n

	if err != nil {
		return p, err
	}

	// write values
	if n, err = out.Write(uint64SliceAsByteSlice(s.containers)); err != nil {
		return p + n, err
	}

	p += n

	if n, err = out.Write(uint64SliceAsByteSlice(s.values)); err != nil {
		return p + n, err
	}

	return p + n, nil
}

func (s *packedArray) Load(in store.Input) (int, error) {
	containersSize, valuesSize := uint32(0), uint32(0)
	n, err := fmt.Fscanln(in, &containersSize, &valuesSize, &s.total)

	if err != nil {
		return 0, err
	}

	offset, err := in.Seek(0, io.SeekCurrent)
	p := int(offset)

	if err != nil {
		return p, err
	}

	var data []byte

	if accessable, ok := in.(store.SliceAccessible); ok {
		n := int(containersSize + valuesSize)
		data = accessable.Data()[p : p+n]

		if _, err = in.Seek(offset+int64(n), io.SeekStart); err != nil {
			return p, err
		}

		p += n
	} else {
		data = make([]byte, containersSize+valuesSize)

		if n, err = in.Read(data); err != nil {
			return p + n, err
		}

		p += n
	}

	s.containers = byteSliceAsUint64Slice(data[:containersSize])
	s.values = byteSliceAsUint64Slice(data[containersSize:])

	return p, nil
}

// find finds the given key in the collection. Returns ContextOffset if the key exists, otherwise returns InvalidContextOffset
func (s *packedArray) find(wordID WordID, context ContextOffset) (packedValue, ContextOffset) {
	i := s.findContainerPos(context)

	if i == -1 {
		return 0, InvalidContextOffset
	}

	values := []packedValue{}
	container := s.containers[i]
	from := getFrom(container)

	if i == len(s.containers)-1 {
		values = s.values[from:]
	} else {
		to := getFrom(s.containers[i+1])
		values = s.values[from:to]
	}

	if getWordID(values[0]) > wordID || getWordID(values[len(values)-1]) < wordID {
		return 0, InvalidContextOffset
	}

	target := newPackedValue(wordID, 0)
	j := sort.Search(len(values), func(i int) bool { return values[i] >= target })

	if j < 0 || j >= len(values) || getWordID(values[j]) != wordID {
		return 0, InvalidContextOffset
	}

	return values[j], from + ContextOffset(j)
}

// findContainerPos searches for a position of a range container with the given context.
// Returns -1 if container does not exist.
func (s *packedArray) findContainerPos(context ContextOffset) int {
	if len(s.containers) == 0 || getContext(s.containers[0]) > context || getContext(s.containers[len(s.containers)-1]) < context {
		return -1
	}

	target := newRangeContainer(context, 0)
	i := sort.Search(len(s.containers), func(i int) bool { return s.containers[i] >= target })

	if i < 0 || i >= len(s.containers) || getContext(s.containers[i]) != context {
		return -1
	}

	return i
}

// CreatePackedArray creates a NGramVector form the channel of NGramNodes.
func CreatePackedArray(ch <-chan NGramNode) NGramVector {
	pa := &packedArray{
		containers: []rangeContainer{},
		values:     []packedValue{},
		total:      0,
	}

	context := InvalidContextOffset
	from := uint32(0)

	for node := range ch {
		pa.total += node.Count
		currContext := node.Key.GetContext()

		if context != currContext || len(pa.containers) == 0 {
			pa.containers = append(pa.containers, newRangeContainer(currContext, from))
			context = currContext
		}

		pa.values = append(pa.values, newPackedValue(node.Key.GetWordID(), node.Count))
		from++
	}

	return pa
}
<|endoftext|>


<commit_before>package metrics

var MetricStats Registry
var UseNilMetrics bool

func init() {
	// init with nil metrics
	initMetricVars(&MetricSettings{})
}

var (
	M_Instance_Start                     		Counter
	M_Page_Status_200                    		Counter
	M_Page_Status_500                    		Counter
	M_Page_Status_404                    		Counter
	M_Page_Status_Unknown                		Counter
	M_Api_Status_200                     		Counter
	M_Api_Status_404                     		Counter
	M_Api_Status_500                     		Counter
	M_Api_Status_Unknown                 		Counter
	M_Proxy_Status_200                   		Counter
	M_Proxy_Status_404                   		Counter
	M_Proxy_Status_500                   		Counter
	M_Proxy_Status_Unknown               		Counter
	M_Api_User_SignUpStarted             		Counter
	M_Api_User_SignUpCompleted           		Counter
	M_Api_User_SignUpInvite              		Counter
	M_Api_Dashboard_Save                 		Timer
	M_Api_Dashboard_Get                  		Timer
	M_Api_Dashboard_Search               		Timer
	M_Api_Admin_User_Create              		Counter
	M_Api_Login_Post                     		Counter
	M_Api_Login_OAuth                    		Counter
	M_Api_Org_Create                     		Counter
	M_Api_Dashboard_Snapshot_Create      		Counter
	M_Api_Dashboard_Snapshot_External    		Counter
	M_Api_Dashboard_Snapshot_Get         		Counter
	M_Models_Dashboard_Insert            		Counter
	M_Alerting_Result_State_Alerting     		Counter
	M_Alerting_Result_State_Ok           		Counter
	M_Alerting_Result_State_Paused       		Counter
	M_Alerting_Result_State_NoData       		Counter
	M_Alerting_Result_State_Pending      		Counter
	M_Alerting_Active_Alerts             		Counter
	M_Alerting_Notification_Sent_Slack   		Counter
	M_Alerting_Notification_Sent_Email   		Counter
	M_Alerting_Notification_Sent_Webhook 		Counter
	M_Alerting_Notification_Sent_PagerDuty	Counter
	M_Alerting_Notification_Sent_Victorops Counter

	// Timers
	M_DataSource_ProxyReq_Timer Timer
	M_Alerting_Exeuction_Time   Timer

	// StatTotals
	M_StatTotal_Dashboards Gauge
	M_StatTotal_Users      Gauge
	M_StatTotal_Orgs       Gauge
	M_StatTotal_Playlists  Gauge
)

func initMetricVars(settings *MetricSettings) {
	UseNilMetrics = settings.Enabled == false
	MetricStats = NewRegistry()

	M_Instance_Start = RegCounter("instance_start")

	M_Page_Status_200 = RegCounter("page.resp_status", "code", "200")
	M_Page_Status_500 = RegCounter("page.resp_status", "code", "500")
	M_Page_Status_404 = RegCounter("page.resp_status", "code", "404")
	M_Page_Status_Unknown = RegCounter("page.resp_status", "code", "unknown")

	M_Api_Status_200 = RegCounter("api.resp_status", "code", "200")
	M_Api_Status_404 = RegCounter("api.resp_status", "code", "404")
	M_Api_Status_500 = RegCounter("api.resp_status", "code", "500")
	M_Api_Status_Unknown = RegCounter("api.resp_status", "code", "unknown")

	M_Proxy_Status_200 = RegCounter("proxy.resp_status", "code", "200")
	M_Proxy_Status_404 = RegCounter("proxy.resp_status", "code", "404")
	M_Proxy_Status_500 = RegCounter("proxy.resp_status", "code", "500")
	M_Proxy_Status_Unknown = RegCounter("proxy.resp_status", "code", "unknown")

	M_Api_User_SignUpStarted = RegCounter("api.user.signup_started")
	M_Api_User_SignUpCompleted = RegCounter("api.user.signup_completed")
	M_Api_User_SignUpInvite = RegCounter("api.user.signup_invite")

	M_Api_Dashboard_Save = RegTimer("api.dashboard.save")
	M_Api_Dashboard_Get = RegTimer("api.dashboard.get")
	M_Api_Dashboard_Search = RegTimer("api.dashboard.search")

	M_Api_Admin_User_Create = RegCounter("api.admin.user_create")
	M_Api_Login_Post = RegCounter("api.login.post")
	M_Api_Login_OAuth = RegCounter("api.login.oauth")
	M_Api_Org_Create = RegCounter("api.org.create")

	M_Api_Dashboard_Snapshot_Create = RegCounter("api.dashboard_snapshot.create")
	M_Api_Dashboard_Snapshot_External = RegCounter("api.dashboard_snapshot.external")
	M_Api_Dashboard_Snapshot_Get = RegCounter("api.dashboard_snapshot.get")

	M_Models_Dashboard_Insert = RegCounter("models.dashboard.insert")

	M_Alerting_Result_State_Alerting = RegCounter("alerting.result", "state", "alerting")
	M_Alerting_Result_State_Ok = RegCounter("alerting.result", "state", "ok")
	M_Alerting_Result_State_Paused = RegCounter("alerting.result", "state", "paused")
	M_Alerting_Result_State_NoData = RegCounter("alerting.result", "state", "no_data")
	M_Alerting_Result_State_Pending = RegCounter("alerting.result", "state", "pending")

	M_Alerting_Active_Alerts = RegCounter("alerting.active_alerts")
	M_Alerting_Notification_Sent_Slack = RegCounter("alerting.notifications_sent", "type", "slack")
	M_Alerting_Notification_Sent_Email = RegCounter("alerting.notifications_sent", "type", "email")
	M_Alerting_Notification_Sent_Webhook = RegCounter("alerting.notifications_sent", "type", "webhook")
	M_Alerting_Notification_Sent_PagerDuty = RegCounter("alerting.notifications_sent", "type", "pagerduty")
	M_Alerting_Notification_Sent_Victorops = RegCounter("alerting.notifications_sent", "type", "victorops")

	// Timers
	M_DataSource_ProxyReq_Timer = RegTimer("api.dataproxy.request.all")
	M_Alerting_Exeuction_Time = RegTimer("alerting.execution_time")

	// StatTotals
	M_StatTotal_Dashboards = RegGauge("stat_totals", "stat", "dashboards")
	M_StatTotal_Users = RegGauge("stat_totals", "stat", "users")
	M_StatTotal_Orgs = RegGauge("stat_totals", "stat", "orgs")
	M_StatTotal_Playlists = RegGauge("stat_totals", "stat", "playlists")
}
<commit_msg>Fix formatting<commit_after>package metrics

var MetricStats Registry
var UseNilMetrics bool

func init() {
	// init with nil metrics
	initMetricVars(&MetricSettings{})
}

var (
	M_Instance_Start                       Counter
	M_Page_Status_200                      Counter
	M_Page_Status_500                      Counter
	M_Page_Status_404                      Counter
	M_Page_Status_Unknown                  Counter
	M_Api_Status_200                       Counter
	M_Api_Status_404                       Counter
	M_Api_Status_500                       Counter
	M_Api_Status_Unknown                   Counter
	M_Proxy_Status_200                     Counter
	M_Proxy_Status_404                     Counter
	M_Proxy_Status_500                     Counter
	M_Proxy_Status_Unknown                 Counter
	M_Api_User_SignUpStarted               Counter
	M_Api_User_SignUpCompleted             Counter
	M_Api_User_SignUpInvite                Counter
	M_Api_Dashboard_Save                   Timer
	M_Api_Dashboard_Get                    Timer
	M_Api_Dashboard_Search                 Timer
	M_Api_Admin_User_Create                Counter
	M_Api_Login_Post                       Counter
	M_Api_Login_OAuth                      Counter
	M_Api_Org_Create                       Counter
	M_Api_Dashboard_Snapshot_Create        Counter
	M_Api_Dashboard_Snapshot_External      Counter
	M_Api_Dashboard_Snapshot_Get           Counter
	M_Models_Dashboard_Insert              Counter
	M_Alerting_Result_State_Alerting       Counter
	M_Alerting_Result_State_Ok             Counter
	M_Alerting_Result_State_Paused         Counter
	M_Alerting_Result_State_NoData         Counter
	M_Alerting_Result_State_Pending        Counter
	M_Alerting_Active_Alerts               Counter
	M_Alerting_Notification_Sent_Slack     Counter
	M_Alerting_Notification_Sent_Email     Counter
	M_Alerting_Notification_Sent_Webhook   Counter
	M_Alerting_Notification_Sent_PagerDuty Counter
	M_Alerting_Notification_Sent_Victorops Counter

	// Timers
	M_DataSource_ProxyReq_Timer Timer
	M_Alerting_Exeuction_Time   Timer

	// StatTotals
	M_StatTotal_Dashboards Gauge
	M_StatTotal_Users      Gauge
	M_StatTotal_Orgs       Gauge
	M_StatTotal_Playlists  Gauge
)

func initMetricVars(settings *MetricSettings) {
	UseNilMetrics = settings.Enabled == false
	MetricStats = NewRegistry()

	M_Instance_Start = RegCounter("instance_start")

	M_Page_Status_200 = RegCounter("page.resp_status", "code", "200")
	M_Page_Status_500 = RegCounter("page.resp_status", "code", "500")
	M_Page_Status_404 = RegCounter("page.resp_status", "code", "404")
	M_Page_Status_Unknown = RegCounter("page.resp_status", "code", "unknown")

	M_Api_Status_200 = RegCounter("api.resp_status", "code", "200")
	M_Api_Status_404 = RegCounter("api.resp_status", "code", "404")
	M_Api_Status_500 = RegCounter("api.resp_status", "code", "500")
	M_Api_Status_Unknown = RegCounter("api.resp_status", "code", "unknown")

	M_Proxy_Status_200 = RegCounter("proxy.resp_status", "code", "200")
	M_Proxy_Status_404 = RegCounter("proxy.resp_status", "code", "404")
	M_Proxy_Status_500 = RegCounter("proxy.resp_status", "code", "500")
	M_Proxy_Status_Unknown = RegCounter("proxy.resp_status", "code", "unknown")

	M_Api_User_SignUpStarted = RegCounter("api.user.signup_started")
	M_Api_User_SignUpCompleted = RegCounter("api.user.signup_completed")
	M_Api_User_SignUpInvite = RegCounter("api.user.signup_invite")

	M_Api_Dashboard_Save = RegTimer("api.dashboard.save")
	M_Api_Dashboard_Get = RegTimer("api.dashboard.get")
	M_Api_Dashboard_Search = RegTimer("api.dashboard.search")

	M_Api_Admin_User_Create = RegCounter("api.admin.user_create")
	M_Api_Login_Post = RegCounter("api.login.post")
	M_Api_Login_OAuth = RegCounter("api.login.oauth")
	M_Api_Org_Create = RegCounter("api.org.create")

	M_Api_Dashboard_Snapshot_Create = RegCounter("api.dashboard_snapshot.create")
	M_Api_Dashboard_Snapshot_External = RegCounter("api.dashboard_snapshot.external")
	M_Api_Dashboard_Snapshot_Get = RegCounter("api.dashboard_snapshot.get")

	M_Models_Dashboard_Insert = RegCounter("models.dashboard.insert")

	M_Alerting_Result_State_Alerting = RegCounter("alerting.result", "state", "alerting")
	M_Alerting_Result_State_Ok = RegCounter("alerting.result", "state", "ok")
	M_Alerting_Result_State_Paused = RegCounter("alerting.result", "state", "paused")
	M_Alerting_Result_State_NoData = RegCounter("alerting.result", "state", "no_data")
	M_Alerting_Result_State_Pending = RegCounter("alerting.result", "state", "pending")

	M_Alerting_Active_Alerts = RegCounter("alerting.active_alerts")
	M_Alerting_Notification_Sent_Slack = RegCounter("alerting.notifications_sent", "type", "slack")
	M_Alerting_Notification_Sent_Email = RegCounter("alerting.notifications_sent", "type", "email")
	M_Alerting_Notification_Sent_Webhook = RegCounter("alerting.notifications_sent", "type", "webhook")
	M_Alerting_Notification_Sent_PagerDuty = RegCounter("alerting.notifications_sent", "type", "pagerduty")
	M_Alerting_Notification_Sent_Victorops = RegCounter("alerting.notifications_sent", "type", "victorops")

	// Timers
	M_DataSource_ProxyReq_Timer = RegTimer("api.dataproxy.request.all")
	M_Alerting_Exeuction_Time = RegTimer("alerting.execution_time")

	// StatTotals
	M_StatTotal_Dashboards = RegGauge("stat_totals", "stat", "dashboards")
	M_StatTotal_Users = RegGauge("stat_totals", "stat", "users")
	M_StatTotal_Orgs = RegGauge("stat_totals", "stat", "orgs")
	M_StatTotal_Playlists = RegGauge("stat_totals", "stat", "playlists")
}
<|endoftext|>