<commit_before>/*
Copyright 2017 - The TXTdirect 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 txtdirect

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
)

const (
	basezone        = "_redirect"
	defaultSub      = "www"
	defaultProtocol = "https"
)

type record struct {
	Version string
	To      string
	Code    int
	Type    string
	Vcs     string
	From    string
	Default string
}

// Config contains the middleware's configuration
type Config struct {
	Enable   []string
	Redirect string
}

func (r *record) Parse(str string) error {
	s := strings.Split(str, ";")
	for _, l := range s {
		switch {
		case strings.HasPrefix(l, "v="):
			l = strings.TrimPrefix(l, "v=")
			r.Version = l
			if r.Version != "txtv0" {
				return fmt.Errorf("unhandled version '%s'", r.Version)
			}
			log.Print("WARN: txtv0 is not suitable for production")

		case strings.HasPrefix(l, "to="):
			l = strings.TrimPrefix(l, "to=")
			r.To = l

		case strings.HasPrefix(l, "code="):
			l = strings.TrimPrefix(l, "code=")
			i, err := strconv.Atoi(l)
			if err != nil {
				return fmt.Errorf("could not parse status code: %s", err)
			}
			r.Code = i

		case strings.HasPrefix(l, "type="):
			l = strings.TrimPrefix(l, "type=")
			r.Type = l

		case strings.HasPrefix(l, "vcs="):
			l = strings.TrimPrefix(l, "vcs=")
			r.Vcs = l

		case strings.HasPrefix(l, "default="):
			l = strings.TrimPrefix(l, "default=")
			r.Default = l

		default:
			if r.To != "" {
				return fmt.Errorf("multiple values without keys")
			}
			r.To = l
		}
	}

	if r.Code == 0 {
		r.Code = 301
	}

	if r.Type == "" {
		r.Type = "host"
	}

	return nil
}

func getBaseTarget(rec record) (string, int) {
	return rec.To, rec.Code
}

func getRecord(host, path string) (record, error) {
	if strings.Contains(host, ":") {
		hostSlice := strings.Split(host, ":")
		host = hostSlice[0]
	}
	zone := strings.Join([]string{basezone, host}, ".")

	var absoluteZone string
	if strings.HasSuffix(zone, ".") {
		absoluteZone = zone
	} else {
		absoluteZone = strings.Join([]string{zone, "."}, "")
	}
	s, err := net.LookupTXT(absoluteZone)

	if err != nil {
		return record{}, fmt.Errorf("could not get TXT record: %s", err)
	}

	rec := record{}
	if err = rec.Parse(s[0]); err != nil {
		return rec, fmt.Errorf("could not parse record: %s", err)
	}

	if rec.To == "" {
		s := []string{defaultProtocol, "://", defaultSub, ".", host}
		rec.To = strings.Join(s, "")
	}

	return rec, nil
}

func contains(array []string, word string) bool {
	for _, w := range array {
		if w == word {
			return true
		}
	}
	return false
}

// Redirect the request depending on the redirect record found
func Redirect(w http.ResponseWriter, r *http.Request, c Config) error {
	host := r.Host
	path := r.URL.Path

	bl := make(map[string]bool)
	bl["/favicon.ico"] = true

	if bl[path] {
		http.Redirect(w, r, strings.Join([]string{host, path}, ""), 200)
		return nil
	}

	rec, err := getRecord(host, path)
	if err != nil {
		if strings.HasSuffix(err.Error(), "no such host") {
			if c.Redirect != "" {
				http.Redirect(w, r, c.Redirect, http.StatusMovedPermanently)
				return nil
			}
			if contains(c.Enable, "www") {
				s := []string{defaultProtocol, "://", defaultSub, ".", host}
				http.Redirect(w, r, strings.Join(s, ""), 301)
				return nil
			}
			http.NotFound(w, r)
			return nil
		}
		return err
	}

	if !contains(c.Enable, rec.Type) {
		return fmt.Errorf("option disabled")
	}

	if path == "/" {
		http.Redirect(w, r, rec.Default, rec.Code)
		return nil
	}

	if rec.Type == "path" && path != "" {
		zone, from, _ := zoneFromPath(host, path, rec)
		rec, err = getFinalRecord(zone, from)
		if err != nil {
			return err
		}
	}

	if rec.Type == "host" {
		to, code := getBaseTarget(rec)
		http.Redirect(w, r, to, code)
		return nil
	}

	if rec.Type == "gometa" {
		return gometa(w, rec, host, path)
	}

	return fmt.Errorf("record type %s unsupported", rec.Type)
}
<commit_msg>Fix host redirect loop<commit_after>/*
Copyright 2017 - The TXTdirect 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 txtdirect

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
)

const (
	basezone        = "_redirect"
	defaultSub      = "www"
	defaultProtocol = "https"
)

type record struct {
	Version string
	To      string
	Code    int
	Type    string
	Vcs     string
	From    string
	Default string
}

// Config contains the middleware's configuration
type Config struct {
	Enable   []string
	Redirect string
}

func (r *record) Parse(str string) error {
	s := strings.Split(str, ";")
	for _, l := range s {
		switch {
		case strings.HasPrefix(l, "v="):
			l = strings.TrimPrefix(l, "v=")
			r.Version = l
			if r.Version != "txtv0" {
				return fmt.Errorf("unhandled version '%s'", r.Version)
			}
			log.Print("WARN: txtv0 is not suitable for production")

		case strings.HasPrefix(l, "to="):
			l = strings.TrimPrefix(l, "to=")
			r.To = l

		case strings.HasPrefix(l, "code="):
			l = strings.TrimPrefix(l, "code=")
			i, err := strconv.Atoi(l)
			if err != nil {
				return fmt.Errorf("could not parse status code: %s", err)
			}
			r.Code = i

		case strings.HasPrefix(l, "type="):
			l = strings.TrimPrefix(l, "type=")
			r.Type = l

		case strings.HasPrefix(l, "vcs="):
			l = strings.TrimPrefix(l, "vcs=")
			r.Vcs = l

		case strings.HasPrefix(l, "default="):
			l = strings.TrimPrefix(l, "default=")
			r.Default = l

		default:
			if r.To != "" {
				return fmt.Errorf("multiple values without keys")
			}
			r.To = l
		}
	}

	if r.Code == 0 {
		r.Code = 301
	}

	if r.Type == "" {
		r.Type = "host"
	}

	return nil
}

func getBaseTarget(rec record) (string, int) {
	return rec.To, rec.Code
}

func getRecord(host, path string) (record, error) {
	if strings.Contains(host, ":") {
		hostSlice := strings.Split(host, ":")
		host = hostSlice[0]
	}
	zone := strings.Join([]string{basezone, host}, ".")

	var absoluteZone string
	if strings.HasSuffix(zone, ".") {
		absoluteZone = zone
	} else {
		absoluteZone = strings.Join([]string{zone, "."}, "")
	}
	s, err := net.LookupTXT(absoluteZone)

	if err != nil {
		return record{}, fmt.Errorf("could not get TXT record: %s", err)
	}

	rec := record{}
	if err = rec.Parse(s[0]); err != nil {
		return rec, fmt.Errorf("could not parse record: %s", err)
	}

	if rec.To == "" {
		s := []string{defaultProtocol, "://", defaultSub, ".", host}
		rec.To = strings.Join(s, "")
	}

	return rec, nil
}

func contains(array []string, word string) bool {
	for _, w := range array {
		if w == word {
			return true
		}
	}
	return false
}

// Redirect the request depending on the redirect record found
func Redirect(w http.ResponseWriter, r *http.Request, c Config) error {
	host := r.Host
	path := r.URL.Path

	bl := make(map[string]bool)
	bl["/favicon.ico"] = true

	if bl[path] {
		http.Redirect(w, r, strings.Join([]string{host, path}, ""), 200)
		return nil
	}

	rec, err := getRecord(host, path)
	if err != nil {
		if strings.HasSuffix(err.Error(), "no such host") {
			if c.Redirect != "" {
				http.Redirect(w, r, c.Redirect, http.StatusMovedPermanently)
				return nil
			}
			if contains(c.Enable, "www") {
				s := []string{defaultProtocol, "://", defaultSub, ".", host}
				http.Redirect(w, r, strings.Join(s, ""), 301)
				return nil
			}
			http.NotFound(w, r)
			return nil
		}
		return err
	}

	if !contains(c.Enable, rec.Type) {
		return fmt.Errorf("option disabled")
	}

	if rec.Type == "path" {
		if path == "/" {
			http.Redirect(w, r, rec.Default, rec.Code)
			return nil
		}
		if path != "" {
			zone, from, _ := zoneFromPath(host, path, rec)
			rec, err = getFinalRecord(zone, from)
			if err != nil {
				return err
			}
		}
	}

	if rec.Type == "host" {
		to, code := getBaseTarget(rec)
		http.Redirect(w, r, to, code)
		return nil
	}

	if rec.Type == "gometa" {
		return gometa(w, rec, host, path)
	}

	return fmt.Errorf("record type %s unsupported", rec.Type)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package e2e

import (
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
	extensions "k8s.io/client-go/pkg/apis/extensions/v1beta1"
	metav1 "k8s.io/client-go/pkg/apis/meta/v1"
	policy "k8s.io/client-go/pkg/apis/policy/v1beta1"
	"k8s.io/client-go/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"
)

// schedulingTimeout is longer specifically because sometimes we need to wait
// awhile to guarantee that we've been patient waiting for something ordinary
// to happen: a pod to get scheduled and move into Ready
const schedulingTimeout = 10 * time.Minute
const bigClusterSize = 7

var _ = framework.KubeDescribe("DisruptionController", func() {
	f := framework.NewDefaultFramework("disruption")
	var ns string
	var cs *kubernetes.Clientset

	BeforeEach(func() {
		cs = f.StagingClient
		ns = f.Namespace.Name
	})

	It("should create a PodDisruptionBudget", func() {
		createPodDisruptionBudgetOrDie(cs, ns, intstr.FromString("1%"))
	})

	It("should update PodDisruptionBudget status", func() {
		createPodDisruptionBudgetOrDie(cs, ns, intstr.FromInt(2))

		createPodsOrDie(cs, ns, 3)
		waitForPodsOrDie(cs, ns, 3)

		// Since disruptionAllowed starts out 0, if we see it ever become positive,
		// that means the controller is working.
		err := wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
			pdb, err := cs.Policy().PodDisruptionBudgets(ns).Get("foo")
			if err != nil {
				return false, err
			}
			return pdb.Status.PodDisruptionsAllowed > 0, nil
		})
		Expect(err).NotTo(HaveOccurred())

	})

	evictionCases := []struct {
		description        string
		minAvailable       intstr.IntOrString
		podCount           int
		replicaSetSize     int32
		shouldDeny         bool
		exclusive          bool
		skipForBigClusters bool
	}{
		{
			description:  "no PDB",
			minAvailable: intstr.FromString(""),
			podCount:     1,
			shouldDeny:   false,
		}, {
			description:  "too few pods, absolute",
			minAvailable: intstr.FromInt(2),
			podCount:     2,
			shouldDeny:   true,
		}, {
			description:  "enough pods, absolute",
			minAvailable: intstr.FromInt(2),
			podCount:     3,
			shouldDeny:   false,
		}, {
			description:    "enough pods, replicaSet, percentage",
			minAvailable:   intstr.FromString("90%"),
			replicaSetSize: 10,
			exclusive:      false,
			shouldDeny:     false,
		}, {
			description:    "too few pods, replicaSet, percentage",
			minAvailable:   intstr.FromString("90%"),
			replicaSetSize: 10,
			exclusive:      true,
			shouldDeny:     true,
			// This tests assumes that there is less than replicaSetSize nodes in the cluster.
			skipForBigClusters: true,
		},
	}
	for i := range evictionCases {
		c := evictionCases[i]
		expectation := "should allow an eviction"
		if c.shouldDeny {
			expectation = "should not allow an eviction"
		}
		It(fmt.Sprintf("evictions: %s => %s", c.description, expectation), func() {
			if c.skipForBigClusters {
				framework.SkipUnlessNodeCountIsAtMost(bigClusterSize - 1)
			}
			createPodsOrDie(cs, ns, c.podCount)
			if c.replicaSetSize > 0 {
				createReplicaSetOrDie(cs, ns, c.replicaSetSize, c.exclusive)
			}

			if c.minAvailable.String() != "" {
				createPodDisruptionBudgetOrDie(cs, ns, c.minAvailable)
			}

			// Locate a running pod.
			var pod v1.Pod
			err := wait.PollImmediate(framework.Poll, schedulingTimeout, func() (bool, error) {
				podList, err := cs.Pods(ns).List(v1.ListOptions{})
				if err != nil {
					return false, err
				}

				for i := range podList.Items {
					if podList.Items[i].Status.Phase == v1.PodRunning {
						pod = podList.Items[i]
						return true, nil
					}
				}

				return false, nil
			})
			Expect(err).NotTo(HaveOccurred())

			e := &policy.Eviction{
				ObjectMeta: v1.ObjectMeta{
					Name:      pod.Name,
					Namespace: ns,
				},
			}

			if c.shouldDeny {
				// Since disruptionAllowed starts out false, wait at least 60s hoping that
				// this gives the controller enough time to have truly set the status.
				time.Sleep(timeout)

				err = cs.Pods(ns).Evict(e)
				Expect(err).Should(MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
			} else {
				// Only wait for running pods in the "allow" case
				// because one of shouldDeny cases relies on the
				// replicaSet not fitting on the cluster.
				waitForPodsOrDie(cs, ns, c.podCount+int(c.replicaSetSize))

				// Since disruptionAllowed starts out false, if an eviction is ever allowed,
				// that means the controller is working.
				err = wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
					err = cs.Pods(ns).Evict(e)
					if err != nil {
						return false, nil
					} else {
						return true, nil
					}
				})
				Expect(err).NotTo(HaveOccurred())
			}
		})
	}

})

func createPodDisruptionBudgetOrDie(cs *kubernetes.Clientset, ns string, minAvailable intstr.IntOrString) {
	pdb := policy.PodDisruptionBudget{
		ObjectMeta: v1.ObjectMeta{
			Name:      "foo",
			Namespace: ns,
		},
		Spec: policy.PodDisruptionBudgetSpec{
			Selector:     &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
			MinAvailable: minAvailable,
		},
	}
	_, err := cs.Policy().PodDisruptionBudgets(ns).Create(&pdb)
	Expect(err).NotTo(HaveOccurred())
}

func createPodsOrDie(cs *kubernetes.Clientset, ns string, n int) {
	for i := 0; i < n; i++ {
		pod := &v1.Pod{
			ObjectMeta: v1.ObjectMeta{
				Name:      fmt.Sprintf("pod-%d", i),
				Namespace: ns,
				Labels:    map[string]string{"foo": "bar"},
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:  "busybox",
						Image: "gcr.io/google_containers/echoserver:1.4",
					},
				},
				RestartPolicy: v1.RestartPolicyAlways,
			},
		}

		_, err := cs.Pods(ns).Create(pod)
		framework.ExpectNoError(err, "Creating pod %q in namespace %q", pod.Name, ns)
	}
}

func waitForPodsOrDie(cs *kubernetes.Clientset, ns string, n int) {
	By("Waiting for all pods to be running")
	err := wait.PollImmediate(framework.Poll, schedulingTimeout, func() (bool, error) {
		pods, err := cs.Core().Pods(ns).List(v1.ListOptions{LabelSelector: "foo=bar"})
		if err != nil {
			return false, err
		}
		if pods == nil {
			return false, fmt.Errorf("pods is nil")
		}
		if len(pods.Items) < n {
			framework.Logf("pods: %v < %v", len(pods.Items), n)
			return false, nil
		}
		ready := 0
		for i := 0; i < n; i++ {
			if pods.Items[i].Status.Phase == v1.PodRunning {
				ready++
			}
		}
		if ready < n {
			framework.Logf("running pods: %v < %v", ready, n)
			return false, nil
		}
		return true, nil
	})
	framework.ExpectNoError(err, "Waiting for pods in namespace %q to be ready", ns)
}

func createReplicaSetOrDie(cs *kubernetes.Clientset, ns string, size int32, exclusive bool) {
	container := v1.Container{
		Name:  "busybox",
		Image: "gcr.io/google_containers/echoserver:1.4",
	}
	if exclusive {
		container.Ports = []v1.ContainerPort{
			{HostPort: 5555, ContainerPort: 5555},
		}
	}

	rs := &extensions.ReplicaSet{
		ObjectMeta: v1.ObjectMeta{
			Name:      "rs",
			Namespace: ns,
		},
		Spec: extensions.ReplicaSetSpec{
			Replicas: &size,
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{"foo": "bar"},
			},
			Template: v1.PodTemplateSpec{
				ObjectMeta: v1.ObjectMeta{
					Labels: map[string]string{"foo": "bar"},
				},
				Spec: v1.PodSpec{
					Containers: []v1.Container{container},
				},
			},
		},
	}

	_, err := cs.Extensions().ReplicaSets(ns).Create(rs)
	framework.ExpectNoError(err, "Creating replica set %q in namespace %q", rs.Name, ns)
}
<commit_msg>Update disruption test<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package e2e

import (
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
	extensions "k8s.io/client-go/pkg/apis/extensions/v1beta1"
	metav1 "k8s.io/client-go/pkg/apis/meta/v1"
	policy "k8s.io/client-go/pkg/apis/policy/v1beta1"
	"k8s.io/client-go/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"
)

// schedulingTimeout is longer specifically because sometimes we need to wait
// awhile to guarantee that we've been patient waiting for something ordinary
// to happen: a pod to get scheduled and move into Ready
const schedulingTimeout = 10 * time.Minute
const bigClusterSize = 7

var _ = framework.KubeDescribe("DisruptionController", func() {
	f := framework.NewDefaultFramework("disruption")
	var ns string
	var cs *kubernetes.Clientset

	BeforeEach(func() {
		cs = f.StagingClient
		ns = f.Namespace.Name
	})

	It("should create a PodDisruptionBudget", func() {
		createPodDisruptionBudgetOrDie(cs, ns, intstr.FromString("1%"))
	})

	It("should update PodDisruptionBudget status", func() {
		createPodDisruptionBudgetOrDie(cs, ns, intstr.FromInt(2))

		createPodsOrDie(cs, ns, 3)
		waitForPodsOrDie(cs, ns, 3)

		// Since disruptionAllowed starts out 0, if we see it ever become positive,
		// that means the controller is working.
		err := wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
			pdb, err := cs.Policy().PodDisruptionBudgets(ns).Get("foo", metav1.GetOptions{})
			if err != nil {
				return false, err
			}
			return pdb.Status.PodDisruptionsAllowed > 0, nil
		})
		Expect(err).NotTo(HaveOccurred())

	})

	evictionCases := []struct {
		description        string
		minAvailable       intstr.IntOrString
		podCount           int
		replicaSetSize     int32
		shouldDeny         bool
		exclusive          bool
		skipForBigClusters bool
	}{
		{
			description:  "no PDB",
			minAvailable: intstr.FromString(""),
			podCount:     1,
			shouldDeny:   false,
		}, {
			description:  "too few pods, absolute",
			minAvailable: intstr.FromInt(2),
			podCount:     2,
			shouldDeny:   true,
		}, {
			description:  "enough pods, absolute",
			minAvailable: intstr.FromInt(2),
			podCount:     3,
			shouldDeny:   false,
		}, {
			description:    "enough pods, replicaSet, percentage",
			minAvailable:   intstr.FromString("90%"),
			replicaSetSize: 10,
			exclusive:      false,
			shouldDeny:     false,
		}, {
			description:    "too few pods, replicaSet, percentage",
			minAvailable:   intstr.FromString("90%"),
			replicaSetSize: 10,
			exclusive:      true,
			shouldDeny:     true,
			// This tests assumes that there is less than replicaSetSize nodes in the cluster.
			skipForBigClusters: true,
		},
	}
	for i := range evictionCases {
		c := evictionCases[i]
		expectation := "should allow an eviction"
		if c.shouldDeny {
			expectation = "should not allow an eviction"
		}
		It(fmt.Sprintf("evictions: %s => %s", c.description, expectation), func() {
			if c.skipForBigClusters {
				framework.SkipUnlessNodeCountIsAtMost(bigClusterSize - 1)
			}
			createPodsOrDie(cs, ns, c.podCount)
			if c.replicaSetSize > 0 {
				createReplicaSetOrDie(cs, ns, c.replicaSetSize, c.exclusive)
			}

			if c.minAvailable.String() != "" {
				createPodDisruptionBudgetOrDie(cs, ns, c.minAvailable)
			}

			// Locate a running pod.
			var pod v1.Pod
			err := wait.PollImmediate(framework.Poll, schedulingTimeout, func() (bool, error) {
				podList, err := cs.Pods(ns).List(v1.ListOptions{})
				if err != nil {
					return false, err
				}

				for i := range podList.Items {
					if podList.Items[i].Status.Phase == v1.PodRunning {
						pod = podList.Items[i]
						return true, nil
					}
				}

				return false, nil
			})
			Expect(err).NotTo(HaveOccurred())

			e := &policy.Eviction{
				ObjectMeta: v1.ObjectMeta{
					Name:      pod.Name,
					Namespace: ns,
				},
			}

			if c.shouldDeny {
				// Since disruptionAllowed starts out false, wait at least 60s hoping that
				// this gives the controller enough time to have truly set the status.
				time.Sleep(timeout)

				err = cs.Pods(ns).Evict(e)
				Expect(err).Should(MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
			} else {
				// Only wait for running pods in the "allow" case
				// because one of shouldDeny cases relies on the
				// replicaSet not fitting on the cluster.
				waitForPodsOrDie(cs, ns, c.podCount+int(c.replicaSetSize))

				// Since disruptionAllowed starts out false, if an eviction is ever allowed,
				// that means the controller is working.
				err = wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
					err = cs.Pods(ns).Evict(e)
					if err != nil {
						return false, nil
					} else {
						return true, nil
					}
				})
				Expect(err).NotTo(HaveOccurred())
			}
		})
	}

})

func createPodDisruptionBudgetOrDie(cs *kubernetes.Clientset, ns string, minAvailable intstr.IntOrString) {
	pdb := policy.PodDisruptionBudget{
		ObjectMeta: v1.ObjectMeta{
			Name:      "foo",
			Namespace: ns,
		},
		Spec: policy.PodDisruptionBudgetSpec{
			Selector:     &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
			MinAvailable: minAvailable,
		},
	}
	_, err := cs.Policy().PodDisruptionBudgets(ns).Create(&pdb)
	Expect(err).NotTo(HaveOccurred())
}

func createPodsOrDie(cs *kubernetes.Clientset, ns string, n int) {
	for i := 0; i < n; i++ {
		pod := &v1.Pod{
			ObjectMeta: v1.ObjectMeta{
				Name:      fmt.Sprintf("pod-%d", i),
				Namespace: ns,
				Labels:    map[string]string{"foo": "bar"},
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:  "busybox",
						Image: "gcr.io/google_containers/echoserver:1.4",
					},
				},
				RestartPolicy: v1.RestartPolicyAlways,
			},
		}

		_, err := cs.Pods(ns).Create(pod)
		framework.ExpectNoError(err, "Creating pod %q in namespace %q", pod.Name, ns)
	}
}

func waitForPodsOrDie(cs *kubernetes.Clientset, ns string, n int) {
	By("Waiting for all pods to be running")
	err := wait.PollImmediate(framework.Poll, schedulingTimeout, func() (bool, error) {
		pods, err := cs.Core().Pods(ns).List(v1.ListOptions{LabelSelector: "foo=bar"})
		if err != nil {
			return false, err
		}
		if pods == nil {
			return false, fmt.Errorf("pods is nil")
		}
		if len(pods.Items) < n {
			framework.Logf("pods: %v < %v", len(pods.Items), n)
			return false, nil
		}
		ready := 0
		for i := 0; i < n; i++ {
			if pods.Items[i].Status.Phase == v1.PodRunning {
				ready++
			}
		}
		if ready < n {
			framework.Logf("running pods: %v < %v", ready, n)
			return false, nil
		}
		return true, nil
	})
	framework.ExpectNoError(err, "Waiting for pods in namespace %q to be ready", ns)
}

func createReplicaSetOrDie(cs *kubernetes.Clientset, ns string, size int32, exclusive bool) {
	container := v1.Container{
		Name:  "busybox",
		Image: "gcr.io/google_containers/echoserver:1.4",
	}
	if exclusive {
		container.Ports = []v1.ContainerPort{
			{HostPort: 5555, ContainerPort: 5555},
		}
	}

	rs := &extensions.ReplicaSet{
		ObjectMeta: v1.ObjectMeta{
			Name:      "rs",
			Namespace: ns,
		},
		Spec: extensions.ReplicaSetSpec{
			Replicas: &size,
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{"foo": "bar"},
			},
			Template: v1.PodTemplateSpec{
				ObjectMeta: v1.ObjectMeta{
					Labels: map[string]string{"foo": "bar"},
				},
				Spec: v1.PodSpec{
					Containers: []v1.Container{container},
				},
			},
		},
	}

	_, err := cs.Extensions().ReplicaSets(ns).Create(rs)
	framework.ExpectNoError(err, "Creating replica set %q in namespace %q", rs.Name, ns)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/url"
	"os"
	"testing"

	"github.com/stretchr/testify/require"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/sapcc/kubernikus/pkg/util"
	"github.com/sapcc/kubernikus/test/e2e/framework"
)

var (
	kubernikusURL = flag.String("kubernikus", "", "Kubernikus URL")
	kluster       = flag.String("kluster", "", "Use existing Kluster")
	reuse         = flag.Bool("reuse", false, "Reuse exisiting Kluster")
	cleanup       = flag.Bool("cleanup", true, "Cleanup after tests have been run")
)

func validate() error {
	if *kubernikusURL == "" {
		return fmt.Errorf("You need to provide the --kubernikus flag")
	}

	k, err := url.Parse(*kubernikusURL)
	if err != nil {
		return fmt.Errorf("You need to provide an URL for --kubernikus: %v", err)
	}

	if k.Host == "" {
		return fmt.Errorf("You need to provide an URL for --kubernikus")
	}

	if reuse != nil && *reuse && (kluster == nil || *kluster == "") {
		return fmt.Errorf("You need to provide the --kluster flag when --reuse is active")
	}

	for _, env := range []string{"OS_AUTH_URL", "OS_USERNAME", "OS_PASSWORD",
		"OS_USER_DOMAIN_NAME", "OS_PROJECT_NAME", "OS_PROJECT_DOMAIN_NAME"} {
		if os.Getenv(env) == "" {
			return fmt.Errorf("You need to provide %s", env)
		}
	}

	return nil
}

func TestMain(m *testing.M) {
	flag.Parse()

	if err := validate(); err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	os.Exit(m.Run())
}

func TestRunner(t *testing.T) {
	var kubernetes *framework.Kubernetes
	var kubernikus *framework.Kubernikus

	namespaceNetwork := util.SimpleNameGenerator.GenerateName("e2e-network-")
	namespaceVolumes := util.SimpleNameGenerator.GenerateName("e2e-volumes-")
	klusterName := util.SimpleNameGenerator.GenerateName("e2e-")

	if kluster != nil && *kluster != "" {
		klusterName = *kluster
	}

	kurl, err := url.Parse(*kubernikusURL)
	require.NoError(t, err, "Must be able to parse Kubernikus URL")
	require.NotEmpty(t, kurl.Host, "There must be a host in the Kubernikus URL")

	fmt.Printf("========================================================================\n")
	fmt.Printf("Authentication\n")
	fmt.Printf("========================================================================\n")
	fmt.Printf("OS_AUTH_URL:            %v\n", os.Getenv("OS_AUTH_URL"))
	fmt.Printf("OS_USERNAME:            %v\n", os.Getenv("OS_USERNAME"))
	fmt.Printf("OS_USER_DOMAIN_NAME:    %v\n", os.Getenv("OS_USER_DOMAIN_NAME"))
	fmt.Printf("OS_PROJECT_NAME:        %v\n", os.Getenv("OS_PROJECT_NAME"))
	fmt.Printf("OS_PROJECT_DOMAIN_NAME: %v\n", os.Getenv("OS_PROJECT_DOMAIN_NAME"))
	fmt.Printf("\n")
	fmt.Printf("========================================================================\n")
	fmt.Printf("Test Parameters\n")
	fmt.Printf("========================================================================\n")
	fmt.Printf("Kubernikus:             %v\n", kurl.Host)
	fmt.Printf("Kluster Name:           %v\n", klusterName)
	fmt.Printf("Reuse:                  %v\n", *reuse)
	fmt.Printf("Cleanup:                %v\n", *cleanup)
	fmt.Printf("\n\n")

	kubernikus, err = framework.NewKubernikusFramework(kurl)
	require.NoError(t, err, "Must be able to connect to Kubernikus")

	api := APITests{kubernikus, klusterName}
	kluster := KlusterTests{kubernikus, klusterName}

	if cleanup != nil && *cleanup == true {
		defer t.Run("Cleanup", func(t *testing.T) {
			if t.Run("TerminateCluster", api.TerminateCluster) {
				t.Run("BecomesTerminating", kluster.KlusterPhaseBecomesTerminating)
				t.Run("IsDeleted", api.WaitForKlusterToBeDeleted)
			}
		})
	}

	setup := t.Run("Setup", func(t *testing.T) {
		if reuse == nil || *reuse == false {
			created := t.Run("CreateCluster", api.CreateCluster)
			require.True(t, created, "The Kluster must have been created")

			t.Run("BecomesCreating", kluster.KlusterPhaseBecomesCreating)
		}

		running := t.Run("BecomesRunning", kluster.KlusterPhaseBecomesRunning)
		require.True(t, running, "The Kluster must be Running")

		kubernetes, err = framework.NewKubernetesFramework(kubernikus, klusterName)
		require.NoError(t, err, "Must be able to create a kubernetes client")

		ready := t.Run("NodesBecomeReady", api.WaitForNodesReady)
		require.True(t, ready, "The Kluster must have Ready nodes")
	})
	require.True(t, setup, "Test setup must complete successfully")

	t.Run("API", func(t *testing.T) {
		t.Run("ListCluster", api.ListClusters)
		t.Run("ShowCluster", api.ShowCluster)
		t.Run("GetClusterInfo", api.GetClusterInfo)
		t.Run("GetCredentials", api.GetCredentials)
	})

	nodes := t.Run("Nodes", func(t *testing.T) {
		nodeTests := NodeTests{kubernetes, SmokeTestNodeCount}

		t.Run("Registered", nodeTests.Registered)
		t.Run("Condition/RouteBroken", nodeTests.RouteBroken)
		t.Run("Condition/NetworkUnavailable", nodeTests.NetworkUnavailable)
		t.Run("Condition/Ready", nodeTests.Ready)
	})
	require.True(t, nodes, "Node test must complete successfully")

	nodeList, err := kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
	require.NoError(t, err, "There must be no error while listing the kluster's nodes")
	require.Equal(t, len(nodeList.Items), SmokeTestNodeCount, "There must be at least %d nodes", SmokeTestNodeCount)

	t.Run("Smoke", func(t *testing.T) {
		t.Run("Network", func(t *testing.T) {
			t.Parallel()
			network := NetworkTests{kubernetes, nodeList, namespaceNetwork}

			defer t.Run("Cleanup", network.DeleteNamespace)
			t.Run("Setup", func(t *testing.T) {
				t.Run("Namespace/Create", network.CreateNamespace)
				t.Run("Namespace/Wait", network.WaitForNamespace)
				t.Run("Pods", func(t *testing.T) {
					t.Parallel()
					t.Run("Create", network.CreatePods)
					t.Run("Wait", network.WaitForPodsRunning)
				})
				t.Run("Services", func(t *testing.T) {
					t.Parallel()
					t.Run("Create", network.CreateServices)
					t.Run("WaitForServiceEndpoints", network.WaitForServiceEndpoints)
					t.Run("WaitForKubeDNS", network.WaitForKubeDNSRunning)
				})
			})

			t.Run("Connectivity/Pods", network.TestPods)
			t.Run("Connectivity/Services", network.TestServices)
			t.Run("ConnectivityServicesWithDNS", network.TestServicesWithDNS)
		})

		t.Run("Volumes", func(t *testing.T) {
			t.Parallel()
			volumes := VolumeTests{kubernetes, nodeList, nil, namespaceVolumes}

			defer t.Run("Cleanup", volumes.DeleteNamespace)
			t.Run("Setup/Namespace", func(t *testing.T) {
				t.Run("Create", volumes.CreateNamespace)
				t.Run("Wait", volumes.WaitForNamespace)
			})
			t.Run("PVC", func(t *testing.T) {
				t.Run("Create", volumes.CreatePVC)
				t.Run("Wait", volumes.WaitForPVCBound)
			})
			t.Run("Pods", func(t *testing.T) {
				t.Run("Create", volumes.CreatePod)
				t.Run("Wait", volumes.WaitForPVCPodsRunning)
			})
		})
	})
}
<commit_msg>fixes premature cleanup<commit_after>package main

import (
	"flag"
	"fmt"
	"net/url"
	"os"
	"testing"

	"github.com/stretchr/testify/require"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/sapcc/kubernikus/pkg/util"
	"github.com/sapcc/kubernikus/test/e2e/framework"
)

var (
	kubernikusURL = flag.String("kubernikus", "", "Kubernikus URL")
	kluster       = flag.String("kluster", "", "Use existing Kluster")
	reuse         = flag.Bool("reuse", false, "Reuse exisiting Kluster")
	cleanup       = flag.Bool("cleanup", true, "Cleanup after tests have been run")
)

func validate() error {
	if *kubernikusURL == "" {
		return fmt.Errorf("You need to provide the --kubernikus flag")
	}

	k, err := url.Parse(*kubernikusURL)
	if err != nil {
		return fmt.Errorf("You need to provide an URL for --kubernikus: %v", err)
	}

	if k.Host == "" {
		return fmt.Errorf("You need to provide an URL for --kubernikus")
	}

	if reuse != nil && *reuse && (kluster == nil || *kluster == "") {
		return fmt.Errorf("You need to provide the --kluster flag when --reuse is active")
	}

	for _, env := range []string{"OS_AUTH_URL", "OS_USERNAME", "OS_PASSWORD",
		"OS_USER_DOMAIN_NAME", "OS_PROJECT_NAME", "OS_PROJECT_DOMAIN_NAME"} {
		if os.Getenv(env) == "" {
			return fmt.Errorf("You need to provide %s", env)
		}
	}

	return nil
}

func TestMain(m *testing.M) {
	flag.Parse()

	if err := validate(); err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	os.Exit(m.Run())
}

func TestRunner(t *testing.T) {
	var kubernetes *framework.Kubernetes
	var kubernikus *framework.Kubernikus

	namespaceNetwork := util.SimpleNameGenerator.GenerateName("e2e-network-")
	namespaceVolumes := util.SimpleNameGenerator.GenerateName("e2e-volumes-")
	klusterName := util.SimpleNameGenerator.GenerateName("e2e-")

	if kluster != nil && *kluster != "" {
		klusterName = *kluster
	}

	kurl, err := url.Parse(*kubernikusURL)
	require.NoError(t, err, "Must be able to parse Kubernikus URL")
	require.NotEmpty(t, kurl.Host, "There must be a host in the Kubernikus URL")

	fmt.Printf("========================================================================\n")
	fmt.Printf("Authentication\n")
	fmt.Printf("========================================================================\n")
	fmt.Printf("OS_AUTH_URL:            %v\n", os.Getenv("OS_AUTH_URL"))
	fmt.Printf("OS_USERNAME:            %v\n", os.Getenv("OS_USERNAME"))
	fmt.Printf("OS_USER_DOMAIN_NAME:    %v\n", os.Getenv("OS_USER_DOMAIN_NAME"))
	fmt.Printf("OS_PROJECT_NAME:        %v\n", os.Getenv("OS_PROJECT_NAME"))
	fmt.Printf("OS_PROJECT_DOMAIN_NAME: %v\n", os.Getenv("OS_PROJECT_DOMAIN_NAME"))
	fmt.Printf("\n")
	fmt.Printf("========================================================================\n")
	fmt.Printf("Test Parameters\n")
	fmt.Printf("========================================================================\n")
	fmt.Printf("Kubernikus:             %v\n", kurl.Host)
	fmt.Printf("Kluster Name:           %v\n", klusterName)
	fmt.Printf("Reuse:                  %v\n", *reuse)
	fmt.Printf("Cleanup:                %v\n", *cleanup)
	fmt.Printf("\n\n")

	kubernikus, err = framework.NewKubernikusFramework(kurl)
	require.NoError(t, err, "Must be able to connect to Kubernikus")

	api := APITests{kubernikus, klusterName}
	kluster := KlusterTests{kubernikus, klusterName}

	if cleanup != nil && *cleanup == true {
		defer t.Run("Cleanup", func(t *testing.T) {
			if t.Run("TerminateCluster", api.TerminateCluster) {
				t.Run("BecomesTerminating", kluster.KlusterPhaseBecomesTerminating)
				t.Run("IsDeleted", api.WaitForKlusterToBeDeleted)
			}
		})
	}

	setup := t.Run("Setup", func(t *testing.T) {
		if reuse == nil || *reuse == false {
			created := t.Run("CreateCluster", api.CreateCluster)
			require.True(t, created, "The Kluster must have been created")

			t.Run("BecomesCreating", kluster.KlusterPhaseBecomesCreating)
		}

		running := t.Run("BecomesRunning", kluster.KlusterPhaseBecomesRunning)
		require.True(t, running, "The Kluster must be Running")

		kubernetes, err = framework.NewKubernetesFramework(kubernikus, klusterName)
		require.NoError(t, err, "Must be able to create a kubernetes client")

		ready := t.Run("NodesBecomeReady", api.WaitForNodesReady)
		require.True(t, ready, "The Kluster must have Ready nodes")
	})
	require.True(t, setup, "Test setup must complete successfully")

	t.Run("API", func(t *testing.T) {
		t.Run("ListCluster", api.ListClusters)
		t.Run("ShowCluster", api.ShowCluster)
		t.Run("GetClusterInfo", api.GetClusterInfo)
		t.Run("GetCredentials", api.GetCredentials)
	})

	nodes := t.Run("Nodes", func(t *testing.T) {
		nodeTests := NodeTests{kubernetes, SmokeTestNodeCount}

		t.Run("Registered", nodeTests.Registered)
		t.Run("Condition/RouteBroken", nodeTests.RouteBroken)
		t.Run("Condition/NetworkUnavailable", nodeTests.NetworkUnavailable)
		t.Run("Condition/Ready", nodeTests.Ready)
	})
	require.True(t, nodes, "Node test must complete successfully")

	nodeList, err := kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
	require.NoError(t, err, "There must be no error while listing the kluster's nodes")
	require.Equal(t, len(nodeList.Items), SmokeTestNodeCount, "There must be at least %d nodes", SmokeTestNodeCount)

	t.Run("Smoke", func(t *testing.T) {
		t.Run("Network", func(t *testing.T) {
			t.Parallel()
			network := NetworkTests{kubernetes, nodeList, namespaceNetwork}

			defer t.Run("Cleanup", network.DeleteNamespace)
			t.Run("Setup", func(t *testing.T) {
				t.Run("Namespace/Create", network.CreateNamespace)
				t.Run("Namespace/Wait", network.WaitForNamespace)
				t.Run("Pods", func(t *testing.T) {
					t.Parallel()
					t.Run("Create", network.CreatePods)
					t.Run("Wait", network.WaitForPodsRunning)
				})
				t.Run("Services", func(t *testing.T) {
					t.Parallel()
					t.Run("Create", network.CreateServices)
					t.Run("WaitForServiceEndpoints", network.WaitForServiceEndpoints)
					t.Run("WaitForKubeDNS", network.WaitForKubeDNSRunning)
				})
			})
			t.Run("Connectivity", func(t *testing.T) {
				t.Run("Connectivity/Pods", network.TestPods)
				t.Run("Connectivity/Services", network.TestServices)
				t.Run("ConnectivityServicesWithDNS", network.TestServicesWithDNS)
			})
		})

		t.Run("Volumes", func(t *testing.T) {
			t.Parallel()
			volumes := VolumeTests{kubernetes, nodeList, nil, namespaceVolumes}

			defer t.Run("Cleanup", volumes.DeleteNamespace)
			t.Run("Setup/Namespace", func(t *testing.T) {
				t.Run("Create", volumes.CreateNamespace)
				t.Run("Wait", volumes.WaitForNamespace)
			})
			t.Run("PVC", func(t *testing.T) {
				t.Run("Create", volumes.CreatePVC)
				t.Run("Wait", volumes.WaitForPVCBound)
			})
			t.Run("Pods", func(t *testing.T) {
				t.Run("Create", volumes.CreatePod)
				t.Run("Wait", volumes.WaitForPVCPodsRunning)
			})
		})
	})
}
<|endoftext|>


<commit_before>// Copyright 2018 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package autocmd

import (
	"context"

	"github.com/neovim/go-client/nvim/plugin"
	"golang.org/x/sync/syncmap"

	"github.com/zchee/nvim-go/pkg/buildctxt"
	"github.com/zchee/nvim-go/pkg/command"
	"github.com/zchee/nvim-go/pkg/logger"
)

// Register registers autocmd to Neovim.
func Register(pctx context.Context, cancel func(), p *plugin.Plugin, buildContext *buildctxt.Context, cmd *command.Command) {
	ctx := logger.NewContext(pctx, logger.FromContext(pctx).Named("autocmd"))

	autocmd := &Autocmd{
		ctx:              ctx,
		cancel:           cancel,
		Nvim:             p.Nvim,
		buildContext:     buildContext,
		cmd:              cmd,
		bufWritePreChan:  make(chan interface{}),
		bufWritePostChan: make(chan error),
		errs:             new(syncmap.Map),
	}

	// Handle the initial start Neovim process.
	// Note that does not run the 'VimEnter' handler if open the *not* go file. Because 'VimEnter' handler already run the other file or directory.
	// TODO(zchee): consider Pattern to '*' instead of '*.go' with get '&filetype' and early return
	// p.HandleAutocmd(&plugin.AutocmdOptions{Event: "VimEnter", Pattern: "*", Group: "nvim-go", Eval: "*"}, autocmd.VimEnter)

	// Handle the open the file.
	// If open the file at first, run the 'BufEnter' -> 'VimEnter'.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufEnter", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.BufEnter)

	// BufNewFile: Handle create the new file.
	// BufReadPre: Handle the before the read to file.
	// If create the new file, does not run the 'BufReadPre', Instead of 'BufNewFile'.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufNewFile,BufReadPre", Group: "nvim-go-autocmd", Pattern: "*.go", Eval: "*"}, autocmd.BufReadPre)

	// p.HandleAutocmd(&plugin.AutocmdOptions{Event: "WinEnter", Group: "nvim-go-autocmd", Pattern: "*.go", Eval: "*"}, autocmd.WinEnter)

	// Handle the before the write to file.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufWritePre", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.BufWritePre)

	// Handle the after the write to file.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufWritePost", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.bufWritePost)

	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "VimLeavePre", Pattern: "*", Group: "nvim-go"}, autocmd.VimLeavePre)
}
<commit_msg>pkg/autocmd: change VimLeavePre Pattern to *.go files<commit_after>// Copyright 2018 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package autocmd

import (
	"context"

	"github.com/neovim/go-client/nvim/plugin"
	"golang.org/x/sync/syncmap"

	"github.com/zchee/nvim-go/pkg/buildctxt"
	"github.com/zchee/nvim-go/pkg/command"
	"github.com/zchee/nvim-go/pkg/logger"
)

// Register registers autocmd to Neovim.
func Register(pctx context.Context, cancel func(), p *plugin.Plugin, buildContext *buildctxt.Context, cmd *command.Command) {
	ctx := logger.NewContext(pctx, logger.FromContext(pctx).Named("autocmd"))

	autocmd := &Autocmd{
		ctx:              ctx,
		cancel:           cancel,
		Nvim:             p.Nvim,
		buildContext:     buildContext,
		cmd:              cmd,
		bufWritePreChan:  make(chan interface{}),
		bufWritePostChan: make(chan error),
		errs:             new(syncmap.Map),
	}

	// Handle the initial start Neovim process.
	// Note that does not run the 'VimEnter' handler if open the *not* go file. Because 'VimEnter' handler already run the other file or directory.
	// TODO(zchee): consider Pattern to '*' instead of '*.go' with get '&filetype' and early return
	// p.HandleAutocmd(&plugin.AutocmdOptions{Event: "VimEnter", Pattern: "*", Group: "nvim-go", Eval: "*"}, autocmd.VimEnter)

	// Handle the open the file.
	// If open the file at first, run the 'BufEnter' -> 'VimEnter'.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufEnter", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.BufEnter)

	// BufNewFile: Handle create the new file.
	// BufReadPre: Handle the before the read to file.
	// If create the new file, does not run the 'BufReadPre', Instead of 'BufNewFile'.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufNewFile,BufReadPre", Group: "nvim-go-autocmd", Pattern: "*.go", Eval: "*"}, autocmd.BufReadPre)

	// p.HandleAutocmd(&plugin.AutocmdOptions{Event: "WinEnter", Group: "nvim-go-autocmd", Pattern: "*.go", Eval: "*"}, autocmd.WinEnter)

	// Handle the before the write to file.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufWritePre", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.BufWritePre)

	// Handle the after the write to file.
	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "BufWritePost", Pattern: "*.go", Group: "nvim-go", Eval: "*"}, autocmd.bufWritePost)

	p.HandleAutocmd(&plugin.AutocmdOptions{Event: "VimLeavePre", Pattern: "*.go", Group: "nvim-go"}, autocmd.VimLeavePre)
}
<|endoftext|>


<commit_before>package install

import (
	"errors"
	"fmt"
	"net"
	"time"
)

// ValidatePlan runs validation against the installation plan to ensure
// that the plan contains valid user input. Returns true, nil if the validation
// is successful. Otherwise, returns false and a collection of validation errors.
func ValidatePlan(p *Plan) (bool, []error) {
	v := newValidator()
	v.validate(p)
	return v.valid()
}

type validatable interface {
	validate() (bool, []error)
}

type validator struct {
	errs []error
}

func newValidator() *validator {
	return &validator{
		errs: []error{},
	}
}

func (v *validator) addError(err ...error) {
	v.errs = append(v.errs, err...)
}

func (v *validator) validate(obj validatable) {
	if ok, err := obj.validate(); !ok {
		v.addError(err...)
	}
}

func (v *validator) validateWithErrPrefix(prefix string, obj validatable) {
	if ok, err := obj.validate(); !ok {
		newErrs := make([]error, len(err), len(err))
		for i, err := range err {
			newErrs[i] = fmt.Errorf("%s: %v", prefix, err)
		}
		v.addError(newErrs...)
	}
}

func (v *validator) valid() (bool, []error) {
	if len(v.errs) > 0 {
		return false, v.errs
	}
	return true, nil
}

func (p *Plan) validate() (bool, []error) {
	v := newValidator()

	v.validate(&p.Cluster)
	v.validateWithErrPrefix("Etcd nodes", &p.Etcd)
	v.validateWithErrPrefix("Master nodes", &p.Master)
	v.validateWithErrPrefix("Worker nodes", &p.Worker)

	return v.valid()
}

func (c *Cluster) validate() (bool, []error) {
	v := newValidator()
	if c.Name == "" {
		v.addError(errors.New("Cluster name cannot be empty"))
	}
	v.validate(&c.Networking)
	v.validate(&c.Certificates)
	v.validate(&c.SSH)

	return v.valid()
}

func (n *NetworkConfig) validate() (bool, []error) {
	v := newValidator()
	if n.Type == "" {
		v.addError(errors.New("Networking type cannot be empty"))
	}
	if n.Type != "bridged" && n.Type != "overlay" {
		v.addError(fmt.Errorf("Invalid networking type %q was provided", n.Type))
	}
	if n.PodCIDRBlock == "" {
		v.addError(errors.New("Pod CIDR block cannot be empty"))
	}
	if _, _, err := net.ParseCIDR(n.PodCIDRBlock); n.PodCIDRBlock != "" && err != nil {
		v.addError(fmt.Errorf("Invalid Pod CIDR block provided: %v", err))
	}

	if n.ServiceCIDRBlock == "" {
		v.addError(errors.New("Service CIDR block cannot be empty"))
	}
	if _, _, err := net.ParseCIDR(n.ServiceCIDRBlock); n.ServiceCIDRBlock != "" && err != nil {
		v.addError(fmt.Errorf("Invalid Service CIDR block provided: %v", err))
	}
	return v.valid()
}

func (c *CertsConfig) validate() (bool, []error) {
	v := newValidator()
	if _, err := time.ParseDuration(c.Expiry); err != nil {
		v.addError(fmt.Errorf("Invalid certificate expiry %q provided: %v", c.Expiry, err))
	}

	if c.LocationCity == "" {
		v.addError(errors.New("Certificate location_city field is required"))
	}
	if c.LocationState == "" {
		v.addError(errors.New("Certificate location_state field is required"))
	}
	if c.LocationCountry == "" {
		v.addError(errors.New("Certificate location_country field is required"))
	}
	return v.valid()
}

func (s *SSHConfig) validate() (bool, []error) {
	v := newValidator()
	if s.User == "" {
		v.addError(errors.New("SSH user field is required"))
	}
	if s.Key == "" {
		v.addError(errors.New("SSH key field is required"))
	}
	if s.Port < 1 || s.Port > 65535 {
		v.addError(fmt.Errorf("SSH port %d is invalid. Port must be in the range 1-65535", s.Port))
	}
	return v.valid()
}

func (ng *NodeGroup) validate() (bool, []error) {
	v := newValidator()
	if len(ng.Nodes) != ng.ExpectedCount {
		v.addError(fmt.Errorf("Expected node count (%d) does not match the number of nodes provided (%d)", ng.ExpectedCount, len(ng.Nodes)))
	}
	for i, n := range ng.Nodes {
		v.validateWithErrPrefix(fmt.Sprintf("Node #%d", i+1), &n)
	}
	return v.valid()
}

func (mng *MasterNodeGroup) validate() (bool, []error) {
	v := newValidator()
	v.validate(&mng.NodeGroup)

	if mng.LoadBalancedFQDN == "" {
		v.addError(fmt.Errorf("Load balanced FQDN is required"))
	}

	if mng.LoadBalancedShortName == "" {
		v.addError(fmt.Errorf("Load balanced shortname is required"))
	}

	return v.valid()
}

func (n *Node) validate() (bool, []error) {
	v := newValidator()
	if n.Host == "" {
		v.addError(fmt.Errorf("Node host field is required"))
	}
	if n.IP == "" {
		v.addError(fmt.Errorf("Node IP field is required"))
	}
	if ip := net.ParseIP(n.IP); ip == nil {
		v.addError(fmt.Errorf("Invalid IP provided"))
	}
	return v.valid()
}
<commit_msg>KIS-32: Add validation to SSH key<commit_after>package install

import (
	"errors"
	"fmt"
	"net"
	"os"
	"time"
)

// ValidatePlan runs validation against the installation plan to ensure
// that the plan contains valid user input. Returns true, nil if the validation
// is successful. Otherwise, returns false and a collection of validation errors.
func ValidatePlan(p *Plan) (bool, []error) {
	v := newValidator()
	v.validate(p)
	return v.valid()
}

type validatable interface {
	validate() (bool, []error)
}

type validator struct {
	errs []error
}

func newValidator() *validator {
	return &validator{
		errs: []error{},
	}
}

func (v *validator) addError(err ...error) {
	v.errs = append(v.errs, err...)
}

func (v *validator) validate(obj validatable) {
	if ok, err := obj.validate(); !ok {
		v.addError(err...)
	}
}

func (v *validator) validateWithErrPrefix(prefix string, obj validatable) {
	if ok, err := obj.validate(); !ok {
		newErrs := make([]error, len(err), len(err))
		for i, err := range err {
			newErrs[i] = fmt.Errorf("%s: %v", prefix, err)
		}
		v.addError(newErrs...)
	}
}

func (v *validator) valid() (bool, []error) {
	if len(v.errs) > 0 {
		return false, v.errs
	}
	return true, nil
}

func (p *Plan) validate() (bool, []error) {
	v := newValidator()

	v.validate(&p.Cluster)
	v.validateWithErrPrefix("Etcd nodes", &p.Etcd)
	v.validateWithErrPrefix("Master nodes", &p.Master)
	v.validateWithErrPrefix("Worker nodes", &p.Worker)

	return v.valid()
}

func (c *Cluster) validate() (bool, []error) {
	v := newValidator()
	if c.Name == "" {
		v.addError(errors.New("Cluster name cannot be empty"))
	}
	v.validate(&c.Networking)
	v.validate(&c.Certificates)
	v.validate(&c.SSH)

	return v.valid()
}

func (n *NetworkConfig) validate() (bool, []error) {
	v := newValidator()
	if n.Type == "" {
		v.addError(errors.New("Networking type cannot be empty"))
	}
	if n.Type != "bridged" && n.Type != "overlay" {
		v.addError(fmt.Errorf("Invalid networking type %q was provided", n.Type))
	}
	if n.PodCIDRBlock == "" {
		v.addError(errors.New("Pod CIDR block cannot be empty"))
	}
	if _, _, err := net.ParseCIDR(n.PodCIDRBlock); n.PodCIDRBlock != "" && err != nil {
		v.addError(fmt.Errorf("Invalid Pod CIDR block provided: %v", err))
	}

	if n.ServiceCIDRBlock == "" {
		v.addError(errors.New("Service CIDR block cannot be empty"))
	}
	if _, _, err := net.ParseCIDR(n.ServiceCIDRBlock); n.ServiceCIDRBlock != "" && err != nil {
		v.addError(fmt.Errorf("Invalid Service CIDR block provided: %v", err))
	}
	return v.valid()
}

func (c *CertsConfig) validate() (bool, []error) {
	v := newValidator()
	if _, err := time.ParseDuration(c.Expiry); err != nil {
		v.addError(fmt.Errorf("Invalid certificate expiry %q provided: %v", c.Expiry, err))
	}

	if c.LocationCity == "" {
		v.addError(errors.New("Certificate location_city field is required"))
	}
	if c.LocationState == "" {
		v.addError(errors.New("Certificate location_state field is required"))
	}
	if c.LocationCountry == "" {
		v.addError(errors.New("Certificate location_country field is required"))
	}
	return v.valid()
}

func (s *SSHConfig) validate() (bool, []error) {
	v := newValidator()
	if s.User == "" {
		v.addError(errors.New("SSH user field is required"))
	}
	if s.Key == "" {
		v.addError(errors.New("SSH key field is required"))
	}
	if _, err := os.Stat(s.Key); os.IsNotExist(err) {
		v.addError(fmt.Errorf("SSH Key file was not found at %q", s.Key))
	}
	if s.Port < 1 || s.Port > 65535 {
		v.addError(fmt.Errorf("SSH port %d is invalid. Port must be in the range 1-65535", s.Port))
	}
	return v.valid()
}

func (ng *NodeGroup) validate() (bool, []error) {
	v := newValidator()
	if len(ng.Nodes) != ng.ExpectedCount {
		v.addError(fmt.Errorf("Expected node count (%d) does not match the number of nodes provided (%d)", ng.ExpectedCount, len(ng.Nodes)))
	}
	for i, n := range ng.Nodes {
		v.validateWithErrPrefix(fmt.Sprintf("Node #%d", i+1), &n)
	}
	return v.valid()
}

func (mng *MasterNodeGroup) validate() (bool, []error) {
	v := newValidator()
	v.validate(&mng.NodeGroup)

	if mng.LoadBalancedFQDN == "" {
		v.addError(fmt.Errorf("Load balanced FQDN is required"))
	}

	if mng.LoadBalancedShortName == "" {
		v.addError(fmt.Errorf("Load balanced shortname is required"))
	}

	return v.valid()
}

func (n *Node) validate() (bool, []error) {
	v := newValidator()
	if n.Host == "" {
		v.addError(fmt.Errorf("Node host field is required"))
	}
	if n.IP == "" {
		v.addError(fmt.Errorf("Node IP field is required"))
	}
	if ip := net.ParseIP(n.IP); ip == nil {
		v.addError(fmt.Errorf("Invalid IP provided"))
	}
	return v.valid()
}
<|endoftext|>


<commit_before>package middleware

import (
	"net/http"
	"net/url"

	"github.com/felixge/httpsnoop"
	"github.com/hellofresh/janus/pkg/metrics"
	"github.com/hellofresh/stats-go/client"
	log "github.com/sirupsen/logrus"
)

const notFoundPath = "/-not-found-"

// Stats represents the stats middleware
type Stats struct {
	statsClient client.Client
}

// NewStats creates a new instance of Stats
func NewStats(statsClient client.Client) *Stats {
	return &Stats{statsClient}
}

// Handler is the middleware function
func (m *Stats) Handler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.WithField("path", r.URL.Path).Debug("Starting Stats middleware")
		r = r.WithContext(metrics.NewContext(r.Context(), m.statsClient))

		timing := m.statsClient.BuildTimer().Start()

		// reverse proxy replaces original request with target request, so keep required fields of the original one
		originalURL := &url.URL{}
		*originalURL = *r.URL
		originalRequest := &http.Request{Method: r.Method, URL: originalURL}

		mt := httpsnoop.CaptureMetrics(handler, w, r)

		log.WithFields(log.Fields{
			"original_path": originalURL.Path,
			"request_url":   r.URL.Path,
		}).Debug("Track request stats")

		success := mt.Code < http.StatusBadRequest
		if mt.Code == http.StatusNotFound {
			log.WithField("path", originalURL.Path).Warn("Unknown endpoint requested")
			originalURL.Path = notFoundPath
		}
		m.statsClient.TrackRequest(originalRequest, timing, success)
	})
}
<commit_msg>New tracking<commit_after>package middleware

import (
	"net/http"
	"net/url"

	"github.com/felixge/httpsnoop"
	"github.com/hellofresh/janus/pkg/metrics"
	"github.com/hellofresh/stats-go/client"
	"github.com/hellofresh/stats-go/timer"
	log "github.com/sirupsen/logrus"
)

const notFoundPath = "/-not-found-"

// Stats represents the stats middleware
type Stats struct {
	statsClient client.Client
}

// NewStats creates a new instance of Stats
func NewStats(statsClient client.Client) *Stats {
	return &Stats{statsClient}
}

// Handler is the middleware function
func (m *Stats) Handler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.WithField("path", r.URL.Path).Debug("Starting Stats middleware")
		r = r.WithContext(metrics.NewContext(r.Context(), m.statsClient))

		// reverse proxy replaces original request with target request, so keep required fields of the original one
		originalURL := &url.URL{}
		*originalURL = *r.URL
		originalRequest := &http.Request{Method: r.Method, URL: originalURL}

		mt := httpsnoop.CaptureMetrics(handler, w, r)
		t := timer.NewDuration(mt.Duration)

		log.WithFields(log.Fields{
			"original_path": originalURL.Path,
			"request_url":   r.URL.Path,
		}).Debug("Track request stats")

		success := mt.Code < http.StatusBadRequest
		if mt.Code == http.StatusNotFound {
			log.WithField("path", originalURL.Path).Warn("Unknown endpoint requested")
			originalURL.Path = notFoundPath
		}
		m.statsClient.TrackRequest(originalRequest, t, success)
	})
}
<|endoftext|>


<commit_before>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This LevelDB Go implementation is based on LevelDB C++ implementation.
// Which contains the following header:
//   Copyright (c) 2011 The LevelDB Authors. All rights reserved.
//   Use of this source code is governed by a BSD-style license that can be
//   found in the LEVELDBCPP_LICENSE file. See the LEVELDBCPP_AUTHORS file
//   for names of contributors.

package db

import (
	"leveldb"
	"leveldb/descriptor"
	"leveldb/log"
	"sync"
)

type session struct {
	sync.RWMutex

	desc    descriptor.Descriptor
	opt     *leveldb.Options
	cmp     leveldb.Comparator
	icmp    *iKeyComparator
	filter  leveldb.FilterPolicy
	ifilter *iFilterPolicy
	tops    *tOps

	manifestNum uint64
	manifest    *log.Writer

	st struct {
		sync.RWMutex
		version         *version
		versions        []*version
		nextNum         stateNum
		logNum          uint64
		sequence        uint64
		compactPointers [kNumLevels]iKey
	}
}

func newSession(desc descriptor.Descriptor, opt *leveldb.Options) *session {
	s := &session{
		desc:   desc,
		opt:    opt,
		cmp:    opt.GetComparator(),
		filter: opt.GetFilterPolicy(),
	}
	s.icmp = &iKeyComparator{s.cmp}
	if s.filter != nil {
		s.ifilter = &iFilterPolicy{s.filter}
	}
	s.tops = newTableOps(s, s.opt.GetMaxOpenFiles())
	s.setVersion(&version{s: s})
	return s
}

// Create a new database session
func (s *session) create() (err error) {
	// create manifest
	s.manifestNum = s.allocFileNum()
	err = s.createManifest(s.manifestNum, nil)
	if err != nil {
		return
	}
	return
}

// Recover a database session
func (s *session) recover() (err error) {
	var m descriptor.File
	m, err = s.desc.GetMainManifest()
	if err != nil {
		return
	}

	r, err := m.Open()
	if err != nil {
		return
	}

	st := &s.st

	cmpName := s.opt.GetComparator().Name()
	staging := st.version.newStaging()
	srec := new(sessionRecord)
	lr := log.NewReader(r, true)
	for lr.Next() {
		rec := new(sessionRecord)
		err = rec.decode(lr.Record())
		if err != nil {
			continue
		}

		if rec.hasComparator && rec.comparator != cmpName {
			return leveldb.ErrInvalid("invalid comparator, " +
				"want '" + cmpName + "', " +
				"got '" + rec.comparator + "'")
		}

		// save compact pointers
		for _, rp := range rec.compactPointers {
			st.compactPointers[rp.level] = iKey(rp.key)
		}

		// commit record to version staging
		staging.commit(rec)

		if rec.hasLogNum {
			srec.setLogNum(rec.logNum)
		}
		if rec.hasNextNum {
			srec.setNextNum(rec.nextNum)
		}
		if rec.hasSequence {
			srec.setSequence(rec.sequence)
		}
	}
	// check for error in log reader
	err = lr.Error()
	if err != nil {
		return
	}

	switch false {
	case srec.hasNextNum:
		err = leveldb.ErrCorrupt("manifest missing next file number")
	case srec.hasLogNum:
		err = leveldb.ErrCorrupt("manifest missing log file number")
	case srec.hasSequence:
		err = leveldb.ErrCorrupt("manifest missing sequence number")
	}
	if err != nil {
		return
	}

	s.setVersion(staging.finish())
	s.setFileNum(srec.nextNum)
	s.recordCommited(srec)
	// allocate manifest file number now, but create new
	// manifest lazily during first session commit
	s.manifestNum = s.allocFileNum()

	return
}

func (s *session) commit(r *sessionRecord) (err error) {
	// spawn new version based on current version
	nv := s.st.version.spawn(r)

	if s.manifest == nil {
		// manifest log writer not yet created, create one
		err = s.createManifest(s.manifestNum, nv)
	} else {
		// fill record
		s.fillRecord(r, false)
		defer func() {
			if err == nil {
				s.recordCommited(r)
			}
		}()
		// write log
		err = s.manifest.Append(r.encode())
	}

	// finally, apply new version if no error rise
	if err == nil {
		s.setVersion(nv)
	}

	return
}

func (s *session) needCompaction() bool {
	st := &s.st
	st.RLock()
	defer st.RUnlock()
	v := st.version
	return v.compactionScore >= 1 || v.seekCompactionTable != nil
}

func (s *session) pickCompaction() (c *compaction) {
	st := &s.st

	st.RLock()
	v := st.version
	bySize := v.compactionScore >= 1
	bySeek := v.seekCompactionTable != nil
	st.RUnlock()

	var level int
	var t0 tFiles
	if bySize {
		level = v.compactionLevel
		cp := s.st.compactPointers[level]
		tt := v.tables[level]
		for _, t := range tt {
			if cp == nil || s.icmp.Compare(t.largest, cp) > 0 {
				t0 = append(t0, t)
				break
			}
		}
		if len(t0) == 0 {
			t0 = append(t0, tt[0])
		}
	} else if bySeek {
		level = v.seekCompactionLevel
		t0 = append(t0, v.seekCompactionTable)
	} else {
		return
	}

	c = &compaction{s: s, version: v, level: level}
	if level == 0 {
		min, max := t0.getRange(s.icmp)
		t0 = nil
		v.tables[0].getOverlaps(min.ukey(), max.ukey(), &t0, false, s.cmp)
	}

	c.tables[0] = t0
	c.expand()
	return
}

type compaction struct {
	s       *session
	version *version

	level  int
	tables [2]tFiles

	gp              tFiles
	gpidx           int
	seenKey         bool
	overlappedBytes uint64
	min, max        iKey

	tPtrs [kNumLevels]int
}

func (c *compaction) expand() {
	s := c.s
	v := c.version

	level := c.level
	vt0, vt1 := v.tables[level], v.tables[level+1]

	t0, t1 := c.tables[0], c.tables[1]
	min, max := t0.getRange(s.icmp)
	vt1.getOverlaps(min.ukey(), max.ukey(), &t1, true, s.cmp)

	// Get entire range covered by compaction
	amin, amax := append(t0, t1...).getRange(s.icmp)

	// See if we can grow the number of inputs in "level" without
	// changing the number of "level+1" files we pick up.
	if len(t1) > 0 {
		var exp0 tFiles
		vt0.getOverlaps(amin.ukey(), amax.ukey(), &exp0, level != 0, s.cmp)
		if len(exp0) > len(t0) && t1.size()+exp0.size() < kExpCompactionMaxBytes {
			var exp1 tFiles
			xmin, xmax := exp0.getRange(s.icmp)
			vt1.getOverlaps(xmin.ukey(), xmax.ukey(), &exp1, true, s.cmp)
			if len(exp1) == len(t1) {
				s.printf("Compaction: expanding@%d %d+%d (%d+%d bytes) to %d+%d (%d+%d bytes)",
					level, len(t0), len(t1), t0.size(), t1.size(),
					len(exp0), len(exp1), exp0.size(), exp1.size())
				min, max = xmin, xmax
				t0, t1 = exp0, exp1
				amin, amax = append(t0, t1...).getRange(s.icmp)
			}
		}
	}

	// Compute the set of grandparent files that overlap this compaction
	// (parent == level+1; grandparent == level+2)
	if level+2 < kNumLevels {
		v.tables[level+2].getOverlaps(amin.ukey(), amax.ukey(), &c.gp, true, s.cmp)
	}

	c.tables[0], c.tables[1] = t0, t1
	c.min, c.max = min, max
}

func (c *compaction) trivial() bool {
	return len(c.tables[0]) == 1 && len(c.tables[1]) == 0 && c.gp.size() <= kMaxGrandParentOverlapBytes
}

func (c *compaction) isBaseLevelForKey(key []byte) bool {
	s := c.s
	v := c.version
	cmp := s.cmp
	for level := c.level; level < kNumLevels; level++ {
		tt := v.tables[level]
		for c.tPtrs[level] < len(tt) {
			t := tt[c.tPtrs[level]]
			if cmp.Compare(key, t.largest.ukey()) <= 0 {
				// We've advanced far enough
				if cmp.Compare(key, t.smallest.ukey()) >= 0 {
					// Key falls in this file's range, so definitely not base level
					return false
				}
				break
			}
			c.tPtrs[level]++
		}
	}
	return true
}

func (c *compaction) shouldStopBefore(key iKey) bool {
	icmp := c.s.icmp
	for ; c.gpidx < len(c.gp); c.gpidx++ {
		gp := c.gp[c.gpidx]
		if icmp.Compare(key, gp.largest) <= 0 {
			break
		}
		if c.seenKey {
			c.overlappedBytes += gp.size
		}
	}
	c.seenKey = true

	if c.overlappedBytes > kMaxGrandParentOverlapBytes {
		// Too much overlap for current output; start new output
		c.overlappedBytes = 0
		return true
	}
	return false
}

func (c *compaction) newIterator() leveldb.Iterator {
	s := c.s

	level := c.level
	icap := 2
	if c.level == 0 {
		icap = len(c.tables[0]) + 1
	}
	iters := make([]leveldb.Iterator, 0, icap)

	ro := &leveldb.ReadOptions{}

	for i, tt := range c.tables {
		if len(tt) == 0 {
			continue
		}

		if level+i == 0 {
			for _, t := range tt {
				iters = append(iters, s.tops.newIterator(t, ro))
			}
		} else {
			iter := leveldb.NewIndexedIterator(tt.newIndexIterator(s.tops, s.icmp, ro))
			iters = append(iters, iter)
		}
	}

	return leveldb.NewMergedIterator(iters, s.icmp)
}
<commit_msg>db: session: sanitize comparator and filter policy in options<commit_after>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This LevelDB Go implementation is based on LevelDB C++ implementation.
// Which contains the following header:
//   Copyright (c) 2011 The LevelDB Authors. All rights reserved.
//   Use of this source code is governed by a BSD-style license that can be
//   found in the LEVELDBCPP_LICENSE file. See the LEVELDBCPP_AUTHORS file
//   for names of contributors.

package db

import (
	"leveldb"
	"leveldb/descriptor"
	"leveldb/log"
	"sync"
)

type session struct {
	sync.RWMutex

	desc    descriptor.Descriptor
	opt     *leveldb.Options
	cmp     leveldb.Comparator
	icmp    *iKeyComparator
	filter  leveldb.FilterPolicy
	ifilter *iFilterPolicy
	tops    *tOps

	manifestNum uint64
	manifest    *log.Writer

	st struct {
		sync.RWMutex
		version         *version
		versions        []*version
		nextNum         stateNum
		logNum          uint64
		sequence        uint64
		compactPointers [kNumLevels]iKey
	}
}

func newSession(desc descriptor.Descriptor, opt *leveldb.Options) *session {
	s := &session{
		desc:   desc,
		opt:    opt,
		cmp:    opt.GetComparator(),
		filter: opt.GetFilterPolicy(),
	}
	s.icmp = &iKeyComparator{s.cmp}
	opt.Comparator = s.icmp
	if s.filter != nil {
		s.ifilter = &iFilterPolicy{s.filter}
		opt.FilterPolicy = s.ifilter
	}
	s.tops = newTableOps(s, s.opt.GetMaxOpenFiles())
	s.setVersion(&version{s: s})
	return s
}

// Create a new database session
func (s *session) create() (err error) {
	// create manifest
	s.manifestNum = s.allocFileNum()
	err = s.createManifest(s.manifestNum, nil)
	if err != nil {
		return
	}
	return
}

// Recover a database session
func (s *session) recover() (err error) {
	var m descriptor.File
	m, err = s.desc.GetMainManifest()
	if err != nil {
		return
	}

	r, err := m.Open()
	if err != nil {
		return
	}

	st := &s.st

	cmpName := s.opt.GetComparator().Name()
	staging := st.version.newStaging()
	srec := new(sessionRecord)
	lr := log.NewReader(r, true)
	for lr.Next() {
		rec := new(sessionRecord)
		err = rec.decode(lr.Record())
		if err != nil {
			continue
		}

		if rec.hasComparator && rec.comparator != cmpName {
			return leveldb.ErrInvalid("invalid comparator, " +
				"want '" + cmpName + "', " +
				"got '" + rec.comparator + "'")
		}

		// save compact pointers
		for _, rp := range rec.compactPointers {
			st.compactPointers[rp.level] = iKey(rp.key)
		}

		// commit record to version staging
		staging.commit(rec)

		if rec.hasLogNum {
			srec.setLogNum(rec.logNum)
		}
		if rec.hasNextNum {
			srec.setNextNum(rec.nextNum)
		}
		if rec.hasSequence {
			srec.setSequence(rec.sequence)
		}
	}
	// check for error in log reader
	err = lr.Error()
	if err != nil {
		return
	}

	switch false {
	case srec.hasNextNum:
		err = leveldb.ErrCorrupt("manifest missing next file number")
	case srec.hasLogNum:
		err = leveldb.ErrCorrupt("manifest missing log file number")
	case srec.hasSequence:
		err = leveldb.ErrCorrupt("manifest missing sequence number")
	}
	if err != nil {
		return
	}

	s.setVersion(staging.finish())
	s.setFileNum(srec.nextNum)
	s.recordCommited(srec)
	// allocate manifest file number now, but create new
	// manifest lazily during first session commit
	s.manifestNum = s.allocFileNum()

	return
}

func (s *session) commit(r *sessionRecord) (err error) {
	// spawn new version based on current version
	nv := s.st.version.spawn(r)

	if s.manifest == nil {
		// manifest log writer not yet created, create one
		err = s.createManifest(s.manifestNum, nv)
	} else {
		// fill record
		s.fillRecord(r, false)
		defer func() {
			if err == nil {
				s.recordCommited(r)
			}
		}()
		// write log
		err = s.manifest.Append(r.encode())
	}

	// finally, apply new version if no error rise
	if err == nil {
		s.setVersion(nv)
	}

	return
}

func (s *session) needCompaction() bool {
	st := &s.st
	st.RLock()
	defer st.RUnlock()
	v := st.version
	return v.compactionScore >= 1 || v.seekCompactionTable != nil
}

func (s *session) pickCompaction() (c *compaction) {
	st := &s.st

	st.RLock()
	v := st.version
	bySize := v.compactionScore >= 1
	bySeek := v.seekCompactionTable != nil
	st.RUnlock()

	var level int
	var t0 tFiles
	if bySize {
		level = v.compactionLevel
		cp := s.st.compactPointers[level]
		tt := v.tables[level]
		for _, t := range tt {
			if cp == nil || s.icmp.Compare(t.largest, cp) > 0 {
				t0 = append(t0, t)
				break
			}
		}
		if len(t0) == 0 {
			t0 = append(t0, tt[0])
		}
	} else if bySeek {
		level = v.seekCompactionLevel
		t0 = append(t0, v.seekCompactionTable)
	} else {
		return
	}

	c = &compaction{s: s, version: v, level: level}
	if level == 0 {
		min, max := t0.getRange(s.icmp)
		t0 = nil
		v.tables[0].getOverlaps(min.ukey(), max.ukey(), &t0, false, s.cmp)
	}

	c.tables[0] = t0
	c.expand()
	return
}

type compaction struct {
	s       *session
	version *version

	level  int
	tables [2]tFiles

	gp              tFiles
	gpidx           int
	seenKey         bool
	overlappedBytes uint64
	min, max        iKey

	tPtrs [kNumLevels]int
}

func (c *compaction) expand() {
	s := c.s
	v := c.version

	level := c.level
	vt0, vt1 := v.tables[level], v.tables[level+1]

	t0, t1 := c.tables[0], c.tables[1]
	min, max := t0.getRange(s.icmp)
	vt1.getOverlaps(min.ukey(), max.ukey(), &t1, true, s.cmp)

	// Get entire range covered by compaction
	amin, amax := append(t0, t1...).getRange(s.icmp)

	// See if we can grow the number of inputs in "level" without
	// changing the number of "level+1" files we pick up.
	if len(t1) > 0 {
		var exp0 tFiles
		vt0.getOverlaps(amin.ukey(), amax.ukey(), &exp0, level != 0, s.cmp)
		if len(exp0) > len(t0) && t1.size()+exp0.size() < kExpCompactionMaxBytes {
			var exp1 tFiles
			xmin, xmax := exp0.getRange(s.icmp)
			vt1.getOverlaps(xmin.ukey(), xmax.ukey(), &exp1, true, s.cmp)
			if len(exp1) == len(t1) {
				s.printf("Compaction: expanding@%d %d+%d (%d+%d bytes) to %d+%d (%d+%d bytes)",
					level, len(t0), len(t1), t0.size(), t1.size(),
					len(exp0), len(exp1), exp0.size(), exp1.size())
				min, max = xmin, xmax
				t0, t1 = exp0, exp1
				amin, amax = append(t0, t1...).getRange(s.icmp)
			}
		}
	}

	// Compute the set of grandparent files that overlap this compaction
	// (parent == level+1; grandparent == level+2)
	if level+2 < kNumLevels {
		v.tables[level+2].getOverlaps(amin.ukey(), amax.ukey(), &c.gp, true, s.cmp)
	}

	c.tables[0], c.tables[1] = t0, t1
	c.min, c.max = min, max
}

func (c *compaction) trivial() bool {
	return len(c.tables[0]) == 1 && len(c.tables[1]) == 0 && c.gp.size() <= kMaxGrandParentOverlapBytes
}

func (c *compaction) isBaseLevelForKey(key []byte) bool {
	s := c.s
	v := c.version
	cmp := s.cmp
	for level := c.level; level < kNumLevels; level++ {
		tt := v.tables[level]
		for c.tPtrs[level] < len(tt) {
			t := tt[c.tPtrs[level]]
			if cmp.Compare(key, t.largest.ukey()) <= 0 {
				// We've advanced far enough
				if cmp.Compare(key, t.smallest.ukey()) >= 0 {
					// Key falls in this file's range, so definitely not base level
					return false
				}
				break
			}
			c.tPtrs[level]++
		}
	}
	return true
}

func (c *compaction) shouldStopBefore(key iKey) bool {
	icmp := c.s.icmp
	for ; c.gpidx < len(c.gp); c.gpidx++ {
		gp := c.gp[c.gpidx]
		if icmp.Compare(key, gp.largest) <= 0 {
			break
		}
		if c.seenKey {
			c.overlappedBytes += gp.size
		}
	}
	c.seenKey = true

	if c.overlappedBytes > kMaxGrandParentOverlapBytes {
		// Too much overlap for current output; start new output
		c.overlappedBytes = 0
		return true
	}
	return false
}

func (c *compaction) newIterator() leveldb.Iterator {
	s := c.s

	level := c.level
	icap := 2
	if c.level == 0 {
		icap = len(c.tables[0]) + 1
	}
	iters := make([]leveldb.Iterator, 0, icap)

	ro := &leveldb.ReadOptions{}

	for i, tt := range c.tables {
		if len(tt) == 0 {
			continue
		}

		if level+i == 0 {
			for _, t := range tt {
				iters = append(iters, s.tops.newIterator(t, ro))
			}
		} else {
			iter := leveldb.NewIndexedIterator(tt.newIndexIterator(s.tops, s.icmp, ro))
			iters = append(iters, iter)
		}
	}

	return leveldb.NewMergedIterator(iters, s.icmp)
}
<|endoftext|>


<commit_before>package settings

import (
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	authsettings "github.com/rancher/rancher/pkg/auth/settings"
	"github.com/sirupsen/logrus"
)

var (
	releasePattern = regexp.MustCompile("^v[0-9]")
	settings       = map[string]Setting{}
	provider       Provider
	InjectDefaults string

	AgentImage                        = NewSetting("agent-image", "rancher/rancher-agent:master-head")
	AuthImage                         = NewSetting("auth-image", v32.ToolsSystemImages.AuthSystemImages.KubeAPIAuth)
	AuthTokenMaxTTLMinutes            = NewSetting("auth-token-max-ttl-minutes", "0") // never expire
	AuthorizationCacheTTLSeconds      = NewSetting("authorization-cache-ttl-seconds", "10")
	AuthorizationDenyCacheTTLSeconds  = NewSetting("authorization-deny-cache-ttl-seconds", "10")
	AzureGroupCacheSize               = NewSetting("azure-group-cache-size", "10000")
	CACerts                           = NewSetting("cacerts", "")
	CLIURLDarwin                      = NewSetting("cli-url-darwin", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-darwin-amd64-v1.0.0-alpha8.tar.gz")
	CLIURLLinux                       = NewSetting("cli-url-linux", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-linux-amd64-v1.0.0-alpha8.tar.gz")
	CLIURLWindows                     = NewSetting("cli-url-windows", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-windows-386-v1.0.0-alpha8.zip")
	ClusterControllerStartCount       = NewSetting("cluster-controller-start-count", "50")
	EngineInstallURL                  = NewSetting("engine-install-url", "https://releases.rancher.com/install-docker/20.10.sh")
	EngineISOURL                      = NewSetting("engine-iso-url", "https://releases.rancher.com/os/latest/rancheros-vmware.iso")
	EngineNewestVersion               = NewSetting("engine-newest-version", "v17.12.0")
	EngineSupportedRange              = NewSetting("engine-supported-range", "~v1.11.2 || ~v1.12.0 || ~v1.13.0 || ~v17.03.0 || ~v17.06.0 || ~v17.09.0 || ~v18.06.0 || ~v18.09.0 || ~v19.03.0 || ~v20.10.0 ")
	FirstLogin                        = NewSetting("first-login", "true")
	GlobalRegistryEnabled             = NewSetting("global-registry-enabled", "false")
	GithubProxyAPIURL                 = NewSetting("github-proxy-api-url", "https://api.github.com")
	HelmVersion                       = NewSetting("helm-version", "dev")
	HelmMaxHistory                    = NewSetting("helm-max-history", "10")
	IngressIPDomain                   = NewSetting("ingress-ip-domain", "xip.io")
	InstallUUID                       = NewSetting("install-uuid", "")
	InternalServerURL                 = NewSetting("internal-server-url", "")
	InternalCACerts                   = NewSetting("internal-cacerts", "")
	JailerTimeout                     = NewSetting("jailer-timeout", "60")
	KubeconfigGenerateToken           = NewSetting("kubeconfig-generate-token", "true")
	KubeconfigTokenTTLMinutes         = NewSetting("kubeconfig-token-ttl-minutes", "960") // 16 hours
	KubernetesVersion                 = NewSetting("k8s-version", "")
	KubernetesVersionToServiceOptions = NewSetting("k8s-version-to-service-options", "")
	KubernetesVersionToSystemImages   = NewSetting("k8s-version-to-images", "")
	KubernetesVersionsCurrent         = NewSetting("k8s-versions-current", "")
	KubernetesVersionsDeprecated      = NewSetting("k8s-versions-deprecated", "")
	KDMBranch                         = NewSetting("kdm-branch", "dev-v2.6")
	MachineVersion                    = NewSetting("machine-version", "dev")
	Namespace                         = NewSetting("namespace", os.Getenv("CATTLE_NAMESPACE"))
	PeerServices                      = NewSetting("peer-service", os.Getenv("CATTLE_PEER_SERVICE"))
	RDNSServerBaseURL                 = NewSetting("rdns-base-url", "https://api.lb.rancher.cloud/v1")
	RkeVersion                        = NewSetting("rke-version", "")
	RkeMetadataConfig                 = NewSetting("rke-metadata-config", getMetadataConfig())
	ServerImage                       = NewSetting("server-image", "rancher/rancher")
	ServerURL                         = NewSetting("server-url", "")
	ServerVersion                     = NewSetting("server-version", "dev")
	SystemAgentVersion                = NewSetting("system-agent-version", "")
	SystemAgentInstallScript          = NewSetting("system-agent-install-script", "")
	SystemAgentInstallerImage         = NewSetting("system-agent-installer-image", "docker.io/rancher/system-agent-installer-")
	SystemAgentUpgradeImage           = NewSetting("system-agent-upgrade-image", "")
	SystemDefaultRegistry             = NewSetting("system-default-registry", "")
	SystemNamespaces                  = NewSetting("system-namespaces", "kube-system,kube-public,cattle-system,cattle-alerting,cattle-logging,cattle-pipeline,cattle-prometheus,ingress-nginx,cattle-global-data,cattle-istio,kube-node-lease,cert-manager,cattle-global-nt,security-scan,cattle-fleet-system")
	TelemetryOpt                      = NewSetting("telemetry-opt", "")
	TokenHashing                      = NewSetting("token-hashing", "true")
	TLSMinVersion                     = NewSetting("tls-min-version", "1.2")
	TLSCiphers                        = NewSetting("tls-ciphers", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305")
	UIBanners                         = NewSetting("ui-banners", "{}")
	UIBrand                           = NewSetting("ui-brand", "")
	UIDefaultLanding                  = NewSetting("ui-default-landing", "vue")
	UIFeedBackForm                    = NewSetting("ui-feedback-form", "")
	UIIndex                           = NewSetting("ui-index", "https://releases.rancher.com/ui/latest2/index.html")
	UIPath                            = NewSetting("ui-path", "/usr/share/rancher/ui")
	UIDashboardIndex                  = NewSetting("ui-dashboard-index", "https://releases.rancher.com/dashboard/latest/index.html")
	UIDashboardPath                   = NewSetting("ui-dashboard-path", "/usr/share/rancher/ui-dashboard")
	UIPreferred                       = NewSetting("ui-preferred", "vue")
	UIOfflinePreferred                = NewSetting("ui-offline-preferred", "dynamic")
	UIIssues                          = NewSetting("ui-issues", "")
	UIPL                              = NewSetting("ui-pl", "rancher")
	UICommunityLinks                  = NewSetting("ui-community-links", "true")
	UIKubernetesSupportedVersions     = NewSetting("ui-k8s-supported-versions-range", ">= 1.11.0 <=1.14.x")
	UIKubernetesDefaultVersion        = NewSetting("ui-k8s-default-version-range", "<=1.14.x")
	WhitelistDomain                   = NewSetting("whitelist-domain", "forums.rancher.com")
	WhitelistEnvironmentVars          = NewSetting("whitelist-envvars", "HTTP_PROXY,HTTPS_PROXY,NO_PROXY")
	AuthUserInfoResyncCron            = NewSetting("auth-user-info-resync-cron", "0 0 * * *")
	AuthUserSessionTTLMinutes         = NewSetting("auth-user-session-ttl-minutes", "960")   // 16 hours
	AuthUserInfoMaxAgeSeconds         = NewSetting("auth-user-info-max-age-seconds", "3600") // 1 hour
	APIUIVersion                      = NewSetting("api-ui-version", "1.1.6")                // Please update the CATTLE_API_UI_VERSION in package/Dockerfile when updating the version here.
	RotateCertsIfExpiringInDays       = NewSetting("rotate-certs-if-expiring-in-days", "7")  // 7 days
	ClusterTemplateEnforcement        = NewSetting("cluster-template-enforcement", "false")
	InitialDockerRootDir              = NewSetting("initial-docker-root-dir", "/var/lib/docker")
	SystemCatalog                     = NewSetting("system-catalog", "external") // Options are 'external' or 'bundled'
	ChartDefaultBranch                = NewSetting("chart-default-branch", "dev-v2.6")
	PartnerChartDefaultBranch         = NewSetting("partner-chart-default-branch", "main")
	RKE2ChartDefaultBranch            = NewSetting("rke2-chart-default-branch", "main")
	FleetDefaultWorkspaceName         = NewSetting("fleet-default-workspace-name", "fleet-default") // fleetWorkspaceName to assign to clusters with none
	ShellImage                        = NewSetting("shell-image", "rancher/shell:v0.1.6")
	IgnoreNodeName                    = NewSetting("ignore-node-name", "") // nodes to ignore when syncing v1.node to v3.node
	NoDefaultAdmin                    = NewSetting("no-default-admin", "")
	RestrictedDefaultAdmin            = NewSetting("restricted-default-admin", "false") // When bootstrapping the admin for the first time, give them the global role restricted-admin
	AKSUpstreamRefresh                = NewSetting("aks-refresh", "300")
	EKSUpstreamRefreshCron            = NewSetting("eks-refresh-cron", "*/5 * * * *") // EKSUpstreamRefreshCron is deprecated and will be replaced by EKSUpstreamRefresh
	EKSUpstreamRefresh                = NewSetting("eks-refresh", "300")
	GKEUpstreamRefresh                = NewSetting("gke-refresh", "300")
	HideLocalCluster                  = NewSetting("hide-local-cluster", "false")
	MachineProvisionImage             = NewSetting("machine-provision-image", "rancher/machine:v0.15.0-rancher60")

	FleetMinVersion          = NewSetting("fleet-min-version", "")
	RancherWebhookMinVersion = NewSetting("rancher-webhook-min-version", "")
)

func FullShellImage() string {
	return PrefixPrivateRegistry(ShellImage.Get())
}

func PrefixPrivateRegistry(image string) string {
	private := SystemDefaultRegistry.Get()
	if private == "" {
		return image
	}
	return private + "/" + image
}

func IsRelease() bool {
	return !strings.Contains(ServerVersion.Get(), "head") && releasePattern.MatchString(ServerVersion.Get())
}

func init() {
	// setup auth setting
	authsettings.AuthUserInfoResyncCron = AuthUserInfoResyncCron
	authsettings.AuthUserSessionTTLMinutes = AuthUserSessionTTLMinutes
	authsettings.AuthUserInfoMaxAgeSeconds = AuthUserInfoMaxAgeSeconds
	authsettings.FirstLogin = FirstLogin

	if InjectDefaults == "" {
		return
	}
	defaults := map[string]string{}
	if err := json.Unmarshal([]byte(InjectDefaults), &defaults); err != nil {
		return
	}
	for name, defaultValue := range defaults {
		value, ok := settings[name]
		if !ok {
			continue
		}
		value.Default = defaultValue
		settings[name] = value
	}
}

type Provider interface {
	Get(name string) string
	Set(name, value string) error
	SetIfUnset(name, value string) error
	SetAll(settings map[string]Setting) error
}

type Setting struct {
	Name     string
	Default  string
	ReadOnly bool
}

func (s Setting) SetIfUnset(value string) error {
	if provider == nil {
		return s.Set(value)
	}
	return provider.SetIfUnset(s.Name, value)
}

func (s Setting) Set(value string) error {
	if provider == nil {
		s, ok := settings[s.Name]
		if ok {
			s.Default = value
			settings[s.Name] = s
		}
	} else {
		return provider.Set(s.Name, value)
	}
	return nil
}

func (s Setting) Get() string {
	if provider == nil {
		s := settings[s.Name]
		return s.Default
	}
	return provider.Get(s.Name)
}

func (s Setting) GetInt() int {
	v := s.Get()
	i, err := strconv.Atoi(v)
	if err == nil {
		return i
	}
	logrus.Errorf("failed to parse setting %s=%s as int: %v", s.Name, v, err)
	i, err = strconv.Atoi(s.Default)
	if err != nil {
		return 0
	}
	return i
}

func SetProvider(p Provider) error {
	if err := p.SetAll(settings); err != nil {
		return err
	}
	provider = p
	return nil
}

func NewSetting(name, def string) Setting {
	s := Setting{
		Name:    name,
		Default: def,
	}
	settings[s.Name] = s
	return s
}

func GetEnvKey(key string) string {
	return "CATTLE_" + strings.ToUpper(strings.Replace(key, "-", "_", -1))
}

func getMetadataConfig() string {
	branch := KDMBranch.Get()
	data := map[string]interface{}{
		"url":                      fmt.Sprintf("https://releases.rancher.com/kontainer-driver-metadata/%s/data.json", branch),
		"refresh-interval-minutes": "1440",
	}
	ans, err := json.Marshal(data)
	if err != nil {
		logrus.Errorf("error getting metadata config %v", err)
		return ""
	}
	return string(ans)
}

// GetSettingByID returns a setting that is stored with the given id
func GetSettingByID(id string) string {
	if provider == nil {
		s := settings[id]
		return s.Default
	}
	return provider.Get(id)
}
<commit_msg>Bump helm shell tag to v0.1.7 for helm v3.6.0<commit_after>package settings

import (
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	authsettings "github.com/rancher/rancher/pkg/auth/settings"
	"github.com/sirupsen/logrus"
)

var (
	releasePattern = regexp.MustCompile("^v[0-9]")
	settings       = map[string]Setting{}
	provider       Provider
	InjectDefaults string

	AgentImage                        = NewSetting("agent-image", "rancher/rancher-agent:master-head")
	AuthImage                         = NewSetting("auth-image", v32.ToolsSystemImages.AuthSystemImages.KubeAPIAuth)
	AuthTokenMaxTTLMinutes            = NewSetting("auth-token-max-ttl-minutes", "0") // never expire
	AuthorizationCacheTTLSeconds      = NewSetting("authorization-cache-ttl-seconds", "10")
	AuthorizationDenyCacheTTLSeconds  = NewSetting("authorization-deny-cache-ttl-seconds", "10")
	AzureGroupCacheSize               = NewSetting("azure-group-cache-size", "10000")
	CACerts                           = NewSetting("cacerts", "")
	CLIURLDarwin                      = NewSetting("cli-url-darwin", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-darwin-amd64-v1.0.0-alpha8.tar.gz")
	CLIURLLinux                       = NewSetting("cli-url-linux", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-linux-amd64-v1.0.0-alpha8.tar.gz")
	CLIURLWindows                     = NewSetting("cli-url-windows", "https://releases.rancher.com/cli/v1.0.0-alpha8/rancher-windows-386-v1.0.0-alpha8.zip")
	ClusterControllerStartCount       = NewSetting("cluster-controller-start-count", "50")
	EngineInstallURL                  = NewSetting("engine-install-url", "https://releases.rancher.com/install-docker/20.10.sh")
	EngineISOURL                      = NewSetting("engine-iso-url", "https://releases.rancher.com/os/latest/rancheros-vmware.iso")
	EngineNewestVersion               = NewSetting("engine-newest-version", "v17.12.0")
	EngineSupportedRange              = NewSetting("engine-supported-range", "~v1.11.2 || ~v1.12.0 || ~v1.13.0 || ~v17.03.0 || ~v17.06.0 || ~v17.09.0 || ~v18.06.0 || ~v18.09.0 || ~v19.03.0 || ~v20.10.0 ")
	FirstLogin                        = NewSetting("first-login", "true")
	GlobalRegistryEnabled             = NewSetting("global-registry-enabled", "false")
	GithubProxyAPIURL                 = NewSetting("github-proxy-api-url", "https://api.github.com")
	HelmVersion                       = NewSetting("helm-version", "dev")
	HelmMaxHistory                    = NewSetting("helm-max-history", "10")
	IngressIPDomain                   = NewSetting("ingress-ip-domain", "xip.io")
	InstallUUID                       = NewSetting("install-uuid", "")
	InternalServerURL                 = NewSetting("internal-server-url", "")
	InternalCACerts                   = NewSetting("internal-cacerts", "")
	JailerTimeout                     = NewSetting("jailer-timeout", "60")
	KubeconfigGenerateToken           = NewSetting("kubeconfig-generate-token", "true")
	KubeconfigTokenTTLMinutes         = NewSetting("kubeconfig-token-ttl-minutes", "960") // 16 hours
	KubernetesVersion                 = NewSetting("k8s-version", "")
	KubernetesVersionToServiceOptions = NewSetting("k8s-version-to-service-options", "")
	KubernetesVersionToSystemImages   = NewSetting("k8s-version-to-images", "")
	KubernetesVersionsCurrent         = NewSetting("k8s-versions-current", "")
	KubernetesVersionsDeprecated      = NewSetting("k8s-versions-deprecated", "")
	KDMBranch                         = NewSetting("kdm-branch", "dev-v2.6")
	MachineVersion                    = NewSetting("machine-version", "dev")
	Namespace                         = NewSetting("namespace", os.Getenv("CATTLE_NAMESPACE"))
	PeerServices                      = NewSetting("peer-service", os.Getenv("CATTLE_PEER_SERVICE"))
	RDNSServerBaseURL                 = NewSetting("rdns-base-url", "https://api.lb.rancher.cloud/v1")
	RkeVersion                        = NewSetting("rke-version", "")
	RkeMetadataConfig                 = NewSetting("rke-metadata-config", getMetadataConfig())
	ServerImage                       = NewSetting("server-image", "rancher/rancher")
	ServerURL                         = NewSetting("server-url", "")
	ServerVersion                     = NewSetting("server-version", "dev")
	SystemAgentVersion                = NewSetting("system-agent-version", "")
	SystemAgentInstallScript          = NewSetting("system-agent-install-script", "")
	SystemAgentInstallerImage         = NewSetting("system-agent-installer-image", "docker.io/rancher/system-agent-installer-")
	SystemAgentUpgradeImage           = NewSetting("system-agent-upgrade-image", "")
	SystemDefaultRegistry             = NewSetting("system-default-registry", "")
	SystemNamespaces                  = NewSetting("system-namespaces", "kube-system,kube-public,cattle-system,cattle-alerting,cattle-logging,cattle-pipeline,cattle-prometheus,ingress-nginx,cattle-global-data,cattle-istio,kube-node-lease,cert-manager,cattle-global-nt,security-scan,cattle-fleet-system")
	TelemetryOpt                      = NewSetting("telemetry-opt", "")
	TokenHashing                      = NewSetting("token-hashing", "true")
	TLSMinVersion                     = NewSetting("tls-min-version", "1.2")
	TLSCiphers                        = NewSetting("tls-ciphers", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305")
	UIBanners                         = NewSetting("ui-banners", "{}")
	UIBrand                           = NewSetting("ui-brand", "")
	UIDefaultLanding                  = NewSetting("ui-default-landing", "vue")
	UIFeedBackForm                    = NewSetting("ui-feedback-form", "")
	UIIndex                           = NewSetting("ui-index", "https://releases.rancher.com/ui/latest2/index.html")
	UIPath                            = NewSetting("ui-path", "/usr/share/rancher/ui")
	UIDashboardIndex                  = NewSetting("ui-dashboard-index", "https://releases.rancher.com/dashboard/latest/index.html")
	UIDashboardPath                   = NewSetting("ui-dashboard-path", "/usr/share/rancher/ui-dashboard")
	UIPreferred                       = NewSetting("ui-preferred", "vue")
	UIOfflinePreferred                = NewSetting("ui-offline-preferred", "dynamic")
	UIIssues                          = NewSetting("ui-issues", "")
	UIPL                              = NewSetting("ui-pl", "rancher")
	UICommunityLinks                  = NewSetting("ui-community-links", "true")
	UIKubernetesSupportedVersions     = NewSetting("ui-k8s-supported-versions-range", ">= 1.11.0 <=1.14.x")
	UIKubernetesDefaultVersion        = NewSetting("ui-k8s-default-version-range", "<=1.14.x")
	WhitelistDomain                   = NewSetting("whitelist-domain", "forums.rancher.com")
	WhitelistEnvironmentVars          = NewSetting("whitelist-envvars", "HTTP_PROXY,HTTPS_PROXY,NO_PROXY")
	AuthUserInfoResyncCron            = NewSetting("auth-user-info-resync-cron", "0 0 * * *")
	AuthUserSessionTTLMinutes         = NewSetting("auth-user-session-ttl-minutes", "960")   // 16 hours
	AuthUserInfoMaxAgeSeconds         = NewSetting("auth-user-info-max-age-seconds", "3600") // 1 hour
	APIUIVersion                      = NewSetting("api-ui-version", "1.1.6")                // Please update the CATTLE_API_UI_VERSION in package/Dockerfile when updating the version here.
	RotateCertsIfExpiringInDays       = NewSetting("rotate-certs-if-expiring-in-days", "7")  // 7 days
	ClusterTemplateEnforcement        = NewSetting("cluster-template-enforcement", "false")
	InitialDockerRootDir              = NewSetting("initial-docker-root-dir", "/var/lib/docker")
	SystemCatalog                     = NewSetting("system-catalog", "external") // Options are 'external' or 'bundled'
	ChartDefaultBranch                = NewSetting("chart-default-branch", "dev-v2.6")
	PartnerChartDefaultBranch         = NewSetting("partner-chart-default-branch", "main")
	RKE2ChartDefaultBranch            = NewSetting("rke2-chart-default-branch", "main")
	FleetDefaultWorkspaceName         = NewSetting("fleet-default-workspace-name", "fleet-default") // fleetWorkspaceName to assign to clusters with none
	ShellImage                        = NewSetting("shell-image", "rancher/shell:v0.1.7")
	IgnoreNodeName                    = NewSetting("ignore-node-name", "") // nodes to ignore when syncing v1.node to v3.node
	NoDefaultAdmin                    = NewSetting("no-default-admin", "")
	RestrictedDefaultAdmin            = NewSetting("restricted-default-admin", "false") // When bootstrapping the admin for the first time, give them the global role restricted-admin
	AKSUpstreamRefresh                = NewSetting("aks-refresh", "300")
	EKSUpstreamRefreshCron            = NewSetting("eks-refresh-cron", "*/5 * * * *") // EKSUpstreamRefreshCron is deprecated and will be replaced by EKSUpstreamRefresh
	EKSUpstreamRefresh                = NewSetting("eks-refresh", "300")
	GKEUpstreamRefresh                = NewSetting("gke-refresh", "300")
	HideLocalCluster                  = NewSetting("hide-local-cluster", "false")
	MachineProvisionImage             = NewSetting("machine-provision-image", "rancher/machine:v0.15.0-rancher60")

	FleetMinVersion          = NewSetting("fleet-min-version", "")
	RancherWebhookMinVersion = NewSetting("rancher-webhook-min-version", "")
)

func FullShellImage() string {
	return PrefixPrivateRegistry(ShellImage.Get())
}

func PrefixPrivateRegistry(image string) string {
	private := SystemDefaultRegistry.Get()
	if private == "" {
		return image
	}
	return private + "/" + image
}

func IsRelease() bool {
	return !strings.Contains(ServerVersion.Get(), "head") && releasePattern.MatchString(ServerVersion.Get())
}

func init() {
	// setup auth setting
	authsettings.AuthUserInfoResyncCron = AuthUserInfoResyncCron
	authsettings.AuthUserSessionTTLMinutes = AuthUserSessionTTLMinutes
	authsettings.AuthUserInfoMaxAgeSeconds = AuthUserInfoMaxAgeSeconds
	authsettings.FirstLogin = FirstLogin

	if InjectDefaults == "" {
		return
	}
	defaults := map[string]string{}
	if err := json.Unmarshal([]byte(InjectDefaults), &defaults); err != nil {
		return
	}
	for name, defaultValue := range defaults {
		value, ok := settings[name]
		if !ok {
			continue
		}
		value.Default = defaultValue
		settings[name] = value
	}
}

type Provider interface {
	Get(name string) string
	Set(name, value string) error
	SetIfUnset(name, value string) error
	SetAll(settings map[string]Setting) error
}

type Setting struct {
	Name     string
	Default  string
	ReadOnly bool
}

func (s Setting) SetIfUnset(value string) error {
	if provider == nil {
		return s.Set(value)
	}
	return provider.SetIfUnset(s.Name, value)
}

func (s Setting) Set(value string) error {
	if provider == nil {
		s, ok := settings[s.Name]
		if ok {
			s.Default = value
			settings[s.Name] = s
		}
	} else {
		return provider.Set(s.Name, value)
	}
	return nil
}

func (s Setting) Get() string {
	if provider == nil {
		s := settings[s.Name]
		return s.Default
	}
	return provider.Get(s.Name)
}

func (s Setting) GetInt() int {
	v := s.Get()
	i, err := strconv.Atoi(v)
	if err == nil {
		return i
	}
	logrus.Errorf("failed to parse setting %s=%s as int: %v", s.Name, v, err)
	i, err = strconv.Atoi(s.Default)
	if err != nil {
		return 0
	}
	return i
}

func SetProvider(p Provider) error {
	if err := p.SetAll(settings); err != nil {
		return err
	}
	provider = p
	return nil
}

func NewSetting(name, def string) Setting {
	s := Setting{
		Name:    name,
		Default: def,
	}
	settings[s.Name] = s
	return s
}

func GetEnvKey(key string) string {
	return "CATTLE_" + strings.ToUpper(strings.Replace(key, "-", "_", -1))
}

func getMetadataConfig() string {
	branch := KDMBranch.Get()
	data := map[string]interface{}{
		"url":                      fmt.Sprintf("https://releases.rancher.com/kontainer-driver-metadata/%s/data.json", branch),
		"refresh-interval-minutes": "1440",
	}
	ans, err := json.Marshal(data)
	if err != nil {
		logrus.Errorf("error getting metadata config %v", err)
		return ""
	}
	return string(ans)
}

// GetSettingByID returns a setting that is stored with the given id
func GetSettingByID(id string) string {
	if provider == nil {
		s := settings[id]
		return s.Default
	}
	return provider.Get(id)
}
<|endoftext|>