<commit_before>// Copyright © 2021 The Knative 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 install

import (
	"fmt"
	"os/exec"
	"strings"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"
)

var servingVersion = "0.23.0"
var kourierVersion = "0.23.0"
var eventingVersion = "0.23.0"

// Kourier installs Kourier networking layer from Github YAML files
func Kourier() error {

	fmt.Println("Starting Networking layer install...")

	kourier := exec.Command("kubectl", "apply", "-f", "https://github.com/knative-sandbox/net-kourier/releases/download/v"+kourierVersion+"/kourier.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(kourier) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	kourierWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "kourier-system")
	if err := runCommand(kourierWait); err != nil {
		return fmt.Errorf("kourier: %w", err)
	}
	servingWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-serving")
	if err := runCommand(servingWait); err != nil {
		return fmt.Errorf("serving: %w", err)
	}
	fmt.Println("    Kourier installed...")

	ingress := exec.Command("kubectl", "patch", "configmap/config-network", "--namespace", "knative-serving", "--type", "merge", "--patch", "{\"data\":{\"ingress.class\":\"kourier.ingress.networking.knative.dev\"}}")
	if err := runCommand(ingress); err != nil {
		return fmt.Errorf("ingress error: %w", err)
	}
	fmt.Println("    Ingress patched...")

	config := `apiVersion: v1
kind: Service
metadata:
  name: kourier-ingress
  namespace: kourier-system
  labels:
    networking.knative.dev/ingress-provider: kourier
spec:
  type: NodePort
  selector:
    app: 3scale-kourier-gateway
  ports:
    - name: http2
      nodePort: 31080
      port: 80
      targetPort: 8080`

	kourierIngress := exec.Command("kubectl", "apply", "-f", "-")
	kourierIngress.Stdin = strings.NewReader(config)
	if err := runCommand(kourierIngress); err != nil {
		return fmt.Errorf("kourier service: %w", err)
	}

	fmt.Println("    Kourier service installed...")

	domainDns := exec.Command("kubectl", "patch", "configmap", "-n", "knative-serving", "config-domain", "-p", "{\"data\": {\"127.0.0.1.nip.io\": \"\"}}")
	if err := domainDns.Run(); err != nil {
		return fmt.Errorf("domain dns: %w", err)
	}
	fmt.Println("    Domain DNS set up...")

	fmt.Println("Finished installing Networking layer")

	return nil
}

// Serving installs Knative Serving from Github YAML files
func Serving() error {
	fmt.Println("Starting Knative Serving install...")

	crds := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/serving/releases/download/v"+servingVersion+"/serving-crds.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(crds) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	crdWait := exec.Command("kubectl", "wait", "--for=condition=Established", "--all", "crd")
	if err := runCommand(crdWait); err != nil {
		return fmt.Errorf("crds: %w", err)
	}
	fmt.Println("    CRDs installed...")

	core := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/serving/releases/download/v"+servingVersion+"/serving-core.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(core) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	coreWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-serving")
	if err := runCommand(coreWait); err != nil {
		return fmt.Errorf("core: %w", err)
	}

	fmt.Println("    Core installed...")

	fmt.Println("Finished installing Knative Serving")

	return nil
}

// Eventing installs Knative Eventing from Github YAML files
func Eventing() error {
	fmt.Println("Starting Knative Eventing install...")

	crds := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/eventing/releases/download/v"+eventingVersion+"/eventing-crds.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(crds) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	crdWait := exec.Command("kubectl", "wait", "--for=condition=Established", "--all", "crd")
	if err := runCommand(crdWait); err != nil {
		return fmt.Errorf("crds: %w", err)
	}
	fmt.Println("    CRDs installed...")

	core := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/eventing/releases/download/v"+eventingVersion+"/eventing-core.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(core) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	coreWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-eventing")
	if err := runCommand(coreWait); err != nil {
		return fmt.Errorf("core: %w", err)
	}
	fmt.Println("    Core installed...")

	channel := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/eventing/releases/download/v"+eventingVersion+"/in-memory-channel.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(channel) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	channelWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-eventing")
	if err := runCommand(channelWait); err != nil {
		return fmt.Errorf("channel: %w", err)
	}
	fmt.Println("    In-memory channel installed...")

	broker := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/eventing/releases/download/v"+eventingVersion+"/mt-channel-broker.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(broker) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	brokerWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-eventing")
	if err := runCommand(brokerWait); err != nil {
		return fmt.Errorf("broker: %w", err)
	}
	fmt.Println("    Mt-channel broker installed...")

	config := `apiVersion: eventing.knative.dev/v1
kind: broker
metadata:
 name: example-broker
 namespace: default`

	exampleBroker := exec.Command("kubectl", "apply", "-f", "-")
	exampleBroker.Stdin = strings.NewReader(config)
	if err := runCommand(exampleBroker); err != nil {
		return fmt.Errorf("example broker: %w", err)
	}

	fmt.Println("    Example broker installed...")

	return nil
}

func runCommand(c *exec.Cmd) error {
	if out, err := c.CombinedOutput(); err != nil {
		fmt.Println(string(out))
		return err
	}
	return nil
}
<commit_msg>adding retry for kourier install (#59)<commit_after>// Copyright © 2021 The Knative 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 install

import (
	"fmt"
	"os/exec"
	"strings"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"
)

var servingVersion = "0.23.0"
var kourierVersion = "0.23.0"
var eventingVersion = "0.23.0"

// Kourier installs Kourier networking layer from Github YAML files
func Kourier() error {

	fmt.Println("Starting Networking layer install...")

	kourier := exec.Command("kubectl", "apply", "-f", "https://github.com/knative-sandbox/net-kourier/releases/download/v"+kourierVersion+"/kourier.yaml")
	// retries installing kourier if it fails, see discussion in:
	// https://github.com/knative-sandbox/kn-plugin-quickstart/pull/58
	for i := 0; i <= 3; i++ {
		if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
			return runCommand(kourier) == nil, nil
		}); err != nil {
			if i >= 3 {
				return fmt.Errorf("wait: %w", err)
			}
			time.Sleep(10 * time.Second)
		} else {
			break
		}
	}

	kourierWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "kourier-system")
	if err := runCommand(kourierWait); err != nil {
		return fmt.Errorf("kourier: %w", err)
	}
	servingWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-serving")
	if err := runCommand(servingWait); err != nil {
		return fmt.Errorf("serving: %w", err)
	}
	fmt.Println("    Kourier installed...")

	ingress := exec.Command("kubectl", "patch", "configmap/config-network", "--namespace", "knative-serving", "--type", "merge", "--patch", "{\"data\":{\"ingress.class\":\"kourier.ingress.networking.knative.dev\"}}")
	if err := runCommand(ingress); err != nil {
		return fmt.Errorf("ingress error: %w", err)
	}
	fmt.Println("    Ingress patched...")

	config := `apiVersion: v1
kind: Service
metadata:
  name: kourier-ingress
  namespace: kourier-system
  labels:
    networking.knative.dev/ingress-provider: kourier
spec:
  type: NodePort
  selector:
    app: 3scale-kourier-gateway
  ports:
    - name: http2
      nodePort: 31080
      port: 80
      targetPort: 8080`

	kourierIngress := exec.Command("kubectl", "apply", "-f", "-")
	kourierIngress.Stdin = strings.NewReader(config)
	if err := runCommand(kourierIngress); err != nil {
		return fmt.Errorf("kourier service: %w", err)
	}

	fmt.Println("    Kourier service installed...")

	domainDns := exec.Command("kubectl", "patch", "configmap", "-n", "knative-serving", "config-domain", "-p", "{\"data\": {\"127.0.0.1.nip.io\": \"\"}}")
	if err := domainDns.Run(); err != nil {
		return fmt.Errorf("domain dns: %w", err)
	}
	fmt.Println("    Domain DNS set up...")

	fmt.Println("Finished installing Networking layer")

	return nil
}

// Serving installs Knative Serving from Github YAML files
func Serving() error {
	fmt.Println("Starting Knative Serving install...")

	crds := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/serving/releases/download/v"+servingVersion+"/serving-crds.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(crds) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	crdWait := exec.Command("kubectl", "wait", "--for=condition=Established", "--all", "crd")
	if err := runCommand(crdWait); err != nil {
		return fmt.Errorf("crds: %w", err)
	}
	fmt.Println("    CRDs installed...")

	core := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/serving/releases/download/v"+servingVersion+"/serving-core.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(core) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	coreWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-serving")
	if err := runCommand(coreWait); err != nil {
		return fmt.Errorf("core: %w", err)
	}

	fmt.Println("    Core installed...")

	fmt.Println("Finished installing Knative Serving")

	return nil
}

// Eventing installs Knative Eventing from Github YAML files
func Eventing() error {
	fmt.Println("Starting Knative Eventing install...")

	crds := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/eventing/releases/download/v"+eventingVersion+"/eventing-crds.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(crds) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	crdWait := exec.Command("kubectl", "wait", "--for=condition=Established", "--all", "crd")
	if err := runCommand(crdWait); err != nil {
		return fmt.Errorf("crds: %w", err)
	}
	fmt.Println("    CRDs installed...")

	core := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/eventing/releases/download/v"+eventingVersion+"/eventing-core.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(core) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	coreWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-eventing")
	if err := runCommand(coreWait); err != nil {
		return fmt.Errorf("core: %w", err)
	}
	fmt.Println("    Core installed...")

	channel := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/eventing/releases/download/v"+eventingVersion+"/in-memory-channel.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(channel) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	channelWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-eventing")
	if err := runCommand(channelWait); err != nil {
		return fmt.Errorf("channel: %w", err)
	}
	fmt.Println("    In-memory channel installed...")

	broker := exec.Command("kubectl", "apply", "-f", "https://github.com/knative/eventing/releases/download/v"+eventingVersion+"/mt-channel-broker.yaml")
	if err := wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
		return runCommand(broker) == nil, nil
	}); err != nil {
		return fmt.Errorf("wait: %w", err)
	}

	brokerWait := exec.Command("kubectl", "wait", "pod", "--timeout=-1s", "--for=condition=Ready", "-l", "!job-name", "-n", "knative-eventing")
	if err := runCommand(brokerWait); err != nil {
		return fmt.Errorf("broker: %w", err)
	}
	fmt.Println("    Mt-channel broker installed...")

	config := `apiVersion: eventing.knative.dev/v1
kind: broker
metadata:
 name: example-broker
 namespace: default`

	exampleBroker := exec.Command("kubectl", "apply", "-f", "-")
	exampleBroker.Stdin = strings.NewReader(config)
	if err := runCommand(exampleBroker); err != nil {
		return fmt.Errorf("example broker: %w", err)
	}

	fmt.Println("    Example broker installed...")

	return nil
}

func runCommand(c *exec.Cmd) error {
	if out, err := c.CombinedOutput(); err != nil {
		fmt.Println(string(out))
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 cmd

import (
	"fmt"
	"io"

	"k8s.io/apiserver/pkg/util/flag"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/kubernetes/pkg/kubectl/cmd/auth"
	cmdconfig "k8s.io/kubernetes/pkg/kubectl/cmd/config"
	"k8s.io/kubernetes/pkg/kubectl/cmd/rollout"
	"k8s.io/kubernetes/pkg/kubectl/cmd/set"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/util/i18n"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

const (
	bashCompletionFunc = `# call kubectl get $1,
__kubectl_override_flag_list=(kubeconfig cluster user context namespace server)
__kubectl_override_flags()
{
    local ${__kubectl_override_flag_list[*]} two_word_of of
    for w in "${words[@]}"; do
        if [ -n "${two_word_of}" ]; then
            eval "${two_word_of}=\"--${two_word_of}=\${w}\""
            two_word_of=
            continue
        fi
        for of in "${__kubectl_override_flag_list[@]}"; do
            case "${w}" in
                --${of}=*)
                    eval "${of}=\"${w}\""
                    ;;
                --${of})
                    two_word_of="${of}"
                    ;;
            esac
        done
        if [ "${w}" == "--all-namespaces" ]; then
            namespace="--all-namespaces"
        fi
    done
    for of in "${__kubectl_override_flag_list[@]}"; do
        if eval "test -n \"\$${of}\""; then
            eval "echo \${${of}}"
        fi
    done
}

__kubectl_get_namespaces()
{
    local template kubectl_out
    template="{{ range .items  }}{{ .metadata.name }} {{ end }}"
    if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then
        COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
    fi
}

__kubectl_config_get_contexts()
{
    __kubectl_parse_config "contexts"
}

__kubectl_config_get_clusters()
{
    __kubectl_parse_config "clusters"
}

__kubectl_config_get_users()
{
    __kubectl_parse_config "users"
}

# $1 has to be "contexts", "clusters" or "users"
__kubectl_parse_config()
{
    local template kubectl_out
    template="{{ range .$1  }}{{ .name }} {{ end }}"
    if kubectl_out=$(kubectl config $(__kubectl_override_flags) -o template --template="${template}" view 2>/dev/null); then
        COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
    fi
}

__kubectl_parse_get()
{
    local template
    template="{{ range .items  }}{{ .metadata.name }} {{ end }}"
    local kubectl_out
    if kubectl_out=$(kubectl get $(__kubectl_override_flags) -o template --template="${template}" "$1" 2>/dev/null); then
        COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
    fi
}

__kubectl_get_resource()
{
    if [[ ${#nouns[@]} -eq 0 ]]; then
        return 1
    fi
    __kubectl_parse_get "${nouns[${#nouns[@]} -1]}"
}

__kubectl_get_resource_pod()
{
    __kubectl_parse_get "pod"
}

__kubectl_get_resource_rc()
{
    __kubectl_parse_get "rc"
}

__kubectl_get_resource_node()
{
    __kubectl_parse_get "node"
}

# $1 is the name of the pod we want to get the list of containers inside
__kubectl_get_containers()
{
    local template
    template="{{ range .spec.containers  }}{{ .name }} {{ end }}"
    __debug "${FUNCNAME} nouns are ${nouns[*]}"

    local len="${#nouns[@]}"
    if [[ ${len} -ne 1 ]]; then
        return
    fi
    local last=${nouns[${len} -1]}
    local kubectl_out
    if kubectl_out=$(kubectl get $(__kubectl_override_flags) -o template --template="${template}" pods "${last}" 2>/dev/null); then
        COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
    fi
}

# Require both a pod and a container to be specified
__kubectl_require_pod_and_container()
{
    if [[ ${#nouns[@]} -eq 0 ]]; then
        __kubectl_parse_get pods
        return 0
    fi;
    __kubectl_get_containers
    return 0
}

__custom_func() {
    case ${last_command} in
        kubectl_get | kubectl_describe | kubectl_delete | kubectl_label | kubectl_stop | kubectl_edit | kubectl_patch |\
        kubectl_annotate | kubectl_expose | kubectl_scale | kubectl_autoscale | kubectl_taint | kubectl_rollout_*)
            __kubectl_get_resource
            return
            ;;
        kubectl_logs | kubectl_attach)
            __kubectl_require_pod_and_container
            return
            ;;
        kubectl_exec | kubectl_port-forward | kubectl_top_pod)
            __kubectl_get_resource_pod
            return
            ;;
        kubectl_rolling-update)
            __kubectl_get_resource_rc
            return
            ;;
        kubectl_cordon | kubectl_uncordon | kubectl_drain | kubectl_top_node)
            __kubectl_get_resource_node
            return
            ;;
        kubectl_config_use-context)
            __kubectl_config_get_contexts
            return
            ;;
        *)
            ;;
    esac
}
`

	// If you add a resource to this list, please also take a look at pkg/kubectl/kubectl.go
	// and add a short forms entry in expandResourceShortcut() when appropriate.
	// TODO: This should be populated using the discovery information from apiserver.
	validResources = `Valid resource types include:

    * all
    * certificatesigningrequests (aka 'csr')
    * clusterrolebindings
    * clusterroles
    * clusters (valid only for federation apiservers)
    * componentstatuses (aka 'cs')
    * configmaps (aka 'cm')
    * controllerrevisions
    * cronjobs
    * daemonsets (aka 'ds')
    * deployments (aka 'deploy')
    * endpoints (aka 'ep')
    * events (aka 'ev')
    * horizontalpodautoscalers (aka 'hpa')
    * ingresses (aka 'ing')
    * jobs
    * limitranges (aka 'limits')
    * namespaces (aka 'ns')
    * networkpolicies (aka 'netpol')
    * nodes (aka 'no')
    * persistentvolumeclaims (aka 'pvc')
    * persistentvolumes (aka 'pv')
    * poddisruptionbudgets (aka 'pdb')
    * podpreset
    * pods (aka 'po')
    * podsecuritypolicies (aka 'psp')
    * podtemplates
    * replicasets (aka 'rs')
    * replicationcontrollers (aka 'rc')
    * resourcequotas (aka 'quota')
    * rolebindings
    * roles
    * secrets
    * serviceaccounts (aka 'sa')
    * services (aka 'svc')
    * statefulsets
    * storageclasses
    * thirdpartyresources
    `
)

var (
	bash_completion_flags = map[string]string{
		"namespace": "__kubectl_get_namespaces",
		"context":   "__kubectl_config_get_contexts",
		"cluster":   "__kubectl_config_get_clusters",
		"user":      "__kubectl_config_get_users",
	}
)

// NewKubectlCommand creates the `kubectl` command and its nested children.
func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Command {
	// Parent command to which all subcommands are added.
	cmds := &cobra.Command{
		Use:   "kubectl",
		Short: i18n.T("kubectl controls the Kubernetes cluster manager"),
		Long: templates.LongDesc(`
      kubectl controls the Kubernetes cluster manager.

      Find more information at https://github.com/kubernetes/kubernetes.`),
		Run: runHelp,
		BashCompletionFunction: bashCompletionFunc,
	}

	f.BindFlags(cmds.PersistentFlags())
	f.BindExternalFlags(cmds.PersistentFlags())

	// Sending in 'nil' for the getLanguageFn() results in using
	// the LANG environment variable.
	//
	// TODO: Consider adding a flag or file preference for setting
	// the language, instead of just loading from the LANG env. variable.
	i18n.LoadTranslations("kubectl", nil)

	// From this point and forward we get warnings on flags that contain "_" separators
	cmds.SetGlobalNormalizationFunc(flag.WarnWordSepNormalizeFunc)

	groups := templates.CommandGroups{
		{
			Message: "Basic Commands (Beginner):",
			Commands: []*cobra.Command{
				NewCmdCreate(f, out, err),
				NewCmdExposeService(f, out),
				NewCmdRun(f, in, out, err),
				deprecatedAlias("run-container", NewCmdRun(f, in, out, err)),
				set.NewCmdSet(f, out, err),
			},
		},
		{
			Message: "Basic Commands (Intermediate):",
			Commands: []*cobra.Command{
				NewCmdGet(f, out, err),
				NewCmdExplain(f, out, err),
				NewCmdEdit(f, out, err),
				NewCmdDelete(f, out, err),
			},
		},
		{
			Message: "Deploy Commands:",
			Commands: []*cobra.Command{
				rollout.NewCmdRollout(f, out, err),
				NewCmdRollingUpdate(f, out),
				deprecatedAlias("rollingupdate", NewCmdRollingUpdate(f, out)),
				NewCmdScale(f, out),
				deprecatedAlias("resize", NewCmdScale(f, out)),
				NewCmdAutoscale(f, out),
			},
		},
		{
			Message: "Cluster Management Commands:",
			Commands: []*cobra.Command{
				NewCmdCertificate(f, out),
				NewCmdClusterInfo(f, out),
				deprecatedAlias("clusterinfo", NewCmdClusterInfo(f, out)),
				NewCmdTop(f, out, err),
				NewCmdCordon(f, out),
				NewCmdUncordon(f, out),
				NewCmdDrain(f, out, err),
				NewCmdTaint(f, out),
			},
		},
		{
			Message: "Troubleshooting and Debugging Commands:",
			Commands: []*cobra.Command{
				NewCmdDescribe(f, out, err),
				NewCmdLogs(f, out),
				NewCmdAttach(f, in, out, err),
				NewCmdExec(f, in, out, err),
				NewCmdPortForward(f, out, err),
				NewCmdProxy(f, out),
				NewCmdCp(f, out, err),
				auth.NewCmdAuth(f, out, err),
			},
		},
		{
			Message: "Advanced Commands:",
			Commands: []*cobra.Command{
				NewCmdApply(f, out, err),
				NewCmdPatch(f, out),
				NewCmdReplace(f, out),
				deprecatedAlias("update", NewCmdReplace(f, out)),
				NewCmdConvert(f, out),
			},
		},
		{
			Message: "Settings Commands:",
			Commands: []*cobra.Command{
				NewCmdLabel(f, out),
				NewCmdAnnotate(f, out),
				NewCmdCompletion(out, ""),
			},
		},
	}
	groups.Add(cmds)

	filters := []string{
		"options",
		deprecated("kubectl", "delete", cmds, NewCmdStop(f, out)),
	}

	// Hide the "alpha" subcommand if there are no alpha commands in this build.
	alpha := NewCmdAlpha(f, in, out, err)
	if !alpha.HasSubCommands() {
		filters = append(filters, alpha.Name())
	}

	templates.ActsAsRootCommand(cmds, filters, groups...)

	for name, completion := range bash_completion_flags {
		if cmds.Flag(name) != nil {
			if cmds.Flag(name).Annotations == nil {
				cmds.Flag(name).Annotations = map[string][]string{}
			}
			cmds.Flag(name).Annotations[cobra.BashCompCustom] = append(
				cmds.Flag(name).Annotations[cobra.BashCompCustom],
				completion,
			)
		}
	}

	cmds.AddCommand(alpha)
	cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), out, err))
	cmds.AddCommand(NewCmdPlugin(f, in, out, err))
	cmds.AddCommand(NewCmdVersion(f, out))
	cmds.AddCommand(NewCmdApiVersions(f, out))
	cmds.AddCommand(deprecatedAlias("apiversions", NewCmdApiVersions(f, out)))
	cmds.AddCommand(NewCmdOptions(out))

	return cmds
}

func runHelp(cmd *cobra.Command, args []string) {
	cmd.Help()
}

func printDeprecationWarning(command, alias string) {
	glog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", alias, command)
}

// deprecatedAlias is intended to be used to create a "wrapper" command around
// an existing command. The wrapper works the same but prints a deprecation
// message before running. This command is identical functionality.
func deprecatedAlias(deprecatedVersion string, cmd *cobra.Command) *cobra.Command {
	// Have to be careful here because Cobra automatically extracts the name
	// of the command from the .Use field.
	originalName := cmd.Name()

	cmd.Use = deprecatedVersion
	cmd.Deprecated = fmt.Sprintf("use %q instead", originalName)
	cmd.Short = fmt.Sprintf("%s. This command is deprecated, use %q instead", cmd.Short, originalName)
	cmd.Hidden = true
	return cmd
}

// deprecated is similar to deprecatedAlias, but it is used for deprecations
// that are not simple aliases; this command is actually a different
// (deprecated) codepath.
func deprecated(baseName, to string, parent, cmd *cobra.Command) string {
	cmd.Long = fmt.Sprintf("Deprecated: all functionality can be found in \"%s %s\"", baseName, to)
	cmd.Short = fmt.Sprintf("Deprecated: use %s", to)
	parent.AddCommand(cmd)
	return cmd.Name()
}
<commit_msg>Fix completions for --namespace to override flags<commit_after>/*
Copyright 2014 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 cmd

import (
	"fmt"
	"io"

	"k8s.io/apiserver/pkg/util/flag"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/kubernetes/pkg/kubectl/cmd/auth"
	cmdconfig "k8s.io/kubernetes/pkg/kubectl/cmd/config"
	"k8s.io/kubernetes/pkg/kubectl/cmd/rollout"
	"k8s.io/kubernetes/pkg/kubectl/cmd/set"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/util/i18n"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

const (
	bashCompletionFunc = `# call kubectl get $1,
__kubectl_override_flag_list=(kubeconfig cluster user context namespace server)
__kubectl_override_flags()
{
    local ${__kubectl_override_flag_list[*]} two_word_of of
    for w in "${words[@]}"; do
        if [ -n "${two_word_of}" ]; then
            eval "${two_word_of}=\"--${two_word_of}=\${w}\""
            two_word_of=
            continue
        fi
        for of in "${__kubectl_override_flag_list[@]}"; do
            case "${w}" in
                --${of}=*)
                    eval "${of}=\"${w}\""
                    ;;
                --${of})
                    two_word_of="${of}"
                    ;;
            esac
        done
        if [ "${w}" == "--all-namespaces" ]; then
            namespace="--all-namespaces"
        fi
    done
    for of in "${__kubectl_override_flag_list[@]}"; do
        if eval "test -n \"\$${of}\""; then
            eval "echo \${${of}}"
        fi
    done
}

__kubectl_get_namespaces()
{
    __kubectl_parse_get "namespace"
}

__kubectl_config_get_contexts()
{
    __kubectl_parse_config "contexts"
}

__kubectl_config_get_clusters()
{
    __kubectl_parse_config "clusters"
}

__kubectl_config_get_users()
{
    __kubectl_parse_config "users"
}

# $1 has to be "contexts", "clusters" or "users"
__kubectl_parse_config()
{
    local template kubectl_out
    template="{{ range .$1  }}{{ .name }} {{ end }}"
    if kubectl_out=$(kubectl config $(__kubectl_override_flags) -o template --template="${template}" view 2>/dev/null); then
        COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
    fi
}

__kubectl_parse_get()
{
    local template
    template="{{ range .items  }}{{ .metadata.name }} {{ end }}"
    local kubectl_out
    if kubectl_out=$(kubectl get $(__kubectl_override_flags) -o template --template="${template}" "$1" 2>/dev/null); then
        COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
    fi
}

__kubectl_get_resource()
{
    if [[ ${#nouns[@]} -eq 0 ]]; then
        return 1
    fi
    __kubectl_parse_get "${nouns[${#nouns[@]} -1]}"
}

__kubectl_get_resource_pod()
{
    __kubectl_parse_get "pod"
}

__kubectl_get_resource_rc()
{
    __kubectl_parse_get "rc"
}

__kubectl_get_resource_node()
{
    __kubectl_parse_get "node"
}

# $1 is the name of the pod we want to get the list of containers inside
__kubectl_get_containers()
{
    local template
    template="{{ range .spec.containers  }}{{ .name }} {{ end }}"
    __debug "${FUNCNAME} nouns are ${nouns[*]}"

    local len="${#nouns[@]}"
    if [[ ${len} -ne 1 ]]; then
        return
    fi
    local last=${nouns[${len} -1]}
    local kubectl_out
    if kubectl_out=$(kubectl get $(__kubectl_override_flags) -o template --template="${template}" pods "${last}" 2>/dev/null); then
        COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
    fi
}

# Require both a pod and a container to be specified
__kubectl_require_pod_and_container()
{
    if [[ ${#nouns[@]} -eq 0 ]]; then
        __kubectl_parse_get pods
        return 0
    fi;
    __kubectl_get_containers
    return 0
}

__custom_func() {
    case ${last_command} in
        kubectl_get | kubectl_describe | kubectl_delete | kubectl_label | kubectl_stop | kubectl_edit | kubectl_patch |\
        kubectl_annotate | kubectl_expose | kubectl_scale | kubectl_autoscale | kubectl_taint | kubectl_rollout_*)
            __kubectl_get_resource
            return
            ;;
        kubectl_logs | kubectl_attach)
            __kubectl_require_pod_and_container
            return
            ;;
        kubectl_exec | kubectl_port-forward | kubectl_top_pod)
            __kubectl_get_resource_pod
            return
            ;;
        kubectl_rolling-update)
            __kubectl_get_resource_rc
            return
            ;;
        kubectl_cordon | kubectl_uncordon | kubectl_drain | kubectl_top_node)
            __kubectl_get_resource_node
            return
            ;;
        kubectl_config_use-context)
            __kubectl_config_get_contexts
            return
            ;;
        *)
            ;;
    esac
}
`

	// If you add a resource to this list, please also take a look at pkg/kubectl/kubectl.go
	// and add a short forms entry in expandResourceShortcut() when appropriate.
	// TODO: This should be populated using the discovery information from apiserver.
	validResources = `Valid resource types include:

    * all
    * certificatesigningrequests (aka 'csr')
    * clusterrolebindings
    * clusterroles
    * clusters (valid only for federation apiservers)
    * componentstatuses (aka 'cs')
    * configmaps (aka 'cm')
    * controllerrevisions
    * cronjobs
    * daemonsets (aka 'ds')
    * deployments (aka 'deploy')
    * endpoints (aka 'ep')
    * events (aka 'ev')
    * horizontalpodautoscalers (aka 'hpa')
    * ingresses (aka 'ing')
    * jobs
    * limitranges (aka 'limits')
    * namespaces (aka 'ns')
    * networkpolicies (aka 'netpol')
    * nodes (aka 'no')
    * persistentvolumeclaims (aka 'pvc')
    * persistentvolumes (aka 'pv')
    * poddisruptionbudgets (aka 'pdb')
    * podpreset
    * pods (aka 'po')
    * podsecuritypolicies (aka 'psp')
    * podtemplates
    * replicasets (aka 'rs')
    * replicationcontrollers (aka 'rc')
    * resourcequotas (aka 'quota')
    * rolebindings
    * roles
    * secrets
    * serviceaccounts (aka 'sa')
    * services (aka 'svc')
    * statefulsets
    * storageclasses
    * thirdpartyresources
    `
)

var (
	bash_completion_flags = map[string]string{
		"namespace": "__kubectl_get_namespaces",
		"context":   "__kubectl_config_get_contexts",
		"cluster":   "__kubectl_config_get_clusters",
		"user":      "__kubectl_config_get_users",
	}
)

// NewKubectlCommand creates the `kubectl` command and its nested children.
func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Command {
	// Parent command to which all subcommands are added.
	cmds := &cobra.Command{
		Use:   "kubectl",
		Short: i18n.T("kubectl controls the Kubernetes cluster manager"),
		Long: templates.LongDesc(`
      kubectl controls the Kubernetes cluster manager.

      Find more information at https://github.com/kubernetes/kubernetes.`),
		Run: runHelp,
		BashCompletionFunction: bashCompletionFunc,
	}

	f.BindFlags(cmds.PersistentFlags())
	f.BindExternalFlags(cmds.PersistentFlags())

	// Sending in 'nil' for the getLanguageFn() results in using
	// the LANG environment variable.
	//
	// TODO: Consider adding a flag or file preference for setting
	// the language, instead of just loading from the LANG env. variable.
	i18n.LoadTranslations("kubectl", nil)

	// From this point and forward we get warnings on flags that contain "_" separators
	cmds.SetGlobalNormalizationFunc(flag.WarnWordSepNormalizeFunc)

	groups := templates.CommandGroups{
		{
			Message: "Basic Commands (Beginner):",
			Commands: []*cobra.Command{
				NewCmdCreate(f, out, err),
				NewCmdExposeService(f, out),
				NewCmdRun(f, in, out, err),
				deprecatedAlias("run-container", NewCmdRun(f, in, out, err)),
				set.NewCmdSet(f, out, err),
			},
		},
		{
			Message: "Basic Commands (Intermediate):",
			Commands: []*cobra.Command{
				NewCmdGet(f, out, err),
				NewCmdExplain(f, out, err),
				NewCmdEdit(f, out, err),
				NewCmdDelete(f, out, err),
			},
		},
		{
			Message: "Deploy Commands:",
			Commands: []*cobra.Command{
				rollout.NewCmdRollout(f, out, err),
				NewCmdRollingUpdate(f, out),
				deprecatedAlias("rollingupdate", NewCmdRollingUpdate(f, out)),
				NewCmdScale(f, out),
				deprecatedAlias("resize", NewCmdScale(f, out)),
				NewCmdAutoscale(f, out),
			},
		},
		{
			Message: "Cluster Management Commands:",
			Commands: []*cobra.Command{
				NewCmdCertificate(f, out),
				NewCmdClusterInfo(f, out),
				deprecatedAlias("clusterinfo", NewCmdClusterInfo(f, out)),
				NewCmdTop(f, out, err),
				NewCmdCordon(f, out),
				NewCmdUncordon(f, out),
				NewCmdDrain(f, out, err),
				NewCmdTaint(f, out),
			},
		},
		{
			Message: "Troubleshooting and Debugging Commands:",
			Commands: []*cobra.Command{
				NewCmdDescribe(f, out, err),
				NewCmdLogs(f, out),
				NewCmdAttach(f, in, out, err),
				NewCmdExec(f, in, out, err),
				NewCmdPortForward(f, out, err),
				NewCmdProxy(f, out),
				NewCmdCp(f, out, err),
				auth.NewCmdAuth(f, out, err),
			},
		},
		{
			Message: "Advanced Commands:",
			Commands: []*cobra.Command{
				NewCmdApply(f, out, err),
				NewCmdPatch(f, out),
				NewCmdReplace(f, out),
				deprecatedAlias("update", NewCmdReplace(f, out)),
				NewCmdConvert(f, out),
			},
		},
		{
			Message: "Settings Commands:",
			Commands: []*cobra.Command{
				NewCmdLabel(f, out),
				NewCmdAnnotate(f, out),
				NewCmdCompletion(out, ""),
			},
		},
	}
	groups.Add(cmds)

	filters := []string{
		"options",
		deprecated("kubectl", "delete", cmds, NewCmdStop(f, out)),
	}

	// Hide the "alpha" subcommand if there are no alpha commands in this build.
	alpha := NewCmdAlpha(f, in, out, err)
	if !alpha.HasSubCommands() {
		filters = append(filters, alpha.Name())
	}

	templates.ActsAsRootCommand(cmds, filters, groups...)

	for name, completion := range bash_completion_flags {
		if cmds.Flag(name) != nil {
			if cmds.Flag(name).Annotations == nil {
				cmds.Flag(name).Annotations = map[string][]string{}
			}
			cmds.Flag(name).Annotations[cobra.BashCompCustom] = append(
				cmds.Flag(name).Annotations[cobra.BashCompCustom],
				completion,
			)
		}
	}

	cmds.AddCommand(alpha)
	cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), out, err))
	cmds.AddCommand(NewCmdPlugin(f, in, out, err))
	cmds.AddCommand(NewCmdVersion(f, out))
	cmds.AddCommand(NewCmdApiVersions(f, out))
	cmds.AddCommand(deprecatedAlias("apiversions", NewCmdApiVersions(f, out)))
	cmds.AddCommand(NewCmdOptions(out))

	return cmds
}

func runHelp(cmd *cobra.Command, args []string) {
	cmd.Help()
}

func printDeprecationWarning(command, alias string) {
	glog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", alias, command)
}

// deprecatedAlias is intended to be used to create a "wrapper" command around
// an existing command. The wrapper works the same but prints a deprecation
// message before running. This command is identical functionality.
func deprecatedAlias(deprecatedVersion string, cmd *cobra.Command) *cobra.Command {
	// Have to be careful here because Cobra automatically extracts the name
	// of the command from the .Use field.
	originalName := cmd.Name()

	cmd.Use = deprecatedVersion
	cmd.Deprecated = fmt.Sprintf("use %q instead", originalName)
	cmd.Short = fmt.Sprintf("%s. This command is deprecated, use %q instead", cmd.Short, originalName)
	cmd.Hidden = true
	return cmd
}

// deprecated is similar to deprecatedAlias, but it is used for deprecations
// that are not simple aliases; this command is actually a different
// (deprecated) codepath.
func deprecated(baseName, to string, parent, cmd *cobra.Command) string {
	cmd.Long = fmt.Sprintf("Deprecated: all functionality can be found in \"%s %s\"", baseName, to)
	cmd.Short = fmt.Sprintf("Deprecated: use %s", to)
	parent.AddCommand(cmd)
	return cmd.Name()
}
<|endoftext|>


<commit_before>package project

var (
	description        = "The aws-operator handles Kubernetes clusters running on a Kubernetes cluster inside of AWS."
	gitSHA             = "n/a"
	name        string = "aws-operator"
	source      string = "https://github.com/giantswarm/aws-operator"
	version            = "8.2.2"
)

func Description() string {
	return description
}

func GitSHA() string {
	return gitSHA
}

func Name() string {
	return name
}

func Source() string {
	return source
}

func Version() string {
	return version
}
<commit_msg>set dev version 8.2.3-dev (#2262)<commit_after>package project

var (
	description        = "The aws-operator handles Kubernetes clusters running on a Kubernetes cluster inside of AWS."
	gitSHA             = "n/a"
	name        string = "aws-operator"
	source      string = "https://github.com/giantswarm/aws-operator"
	version            = "8.2.3-dev"
)

func Description() string {
	return description
}

func GitSHA() string {
	return gitSHA
}

func Name() string {
	return name
}

func Source() string {
	return source
}

func Version() string {
	return version
}
<|endoftext|>


<commit_before>package project

var (
	description        = "The aws-operator manages Kubernetes clusters running on AWS."
	gitSHA             = "n/a"
	name        string = "aws-operator"
	source      string = "https://github.com/giantswarm/aws-operator"
	version            = "9.3.1"
)

func Description() string {
	return description
}

func GitSHA() string {
	return gitSHA
}

func Name() string {
	return name
}

func Source() string {
	return source
}

func Version() string {
	return version
}
<commit_msg>Bump version to 9.3.2-dev (#2858)<commit_after>package project

var (
	description        = "The aws-operator manages Kubernetes clusters running on AWS."
	gitSHA             = "n/a"
	name        string = "aws-operator"
	source      string = "https://github.com/giantswarm/aws-operator"
	version            = "9.3.2-dev"
)

func Description() string {
	return description
}

func GitSHA() string {
	return gitSHA
}

func Name() string {
	return name
}

func Source() string {
	return source
}

func Version() string {
	return version
}
<|endoftext|>


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

package publish

import (
	"log"
	"net/http"

	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
)

// WithTransport is a functional option for overriding the default transport
// on a default publisher.
func WithTransport(t http.RoundTripper) Option {
	return func(i *defaultOpener) error {
		i.t = t
		return nil
	}
}

// WithAuth is a functional option for overriding the default authenticator
// on a default publisher.
func WithAuth(auth authn.Authenticator) Option {
	return func(i *defaultOpener) error {
		i.auth = auth
		return nil
	}
}

// WithAuthFromKeychain is a functional option for overriding the default
// authenticator on a default publisher using an authn.Keychain
func WithAuthFromKeychain(keys authn.Keychain) Option {
	return func(i *defaultOpener) error {
		// We parse this lazily because it is a repository prefix, which
		// means that docker.io/mattmoor actually gets interpreted as
		// docker.io/library/mattmoor, which gets tricky when we start
		// appending things to it in the publisher.
		repo, err := name.NewRepository(i.base)
		if err != nil {
			// Workaround for localhost:5000 as KO_DOCKER_REPO.
			reg, err := name.NewRegistry(i.base)
			if err != nil {
				return err
			}
			repo = name.Repository{Registry: reg}
		}
		auth, err := keys.Resolve(repo.Registry)
		if err != nil {
			return err
		}
		if auth == authn.Anonymous {
			log.Println("No matching credentials were found, falling back on anonymous")
		}
		i.auth = auth
		return nil
	}
}

// WithNamer is a functional option for overriding the image naming behavior
// in our default publisher.
func WithNamer(n Namer) Option {
	return func(i *defaultOpener) error {
		i.namer = n
		return nil
	}
}

// WithTags is a functional option for overriding the image tags
func WithTags(tags []string) Option {
	return func(i *defaultOpener) error {
		i.tags = tags
		return nil
	}
}

func Insecure(b bool) Option {
	return func(i *defaultOpener) error {
		i.insecure = b
		return nil
	}
}
<commit_msg>Rework parsing logic (#138)<commit_after>// Copyright 2018 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package publish

import (
	"log"
	"net/http"
	"path"

	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
)

// WithTransport is a functional option for overriding the default transport
// on a default publisher.
func WithTransport(t http.RoundTripper) Option {
	return func(i *defaultOpener) error {
		i.t = t
		return nil
	}
}

// WithAuth is a functional option for overriding the default authenticator
// on a default publisher.
func WithAuth(auth authn.Authenticator) Option {
	return func(i *defaultOpener) error {
		i.auth = auth
		return nil
	}
}

// WithAuthFromKeychain is a functional option for overriding the default
// authenticator on a default publisher using an authn.Keychain
func WithAuthFromKeychain(keys authn.Keychain) Option {
	return func(i *defaultOpener) error {
		// We parse this lazily because it is a repository prefix, which
		// means that docker.io/mattmoor actually gets interpreted as
		// docker.io/library/mattmoor, which gets tricky when we start
		// appending things to it in the publisher.
		//
		// We append a fake path "ko" to KO_DOCKER_REPO in order to
		// make parsing out the registry easier.
		repo, err := name.NewRepository(path.Join(i.base, "ko"))
		if err != nil {
			return err
		}
		auth, err := keys.Resolve(repo.Registry)
		if err != nil {
			return err
		}
		if auth == authn.Anonymous {
			log.Println("No matching credentials were found, falling back on anonymous")
		}
		i.auth = auth
		return nil
	}
}

// WithNamer is a functional option for overriding the image naming behavior
// in our default publisher.
func WithNamer(n Namer) Option {
	return func(i *defaultOpener) error {
		i.namer = n
		return nil
	}
}

// WithTags is a functional option for overriding the image tags
func WithTags(tags []string) Option {
	return func(i *defaultOpener) error {
		i.tags = tags
		return nil
	}
}

func Insecure(b bool) Option {
	return func(i *defaultOpener) error {
		i.insecure = b
		return nil
	}
}
<|endoftext|>


<commit_before>package resource

type Source struct {
	Username         string `json:"username"`
	APIKey           string `json:"api_key"`
	AuthURL          string `json:"auth_url"`
	Domain           string `json:"domain"`
	Tenant           string `json:"tenant"`
	TenantID         string `json:"tenant_id"`
	Container        string `json:"container"`
	Regex            string `json:"regex"`
	DisableTLSVerify bool   `json:"disable_tls_verify"`
}

type Version struct {
	Path string `json:"path,omitempty"`
}

type Metadata struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type CheckRequest struct {
	Resource Source  `json:"source"`
	Version  Version `json:"version"`
}

type InRequest struct {
	Resource Source  `json:"source"`
	Version  Version `json:"version"`
}

type InResponse struct {
	Version  Version    `json:"version"`
	Metadata []Metadata `json:"metadata"`
}

type OutParams struct {
	From             string `json:"from"`
	SegmentContainer string `json:"SegmentContainer"`
	SegmentSize      int64  `json:"SegmentSize"`
}

type OutRequest struct {
	Resource Source    `json:"source"`
	Params   OutParams `json:"params"`
}

type OutResponse struct {
	Version  Version    `json:"version"`
	Metadata []Metadata `json:"metadata"`
}
<commit_msg>changed to lowercase<commit_after>package resource

type Source struct {
	Username         string `json:"username"`
	APIKey           string `json:"api_key"`
	AuthURL          string `json:"auth_url"`
	Domain           string `json:"domain"`
	Tenant           string `json:"tenant"`
	TenantID         string `json:"tenant_id"`
	Container        string `json:"container"`
	Regex            string `json:"regex"`
	DisableTLSVerify bool   `json:"disable_tls_verify"`
}

type Version struct {
	Path string `json:"path,omitempty"`
}

type Metadata struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type CheckRequest struct {
	Resource Source  `json:"source"`
	Version  Version `json:"version"`
}

type InRequest struct {
	Resource Source  `json:"source"`
	Version  Version `json:"version"`
}

type InResponse struct {
	Version  Version    `json:"version"`
	Metadata []Metadata `json:"metadata"`
}

type OutParams struct {
	From             string `json:"from"`
	SegmentContainer string `json:"segment_container"`
	SegmentSize      int64  `json:"segment_size"`
}

type OutRequest struct {
	Resource Source    `json:"source"`
	Params   OutParams `json:"params"`
}

type OutResponse struct {
	Version  Version    `json:"version"`
	Metadata []Metadata `json:"metadata"`
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package util

import (
	"bytes"
	"fmt"
	"strings"
	"sync"
	"testing"

	"github.com/pkg/errors"
)

// Returns a function that will return n errors, then return successfully forever.
func errorGenerator(n int, retryable bool) func() error {
	errorCount := 0
	return func() (err error) {
		if errorCount < n {
			errorCount++
			e := errors.New("Error")
			if retryable {
				return &RetriableError{Err: e}
			}
			return e

		}

		return nil
	}
}

func TestErrorGenerator(t *testing.T) {
	errors := 3
	f := errorGenerator(errors, false)
	for i := 0; i < errors-1; i++ {
		if err := f(); err == nil {
			t.Fatalf("Error should have been thrown at iteration %v", i)
		}
	}
	if err := f(); err == nil {
		t.Fatalf("Error should not have been thrown this call!")
	}
}

func TestRetry(t *testing.T) {
	f := errorGenerator(4, true)
	if err := Retry(5, f); err != nil {
		t.Fatalf("Error should not have been raised by retry.")
	}

	f = errorGenerator(5, true)
	if err := Retry(4, f); err == nil {
		t.Fatalf("Error should have been raised by retry.")
	}
}

func TestRetryNotRetriableError(t *testing.T) {
	f := errorGenerator(4, false)
	if err := Retry(5, f); err == nil {
		t.Fatalf("Error should have been raised by retry.")
	}

	f = errorGenerator(5, false)
	if err := Retry(4, f); err == nil {
		t.Fatalf("Error should have been raised by retry.")
	}
	f = errorGenerator(0, false)
	if err := Retry(5, f); err != nil {
		t.Fatalf("Error should not have been raised by retry.")
	}
}

type getTestArgs struct {
	input         string
	expected      string
	expectedError bool
}

func TestMultiError(t *testing.T) {
	m := MultiError{}

	m.Collect(errors.New("Error 1"))
	m.Collect(errors.New("Error 2"))

	err := m.ToError()
	expected := `Error 1
Error 2`
	if err.Error() != expected {
		t.Fatalf("%s != %s", err.Error(), expected)
	}

	m = MultiError{}
	if err := m.ToError(); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
}

func TestGetBinaryDownloadURL(t *testing.T) {
	testData := []struct {
		version     string
		platform    string
		expectedURL string
	}{
		{"v0.0.1", "linux", "https://storage.googleapis.com/minikube/releases/v0.0.1/minikube-linux-amd64"},
		{"v0.0.1", "darwin", "https://storage.googleapis.com/minikube/releases/v0.0.1/minikube-darwin-amd64"},
		{"v0.0.1", "windows", "https://storage.googleapis.com/minikube/releases/v0.0.1/minikube-windows-amd64.exe"},
	}

	for _, tt := range testData {
		url := GetBinaryDownloadURL(tt.version, tt.platform)
		if url != tt.expectedURL {
			t.Fatalf("Expected '%s' but got '%s'", tt.expectedURL, url)
		}
	}

}

func TestTeePrefix(t *testing.T) {
	var in bytes.Buffer
	var out bytes.Buffer
	var logged strings.Builder

	logSink := func(format string, args ...interface{}) {
		logged.WriteString("(" + fmt.Sprintf(format, args...) + ")")
	}

	// Simulate the primary use case: tee in the background. This also helps avoid I/O races.
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		if err := TeePrefix(":", &in, &out, logSink); err != nil {
			t.Errorf("TeePrefix: %v", err)
		}
		wg.Done()
	}()

	in.Write([]byte("goo"))
	in.Write([]byte("\n"))
	in.Write([]byte("g\r\n\r\n"))
	in.Write([]byte("le"))
	wg.Wait()

	gotBytes := out.Bytes()
	wantBytes := []byte("goo\ng\r\n\r\nle")
	if !bytes.Equal(gotBytes, wantBytes) {
		t.Errorf("output=%q, want: %q", gotBytes, wantBytes)
	}

	gotLog := logged.String()
	wantLog := "(:goo)(:g)(:le)"
	if gotLog != wantLog {
		t.Errorf("log=%q, want: %q", gotLog, wantLog)
	}
}

func TestReplaceChars(t *testing.T) {
	testData := []struct {
		src         []string
		replacer    *strings.Replacer
		expectedRes []string
	}{
		{[]string{"abc%def", "%Y%"}, strings.NewReplacer("%", "X"), []string{"abcXdef", "XYX"}},
	}

	for _, tt := range testData {
		res := ReplaceChars(tt.src, tt.replacer)
		for i, val := range res {
			if val != tt.expectedRes[i] {
				t.Fatalf("Expected '%s' but got '%s'", tt.expectedRes, res)
			}
		}
	}
}

func TestConcatStrings(t *testing.T) {
	testData := []struct {
		src         []string
		prefix      string
		postfix     string
		expectedRes []string
	}{
		{[]string{"abc", ""}, "xx", "yy", []string{"xxabcyy", "xxyy"}},
		{[]string{"abc", ""}, "", "", []string{"abc", ""}},
	}

	for _, tt := range testData {
		res := ConcatStrings(tt.src, tt.prefix, tt.postfix)
		for i, val := range res {
			if val != tt.expectedRes[i] {
				t.Fatalf("Expected '%s' but got '%s'", tt.expectedRes, res)
			}
		}
	}
}
<commit_msg>Remove unused getTestArgs struct<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package util

import (
	"bytes"
	"fmt"
	"strings"
	"sync"
	"testing"

	"github.com/pkg/errors"
)

// Returns a function that will return n errors, then return successfully forever.
func errorGenerator(n int, retryable bool) func() error {
	errorCount := 0
	return func() (err error) {
		if errorCount < n {
			errorCount++
			e := errors.New("Error")
			if retryable {
				return &RetriableError{Err: e}
			}
			return e

		}

		return nil
	}
}

func TestErrorGenerator(t *testing.T) {
	errors := 3
	f := errorGenerator(errors, false)
	for i := 0; i < errors-1; i++ {
		if err := f(); err == nil {
			t.Fatalf("Error should have been thrown at iteration %v", i)
		}
	}
	if err := f(); err == nil {
		t.Fatalf("Error should not have been thrown this call!")
	}
}

func TestRetry(t *testing.T) {
	f := errorGenerator(4, true)
	if err := Retry(5, f); err != nil {
		t.Fatalf("Error should not have been raised by retry.")
	}

	f = errorGenerator(5, true)
	if err := Retry(4, f); err == nil {
		t.Fatalf("Error should have been raised by retry.")
	}
}

func TestRetryNotRetriableError(t *testing.T) {
	f := errorGenerator(4, false)
	if err := Retry(5, f); err == nil {
		t.Fatalf("Error should have been raised by retry.")
	}

	f = errorGenerator(5, false)
	if err := Retry(4, f); err == nil {
		t.Fatalf("Error should have been raised by retry.")
	}
	f = errorGenerator(0, false)
	if err := Retry(5, f); err != nil {
		t.Fatalf("Error should not have been raised by retry.")
	}
}

func TestMultiError(t *testing.T) {
	m := MultiError{}

	m.Collect(errors.New("Error 1"))
	m.Collect(errors.New("Error 2"))

	err := m.ToError()
	expected := `Error 1
Error 2`
	if err.Error() != expected {
		t.Fatalf("%s != %s", err.Error(), expected)
	}

	m = MultiError{}
	if err := m.ToError(); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
}

func TestGetBinaryDownloadURL(t *testing.T) {
	testData := []struct {
		version     string
		platform    string
		expectedURL string
	}{
		{"v0.0.1", "linux", "https://storage.googleapis.com/minikube/releases/v0.0.1/minikube-linux-amd64"},
		{"v0.0.1", "darwin", "https://storage.googleapis.com/minikube/releases/v0.0.1/minikube-darwin-amd64"},
		{"v0.0.1", "windows", "https://storage.googleapis.com/minikube/releases/v0.0.1/minikube-windows-amd64.exe"},
	}

	for _, tt := range testData {
		url := GetBinaryDownloadURL(tt.version, tt.platform)
		if url != tt.expectedURL {
			t.Fatalf("Expected '%s' but got '%s'", tt.expectedURL, url)
		}
	}

}

func TestTeePrefix(t *testing.T) {
	var in bytes.Buffer
	var out bytes.Buffer
	var logged strings.Builder

	logSink := func(format string, args ...interface{}) {
		logged.WriteString("(" + fmt.Sprintf(format, args...) + ")")
	}

	// Simulate the primary use case: tee in the background. This also helps avoid I/O races.
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		if err := TeePrefix(":", &in, &out, logSink); err != nil {
			t.Errorf("TeePrefix: %v", err)
		}
		wg.Done()
	}()

	in.Write([]byte("goo"))
	in.Write([]byte("\n"))
	in.Write([]byte("g\r\n\r\n"))
	in.Write([]byte("le"))
	wg.Wait()

	gotBytes := out.Bytes()
	wantBytes := []byte("goo\ng\r\n\r\nle")
	if !bytes.Equal(gotBytes, wantBytes) {
		t.Errorf("output=%q, want: %q", gotBytes, wantBytes)
	}

	gotLog := logged.String()
	wantLog := "(:goo)(:g)(:le)"
	if gotLog != wantLog {
		t.Errorf("log=%q, want: %q", gotLog, wantLog)
	}
}

func TestReplaceChars(t *testing.T) {
	testData := []struct {
		src         []string
		replacer    *strings.Replacer
		expectedRes []string
	}{
		{[]string{"abc%def", "%Y%"}, strings.NewReplacer("%", "X"), []string{"abcXdef", "XYX"}},
	}

	for _, tt := range testData {
		res := ReplaceChars(tt.src, tt.replacer)
		for i, val := range res {
			if val != tt.expectedRes[i] {
				t.Fatalf("Expected '%s' but got '%s'", tt.expectedRes, res)
			}
		}
	}
}

func TestConcatStrings(t *testing.T) {
	testData := []struct {
		src         []string
		prefix      string
		postfix     string
		expectedRes []string
	}{
		{[]string{"abc", ""}, "xx", "yy", []string{"xxabcyy", "xxyy"}},
		{[]string{"abc", ""}, "", "", []string{"abc", ""}},
	}

	for _, tt := range testData {
		res := ConcatStrings(tt.src, tt.prefix, tt.postfix)
		for i, val := range res {
			if val != tt.expectedRes[i] {
				t.Fatalf("Expected '%s' but got '%s'", tt.expectedRes, res)
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

// Package version represents the current version of the project.
package version // import "k8s.io/helm/pkg/version"

// Version is the current version of the Helm.
// Update this whenever making a new release.
// The version is of the format Major.Minor.Patch
// Increment major number for new feature additions and behavioral changes.
// Increment minor number for bug fixes and performance enhancements.
// Increment patch number for critical fixes to existing releases.
var Version = "v2.0.0-alpha.2"
<commit_msg>chore(*): bump version to v2.0.0-alpha.3<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

// Package version represents the current version of the project.
package version // import "k8s.io/helm/pkg/version"

// Version is the current version of the Helm.
// Update this whenever making a new release.
// The version is of the format Major.Minor.Patch
// Increment major number for new feature additions and behavioral changes.
// Increment minor number for bug fixes and performance enhancements.
// Increment patch number for critical fixes to existing releases.
var Version = "v2.0.0-alpha.3"
<|endoftext|>


<commit_before>package plans

import (
	"github.com/zclconf/go-cty/cty"
	ctymsgpack "github.com/zclconf/go-cty/cty/msgpack"
)

// DynamicValue is the representation in the plan of a value whose type cannot
// be determined at compile time, such as because it comes from a schema
// defined in a plugin.
//
// This type is used as an indirection so that the overall plan structure can
// be decoded without schema available, and then the dynamic values accessed
// at a later time once the appropriate schema has been determined.
//
// Internally, DynamicValue is a serialized version of a cty.Value created
// against a particular type constraint. Callers should not access directly
// the serialized form, whose format may change in future. Values of this
// type must always be created by calling NewDynamicValue.
//
// The zero value of DynamicValue is nil, and represents the absense of a
// value within the Go type system. This is distinct from a cty.NullVal
// result, which represents the absense of a value within the cty type system.
type DynamicValue []byte

// NewDynamicValue creates a DynamicValue by serializing the given value
// against the given type constraint. The value must conform to the type
// constraint, or the result is undefined.
//
// If the value to be encoded has no predefined schema (for example, for
// module output values and input variables), set the type constraint to
// cty.DynamicPseudoType in order to save type information as part of the
// value, and then also pass cty.DynamicPseudoType to method Decode to recover
// the original value.
//
// cty.NilVal can be used to represent the absense of a value, but callers
// must be careful to distinguish values that are absent at the Go layer
// (cty.NilVal) vs. values that are absent at the cty layer (cty.NullVal
// results).
func NewDynamicValue(val cty.Value, ty cty.Type) (DynamicValue, error) {
	// If we're given cty.NilVal (the zero value of cty.Value, which is
	// distinct from a typed null value created by cty.NullVal) then we'll
	// assume the caller is trying to represent the _absense_ of a value,
	// and so we'll return a nil DynamicValue.
	if val == cty.NilVal {
		return DynamicValue(nil), nil
	}

	// Currently our internal encoding is msgpack, via ctymsgpack.
	buf, err := ctymsgpack.Marshal(val, ty)
	if err != nil {
		return nil, err
	}

	return DynamicValue(buf), nil
}

// Decode retrieves the effective value from the receiever by interpreting the
// serialized form against the given type constraint. For correct results,
// the type constraint must match (or be consistent with) the one that was
// used to create the receiver.
//
// A nil DynamicValue decodes to cty.NilVal, which is not a valid value and
// instead represents the absense of a value.
func (v DynamicValue) Decode(ty cty.Type) (cty.Value, error) {
	if v == nil {
		return cty.NilVal, nil
	}

	return ctymsgpack.Unmarshal([]byte(v), ty)
}

// ImpliedType returns the type implied by the serialized structure of the
// receiving value.
//
// This will not necessarily be exactly the type that was given when the
// value was encoded, and in particular must not be used for values that
// were encoded with their static type given as cty.DynamicPseudoType.
// It is however safe to use this method for values that were encoded using
// their runtime type as the conforming type, with the result being
// semantically equivalent but with all lists and sets represented as tuples,
// and maps as objects, due to ambiguities of the serialization.
func (v DynamicValue) ImpliedType() (cty.Type, error) {
	return ctymsgpack.ImpliedType([]byte(v))
}

// Copy produces a copy of the receiver with a distinct backing array.
func (v DynamicValue) Copy() DynamicValue {
	if len(v) == 0 {
		return nil
	}

	ret := make(DynamicValue, len(v))
	copy(ret, v)
	return ret
}
<commit_msg>don't convert empty DynamicValue to nil<commit_after>package plans

import (
	"github.com/zclconf/go-cty/cty"
	ctymsgpack "github.com/zclconf/go-cty/cty/msgpack"
)

// DynamicValue is the representation in the plan of a value whose type cannot
// be determined at compile time, such as because it comes from a schema
// defined in a plugin.
//
// This type is used as an indirection so that the overall plan structure can
// be decoded without schema available, and then the dynamic values accessed
// at a later time once the appropriate schema has been determined.
//
// Internally, DynamicValue is a serialized version of a cty.Value created
// against a particular type constraint. Callers should not access directly
// the serialized form, whose format may change in future. Values of this
// type must always be created by calling NewDynamicValue.
//
// The zero value of DynamicValue is nil, and represents the absense of a
// value within the Go type system. This is distinct from a cty.NullVal
// result, which represents the absense of a value within the cty type system.
type DynamicValue []byte

// NewDynamicValue creates a DynamicValue by serializing the given value
// against the given type constraint. The value must conform to the type
// constraint, or the result is undefined.
//
// If the value to be encoded has no predefined schema (for example, for
// module output values and input variables), set the type constraint to
// cty.DynamicPseudoType in order to save type information as part of the
// value, and then also pass cty.DynamicPseudoType to method Decode to recover
// the original value.
//
// cty.NilVal can be used to represent the absense of a value, but callers
// must be careful to distinguish values that are absent at the Go layer
// (cty.NilVal) vs. values that are absent at the cty layer (cty.NullVal
// results).
func NewDynamicValue(val cty.Value, ty cty.Type) (DynamicValue, error) {
	// If we're given cty.NilVal (the zero value of cty.Value, which is
	// distinct from a typed null value created by cty.NullVal) then we'll
	// assume the caller is trying to represent the _absense_ of a value,
	// and so we'll return a nil DynamicValue.
	if val == cty.NilVal {
		return DynamicValue(nil), nil
	}

	// Currently our internal encoding is msgpack, via ctymsgpack.
	buf, err := ctymsgpack.Marshal(val, ty)
	if err != nil {
		return nil, err
	}

	return DynamicValue(buf), nil
}

// Decode retrieves the effective value from the receiever by interpreting the
// serialized form against the given type constraint. For correct results,
// the type constraint must match (or be consistent with) the one that was
// used to create the receiver.
//
// A nil DynamicValue decodes to cty.NilVal, which is not a valid value and
// instead represents the absense of a value.
func (v DynamicValue) Decode(ty cty.Type) (cty.Value, error) {
	if v == nil {
		return cty.NilVal, nil
	}

	return ctymsgpack.Unmarshal([]byte(v), ty)
}

// ImpliedType returns the type implied by the serialized structure of the
// receiving value.
//
// This will not necessarily be exactly the type that was given when the
// value was encoded, and in particular must not be used for values that
// were encoded with their static type given as cty.DynamicPseudoType.
// It is however safe to use this method for values that were encoded using
// their runtime type as the conforming type, with the result being
// semantically equivalent but with all lists and sets represented as tuples,
// and maps as objects, due to ambiguities of the serialization.
func (v DynamicValue) ImpliedType() (cty.Type, error) {
	return ctymsgpack.ImpliedType([]byte(v))
}

// Copy produces a copy of the receiver with a distinct backing array.
func (v DynamicValue) Copy() DynamicValue {
	if v == nil {
		return nil
	}

	ret := make(DynamicValue, len(v))
	copy(ret, v)
	return ret
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcd

import (
	"container/list"
	"fmt"
	"github.com/FactomProject/FactomCode/common"
	cp "github.com/FactomProject/FactomCode/controlpanel"
	"github.com/FactomProject/FactomCode/util"
	"github.com/FactomProject/btcd/wire"
	"github.com/FactomProject/go-spew/spew"
	"sync/atomic"
)

// dirBlockMsg packages a directory block message and the peer it came from together
// so the block handler has access to that information.
type dirBlockMsg struct {
	block *common.DirectoryBlock
	peer  *peer
}

// dirInvMsg packages a dir block inv message and the peer it came from together
// so the block handler has access to that information.
type dirInvMsg struct {
	inv  *wire.MsgDirInv
	peer *peer
}

// handleDirInvMsg handles dir inv messages from all peers.
// We examine the inventory advertised by the remote peer and act accordingly.
func (b *blockManager) handleDirInvMsg(imsg *dirInvMsg) {
	bmgrLog.Debug("handleDirInvMsg: ", spew.Sdump(imsg))

	// Ignore invs from peers that aren't the sync if we are not current.
	// Helps prevent fetching a mass of orphans.
	if imsg.peer != b.syncPeer && !b.current() {
		return
	}

	// Attempt to find the final block in the inventory list.  There may
	// not be one.
	lastBlock := -1
	invVects := imsg.inv.InvList
	bmgrLog.Debugf("len(InvVects)=%d", len(invVects))
	for i := len(invVects) - 1; i >= 0; i-- {
		if invVects[i].Type == wire.InvTypeFactomDirBlock {
			lastBlock = i
			bmgrLog.Debugf("lastBlock=%d", lastBlock)
			break
		}
	}

	// Request the advertised inventory if we don't already have it.  Also,
	// request parent blocks of orphans if we receive one we already have.
	// Finally, attempt to detect potential stalls due to long side chains
	// we already have and request more blocks to prevent them.
	for i, iv := range invVects {
		// Ignore unsupported inventory types.
		if iv.Type != wire.InvTypeFactomDirBlock { //} && iv.Type != wire.InvTypeTx {
			continue
		}

		// Add the inventory to the cache of known inventory
		// for the peer.
		imsg.peer.AddKnownInventory(iv)

		// Request the inventory if we don't already have it.
		haveInv, err := b.haveInventory(iv)
		if err != nil {
			bmgrLog.Warnf("Unexpected failure when checking for "+
				"existing inventory during inv message "+
				"processing: %v", err)
			continue
		}
		if !haveInv {
			// Add it to the request queue.
			imsg.peer.requestQueue = append(imsg.peer.requestQueue, iv)
			continue
		}

		if iv.Type == wire.InvTypeFactomDirBlock {

			// We already have the final block advertised by this
			// inventory message, so force a request for more.  This
			// should only happen if we're on a really long side
			// chain.
			if i == lastBlock {
				// Request blocks after this one up to the
				// final one the remote peer knows about (zero
				// stop hash).
				bmgrLog.Debug("push for more dir blocks: PushGetDirBlocksMsg")
				locator := DirBlockLocatorFromHash(&iv.Hash)
				imsg.peer.PushGetDirBlocksMsg(locator, &zeroHash)
			}
		}
	}

	// Request as much as possible at once.  Anything that won't fit into
	// the request will be requested on the next inv message.
	numRequested := 0
	gdmsg := wire.NewMsgGetDirData()
	requestQueue := imsg.peer.requestQueue
	for len(requestQueue) != 0 {
		iv := requestQueue[0]
		requestQueue[0] = nil
		requestQueue = requestQueue[1:]

		switch iv.Type {
		case wire.InvTypeFactomDirBlock:
			// Request the block if there is not already a pending
			// request.
			if _, exists := b.requestedBlocks[iv.Hash]; !exists {
				b.requestedBlocks[iv.Hash] = struct{}{}
				imsg.peer.requestedBlocks[iv.Hash] = struct{}{}
				gdmsg.AddInvVect(iv)
				numRequested++
			}

		case wire.InvTypeTx:
			// Request the transaction if there is not already a
			// pending request.
			if _, exists := b.requestedTxns[iv.Hash]; !exists {
				b.requestedTxns[iv.Hash] = struct{}{}
				imsg.peer.requestedTxns[iv.Hash] = struct{}{}
				gdmsg.AddInvVect(iv)
				numRequested++
			}
		}

		if numRequested >= wire.MaxInvPerMsg {
			break
		}
	}
	imsg.peer.requestQueue = requestQueue
	if len(gdmsg.InvList) > 0 {
		imsg.peer.QueueMessage(gdmsg, nil)
	}
}

// QueueDirBlock adds the passed GetDirBlocks message and peer to the block handling queue.
func (b *blockManager) QueueDirBlock(msg *wire.MsgDirBlock, p *peer) {
	// Don't accept more blocks if we're shutting down.
	if atomic.LoadInt32(&b.shutdown) != 0 {
		p.blockProcessed <- struct{}{}
		return
	}

	b.msgChan <- &dirBlockMsg{block: msg.DBlk, peer: p}
}

// QueueDirInv adds the passed inv message and peer to the block handling queue.
func (b *blockManager) QueueDirInv(inv *wire.MsgDirInv, p *peer) {
	// No channel handling here because peers do not need to block on inv
	// messages.
	if atomic.LoadInt32(&b.shutdown) != 0 {
		return
	}

	b.msgChan <- &dirInvMsg{inv: inv, peer: p}
}

// startSyncFactom will choose the best peer among the available candidate peers to
// download/sync the blockchain from.  When syncing is already running, it
// simply returns.  It also examines the candidates for any which are no longer
// candidates and removes them as needed.
func (b *blockManager) startSyncFactom(peers *list.List) {
	// Return now if we're already syncing.
	if b.syncPeer != nil {
		return
	}

	// Find the height of the current known best block.
	_, height, err := db.FetchBlockHeightCache()
	if err != nil {
		bmgrLog.Errorf("%v", err)
		return
	}

	bmgrLog.Infof("Latest DirBlock Height: %d", height)

	var bestPeer *peer
	var enext *list.Element
	for e := peers.Front(); e != nil; e = enext {
		enext = e.Next()
		p := e.Value.(*peer)

		// Remove sync candidate peers that are no longer candidates due
		// to passing their latest known block.  NOTE: The < is
		// intentional as opposed to <=.  While techcnically the peer
		// doesn't have a later block when it's equal, it will likely
		// have one soon so it is a reasonable choice.  It also allows
		// the case where both are at 0 such as during regression test.
		if p.lastBlock < int32(height) {
			peers.Remove(e)
			continue
		}

		// TODO(davec): Use a better algorithm to choose the best peer.
		// For now, just pick the first available candidate.
		bestPeer = p
	}

	// Start syncing from the best peer if one was selected.
	if bestPeer != nil {
		locator, err := LatestDirBlockLocator()
		if err != nil {
			bmgrLog.Errorf("Failed to get block locator for the "+
				"latest block: %v", err)
			return
		}

		bmgrLog.Infof("LatestDirBlockLocator: %s", spew.Sdump(locator))

		str := fmt.Sprintf("At %d: syncing to block height %d from peer %v",
			height, bestPeer.lastBlock, bestPeer.addr)
		bmgrLog.Infof(str)

		cp.CP.AddUpdate(
			"Syncing", // tag
			"status",  // Category
			"Client is Syncing with Federated Server(s)", // Title
			str, // Message
			60)

		bestPeer.PushGetDirBlocksMsg(locator, &zeroHash)
		b.syncPeer = bestPeer
	} else {
		bmgrLog.Warnf("No sync peer candidates available")
	}
}

// isSyncCandidateFactom returns whether or not the peer is a candidate to consider
// syncing from.
func (b *blockManager) isSyncCandidateFactom(p *peer) bool {
	// Typically a peer is not a candidate for sync if it's not a Factom SERVER node,
	if common.SERVER_NODE == util.ReadConfig().App.NodeMode {
		return true
	}
	return true
}
<commit_msg>Added a workaround for the spew error.<commit_after>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcd

import (
	"container/list"
	"fmt"
	"github.com/FactomProject/FactomCode/common"
	cp "github.com/FactomProject/FactomCode/controlpanel"
	"github.com/FactomProject/FactomCode/util"
	"github.com/FactomProject/btcd/wire"
	"github.com/FactomProject/go-spew/spew"
	"sync/atomic"
)

// dirBlockMsg packages a directory block message and the peer it came from together
// so the block handler has access to that information.
type dirBlockMsg struct {
	block *common.DirectoryBlock
	peer  *peer
}

// dirInvMsg packages a dir block inv message and the peer it came from together
// so the block handler has access to that information.
type dirInvMsg struct {
	inv  *wire.MsgDirInv
	peer *peer
}

// handleDirInvMsg handles dir inv messages from all peers.
// We examine the inventory advertised by the remote peer and act accordingly.
func (b *blockManager) handleDirInvMsg(imsg *dirInvMsg) {
	bmgrLog.Debug("handleDirInvMsg: ", spew.Sdump(imsg.inv))

	// Ignore invs from peers that aren't the sync if we are not current.
	// Helps prevent fetching a mass of orphans.
	if imsg.peer != b.syncPeer && !b.current() {
		return
	}

	// Attempt to find the final block in the inventory list.  There may
	// not be one.
	lastBlock := -1
	invVects := imsg.inv.InvList
	bmgrLog.Debugf("len(InvVects)=%d", len(invVects))
	for i := len(invVects) - 1; i >= 0; i-- {
		if invVects[i].Type == wire.InvTypeFactomDirBlock {
			lastBlock = i
			bmgrLog.Debugf("lastBlock=%d", lastBlock)
			break
		}
	}

	// Request the advertised inventory if we don't already have it.  Also,
	// request parent blocks of orphans if we receive one we already have.
	// Finally, attempt to detect potential stalls due to long side chains
	// we already have and request more blocks to prevent them.
	for i, iv := range invVects {
		// Ignore unsupported inventory types.
		if iv.Type != wire.InvTypeFactomDirBlock { //} && iv.Type != wire.InvTypeTx {
			continue
		}

		// Add the inventory to the cache of known inventory
		// for the peer.
		imsg.peer.AddKnownInventory(iv)

		// Request the inventory if we don't already have it.
		haveInv, err := b.haveInventory(iv)
		if err != nil {
			bmgrLog.Warnf("Unexpected failure when checking for "+
				"existing inventory during inv message "+
				"processing: %v", err)
			continue
		}
		if !haveInv {
			// Add it to the request queue.
			imsg.peer.requestQueue = append(imsg.peer.requestQueue, iv)
			continue
		}

		if iv.Type == wire.InvTypeFactomDirBlock {

			// We already have the final block advertised by this
			// inventory message, so force a request for more.  This
			// should only happen if we're on a really long side
			// chain.
			if i == lastBlock {
				// Request blocks after this one up to the
				// final one the remote peer knows about (zero
				// stop hash).
				bmgrLog.Debug("push for more dir blocks: PushGetDirBlocksMsg")
				locator := DirBlockLocatorFromHash(&iv.Hash)
				imsg.peer.PushGetDirBlocksMsg(locator, &zeroHash)
			}
		}
	}

	// Request as much as possible at once.  Anything that won't fit into
	// the request will be requested on the next inv message.
	numRequested := 0
	gdmsg := wire.NewMsgGetDirData()
	requestQueue := imsg.peer.requestQueue
	for len(requestQueue) != 0 {
		iv := requestQueue[0]
		requestQueue[0] = nil
		requestQueue = requestQueue[1:]

		switch iv.Type {
		case wire.InvTypeFactomDirBlock:
			// Request the block if there is not already a pending
			// request.
			if _, exists := b.requestedBlocks[iv.Hash]; !exists {
				b.requestedBlocks[iv.Hash] = struct{}{}
				imsg.peer.requestedBlocks[iv.Hash] = struct{}{}
				gdmsg.AddInvVect(iv)
				numRequested++
			}

		case wire.InvTypeTx:
			// Request the transaction if there is not already a
			// pending request.
			if _, exists := b.requestedTxns[iv.Hash]; !exists {
				b.requestedTxns[iv.Hash] = struct{}{}
				imsg.peer.requestedTxns[iv.Hash] = struct{}{}
				gdmsg.AddInvVect(iv)
				numRequested++
			}
		}

		if numRequested >= wire.MaxInvPerMsg {
			break
		}
	}
	imsg.peer.requestQueue = requestQueue
	if len(gdmsg.InvList) > 0 {
		imsg.peer.QueueMessage(gdmsg, nil)
	}
}

// QueueDirBlock adds the passed GetDirBlocks message and peer to the block handling queue.
func (b *blockManager) QueueDirBlock(msg *wire.MsgDirBlock, p *peer) {
	// Don't accept more blocks if we're shutting down.
	if atomic.LoadInt32(&b.shutdown) != 0 {
		p.blockProcessed <- struct{}{}
		return
	}

	b.msgChan <- &dirBlockMsg{block: msg.DBlk, peer: p}
}

// QueueDirInv adds the passed inv message and peer to the block handling queue.
func (b *blockManager) QueueDirInv(inv *wire.MsgDirInv, p *peer) {
	// No channel handling here because peers do not need to block on inv
	// messages.
	if atomic.LoadInt32(&b.shutdown) != 0 {
		return
	}

	b.msgChan <- &dirInvMsg{inv: inv, peer: p}
}

// startSyncFactom will choose the best peer among the available candidate peers to
// download/sync the blockchain from.  When syncing is already running, it
// simply returns.  It also examines the candidates for any which are no longer
// candidates and removes them as needed.
func (b *blockManager) startSyncFactom(peers *list.List) {
	// Return now if we're already syncing.
	if b.syncPeer != nil {
		return
	}

	// Find the height of the current known best block.
	_, height, err := db.FetchBlockHeightCache()
	if err != nil {
		bmgrLog.Errorf("%v", err)
		return
	}

	bmgrLog.Infof("Latest DirBlock Height: %d", height)

	var bestPeer *peer
	var enext *list.Element
	for e := peers.Front(); e != nil; e = enext {
		enext = e.Next()
		p := e.Value.(*peer)

		// Remove sync candidate peers that are no longer candidates due
		// to passing their latest known block.  NOTE: The < is
		// intentional as opposed to <=.  While techcnically the peer
		// doesn't have a later block when it's equal, it will likely
		// have one soon so it is a reasonable choice.  It also allows
		// the case where both are at 0 such as during regression test.
		if p.lastBlock < int32(height) {
			peers.Remove(e)
			continue
		}

		// TODO(davec): Use a better algorithm to choose the best peer.
		// For now, just pick the first available candidate.
		bestPeer = p
	}

	// Start syncing from the best peer if one was selected.
	if bestPeer != nil {
		locator, err := LatestDirBlockLocator()
		if err != nil {
			bmgrLog.Errorf("Failed to get block locator for the "+
				"latest block: %v", err)
			return
		}

		bmgrLog.Infof("LatestDirBlockLocator: %s", spew.Sdump(locator))

		str := fmt.Sprintf("At %d: syncing to block height %d from peer %v",
			height, bestPeer.lastBlock, bestPeer.addr)
		bmgrLog.Infof(str)

		cp.CP.AddUpdate(
			"Syncing", // tag
			"status",  // Category
			"Client is Syncing with Federated Server(s)", // Title
			str, // Message
			60)

		bestPeer.PushGetDirBlocksMsg(locator, &zeroHash)
		b.syncPeer = bestPeer
	} else {
		bmgrLog.Warnf("No sync peer candidates available")
	}
}

// isSyncCandidateFactom returns whether or not the peer is a candidate to consider
// syncing from.
func (b *blockManager) isSyncCandidateFactom(p *peer) bool {
	// Typically a peer is not a candidate for sync if it's not a Factom SERVER node,
	if common.SERVER_NODE == util.ReadConfig().App.NodeMode {
		return true
	}
	return true
}
<|endoftext|>


<commit_before>package gardenhealth

import (
	"fmt"
	"time"

	"github.com/cloudfoundry-incubator/executor"
	"github.com/cloudfoundry-incubator/executor/depot/gardenstore"
	"github.com/cloudfoundry-incubator/executor/guidgen"
	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/garden/server"
	"github.com/pivotal-golang/lager"
)

const (
	HealthcheckPrefix   = "executor-healthcheck-"
	HealthcheckTag      = "healthcheck-tag"
	HealthcheckTagValue = "healthcheck"
)

type UnrecoverableError string

func (e UnrecoverableError) Error() string {
	return string(e)
}

type HealthcheckFailedError int

func (e HealthcheckFailedError) Error() string {
	return fmt.Sprintf("Healthcheck exited with %d", e)
}

//go:generate counterfeiter -o fakegardenhealth/fake_checker.go . Checker

type Checker interface {
	Healthcheck(lager.Logger) error
}

type checker struct {
	rootFSPath         string
	containerOwnerName string
	retryInterval      time.Duration
	healthcheckSpec    garden.ProcessSpec
	executorClient     executor.Client
	gardenClient       garden.Client
	guidGenerator      guidgen.Generator
}

// NewChecker constructs a checker.
//
// healthcheckSpec describes the process to run in the healthcheck container and
// retryInterval describes the amount of time to wait to sleep when retrying a
// failed garden command.
func NewChecker(
	rootFSPath string,
	containerOwnerName string,
	retryInterval time.Duration,
	healthcheckSpec garden.ProcessSpec,
	gardenClient garden.Client,
	guidGenerator guidgen.Generator,
) Checker {
	return &checker{
		rootFSPath:         rootFSPath,
		containerOwnerName: containerOwnerName,
		retryInterval:      retryInterval,
		healthcheckSpec:    healthcheckSpec,
		gardenClient:       gardenClient,
		guidGenerator:      guidGenerator,
	}
}

// Healthcheck destroys any existing healthcheck containers, creates a new container,
// runs a process in the new container, waits for the process to exit, then destroys
// the created container.
//
// If any of these steps fail, the failed step will be retried
// up to gardenhealth.MaxRetries times. If the command continues to fail after the
// retries, an error will be returned, indicating the healthcheck failed.
func (c *checker) Healthcheck(logger lager.Logger) (healthcheckResult error) {
	logger = logger.Session("healthcheck")
	logger.Debug("starting")

	logger.Debug("attempting-list")
	var containers []garden.Container
	err := RetryOnFail(c.retryInterval, func() (listErr error) {
		containers, listErr = c.gardenClient.Containers(garden.Properties{
			gardenstore.TagPropertyPrefix + HealthcheckTag: HealthcheckTagValue,
		})
		if listErr != nil {
			logger.Debug("failed-list", lager.Data{"error": listErr})
		}
		return listErr
	})

	if err != nil {
		return err
	}
	logger.Debug("list-succeeded")

	logger.Debug("attempting-initial-destroy")
	for i := range containers {
		err = RetryOnFail(c.retryInterval, func() (destroyErr error) {
			handle := containers[i].Handle()
			destroyErr = c.gardenClient.Destroy(handle)
			if destroyErr != nil {
				if destroyErr.Error() == server.ErrConcurrentDestroy.Error() {
					// Log but don't fail if container is already being destroyed
					logger.Debug("already-being-destroyed-during-initial-destroy", lager.Data{"handle": handle})
					return nil
				}

				logger.Debug("failed-initial-destroy", lager.Data{"error": destroyErr})
			}
			return destroyErr
		})

		if err != nil {
			return err
		}
	}
	logger.Debug("initial-destroy-succeeded")

	logger.Debug("attempting-create")
	guid := HealthcheckPrefix + c.guidGenerator.Guid(logger)
	var container garden.Container
	err = RetryOnFail(c.retryInterval, func() (createErr error) {
		container, createErr = c.gardenClient.Create(garden.ContainerSpec{
			Handle:     guid,
			RootFSPath: c.rootFSPath,
			Properties: garden.Properties{
				gardenstore.ContainerOwnerProperty:             c.containerOwnerName,
				gardenstore.TagPropertyPrefix + HealthcheckTag: HealthcheckTagValue,
			},
		})
		if createErr != nil {
			logger.Debug("failed-create", lager.Data{"error": createErr})
		}
		return createErr
	})

	if err != nil {
		return err
	}
	logger.Debug("create-succeeded")

	defer func() {
		err := RetryOnFail(c.retryInterval, func() (destroyErr error) {
			destroyErr = c.destroyContainer(guid)
			if destroyErr != nil {
				if destroyErr.Error() == server.ErrConcurrentDestroy.Error() {
					// Log but don't fail if container is already being destroyed
					logger.Debug("already-being-destroyed-during-cleanup-destroy", lager.Data{"handle": guid})
					return nil
				}
				logger.Debug("failed-cleanup-destroy", lager.Data{"error": destroyErr})
			}
			return destroyErr
		})
		if err != nil {
			healthcheckResult = err
		}

		logger.Debug("finished")
	}()

	logger.Debug("attempting-run", lager.Data{
		"processPath": c.healthcheckSpec.Path,
		"processArgs": c.healthcheckSpec.Args,
		"processUser": c.healthcheckSpec.User,
		"processEnv":  c.healthcheckSpec.Env,
		"processDir":  c.healthcheckSpec.Dir,
	})

	var proc garden.Process
	err = RetryOnFail(c.retryInterval, func() (runErr error) {
		proc, runErr = container.Run(c.healthcheckSpec, garden.ProcessIO{})
		if runErr != nil {
			logger.Debug("failed-run", lager.Data{"error": runErr})
		}
		return runErr
	})

	if err != nil {
		return err
	}
	logger.Debug("run-succeeded")

	logger.Debug("attempting-wait")
	var exitCode int
	err = RetryOnFail(c.retryInterval, func() (waitErr error) {
		exitCode, waitErr = proc.Wait()
		if waitErr != nil {
			logger.Debug("failed-wait", lager.Data{"error": waitErr})
		}
		return waitErr
	})
	if err != nil {
		return err
	}

	if exitCode != 0 {
		return HealthcheckFailedError(exitCode)
	}

	logger.Debug("wait-succeeded")

	return nil
}

func (c *checker) destroyContainer(guid string) error {
	err := c.gardenClient.Destroy(guid)
	switch err.(type) {
	case nil:
		return nil
	case garden.ContainerNotFoundError:
		return err
	default:
		return UnrecoverableError(err.Error())
	}
}

const (
	MaxRetries = 3
)

func RetryOnFail(retryInterval time.Duration, cmd func() error) error {
	var err error

	for i := 0; i < MaxRetries; i++ {
		err = cmd()
		if err == nil {
			return nil
		}

		time.Sleep(retryInterval)
	}

	return err
}
<commit_msg>Refactor healthcheck for better logging<commit_after>package gardenhealth

import (
	"fmt"
	"time"

	"github.com/cloudfoundry-incubator/executor"
	"github.com/cloudfoundry-incubator/executor/depot/gardenstore"
	"github.com/cloudfoundry-incubator/executor/guidgen"
	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/garden/server"
	"github.com/pivotal-golang/lager"
)

const (
	HealthcheckPrefix   = "executor-healthcheck-"
	HealthcheckTag      = "healthcheck-tag"
	HealthcheckTagValue = "healthcheck"
)

type UnrecoverableError string

func (e UnrecoverableError) Error() string {
	return string(e)
}

type HealthcheckFailedError int

func (e HealthcheckFailedError) Error() string {
	return fmt.Sprintf("Healthcheck exited with %d", e)
}

//go:generate counterfeiter -o fakegardenhealth/fake_checker.go . Checker

type Checker interface {
	Healthcheck(lager.Logger) error
}

type checker struct {
	rootFSPath         string
	containerOwnerName string
	retryInterval      time.Duration
	healthcheckSpec    garden.ProcessSpec
	executorClient     executor.Client
	gardenClient       garden.Client
	guidGenerator      guidgen.Generator
}

// NewChecker constructs a checker.
//
// healthcheckSpec describes the process to run in the healthcheck container and
// retryInterval describes the amount of time to wait to sleep when retrying a
// failed garden command.
func NewChecker(
	rootFSPath string,
	containerOwnerName string,
	retryInterval time.Duration,
	healthcheckSpec garden.ProcessSpec,
	gardenClient garden.Client,
	guidGenerator guidgen.Generator,
) Checker {
	return &checker{
		rootFSPath:         rootFSPath,
		containerOwnerName: containerOwnerName,
		retryInterval:      retryInterval,
		healthcheckSpec:    healthcheckSpec,
		gardenClient:       gardenClient,
		guidGenerator:      guidGenerator,
	}
}

func (c *checker) list(logger lager.Logger) ([]garden.Container, error) {
	logger = logger.Session("list")
	logger.Debug("starting")
	defer logger.Debug("finished")

	var containers []garden.Container
	err := retryOnFail(c.retryInterval, func(attempt uint) (listErr error) {
		containers, listErr = c.gardenClient.Containers(garden.Properties{
			gardenstore.TagPropertyPrefix + HealthcheckTag: HealthcheckTagValue,
		})
		if listErr != nil {
			logger.Error("failed", listErr, lager.Data{"attempt": attempt})
			return listErr
		}

		logger.Debug("succeeded", lager.Data{"attempt": attempt})
		return nil
	})

	return containers, err
}

func (c *checker) initialDestroy(logger lager.Logger, containers []garden.Container) error {
	logger = logger.Session("initial-destroy")
	logger.Debug("starting", lager.Data{"numContainers": len(containers)})
	defer logger.Debug("finished")

	for i := range containers {
		err := retryOnFail(c.retryInterval, func(attempt uint) (destroyErr error) {
			handle := containers[i].Handle()
			destroyErr = c.gardenClient.Destroy(handle)
			if destroyErr != nil {
				if destroyErr.Error() == server.ErrConcurrentDestroy.Error() {
					// Log but don't fail if container is already being destroyed
					logger.Debug("already-being-destroyed", lager.Data{"handle": handle})
					return nil
				}

				logger.Error("failed", destroyErr, lager.Data{"handle": handle, "attempt": attempt})
				return destroyErr
			}

			logger.Debug("succeeded", lager.Data{"handle": handle, "attempt": attempt})
			return nil
		})

		if err != nil {
			return err
		}
	}

	logger.Debug("succeeded")
	return nil
}

func (c *checker) create(logger lager.Logger) (string, garden.Container, error) {
	logger = logger.Session("create")
	logger.Debug("starting")
	defer logger.Debug("finished")

	guid := HealthcheckPrefix + c.guidGenerator.Guid(logger)
	var container garden.Container
	err := retryOnFail(c.retryInterval, func(attempt uint) (createErr error) {
		container, createErr = c.gardenClient.Create(garden.ContainerSpec{
			Handle:     guid,
			RootFSPath: c.rootFSPath,
			Properties: garden.Properties{
				gardenstore.ContainerOwnerProperty:             c.containerOwnerName,
				gardenstore.TagPropertyPrefix + HealthcheckTag: HealthcheckTagValue,
			},
		})
		if createErr != nil {
			logger.Error("failed", createErr, lager.Data{"attempt": attempt})
			return createErr
		}

		logger.Debug("succeeded", lager.Data{"attempt": attempt})
		return nil
	})

	return guid, container, err
}

func (c *checker) cleanupDestroy(logger lager.Logger, guid string) error {
	logger = logger.Session("cleanup-destroy")
	logger.Debug("starting")
	defer logger.Debug("finished")

	err := retryOnFail(c.retryInterval, func(attempt uint) (destroyErr error) {
		destroyErr = c.destroyContainer(guid)
		if destroyErr != nil {
			if destroyErr.Error() == server.ErrConcurrentDestroy.Error() {
				// Log but don't fail if container is already being destroyed
				logger.Debug("already-being-destroyed", lager.Data{"handle": guid})
				return nil
			}

			logger.Error("failed", destroyErr, lager.Data{"handle": guid, "attempt": attempt})
			return destroyErr
		}

		logger.Debug("succeeded", lager.Data{"attempt": attempt})
		return nil
	})

	return err
}

func (c *checker) run(logger lager.Logger, container garden.Container) (garden.Process, error) {
	logger = logger.Session("run", lager.Data{
		"processPath": c.healthcheckSpec.Path,
		"processArgs": c.healthcheckSpec.Args,
		"processUser": c.healthcheckSpec.User,
		"processEnv":  c.healthcheckSpec.Env,
		"processDir":  c.healthcheckSpec.Dir,
	})
	logger.Debug("starting")
	defer logger.Debug("finished")

	var proc garden.Process
	err := retryOnFail(c.retryInterval, func(attempt uint) (runErr error) {
		proc, runErr = container.Run(c.healthcheckSpec, garden.ProcessIO{})
		if runErr != nil {
			logger.Error("failed", runErr, lager.Data{"attempt": attempt})
			return runErr
		}

		logger.Debug("succeeded", lager.Data{"attempt": attempt})
		return nil
	})

	return proc, err
}

func (c *checker) wait(logger lager.Logger, proc garden.Process) (int, error) {
	logger = logger.Session("wait")
	logger.Debug("starting")
	defer logger.Debug("finished")

	var exitCode int
	err := retryOnFail(c.retryInterval, func(attempt uint) (waitErr error) {
		exitCode, waitErr = proc.Wait()
		if waitErr != nil {
			logger.Error("failed", waitErr, lager.Data{"attempt": attempt})
			return waitErr
		}

		logger.Debug("succeeded", lager.Data{"attempt": attempt})
		return nil
	})

	return exitCode, err
}

// Healthcheck destroys any existing healthcheck containers, creates a new container,
// runs a process in the new container, waits for the process to exit, then destroys
// the created container.
//
// If any of these steps fail, the failed step will be retried
// up to gardenhealth.MaxRetries times. If the command continues to fail after the
// retries, an error will be returned, indicating the healthcheck failed.
func (c *checker) Healthcheck(logger lager.Logger) (healthcheckResult error) {
	logger = logger.Session("healthcheck")
	logger.Debug("starting")

	containers, err := c.list(logger)
	if err != nil {
		return err
	}

	err = c.initialDestroy(logger, containers)
	if err != nil {
		return err
	}

	guid, container, err := c.create(logger)
	if err != nil {
		return err
	}

	defer func() {
		err := c.cleanupDestroy(logger, guid)
		if err != nil {
			healthcheckResult = err
		}
	}()

	proc, err := c.run(logger, container)
	if err != nil {
		return err
	}

	exitCode, err := c.wait(logger, proc)
	if err != nil {
		return err
	}

	if exitCode != 0 {
		return HealthcheckFailedError(exitCode)
	}

	return nil
}

func (c *checker) destroyContainer(guid string) error {
	err := c.gardenClient.Destroy(guid)
	switch err.(type) {
	case nil:
		return nil
	case garden.ContainerNotFoundError:
		return err
	default:
		return UnrecoverableError(err.Error())
	}
}

const (
	maxRetries = 3
)

func retryOnFail(retryInterval time.Duration, cmd func(attempt uint) error) error {
	var err error

	for i := uint(0); i < maxRetries; i++ {
		err = cmd(i)
		if err == nil {
			return nil
		}

		time.Sleep(retryInterval)
	}

	return err
}
<|endoftext|>