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

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

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

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

package e2e

import (
	"fmt"
	"os"
	"time"

	"k8s.io/kubernetes/federation/apis/federation"
	"k8s.io/kubernetes/federation/client/clientset_generated/federation_release_1_3"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_3"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	"k8s.io/kubernetes/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	UserAgentName = "federation-e2e-service-controller"
	// TODO(madhusudancs): Using the same values as defined in the federated
	// service controller. Replace it with the values from the e2e framework.
	KubeAPIQPS   = 20.0
	KubeAPIBurst = 30

	FederatedServiceTimeout = 5 * time.Minute

	FederatedServiceName = "federated-service"
	FederatedServicePod  = "federated-service-test-pod"

	DefaultFederationName = "federation"

	// We use this to decide how long to wait for our DNS probes to succeed.
	DNSTTL = 180 * time.Second
)

var _ = framework.KubeDescribe("Service [Feature:Federation]", func() {
	var clusterClientSets []*release_1_3.Clientset
	var federationName string
	f := framework.NewDefaultFederatedFramework("service")

	BeforeEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// TODO: Federation API server should be able to answer this.
		if federationName = os.Getenv("FEDERATION_NAME"); federationName == "" {
			federationName = DefaultFederationName
		}

		contexts := f.GetUnderlyingFederatedContexts()

		for _, context := range contexts {
			framework.Logf("Creating cluster object: %s (%s)", context.Name, context.Cluster.Cluster.Server)
			cluster := federation.Cluster{
				ObjectMeta: api.ObjectMeta{
					Name: context.Name,
				},
				Spec: federation.ClusterSpec{
					ServerAddressByClientCIDRs: []federation.ServerAddressByClientCIDR{
						{
							ClientCIDR:    "0.0.0.0/0",
							ServerAddress: context.Cluster.Cluster.Server,
						},
					},
					SecretRef: &api.LocalObjectReference{
						// Note: Name must correlate with federation build script secret name,
						//       which currently matches the cluster name.
						//       See federation/cluster/common.sh:132
						Name: context.Name,
					},
				},
			}
			_, err := f.FederationClientset.Federation().Clusters().Create(&cluster)
			framework.ExpectNoError(err, "Creating cluster")
		}

		var clusterList *federation.ClusterList
		By("Obtaining a list of all the clusters")
		if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
			var err error
			clusterList, err = f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
			if err != nil {
				return false, err
			}
			framework.Logf("%d clusters registered, waiting for %d", len(clusterList.Items), len(contexts))
			if len(clusterList.Items) == len(contexts) {
				return true, nil
			}
			return false, nil
		}); err != nil {
			framework.Failf("Failed to list registered clusters: %+v", err)
		}

		for _, cluster := range clusterList.Items {
			framework.Logf("Creating a clientset for the cluster %s", cluster.Name)

			Expect(framework.TestContext.KubeConfig).ToNot(Equal(""), "KubeConfig must be specified to load clusters' client config")
			kubecfg, err := clientcmd.LoadFromFile(framework.TestContext.KubeConfig)
			framework.ExpectNoError(err, "error loading KubeConfig: %v", err)

			cfgOverride := &clientcmd.ConfigOverrides{
				ClusterInfo: clientcmdapi.Cluster{
					Server: cluster.Spec.ServerAddressByClientCIDRs[0].ServerAddress,
				},
			}
			ccfg := clientcmd.NewNonInteractiveClientConfig(*kubecfg, cluster.Name, cfgOverride, clientcmd.NewDefaultClientConfigLoadingRules())
			cfg, err := ccfg.ClientConfig()
			Expect(err).NotTo(HaveOccurred())

			cfg.QPS = KubeAPIQPS
			cfg.Burst = KubeAPIBurst
			clset := release_1_3.NewForConfigOrDie(restclient.AddUserAgent(cfg, UserAgentName))
			clusterClientSets = append(clusterClientSets, clset)
		}
	})

	AfterEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// Delete the registered clusters in the federation API server.
		clusterList, err := f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
		Expect(err).NotTo(HaveOccurred())
		for _, cluster := range clusterList.Items {
			err := f.FederationClientset.Federation().Clusters().Delete(cluster.Name, &api.DeleteOptions{})
			Expect(err).NotTo(HaveOccurred())
		}
	})

	Describe("DNS", func() {
		BeforeEach(func() {
			framework.SkipUnlessFederated(f.Client)
			createService(f.FederationClientset_1_3, clusterClientSets, f.Namespace.Name)
		})

		It("should be able to discover a federated service", func() {
			framework.SkipUnlessFederated(f.Client)

			svcDNSNames := []string{
				FederatedServiceName,
				fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
				fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
				fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
				fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
			}
			// TODO(mml): This could be much faster.  We can launch all the test
			// pods, perhaps in the BeforeEach, and then just poll until we get
			// successes/failures from them all.
			for _, name := range svcDNSNames {
				discoverService(f, name)
			}
		})

		Context("non-local federated service", func() {
			BeforeEach(func() {
				framework.SkipUnlessFederated(f.Client)

				// Delete a federated service shard in the default e2e Kubernetes cluster.
				err := f.Clientset_1_3.Core().Services(f.Namespace.Name).Delete(FederatedServiceName, &api.DeleteOptions{})
				Expect(err).NotTo(HaveOccurred())
				waitForFederatedServiceShard(f.Clientset_1_3, f.Namespace.Name, nil, 0)
			})

			It("should be able to discover a non-local federated service", func() {
				framework.SkipUnlessFederated(f.Client)

				svcDNSNames := []string{
					fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
					fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
				}
				for _, name := range svcDNSNames {
					discoverService(f, name)
				}

				// TODO(mml): Unclear how to make this meaningful and not terribly
				// slow.  How long (how many minutes?) do we verify that a given DNS
				// lookup *doesn't* work before we call it a success?  For now,
				// commenting out.
				/*
					localSvcDNSNames := []string{
						FederatedServiceName,
						fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
						fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
					}
					for _, name := range localSvcDNSNames {
						discoverService(f, name, false)
					}
				*/
			})
		})
	})
})

// waitForFederatedServiceShard waits until the number of shards of a given federated
// service reaches the expected value, i.e. numSvcs in the given individual Kubernetes
// cluster. If the shard count, i.e. numSvcs is expected to be at least one, then
// it also checks if the first shard's name and spec matches that of the given service.
func waitForFederatedServiceShard(cs *release_1_3.Clientset, namespace string, service *v1.Service, numSvcs int) {
	By("Fetching a federated service shard")
	var clSvcList *v1.ServiceList
	if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
		var err error
		clSvcList, err = cs.Core().Services(namespace).List(api.ListOptions{})
		if err != nil {
			return false, err
		}
		n := len(clSvcList.Items)
		if n == numSvcs {
			return true, nil
		}
		framework.Logf("%d services found, waiting for %d, trying again in %s", n, numSvcs, framework.Poll)
		return false, nil
	}); err != nil {
		framework.Failf("Failed to list registered clusters: %+v", err)
	}

	if numSvcs > 0 && service != nil {
		// Renaming for clarity/readability
		clSvc := clSvcList.Items[0]

		// The federation service has no cluster IP.  Clear any cluster IP before
		// comparison.
		clSvc.Spec.ClusterIP = ""

		Expect(clSvc.Name).To(Equal(service.Name))
		Expect(clSvc.Spec).To(Equal(service.Spec))
	}
}

func createService(fcs *federation_release_1_3.Clientset, clusterClientSets []*release_1_3.Clientset, namespace string) {
	By(fmt.Sprintf("Creating federated service %q in namespace %q", FederatedServiceName, namespace))

	labels := map[string]string{
		"foo": "bar",
	}

	svc1port := "svc1"
	svc2port := "svc2"

	service := &v1.Service{
		ObjectMeta: v1.ObjectMeta{
			Name: FederatedServiceName,
		},
		Spec: v1.ServiceSpec{
			Selector: labels,
			Ports: []v1.ServicePort{
				{
					Name:       "portname1",
					Port:       80,
					TargetPort: intstr.FromString(svc1port),
				},
				{
					Name:       "portname2",
					Port:       81,
					TargetPort: intstr.FromString(svc2port),
				},
			},
		},
	}
	nservice, err := fcs.Core().Services(namespace).Create(service)
	Expect(err).NotTo(HaveOccurred())
	for _, cs := range clusterClientSets {
		waitForFederatedServiceShard(cs, namespace, nservice, 1)
	}
}

func discoverService(f *framework.Framework, name string) {
	command := []string{"sh", "-c", fmt.Sprintf("until nslookup '%s'; do sleep 1; done", name)}

	defer f.Client.Pods(f.Namespace.Name).Delete(FederatedServicePod, api.NewDeleteOptions(0))

	pod := &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name:   FederatedServicePod,
			Labels: map[string]string{"name": FederatedServicePod},
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				{
					Name:    "federated-service-discovery-container",
					Image:   "gcr.io/google_containers/busybox:1.24",
					Command: command,
				},
			},
			RestartPolicy: api.RestartPolicyOnFailure,
		},
	}

	_, err := f.Client.Pods(f.Namespace.Name).Create(pod)
	Expect(err).
		NotTo(HaveOccurred(), "Trying to create pod to run %q", command)

	// If we ever get any container logs, stash them here.
	logs := ""

	logerr := func(err error) error {
		if err == nil {
			return nil
		}
		if logs == "" {
			return err
		}
		return fmt.Errorf("%s (%v)", logs, err)
	}

	// TODO(mml): Eventually check the IP address is correct, too.
	Eventually(func() error {
		pod, err := f.Client.Pods(f.Namespace.Name).Get(FederatedServicePod)
		if err != nil {
			return logerr(err)
		}
		if len(pod.Status.ContainerStatuses) < 1 {
			return logerr(fmt.Errorf("no container statuses"))
		}

		// Best effort attempt to grab pod logs for debugging
		logs, err = framework.GetPodLogs(f.Client, f.Namespace.Name, FederatedServicePod, "federated-service-discovery-container")
		if err != nil {
			framework.Logf("Cannot fetch pod logs: %v", err)
		}

		status := pod.Status.ContainerStatuses[0]
		if status.State.Terminated == nil {
			return logerr(fmt.Errorf("container is not in terminated state"))
		}
		if status.State.Terminated.ExitCode == 0 {
			return nil
		}

		return logerr(fmt.Errorf("exited %d", status.State.Terminated.ExitCode))
	}, DNSTTL, time.Second*2).
		Should(BeNil(), "%q should exit 0, but it never did", command)
}
<commit_msg>Add a "this should not work" comment.<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package e2e

import (
	"fmt"
	"os"
	"time"

	"k8s.io/kubernetes/federation/apis/federation"
	"k8s.io/kubernetes/federation/client/clientset_generated/federation_release_1_3"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_3"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	"k8s.io/kubernetes/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	UserAgentName = "federation-e2e-service-controller"
	// TODO(madhusudancs): Using the same values as defined in the federated
	// service controller. Replace it with the values from the e2e framework.
	KubeAPIQPS   = 20.0
	KubeAPIBurst = 30

	FederatedServiceTimeout = 5 * time.Minute

	FederatedServiceName = "federated-service"
	FederatedServicePod  = "federated-service-test-pod"

	DefaultFederationName = "federation"

	// We use this to decide how long to wait for our DNS probes to succeed.
	DNSTTL = 180 * time.Second
)

var _ = framework.KubeDescribe("Service [Feature:Federation]", func() {
	var clusterClientSets []*release_1_3.Clientset
	var federationName string
	f := framework.NewDefaultFederatedFramework("service")

	BeforeEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// TODO: Federation API server should be able to answer this.
		if federationName = os.Getenv("FEDERATION_NAME"); federationName == "" {
			federationName = DefaultFederationName
		}

		contexts := f.GetUnderlyingFederatedContexts()

		for _, context := range contexts {
			framework.Logf("Creating cluster object: %s (%s)", context.Name, context.Cluster.Cluster.Server)
			cluster := federation.Cluster{
				ObjectMeta: api.ObjectMeta{
					Name: context.Name,
				},
				Spec: federation.ClusterSpec{
					ServerAddressByClientCIDRs: []federation.ServerAddressByClientCIDR{
						{
							ClientCIDR:    "0.0.0.0/0",
							ServerAddress: context.Cluster.Cluster.Server,
						},
					},
					SecretRef: &api.LocalObjectReference{
						// Note: Name must correlate with federation build script secret name,
						//       which currently matches the cluster name.
						//       See federation/cluster/common.sh:132
						Name: context.Name,
					},
				},
			}
			_, err := f.FederationClientset.Federation().Clusters().Create(&cluster)
			framework.ExpectNoError(err, "Creating cluster")
		}

		var clusterList *federation.ClusterList
		By("Obtaining a list of all the clusters")
		if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
			var err error
			clusterList, err = f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
			if err != nil {
				return false, err
			}
			framework.Logf("%d clusters registered, waiting for %d", len(clusterList.Items), len(contexts))
			if len(clusterList.Items) == len(contexts) {
				return true, nil
			}
			return false, nil
		}); err != nil {
			framework.Failf("Failed to list registered clusters: %+v", err)
		}

		for _, cluster := range clusterList.Items {
			framework.Logf("Creating a clientset for the cluster %s", cluster.Name)

			Expect(framework.TestContext.KubeConfig).ToNot(Equal(""), "KubeConfig must be specified to load clusters' client config")
			kubecfg, err := clientcmd.LoadFromFile(framework.TestContext.KubeConfig)
			framework.ExpectNoError(err, "error loading KubeConfig: %v", err)

			cfgOverride := &clientcmd.ConfigOverrides{
				ClusterInfo: clientcmdapi.Cluster{
					Server: cluster.Spec.ServerAddressByClientCIDRs[0].ServerAddress,
				},
			}
			ccfg := clientcmd.NewNonInteractiveClientConfig(*kubecfg, cluster.Name, cfgOverride, clientcmd.NewDefaultClientConfigLoadingRules())
			cfg, err := ccfg.ClientConfig()
			Expect(err).NotTo(HaveOccurred())

			cfg.QPS = KubeAPIQPS
			cfg.Burst = KubeAPIBurst
			clset := release_1_3.NewForConfigOrDie(restclient.AddUserAgent(cfg, UserAgentName))
			clusterClientSets = append(clusterClientSets, clset)
		}
	})

	AfterEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// Delete the registered clusters in the federation API server.
		clusterList, err := f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
		Expect(err).NotTo(HaveOccurred())
		for _, cluster := range clusterList.Items {
			err := f.FederationClientset.Federation().Clusters().Delete(cluster.Name, &api.DeleteOptions{})
			Expect(err).NotTo(HaveOccurred())
		}
	})

	Describe("DNS", func() {
		BeforeEach(func() {
			framework.SkipUnlessFederated(f.Client)
			createService(f.FederationClientset_1_3, clusterClientSets, f.Namespace.Name)
		})

		It("should be able to discover a federated service", func() {
			framework.SkipUnlessFederated(f.Client)

			svcDNSNames := []string{
				FederatedServiceName,
				fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
				fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
				fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
				fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
			}
			// TODO(mml): This could be much faster.  We can launch all the test
			// pods, perhaps in the BeforeEach, and then just poll until we get
			// successes/failures from them all.
			for _, name := range svcDNSNames {
				discoverService(f, name)
			}
		})

		Context("non-local federated service", func() {
			BeforeEach(func() {
				framework.SkipUnlessFederated(f.Client)

				// Delete a federated service shard in the default e2e Kubernetes cluster.
				// TODO(mml): This should not work: #27623.  We should use a load
				// balancer with actual back-ends, some of which we delete or disable.
				err := f.Clientset_1_3.Core().Services(f.Namespace.Name).Delete(FederatedServiceName, &api.DeleteOptions{})
				Expect(err).NotTo(HaveOccurred())
				waitForFederatedServiceShard(f.Clientset_1_3, f.Namespace.Name, nil, 0)
			})

			It("should be able to discover a non-local federated service", func() {
				framework.SkipUnlessFederated(f.Client)

				svcDNSNames := []string{
					fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
					fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
				}
				for _, name := range svcDNSNames {
					discoverService(f, name)
				}

				// TODO(mml): Unclear how to make this meaningful and not terribly
				// slow.  How long (how many minutes?) do we verify that a given DNS
				// lookup *doesn't* work before we call it a success?  For now,
				// commenting out.
				/*
					localSvcDNSNames := []string{
						FederatedServiceName,
						fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
						fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
					}
					for _, name := range localSvcDNSNames {
						discoverService(f, name, false)
					}
				*/
			})
		})
	})
})

// waitForFederatedServiceShard waits until the number of shards of a given federated
// service reaches the expected value, i.e. numSvcs in the given individual Kubernetes
// cluster. If the shard count, i.e. numSvcs is expected to be at least one, then
// it also checks if the first shard's name and spec matches that of the given service.
func waitForFederatedServiceShard(cs *release_1_3.Clientset, namespace string, service *v1.Service, numSvcs int) {
	By("Fetching a federated service shard")
	var clSvcList *v1.ServiceList
	if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
		var err error
		clSvcList, err = cs.Core().Services(namespace).List(api.ListOptions{})
		if err != nil {
			return false, err
		}
		n := len(clSvcList.Items)
		if n == numSvcs {
			return true, nil
		}
		framework.Logf("%d services found, waiting for %d, trying again in %s", n, numSvcs, framework.Poll)
		return false, nil
	}); err != nil {
		framework.Failf("Failed to list registered clusters: %+v", err)
	}

	if numSvcs > 0 && service != nil {
		// Renaming for clarity/readability
		clSvc := clSvcList.Items[0]

		// The federation service has no cluster IP.  Clear any cluster IP before
		// comparison.
		clSvc.Spec.ClusterIP = ""

		Expect(clSvc.Name).To(Equal(service.Name))
		Expect(clSvc.Spec).To(Equal(service.Spec))
	}
}

func createService(fcs *federation_release_1_3.Clientset, clusterClientSets []*release_1_3.Clientset, namespace string) {
	By(fmt.Sprintf("Creating federated service %q in namespace %q", FederatedServiceName, namespace))

	labels := map[string]string{
		"foo": "bar",
	}

	svc1port := "svc1"
	svc2port := "svc2"

	service := &v1.Service{
		ObjectMeta: v1.ObjectMeta{
			Name: FederatedServiceName,
		},
		Spec: v1.ServiceSpec{
			Selector: labels,
			Ports: []v1.ServicePort{
				{
					Name:       "portname1",
					Port:       80,
					TargetPort: intstr.FromString(svc1port),
				},
				{
					Name:       "portname2",
					Port:       81,
					TargetPort: intstr.FromString(svc2port),
				},
			},
		},
	}
	nservice, err := fcs.Core().Services(namespace).Create(service)
	Expect(err).NotTo(HaveOccurred())
	for _, cs := range clusterClientSets {
		waitForFederatedServiceShard(cs, namespace, nservice, 1)
	}
}

func discoverService(f *framework.Framework, name string) {
	command := []string{"sh", "-c", fmt.Sprintf("until nslookup '%s'; do sleep 1; done", name)}

	defer f.Client.Pods(f.Namespace.Name).Delete(FederatedServicePod, api.NewDeleteOptions(0))

	pod := &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name:   FederatedServicePod,
			Labels: map[string]string{"name": FederatedServicePod},
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				{
					Name:    "federated-service-discovery-container",
					Image:   "gcr.io/google_containers/busybox:1.24",
					Command: command,
				},
			},
			RestartPolicy: api.RestartPolicyOnFailure,
		},
	}

	_, err := f.Client.Pods(f.Namespace.Name).Create(pod)
	Expect(err).
		NotTo(HaveOccurred(), "Trying to create pod to run %q", command)

	// If we ever get any container logs, stash them here.
	logs := ""

	logerr := func(err error) error {
		if err == nil {
			return nil
		}
		if logs == "" {
			return err
		}
		return fmt.Errorf("%s (%v)", logs, err)
	}

	// TODO(mml): Eventually check the IP address is correct, too.
	Eventually(func() error {
		pod, err := f.Client.Pods(f.Namespace.Name).Get(FederatedServicePod)
		if err != nil {
			return logerr(err)
		}
		if len(pod.Status.ContainerStatuses) < 1 {
			return logerr(fmt.Errorf("no container statuses"))
		}

		// Best effort attempt to grab pod logs for debugging
		logs, err = framework.GetPodLogs(f.Client, f.Namespace.Name, FederatedServicePod, "federated-service-discovery-container")
		if err != nil {
			framework.Logf("Cannot fetch pod logs: %v", err)
		}

		status := pod.Status.ContainerStatuses[0]
		if status.State.Terminated == nil {
			return logerr(fmt.Errorf("container is not in terminated state"))
		}
		if status.State.Terminated.ExitCode == 0 {
			return nil
		}

		return logerr(fmt.Errorf("exited %d", status.State.Terminated.ExitCode))
	}, DNSTTL, time.Second*2).
		Should(BeNil(), "%q should exit 0, but it never did", command)
}
<|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_node

import (
	"fmt"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gstruct"
	"github.com/onsi/gomega/types"
)

var _ = framework.KubeDescribe("Summary API", func() {
	f := framework.NewDefaultFramework("summary-test")
	Context("when querying /stats/summary", func() {
		AfterEach(func() {
			if CurrentGinkgoTestDescription().Failed && framework.TestContext.DumpLogsOnFailure {
				framework.LogFailedContainers(f.ClientSet, f.Namespace.Name, framework.Logf)
			}
		})
		It("should report resource usage through the stats api", func() {
			const pod0 = "stats-busybox-0"
			const pod1 = "stats-busybox-1"

			By("Creating test pods")
			createSummaryTestPods(f, pod0, pod1)
			// Wait for cAdvisor to collect 2 stats points
			time.Sleep(15 * time.Second)

			// Setup expectations.
			const (
				kb int64 = 1000
				mb int64 = 1000 * kb
				gb int64 = 1000 * mb
				tb int64 = 1000 * gb

				maxStartAge = time.Hour * 24 * 365 // 1 year
				maxStatsAge = time.Minute
			)
			fsCapacityBounds := bounded(100*mb, 100*gb)
			// Expectations for system containers.
			sysContExpectations := gstruct.MatchAllFields(gstruct.Fields{
				"Name":      gstruct.Ignore(),
				"StartTime": recent(maxStartAge),
				"CPU": ptrMatchAllFields(gstruct.Fields{
					"Time":                 recent(maxStatsAge),
					"UsageNanoCores":       bounded(100000, 2E9),
					"UsageCoreNanoSeconds": bounded(10000000, 1E15),
				}),
				"Memory": ptrMatchAllFields(gstruct.Fields{
					"Time": recent(maxStatsAge),
					// We don't limit system container memory.
					"AvailableBytes":  BeNil(),
					"UsageBytes":      bounded(1*mb, 1*gb),
					"WorkingSetBytes": bounded(1*mb, 1*gb),
					"RSSBytes":        bounded(1*mb, 1*gb),
					"PageFaults":      bounded(1000, 1E9),
					"MajorPageFaults": bounded(0, 100000),
				}),
				"Rootfs":             BeNil(),
				"Logs":               BeNil(),
				"UserDefinedMetrics": BeEmpty(),
			})
			// Expectations for pods.
			podExpectations := gstruct.MatchAllFields(gstruct.Fields{
				"PodRef":    gstruct.Ignore(),
				"StartTime": recent(maxStartAge),
				"Containers": gstruct.MatchAllElements(summaryObjectID, gstruct.Elements{
					"busybox-container": gstruct.MatchAllFields(gstruct.Fields{
						"Name":      Equal("busybox-container"),
						"StartTime": recent(maxStartAge),
						"CPU": ptrMatchAllFields(gstruct.Fields{
							"Time":                 recent(maxStatsAge),
							"UsageNanoCores":       bounded(100000, 100000000),
							"UsageCoreNanoSeconds": bounded(10000000, 1000000000),
						}),
						"Memory": ptrMatchAllFields(gstruct.Fields{
							"Time":            recent(maxStatsAge),
							"AvailableBytes":  bounded(1*mb, 10*mb),
							"UsageBytes":      bounded(10*kb, 5*mb),
							"WorkingSetBytes": bounded(10*kb, mb),
							"RSSBytes":        bounded(1*kb, mb),
							"PageFaults":      bounded(100, 100000),
							"MajorPageFaults": bounded(0, 10),
						}),
						"Rootfs": ptrMatchAllFields(gstruct.Fields{
							"AvailableBytes": fsCapacityBounds,
							"CapacityBytes":  fsCapacityBounds,
							"UsedBytes":      bounded(kb, 10*mb),
							"InodesFree":     bounded(1E4, 1E8),
							"Inodes":         bounded(1E4, 1E8),
						}),
						"Logs": ptrMatchAllFields(gstruct.Fields{
							"AvailableBytes": fsCapacityBounds,
							"CapacityBytes":  fsCapacityBounds,
							"UsedBytes":      bounded(kb, 10*mb),
							"InodesFree":     bounded(1E4, 1E8),
							"Inodes":         bounded(1E4, 1E8),
						}),
						"UserDefinedMetrics": BeEmpty(),
					}),
				}),
				"Network": ptrMatchAllFields(gstruct.Fields{
					"Time":     recent(maxStatsAge),
					"RxBytes":  bounded(10, 10*mb),
					"RxErrors": bounded(0, 1000),
					"TxBytes":  bounded(10, 10*mb),
					"TxErrors": bounded(0, 1000),
				}),
				"VolumeStats": gstruct.MatchAllElements(summaryObjectID, gstruct.Elements{
					"test-empty-dir": gstruct.MatchAllFields(gstruct.Fields{
						"Name": Equal("test-empty-dir"),
						"FsStats": gstruct.MatchAllFields(gstruct.Fields{
							"AvailableBytes": fsCapacityBounds,
							"CapacityBytes":  fsCapacityBounds,
							"UsedBytes":      bounded(kb, 1*mb),
							// Inodes are not reported for Volumes.
							"InodesFree": BeNil(),
							"Inodes":     BeNil(),
						}),
					}),
				}),
			})
			matchExpectations := ptrMatchAllFields(gstruct.Fields{
				"Node": gstruct.MatchAllFields(gstruct.Fields{
					"NodeName":  Equal(framework.TestContext.NodeName),
					"StartTime": recent(maxStartAge),
					"SystemContainers": gstruct.MatchElements(summaryObjectID, gstruct.IgnoreExtras, gstruct.Elements{
						"kubelet": sysContExpectations,
						"runtime": sysContExpectations,
					}),
					"CPU": ptrMatchAllFields(gstruct.Fields{
						"Time":                 recent(maxStatsAge),
						"UsageNanoCores":       bounded(100E3, 2E9),
						"UsageCoreNanoSeconds": bounded(1E9, 1E15),
					}),
					"Memory": ptrMatchAllFields(gstruct.Fields{
						"Time":            recent(maxStatsAge),
						"AvailableBytes":  bounded(100*mb, 100*gb),
						"UsageBytes":      bounded(10*mb, 10*gb),
						"WorkingSetBytes": bounded(10*mb, 10*gb),
						"RSSBytes":        bounded(1*kb, 1*gb),
						"PageFaults":      bounded(1000, 1E9),
						"MajorPageFaults": bounded(0, 100000),
					}),
					// TODO(#28407): Handle non-eth0 network interface names.
					"Network": Or(BeNil(), ptrMatchAllFields(gstruct.Fields{
						"Time":     recent(maxStatsAge),
						"RxBytes":  bounded(1*mb, 100*gb),
						"RxErrors": bounded(0, 100000),
						"TxBytes":  bounded(10*kb, 10*gb),
						"TxErrors": bounded(0, 100000),
					})),
					"Fs": ptrMatchAllFields(gstruct.Fields{
						"AvailableBytes": fsCapacityBounds,
						"CapacityBytes":  fsCapacityBounds,
						"UsedBytes":      bounded(kb, 10*gb),
						"InodesFree":     bounded(1E4, 1E8),
						"Inodes":         bounded(1E4, 1E8),
					}),
					"Runtime": ptrMatchAllFields(gstruct.Fields{
						"ImageFs": ptrMatchAllFields(gstruct.Fields{
							"AvailableBytes": fsCapacityBounds,
							"CapacityBytes":  fsCapacityBounds,
							"UsedBytes":      bounded(kb, 10*gb),
							"InodesFree":     bounded(1E4, 1E8),
							"Inodes":         bounded(1E4, 1E8),
						}),
					}),
				}),
				// Ignore extra pods since the tests run in parallel.
				"Pods": gstruct.MatchElements(summaryObjectID, gstruct.IgnoreExtras, gstruct.Elements{
					fmt.Sprintf("%s::%s", f.Namespace.Name, pod0): podExpectations,
					fmt.Sprintf("%s::%s", f.Namespace.Name, pod1): podExpectations,
				}),
			})

			By("Validating /stats/summary")
			// Give pods a minute to actually start up.
			Eventually(getNodeSummary, 1*time.Minute, 15*time.Second).Should(matchExpectations)
			// Then the summary should match the expectations a few more times.
			Consistently(getNodeSummary, 30*time.Second, 15*time.Second).Should(matchExpectations)
		})
	})
})

func createSummaryTestPods(f *framework.Framework, names ...string) {
	pods := make([]*api.Pod, 0, len(names))
	for _, name := range names {
		pods = append(pods, &api.Pod{
			ObjectMeta: api.ObjectMeta{
				Name: name,
			},
			Spec: api.PodSpec{
				RestartPolicy: api.RestartPolicyAlways,
				Containers: []api.Container{
					{
						Name:    "busybox-container",
						Image:   "gcr.io/google_containers/busybox:1.24",
						Command: []string{"sh", "-c", "ping -c 1 google.com; while true; do echo 'hello world' >> /test-empty-dir-mnt/file ; sleep 1; done"},
						Resources: api.ResourceRequirements{
							Limits: api.ResourceList{
								// Must set memory limit to get MemoryStats.AvailableBytes
								api.ResourceMemory: resource.MustParse("10M"),
							},
						},
						VolumeMounts: []api.VolumeMount{
							{MountPath: "/test-empty-dir-mnt", Name: "test-empty-dir"},
						},
					},
				},
				SecurityContext: &api.PodSecurityContext{
					SELinuxOptions: &api.SELinuxOptions{
						Level: "s0",
					},
				},
				Volumes: []api.Volume{
					// TODO(#28393): Test secret volumes
					// TODO(#28394): Test hostpath volumes
					{Name: "test-empty-dir", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
				},
			},
		})
	}
	f.PodClient().CreateBatch(pods)
}

// Mapping function for gstruct.MatchAllElements
func summaryObjectID(element interface{}) string {
	switch el := element.(type) {
	case stats.PodStats:
		return fmt.Sprintf("%s::%s", el.PodRef.Namespace, el.PodRef.Name)
	case stats.ContainerStats:
		return el.Name
	case stats.VolumeStats:
		return el.Name
	case stats.UserDefinedMetric:
		return el.Name
	default:
		framework.Failf("Unknown type: %T", el)
		return "???"
	}
}

// Convenience functions for common matcher combinations.
func ptrMatchAllFields(fields gstruct.Fields) types.GomegaMatcher {
	return gstruct.PointTo(gstruct.MatchAllFields(fields))
}

func bounded(lower, upper interface{}) types.GomegaMatcher {
	return gstruct.PointTo(And(
		BeNumerically(">=", lower),
		BeNumerically("<=", upper)))
}

func recent(d time.Duration) types.GomegaMatcher {
	return WithTransform(func(t unversioned.Time) time.Time {
		return t.Time
	}, And(
		BeTemporally(">=", time.Now().Add(-d)),
		// Now() is the test start time, not the match time, so permit a few extra minutes.
		BeTemporally("<", time.Now().Add(2*time.Minute))))
}
<commit_msg>Increase sys container usageBytes upper bound<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_node

import (
	"fmt"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gstruct"
	"github.com/onsi/gomega/types"
)

var _ = framework.KubeDescribe("Summary API", func() {
	f := framework.NewDefaultFramework("summary-test")
	Context("when querying /stats/summary", func() {
		AfterEach(func() {
			if CurrentGinkgoTestDescription().Failed && framework.TestContext.DumpLogsOnFailure {
				framework.LogFailedContainers(f.ClientSet, f.Namespace.Name, framework.Logf)
			}
		})
		It("should report resource usage through the stats api", func() {
			const pod0 = "stats-busybox-0"
			const pod1 = "stats-busybox-1"

			By("Creating test pods")
			createSummaryTestPods(f, pod0, pod1)
			// Wait for cAdvisor to collect 2 stats points
			time.Sleep(15 * time.Second)

			// Setup expectations.
			const (
				kb int64 = 1000
				mb int64 = 1000 * kb
				gb int64 = 1000 * mb
				tb int64 = 1000 * gb

				maxStartAge = time.Hour * 24 * 365 // 1 year
				maxStatsAge = time.Minute
			)
			fsCapacityBounds := bounded(100*mb, 100*gb)
			// Expectations for system containers.
			sysContExpectations := gstruct.MatchAllFields(gstruct.Fields{
				"Name":      gstruct.Ignore(),
				"StartTime": recent(maxStartAge),
				"CPU": ptrMatchAllFields(gstruct.Fields{
					"Time":                 recent(maxStatsAge),
					"UsageNanoCores":       bounded(100000, 2E9),
					"UsageCoreNanoSeconds": bounded(10000000, 1E15),
				}),
				"Memory": ptrMatchAllFields(gstruct.Fields{
					"Time": recent(maxStatsAge),
					// We don't limit system container memory.
					"AvailableBytes":  BeNil(),
					"UsageBytes":      bounded(1*mb, 10*gb),
					"WorkingSetBytes": bounded(1*mb, 1*gb),
					"RSSBytes":        bounded(1*mb, 1*gb),
					"PageFaults":      bounded(1000, 1E9),
					"MajorPageFaults": bounded(0, 100000),
				}),
				"Rootfs":             BeNil(),
				"Logs":               BeNil(),
				"UserDefinedMetrics": BeEmpty(),
			})
			// Expectations for pods.
			podExpectations := gstruct.MatchAllFields(gstruct.Fields{
				"PodRef":    gstruct.Ignore(),
				"StartTime": recent(maxStartAge),
				"Containers": gstruct.MatchAllElements(summaryObjectID, gstruct.Elements{
					"busybox-container": gstruct.MatchAllFields(gstruct.Fields{
						"Name":      Equal("busybox-container"),
						"StartTime": recent(maxStartAge),
						"CPU": ptrMatchAllFields(gstruct.Fields{
							"Time":                 recent(maxStatsAge),
							"UsageNanoCores":       bounded(100000, 100000000),
							"UsageCoreNanoSeconds": bounded(10000000, 1000000000),
						}),
						"Memory": ptrMatchAllFields(gstruct.Fields{
							"Time":            recent(maxStatsAge),
							"AvailableBytes":  bounded(1*mb, 10*mb),
							"UsageBytes":      bounded(10*kb, 5*mb),
							"WorkingSetBytes": bounded(10*kb, mb),
							"RSSBytes":        bounded(1*kb, mb),
							"PageFaults":      bounded(100, 100000),
							"MajorPageFaults": bounded(0, 10),
						}),
						"Rootfs": ptrMatchAllFields(gstruct.Fields{
							"AvailableBytes": fsCapacityBounds,
							"CapacityBytes":  fsCapacityBounds,
							"UsedBytes":      bounded(kb, 10*mb),
							"InodesFree":     bounded(1E4, 1E8),
							"Inodes":         bounded(1E4, 1E8),
						}),
						"Logs": ptrMatchAllFields(gstruct.Fields{
							"AvailableBytes": fsCapacityBounds,
							"CapacityBytes":  fsCapacityBounds,
							"UsedBytes":      bounded(kb, 10*mb),
							"InodesFree":     bounded(1E4, 1E8),
							"Inodes":         bounded(1E4, 1E8),
						}),
						"UserDefinedMetrics": BeEmpty(),
					}),
				}),
				"Network": ptrMatchAllFields(gstruct.Fields{
					"Time":     recent(maxStatsAge),
					"RxBytes":  bounded(10, 10*mb),
					"RxErrors": bounded(0, 1000),
					"TxBytes":  bounded(10, 10*mb),
					"TxErrors": bounded(0, 1000),
				}),
				"VolumeStats": gstruct.MatchAllElements(summaryObjectID, gstruct.Elements{
					"test-empty-dir": gstruct.MatchAllFields(gstruct.Fields{
						"Name": Equal("test-empty-dir"),
						"FsStats": gstruct.MatchAllFields(gstruct.Fields{
							"AvailableBytes": fsCapacityBounds,
							"CapacityBytes":  fsCapacityBounds,
							"UsedBytes":      bounded(kb, 1*mb),
							// Inodes are not reported for Volumes.
							"InodesFree": BeNil(),
							"Inodes":     BeNil(),
						}),
					}),
				}),
			})
			matchExpectations := ptrMatchAllFields(gstruct.Fields{
				"Node": gstruct.MatchAllFields(gstruct.Fields{
					"NodeName":  Equal(framework.TestContext.NodeName),
					"StartTime": recent(maxStartAge),
					"SystemContainers": gstruct.MatchElements(summaryObjectID, gstruct.IgnoreExtras, gstruct.Elements{
						"kubelet": sysContExpectations,
						"runtime": sysContExpectations,
					}),
					"CPU": ptrMatchAllFields(gstruct.Fields{
						"Time":                 recent(maxStatsAge),
						"UsageNanoCores":       bounded(100E3, 2E9),
						"UsageCoreNanoSeconds": bounded(1E9, 1E15),
					}),
					"Memory": ptrMatchAllFields(gstruct.Fields{
						"Time":            recent(maxStatsAge),
						"AvailableBytes":  bounded(100*mb, 100*gb),
						"UsageBytes":      bounded(10*mb, 10*gb),
						"WorkingSetBytes": bounded(10*mb, 10*gb),
						"RSSBytes":        bounded(1*kb, 1*gb),
						"PageFaults":      bounded(1000, 1E9),
						"MajorPageFaults": bounded(0, 100000),
					}),
					// TODO(#28407): Handle non-eth0 network interface names.
					"Network": Or(BeNil(), ptrMatchAllFields(gstruct.Fields{
						"Time":     recent(maxStatsAge),
						"RxBytes":  bounded(1*mb, 100*gb),
						"RxErrors": bounded(0, 100000),
						"TxBytes":  bounded(10*kb, 10*gb),
						"TxErrors": bounded(0, 100000),
					})),
					"Fs": ptrMatchAllFields(gstruct.Fields{
						"AvailableBytes": fsCapacityBounds,
						"CapacityBytes":  fsCapacityBounds,
						"UsedBytes":      bounded(kb, 10*gb),
						"InodesFree":     bounded(1E4, 1E8),
						"Inodes":         bounded(1E4, 1E8),
					}),
					"Runtime": ptrMatchAllFields(gstruct.Fields{
						"ImageFs": ptrMatchAllFields(gstruct.Fields{
							"AvailableBytes": fsCapacityBounds,
							"CapacityBytes":  fsCapacityBounds,
							"UsedBytes":      bounded(kb, 10*gb),
							"InodesFree":     bounded(1E4, 1E8),
							"Inodes":         bounded(1E4, 1E8),
						}),
					}),
				}),
				// Ignore extra pods since the tests run in parallel.
				"Pods": gstruct.MatchElements(summaryObjectID, gstruct.IgnoreExtras, gstruct.Elements{
					fmt.Sprintf("%s::%s", f.Namespace.Name, pod0): podExpectations,
					fmt.Sprintf("%s::%s", f.Namespace.Name, pod1): podExpectations,
				}),
			})

			By("Validating /stats/summary")
			// Give pods a minute to actually start up.
			Eventually(getNodeSummary, 1*time.Minute, 15*time.Second).Should(matchExpectations)
			// Then the summary should match the expectations a few more times.
			Consistently(getNodeSummary, 30*time.Second, 15*time.Second).Should(matchExpectations)
		})
	})
})

func createSummaryTestPods(f *framework.Framework, names ...string) {
	pods := make([]*api.Pod, 0, len(names))
	for _, name := range names {
		pods = append(pods, &api.Pod{
			ObjectMeta: api.ObjectMeta{
				Name: name,
			},
			Spec: api.PodSpec{
				RestartPolicy: api.RestartPolicyAlways,
				Containers: []api.Container{
					{
						Name:    "busybox-container",
						Image:   "gcr.io/google_containers/busybox:1.24",
						Command: []string{"sh", "-c", "ping -c 1 google.com; while true; do echo 'hello world' >> /test-empty-dir-mnt/file ; sleep 1; done"},
						Resources: api.ResourceRequirements{
							Limits: api.ResourceList{
								// Must set memory limit to get MemoryStats.AvailableBytes
								api.ResourceMemory: resource.MustParse("10M"),
							},
						},
						VolumeMounts: []api.VolumeMount{
							{MountPath: "/test-empty-dir-mnt", Name: "test-empty-dir"},
						},
					},
				},
				SecurityContext: &api.PodSecurityContext{
					SELinuxOptions: &api.SELinuxOptions{
						Level: "s0",
					},
				},
				Volumes: []api.Volume{
					// TODO(#28393): Test secret volumes
					// TODO(#28394): Test hostpath volumes
					{Name: "test-empty-dir", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
				},
			},
		})
	}
	f.PodClient().CreateBatch(pods)
}

// Mapping function for gstruct.MatchAllElements
func summaryObjectID(element interface{}) string {
	switch el := element.(type) {
	case stats.PodStats:
		return fmt.Sprintf("%s::%s", el.PodRef.Namespace, el.PodRef.Name)
	case stats.ContainerStats:
		return el.Name
	case stats.VolumeStats:
		return el.Name
	case stats.UserDefinedMetric:
		return el.Name
	default:
		framework.Failf("Unknown type: %T", el)
		return "???"
	}
}

// Convenience functions for common matcher combinations.
func ptrMatchAllFields(fields gstruct.Fields) types.GomegaMatcher {
	return gstruct.PointTo(gstruct.MatchAllFields(fields))
}

func bounded(lower, upper interface{}) types.GomegaMatcher {
	return gstruct.PointTo(And(
		BeNumerically(">=", lower),
		BeNumerically("<=", upper)))
}

func recent(d time.Duration) types.GomegaMatcher {
	return WithTransform(func(t unversioned.Time) time.Time {
		return t.Time
	}, And(
		BeTemporally(">=", time.Now().Add(-d)),
		// Now() is the test start time, not the match time, so permit a few extra minutes.
		BeTemporally("<", time.Now().Add(2*time.Minute))))
}
<|endoftext|>


<commit_before>// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package dash

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

	"github.com/google/syzkaller/dashboard/dashapi"
	"golang.org/x/net/context"
	"google.golang.org/appengine/log"
)

// Interface with external reporting systems.
// The external system is meant to poll for new bugs with apiReportingPoll,
// and report back bug status updates with apiReportingUpdate.

type ExternalConfig struct {
	ID string
}

func (cfg *ExternalConfig) Type() string {
	return cfg.ID
}

func apiReportingPollBugs(c context.Context, r *http.Request, payload []byte) (interface{}, error) {
	req := new(dashapi.PollBugsRequest)
	if err := json.Unmarshal(payload, req); err != nil {
		return nil, fmt.Errorf("failed to unmarshal request: %v", err)
	}
	reports := reportingPollBugs(c, req.Type)
	resp := &dashapi.PollBugsResponse{
		Reports: reports,
	}
	jobs, err := pollCompletedJobs(c, req.Type)
	if err != nil {
		log.Errorf(c, "failed to poll jobs: %v", err)
	}
	resp.Reports = append(resp.Reports, jobs...)
	return resp, nil
}

func apiReportingPollNotifications(c context.Context, r *http.Request, payload []byte) (interface{}, error) {
	req := new(dashapi.PollNotificationsRequest)
	if err := json.Unmarshal(payload, req); err != nil {
		return nil, fmt.Errorf("failed to unmarshal request: %v", err)
	}
	notifs := reportingPollNotifications(c, req.Type)
	resp := &dashapi.PollNotificationsResponse{
		Notifications: notifs,
	}
	return resp, nil
}

func apiReportingPollClosed(c context.Context, r *http.Request, payload []byte) (interface{}, error) {
	req := new(dashapi.PollClosedRequest)
	if err := json.Unmarshal(payload, req); err != nil {
		return nil, fmt.Errorf("failed to unmarshal request: %v", err)
	}
	ids, err := reportingPollClosed(c, req.IDs)
	if err != nil {
		log.Errorf(c, "failed to poll closed bugs: %v", err)
		return nil, err
	}
	resp := &dashapi.PollClosedResponse{
		IDs: ids,
	}
	return resp, nil
}

func apiReportingUpdate(c context.Context, r *http.Request, payload []byte) (interface{}, error) {
	req := new(dashapi.BugUpdate)
	if err := json.Unmarshal(payload, req); err != nil {
		return nil, fmt.Errorf("failed to unmarshal request: %v", err)
	}
	if req.JobID != "" {
		resp := &dashapi.BugUpdateReply{
			OK:    true,
			Error: false,
		}
		if err := jobReported(c, req.JobID); err != nil {
			log.Errorf(c, "failed to mark job reported: %v", err)
			resp.Text = err.Error()
			resp.Error = true
		}
		return resp, nil
	}
	ok, reason, err := incomingCommand(c, req)
	return &dashapi.BugUpdateReply{
		OK:    ok,
		Error: err != nil,
		Text:  reason,
	}, nil
}
<commit_msg>dashboard/app: remove duplicate error log<commit_after>// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package dash

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

	"github.com/google/syzkaller/dashboard/dashapi"
	"golang.org/x/net/context"
	"google.golang.org/appengine/log"
)

// Interface with external reporting systems.
// The external system is meant to poll for new bugs with apiReportingPoll,
// and report back bug status updates with apiReportingUpdate.

type ExternalConfig struct {
	ID string
}

func (cfg *ExternalConfig) Type() string {
	return cfg.ID
}

func apiReportingPollBugs(c context.Context, r *http.Request, payload []byte) (interface{}, error) {
	req := new(dashapi.PollBugsRequest)
	if err := json.Unmarshal(payload, req); err != nil {
		return nil, fmt.Errorf("failed to unmarshal request: %v", err)
	}
	reports := reportingPollBugs(c, req.Type)
	resp := &dashapi.PollBugsResponse{
		Reports: reports,
	}
	jobs, err := pollCompletedJobs(c, req.Type)
	if err != nil {
		log.Errorf(c, "failed to poll jobs: %v", err)
	}
	resp.Reports = append(resp.Reports, jobs...)
	return resp, nil
}

func apiReportingPollNotifications(c context.Context, r *http.Request, payload []byte) (interface{}, error) {
	req := new(dashapi.PollNotificationsRequest)
	if err := json.Unmarshal(payload, req); err != nil {
		return nil, fmt.Errorf("failed to unmarshal request: %v", err)
	}
	notifs := reportingPollNotifications(c, req.Type)
	resp := &dashapi.PollNotificationsResponse{
		Notifications: notifs,
	}
	return resp, nil
}

func apiReportingPollClosed(c context.Context, r *http.Request, payload []byte) (interface{}, error) {
	req := new(dashapi.PollClosedRequest)
	if err := json.Unmarshal(payload, req); err != nil {
		return nil, fmt.Errorf("failed to unmarshal request: %v", err)
	}
	ids, err := reportingPollClosed(c, req.IDs)
	if err != nil {
		return nil, err
	}
	resp := &dashapi.PollClosedResponse{
		IDs: ids,
	}
	return resp, nil
}

func apiReportingUpdate(c context.Context, r *http.Request, payload []byte) (interface{}, error) {
	req := new(dashapi.BugUpdate)
	if err := json.Unmarshal(payload, req); err != nil {
		return nil, fmt.Errorf("failed to unmarshal request: %v", err)
	}
	if req.JobID != "" {
		resp := &dashapi.BugUpdateReply{
			OK:    true,
			Error: false,
		}
		if err := jobReported(c, req.JobID); err != nil {
			log.Errorf(c, "failed to mark job reported: %v", err)
			resp.Text = err.Error()
			resp.Error = true
		}
		return resp, nil
	}
	ok, reason, err := incomingCommand(c, req)
	return &dashapi.BugUpdateReply{
		OK:    ok,
		Error: err != nil,
		Text:  reason,
	}, nil
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 sdb

import (
	"github.com/jacobsa/aws/sdb/conn"
)

// A precondition for a conditional Put or Delete operation. Preconditions may
// specify a value that an attribute must have or that the attribute must not
// exist.
type Precondition struct {
	// The name of the attribute to be inspected. Attributes with multiple values
	// are not supported.
	Name string

	// If non-nil, the value that the attribute must possess at the time of the
	// update. If nil, the attribute must not exist at the time of the update.
	Value *string
}

// An update to make to a particular attribute as part of a Put request.
type PutUpdate struct {
	// The name of the attribute.
	Name string

	// The value to set for the attribute.
	Value string

	// If true, add this (name, value) attribute rather than replacing any
	// existing attributes with the given name (the default).
	Add bool
}

// An update to make to a particular attribute as part of a Delete request.
type DeleteUpdate struct {
	// The name of the attribute.
	Name string

	// Te requests, the particular value of the attribute to delete if present.
	// Otherwise, all values will be deleted.
	Value *string
}

// A map from item names to updates that should be applied to those items.
type BatchPutMap map[ItemName][]PutUpdate

// A map from item names to delete updates that should be applied to those
// items. A nil slice indicates that all of the item's attributes should be
// deleted.
type BatchDeleteMap map[ItemName][]DeleteUpdate

// A domain represents a named domain within the SimpleDB service. It is a
// collection of named items, each of which possesses a set of attributes.
type Domain interface {
	// Return the name of this domain.
	Name() string

	// Atomically apply the supplied updates to the attributes of the named item,
	// but only if the supplied precondition holds. If no precondition is
	// desired, pass nil.
	//
	// The length of updates must be in [1, 256].
	PutAttributes(
		item ItemName,
		updates []PutUpdate,
		precondition *Precondition) error

	// Atomically apply updates to multiple items simultaneously.
	//
	// The length of the map must be in [1, 25]. The length of each of its values
	// must be in [1, 256]. An error may be returned if the underlying request to
	// SimpleDB is too large.
	BatchPutAttributes(updateMap BatchPutMap) error

	// Retrieve a set of attributes for the named item, or all attributes if the
	// attribute name slice is empty.
	//
	// If the named item doesn't exist, the empty set is returned.
	//
	// constistentRead specifies whether completely fresh data is needed or not.
	GetAttributes(
		item ItemName,
		constistentRead bool,
		attrNames []string) (attrs []Attribute, err error)

	// Atomically delete attributes from the named item, but only if the supplied
	// precondition holds. If no precondition is desired, pass nil.
	//
	// If deletes is empty, delete all attributes from the item. Otherwise
	// perform only the deletes is specifies. Deleting a non-existent attribute
	// does not result in an error.
	DeleteAttributes(
		item ItemName,
		deletes []DeleteUpdate,
		precondition *Precondition) error

	// Atomically delete attributes from multiple items simultaneously.
	//
	// If no updates are supplied for a particular item, delete all of its
	// attributes.
	BatchDeleteAttributes(deleteMap BatchDeleteMap) error
}

func newDomain(name string, c conn.Conn) (Domain, error) {
	return &domain{name, c}, nil
}

type domain struct {
	name string
	c    conn.Conn
}

func (d *domain) Name() string {
	return d.name
}
<commit_msg>Fixed a broken comment.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 sdb

import (
	"github.com/jacobsa/aws/sdb/conn"
)

// A precondition for a conditional Put or Delete operation. Preconditions may
// specify a value that an attribute must have or that the attribute must not
// exist.
type Precondition struct {
	// The name of the attribute to be inspected. Attributes with multiple values
	// are not supported.
	Name string

	// If non-nil, the value that the attribute must possess at the time of the
	// update. If nil, the attribute must not exist at the time of the update.
	Value *string
}

// An update to make to a particular attribute as part of a Put request.
type PutUpdate struct {
	// The name of the attribute.
	Name string

	// The value to set for the attribute.
	Value string

	// If true, add this (name, value) attribute rather than replacing any
	// existing attributes with the given name (the default).
	Add bool
}

// An update to make to a particular attribute as part of a Delete request.
type DeleteUpdate struct {
	// The name of the attribute.
	Name string

	// The particular value of the attribute to delete, if present. Otherwise,
	// all values for the given name will be deleted.
	Value *string
}

// A map from item names to updates that should be applied to those items.
type BatchPutMap map[ItemName][]PutUpdate

// A map from item names to delete updates that should be applied to those
// items. A nil slice indicates that all of the item's attributes should be
// deleted.
type BatchDeleteMap map[ItemName][]DeleteUpdate

// A domain represents a named domain within the SimpleDB service. It is a
// collection of named items, each of which possesses a set of attributes.
type Domain interface {
	// Return the name of this domain.
	Name() string

	// Atomically apply the supplied updates to the attributes of the named item,
	// but only if the supplied precondition holds. If no precondition is
	// desired, pass nil.
	//
	// The length of updates must be in [1, 256].
	PutAttributes(
		item ItemName,
		updates []PutUpdate,
		precondition *Precondition) error

	// Atomically apply updates to multiple items simultaneously.
	//
	// The length of the map must be in [1, 25]. The length of each of its values
	// must be in [1, 256]. An error may be returned if the underlying request to
	// SimpleDB is too large.
	BatchPutAttributes(updateMap BatchPutMap) error

	// Retrieve a set of attributes for the named item, or all attributes if the
	// attribute name slice is empty.
	//
	// If the named item doesn't exist, the empty set is returned.
	//
	// constistentRead specifies whether completely fresh data is needed or not.
	GetAttributes(
		item ItemName,
		constistentRead bool,
		attrNames []string) (attrs []Attribute, err error)

	// Atomically delete attributes from the named item, but only if the supplied
	// precondition holds. If no precondition is desired, pass nil.
	//
	// If deletes is empty, delete all attributes from the item. Otherwise
	// perform only the deletes is specifies. Deleting a non-existent attribute
	// does not result in an error.
	DeleteAttributes(
		item ItemName,
		deletes []DeleteUpdate,
		precondition *Precondition) error

	// Atomically delete attributes from multiple items simultaneously.
	//
	// If no updates are supplied for a particular item, delete all of its
	// attributes.
	BatchDeleteAttributes(deleteMap BatchDeleteMap) error
}

func newDomain(name string, c conn.Conn) (Domain, error) {
	return &domain{name, c}, nil
}

type domain struct {
	name string
	c    conn.Conn
}

func (d *domain) Name() string {
	return d.name
}
<|endoftext|>


<commit_before>package logger

type field struct {
	key   string
	value interface{}
}

// Value returns the key of field
func (f *field) Key() string {
	return f.key
}

// Value returns the value of field
func (f *field) Value() interface{} {
	return f.value
}
<commit_msg>Fix docs typo<commit_after>package logger

type field struct {
	key   string
	value interface{}
}

// Key returns the key of field
func (f *field) Key() string {
	return f.key
}

// Value returns the value of field
func (f *field) Value() interface{} {
	return f.value
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package template

import (
	"bytes"
	"crypto/md5"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"syscall"
	"text/template"

	"github.com/BurntSushi/toml"
	"github.com/kelseyhightower/confd/config"
	"github.com/kelseyhightower/confd/etcd/etcdutil"
	"github.com/kelseyhightower/confd/log"
)

// TemplateResourceConfig holds the parsed template resource.
type TemplateResourceConfig struct {
	TemplateResource TemplateResource `toml:"template"`
}

// TemplateResource is the representation of a parsed template resource.
type TemplateResource struct {
	Dest       string
	FileMode   os.FileMode
	Gid        int
	Keys       []string
	Mode       string
	Uid        int
	ReloadCmd  string `toml:"reload_cmd"`
	CheckCmd   string `toml:"check_cmd"`
	StageFile  *os.File
	Src        string
	Vars       map[string]interface{}
	etcdClient etcdutil.EtcdClient
}

// NewTemplateResourceFromPath creates a TemplateResource using a decoded file path
// and the supplied EtcdClient as input.
// It returns a TemplateResource and an error if any.
func NewTemplateResourceFromPath(path string, c etcdutil.EtcdClient) (*TemplateResource, error) {
	if c == nil {
		return nil, errors.New("A valid EtcdClient is required.")
	}
	var tc *TemplateResourceConfig
	log.Debug("Loading template resource from " + path)
	_, err := toml.DecodeFile(path, &tc)
	if err != nil {
		return nil, fmt.Errorf("Cannot process template resource %s - %s", path, err.Error())
	}
	tc.TemplateResource.etcdClient = c
	return &tc.TemplateResource, nil
}

// setVars sets the Vars for template resource.
func (t *TemplateResource) setVars() error {
	var err error
	log.Debug("Retrieving keys from etcd")
	log.Debug("Key prefix set to " + config.Prefix())
	t.Vars, err = etcdutil.GetValues(t.etcdClient, config.Prefix(), t.Keys)
	if err != nil {
		return err
	}
	return nil
}

// createStageFile stages the src configuration file by processing the src
// template and setting the desired owner, group, and mode. It also sets the
// StageFile for the template resource.
// It returns an error if any.
func (t *TemplateResource) createStageFile() error {
	t.Src = filepath.Join(config.TemplateDir(), t.Src)
	log.Debug("Using source template " + t.Src)
	if !isFileExist(t.Src) {
		return errors.New("Missing template: " + t.Src)
	}
	temp, err := ioutil.TempFile("", "")
	if err != nil {
		os.Remove(temp.Name())
		return err
	}
        defer temp.Close()
	log.Debug("Compiling source template " + t.Src)
	tplFuncMap := make(template.FuncMap)
	tplFuncMap["Base"] = path.Base
	tmpl := template.Must(template.New(path.Base(t.Src)).Funcs(tplFuncMap).ParseFiles(t.Src))
	if err = tmpl.Execute(temp, t.Vars); err != nil {
		return err
	}
	// Set the owner, group, and mode on the stage file now to make it easier to
	// compare against the destination configuration file later.
	os.Chmod(temp.Name(), t.FileMode)
	os.Chown(temp.Name(), t.Uid, t.Gid)
	t.StageFile = temp
	return nil
}

// sync compares the staged and dest config files and attempts to sync them
// if they differ. sync will run a config check command if set before
// overwriting the target config file. Finally, sync will run a reload command
// if set to have the application or service pick up the changes.
// It returns an error if any.
func (t *TemplateResource) sync() error {
	staged := t.StageFile.Name()
	defer os.Remove(staged)
	log.Debug("Comparing candidate config to " + t.Dest)
	ok, err := sameConfig(staged, t.Dest)
	if err != nil {
		log.Error(err.Error())
	}
	if config.Noop() {
		log.Warning("Noop mode enabled " + t.Dest + " will not be modified")
		return nil
	}
	if !ok {
		log.Info("Target config " + t.Dest + " out of sync")
		if t.CheckCmd != "" {
			if err := t.check(); err != nil {
				return errors.New("Config check failed: " + err.Error())
			}
		}
		log.Debug("Overwriting target config " + t.Dest)
		if err := os.Rename(staged, t.Dest); err != nil {
			return err
		}
		if t.ReloadCmd != "" {
			if err := t.reload(); err != nil {
				return err
			}
		}
		log.Info("Target config " + t.Dest + " has been updated")
	} else {
		log.Info("Target config " + t.Dest + " in sync")
	}
	return nil
}

// check executes the check command to validate the staged config file. The
// command is modified so that any references to src template are substituted
// with a string representing the full path of the staged file. This allows the
// check to be run on the staged file before overwriting the destination config
// file.
// It returns nil if the check command returns 0 and there are no other errors.
func (t *TemplateResource) check() error {
	var cmdBuffer bytes.Buffer
	data := make(map[string]string)
	data["src"] = t.StageFile.Name()
	tmpl, err := template.New("checkcmd").Parse(t.CheckCmd)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(&cmdBuffer, data); err != nil {
		return err
	}
	log.Debug("Running " + cmdBuffer.String())
	c := exec.Command("/bin/sh", "-c", cmdBuffer.String())
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// reload executes the reload command.
// It returns nil if the reload command returns 0.
func (t *TemplateResource) reload() error {
	log.Debug("Running " + t.ReloadCmd)
	c := exec.Command("/bin/sh", "-c", t.ReloadCmd)
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// process is a convenience function that wraps calls to the three main tasks
// required to keep local configuration files in sync. First we gather vars
// from etcd, then we stage a candidate configuration file, and finally sync
// things up.
// It returns an error if any.
func (t *TemplateResource) process() error {
	if err := t.setFileMode(); err != nil {
		return err
	}
	if err := t.setVars(); err != nil {
		return err
	}
	if err := t.createStageFile(); err != nil {
		return err
	}
	if err := t.sync(); err != nil {
		return err
	}
	return nil
}

// setFileMode sets the FileMode.
func (t *TemplateResource) setFileMode() error {
	if t.Mode == "" {
		if !isFileExist(t.Dest) {
			t.FileMode = 0644
		} else {
			fi, err := os.Stat(t.Dest)
			if err != nil {
				return err
			}
			t.FileMode = fi.Mode()
		}
	} else {
		mode, err := strconv.ParseUint(t.Mode, 0, 32)
		if err != nil {
			return err
		}
		t.FileMode = os.FileMode(mode)
	}
	return nil
}

// ProcessTemplateResources is a convenience function that loads all the
// template resources and processes them serially. Called from main.
// It returns a list of errors if any.
func ProcessTemplateResources(c etcdutil.EtcdClient) []error {
	runErrors := make([]error, 0)
	var err error
	if c == nil {
		runErrors = append(runErrors, errors.New("An etcd client is required"))
		return runErrors
	}
	log.Debug("Loading template resources from confdir " + config.ConfDir())
	if !isFileExist(config.ConfDir()) {
		log.Warning(fmt.Sprintf("Cannot load template resources confdir '%s' does not exist", config.ConfDir()))
		return runErrors
	}
	paths, err := filepath.Glob(filepath.Join(config.ConfigDir(), "*toml"))
	if err != nil {
		runErrors = append(runErrors, err)
		return runErrors
	}
	for _, p := range paths {
		log.Debug("Processing template resource " + p)
		t, err := NewTemplateResourceFromPath(p, c)
		if err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		if err := t.process(); err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		log.Debug("Processing of template resource " + p + " complete")
	}
	return runErrors
}

// fileStat return a fileInfo describing the named file.
func fileStat(name string) (fi fileInfo, err error) {
	if isFileExist(name) {
		f, err := os.Open(name)
		defer f.Close()
		if err != nil {
			return fi, err
		}
		stats, _ := f.Stat()
		fi.Uid = stats.Sys().(*syscall.Stat_t).Uid
		fi.Gid = stats.Sys().(*syscall.Stat_t).Gid
		fi.Mode = stats.Sys().(*syscall.Stat_t).Mode
		h := md5.New()
		io.Copy(h, f)
		fi.Md5 = fmt.Sprintf("%x", h.Sum(nil))
		return fi, nil
	} else {
		return fi, errors.New("File not found")
	}
}

// sameConfig reports whether src and dest config files are equal.
// Two config files are equal when they have the same file contents and
// Unix permissions. The owner, group, and mode must match.
// It return false in other cases.
func sameConfig(src, dest string) (bool, error) {
	if !isFileExist(dest) {
		return false, nil
	}
	d, err := fileStat(dest)
	if err != nil {
		return false, err
	}
	s, err := fileStat(src)
	if err != nil {
		return false, err
	}
	if d.Uid != s.Uid {
		log.Info(fmt.Sprintf("%s has UID %d should be %d", dest, d.Uid, s.Uid))
	}
	if d.Gid != s.Gid {
		log.Info(fmt.Sprintf("%s has GID %d should be %d", dest, d.Gid, s.Gid))
	}
	if d.Mode != s.Mode {
		log.Info(fmt.Sprintf("%s has mode %s should be %s", dest, os.FileMode(d.Mode), os.FileMode(s.Mode)))
	}
	if d.Md5 != s.Md5 {
		log.Info(fmt.Sprintf("%s has md5sum %s should be %s", dest, d.Md5, s.Md5))
	}
	if d.Uid != s.Uid || d.Gid != s.Gid || d.Mode != s.Mode || d.Md5 != s.Md5 {
		return false, nil
	}
	return true, nil
}

// isFileExist reports whether path exits.
func isFileExist(fpath string) bool {
	if _, err := os.Stat(fpath); os.IsNotExist(err) {
		return false
	}
	return true
}
<commit_msg>Switch concrete etcdClient to StoreClient interface<commit_after>// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package template

import (
	"bytes"
	"crypto/md5"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"syscall"
	"text/template"

	"github.com/BurntSushi/toml"
	"github.com/kelseyhightower/confd/config"
	"github.com/kelseyhightower/confd/log"
)

// StoreClient is used to swap out the
type StoreClient interface {
	GetValues(prefix string, keys []string) (map[string]interface{}, error)
}

// TemplateResourceConfig holds the parsed template resource.
type TemplateResourceConfig struct {
	TemplateResource TemplateResource `toml:"template"`
}

// TemplateResource is the representation of a parsed template resource.
type TemplateResource struct {
	Dest        string
	FileMode    os.FileMode
	Gid         int
	Keys        []string
	Mode        string
	Uid         int
	ReloadCmd   string `toml:"reload_cmd"`
	CheckCmd    string `toml:"check_cmd"`
	StageFile   *os.File
	Src         string
	Vars        map[string]interface{}
	storeClient StoreClient
}

// NewTemplateResourceFromPath creates a TemplateResource using a decoded file path
// and the supplied StoreClient as input.
// It returns a TemplateResource and an error if any.
func NewTemplateResourceFromPath(path string, s StoreClient) (*TemplateResource, error) {
	if s == nil {
		return nil, errors.New("A valid StoreClient is required.")
	}
	var tc *TemplateResourceConfig
	log.Debug("Loading template resource from " + path)
	_, err := toml.DecodeFile(path, &tc)
	if err != nil {
		return nil, fmt.Errorf("Cannot process template resource %s - %s", path, err.Error())
	}
	tc.TemplateResource.storeClient = s
	return &tc.TemplateResource, nil
}

// setVars sets the Vars for template resource.
func (t *TemplateResource) setVars() error {
	var err error
	log.Debug("Retrieving keys from store")
	log.Debug("Key prefix set to " + config.Prefix())
	t.Vars, err = t.storeClient.GetValues(config.Prefix(), t.Keys)
	if err != nil {
		return err
	}
	return nil
}

// createStageFile stages the src configuration file by processing the src
// template and setting the desired owner, group, and mode. It also sets the
// StageFile for the template resource.
// It returns an error if any.
func (t *TemplateResource) createStageFile() error {
	t.Src = filepath.Join(config.TemplateDir(), t.Src)
	log.Debug("Using source template " + t.Src)
	if !isFileExist(t.Src) {
		return errors.New("Missing template: " + t.Src)
	}
	temp, err := ioutil.TempFile("", "")
	if err != nil {
		os.Remove(temp.Name())
		return err
	}
	defer temp.Close()
	log.Debug("Compiling source template " + t.Src)
	tplFuncMap := make(template.FuncMap)
	tplFuncMap["Base"] = path.Base
	tmpl := template.Must(template.New(path.Base(t.Src)).Funcs(tplFuncMap).ParseFiles(t.Src))
	if err = tmpl.Execute(temp, t.Vars); err != nil {
		return err
	}
	// Set the owner, group, and mode on the stage file now to make it easier to
	// compare against the destination configuration file later.
	os.Chmod(temp.Name(), t.FileMode)
	os.Chown(temp.Name(), t.Uid, t.Gid)
	t.StageFile = temp
	return nil
}

// sync compares the staged and dest config files and attempts to sync them
// if they differ. sync will run a config check command if set before
// overwriting the target config file. Finally, sync will run a reload command
// if set to have the application or service pick up the changes.
// It returns an error if any.
func (t *TemplateResource) sync() error {
	staged := t.StageFile.Name()
	defer os.Remove(staged)
	log.Debug("Comparing candidate config to " + t.Dest)
	ok, err := sameConfig(staged, t.Dest)
	if err != nil {
		log.Error(err.Error())
	}
	if config.Noop() {
		log.Warning("Noop mode enabled " + t.Dest + " will not be modified")
		return nil
	}
	if !ok {
		log.Info("Target config " + t.Dest + " out of sync")
		if t.CheckCmd != "" {
			if err := t.check(); err != nil {
				return errors.New("Config check failed: " + err.Error())
			}
		}
		log.Debug("Overwriting target config " + t.Dest)
		if err := os.Rename(staged, t.Dest); err != nil {
			return err
		}
		if t.ReloadCmd != "" {
			if err := t.reload(); err != nil {
				return err
			}
		}
		log.Info("Target config " + t.Dest + " has been updated")
	} else {
		log.Info("Target config " + t.Dest + " in sync")
	}
	return nil
}

// check executes the check command to validate the staged config file. The
// command is modified so that any references to src template are substituted
// with a string representing the full path of the staged file. This allows the
// check to be run on the staged file before overwriting the destination config
// file.
// It returns nil if the check command returns 0 and there are no other errors.
func (t *TemplateResource) check() error {
	var cmdBuffer bytes.Buffer
	data := make(map[string]string)
	data["src"] = t.StageFile.Name()
	tmpl, err := template.New("checkcmd").Parse(t.CheckCmd)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(&cmdBuffer, data); err != nil {
		return err
	}
	log.Debug("Running " + cmdBuffer.String())
	c := exec.Command("/bin/sh", "-c", cmdBuffer.String())
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// reload executes the reload command.
// It returns nil if the reload command returns 0.
func (t *TemplateResource) reload() error {
	log.Debug("Running " + t.ReloadCmd)
	c := exec.Command("/bin/sh", "-c", t.ReloadCmd)
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// process is a convenience function that wraps calls to the three main tasks
// required to keep local configuration files in sync. First we gather vars
// from the store, then we stage a candidate configuration file, and finally sync
// things up.
// It returns an error if any.
func (t *TemplateResource) process() error {
	if err := t.setFileMode(); err != nil {
		return err
	}
	if err := t.setVars(); err != nil {
		return err
	}
	if err := t.createStageFile(); err != nil {
		return err
	}
	if err := t.sync(); err != nil {
		return err
	}
	return nil
}

// setFileMode sets the FileMode.
func (t *TemplateResource) setFileMode() error {
	if t.Mode == "" {
		if !isFileExist(t.Dest) {
			t.FileMode = 0644
		} else {
			fi, err := os.Stat(t.Dest)
			if err != nil {
				return err
			}
			t.FileMode = fi.Mode()
		}
	} else {
		mode, err := strconv.ParseUint(t.Mode, 0, 32)
		if err != nil {
			return err
		}
		t.FileMode = os.FileMode(mode)
	}
	return nil
}

// ProcessTemplateResources is a convenience function that loads all the
// template resources and processes them serially. Called from main.
// It returns a list of errors if any.
func ProcessTemplateResources(s StoreClient) []error {
	runErrors := make([]error, 0)
	var err error
	if s == nil {
		runErrors = append(runErrors, errors.New("A StoreClient client is required"))
		return runErrors
	}
	log.Debug("Loading template resources from confdir " + config.ConfDir())
	if !isFileExist(config.ConfDir()) {
		log.Warning(fmt.Sprintf("Cannot load template resources confdir '%s' does not exist", config.ConfDir()))
		return runErrors
	}
	paths, err := filepath.Glob(filepath.Join(config.ConfigDir(), "*toml"))
	if err != nil {
		runErrors = append(runErrors, err)
		return runErrors
	}
	for _, p := range paths {
		log.Debug("Processing template resource " + p)
		t, err := NewTemplateResourceFromPath(p, s)
		if err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		if err := t.process(); err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		log.Debug("Processing of template resource " + p + " complete")
	}
	return runErrors
}

// fileStat return a fileInfo describing the named file.
func fileStat(name string) (fi fileInfo, err error) {
	if isFileExist(name) {
		f, err := os.Open(name)
		defer f.Close()
		if err != nil {
			return fi, err
		}
		stats, _ := f.Stat()
		fi.Uid = stats.Sys().(*syscall.Stat_t).Uid
		fi.Gid = stats.Sys().(*syscall.Stat_t).Gid
		fi.Mode = stats.Sys().(*syscall.Stat_t).Mode
		h := md5.New()
		io.Copy(h, f)
		fi.Md5 = fmt.Sprintf("%x", h.Sum(nil))
		return fi, nil
	} else {
		return fi, errors.New("File not found")
	}
}

// sameConfig reports whether src and dest config files are equal.
// Two config files are equal when they have the same file contents and
// Unix permissions. The owner, group, and mode must match.
// It return false in other cases.
func sameConfig(src, dest string) (bool, error) {
	if !isFileExist(dest) {
		return false, nil
	}
	d, err := fileStat(dest)
	if err != nil {
		return false, err
	}
	s, err := fileStat(src)
	if err != nil {
		return false, err
	}
	if d.Uid != s.Uid {
		log.Info(fmt.Sprintf("%s has UID %d should be %d", dest, d.Uid, s.Uid))
	}
	if d.Gid != s.Gid {
		log.Info(fmt.Sprintf("%s has GID %d should be %d", dest, d.Gid, s.Gid))
	}
	if d.Mode != s.Mode {
		log.Info(fmt.Sprintf("%s has mode %s should be %s", dest, os.FileMode(d.Mode), os.FileMode(s.Mode)))
	}
	if d.Md5 != s.Md5 {
		log.Info(fmt.Sprintf("%s has md5sum %s should be %s", dest, d.Md5, s.Md5))
	}
	if d.Uid != s.Uid || d.Gid != s.Gid || d.Mode != s.Mode || d.Md5 != s.Md5 {
		return false, nil
	}
	return true, nil
}

// isFileExist reports whether path exits.
func isFileExist(fpath string) bool {
	if _, err := os.Stat(fpath); os.IsNotExist(err) {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package aws

import (
	"errors"
	"fmt"
	"strings"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSAMICopy(t *testing.T) {
	var amiId string
	snapshots := []string{}

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSAMICopyConfig,
				Check: func(state *terraform.State) error {
					rs, ok := state.RootModule().Resources["aws_ami_copy.test"]
					if !ok {
						return fmt.Errorf("AMI resource not found")
					}

					amiId = rs.Primary.ID

					if amiId == "" {
						return fmt.Errorf("AMI id is not set")
					}

					conn := testAccProvider.Meta().(*AWSClient).ec2conn
					req := &ec2.DescribeImagesInput{
						ImageIds: []*string{aws.String(amiId)},
					}
					describe, err := conn.DescribeImages(req)
					if err != nil {
						return err
					}

					if len(describe.Images) != 1 ||
						*describe.Images[0].ImageId != rs.Primary.ID {
						return fmt.Errorf("AMI not found")
					}

					image := describe.Images[0]
					if expected := "available"; *image.State != expected {
						return fmt.Errorf("invalid image state; expected %v, got %v", expected, image.State)
					}
					if expected := "machine"; *image.ImageType != expected {
						return fmt.Errorf("wrong image type; expected %v, got %v", expected, image.ImageType)
					}
					if expected := "terraform-acc-ami-copy"; *image.Name != expected {
						return fmt.Errorf("wrong name; expected %v, got %v", expected, image.Name)
					}

					for _, bdm := range image.BlockDeviceMappings {
						if bdm.Ebs != nil && bdm.Ebs.SnapshotId != nil {
							snapshots = append(snapshots, *bdm.Ebs.SnapshotId)
						}
					}

					if expected := 1; len(snapshots) != expected {
						return fmt.Errorf("wrong number of snapshots; expected %v, got %v", expected, len(snapshots))
					}

					return nil
				},
			},
		},
		CheckDestroy: func(state *terraform.State) error {
			conn := testAccProvider.Meta().(*AWSClient).ec2conn
			diReq := &ec2.DescribeImagesInput{
				ImageIds: []*string{aws.String(amiId)},
			}
			diRes, err := conn.DescribeImages(diReq)
			if err != nil {
				return err
			}

			if len(diRes.Images) > 0 {
				state := diRes.Images[0].State
				return fmt.Errorf("AMI %v remains in state %v", amiId, state)
			}

			stillExist := make([]string, 0, len(snapshots))
			checkErrors := make(map[string]error)
			for _, snapshotId := range snapshots {
				dsReq := &ec2.DescribeSnapshotsInput{
					SnapshotIds: []*string{aws.String(snapshotId)},
				}
				_, err := conn.DescribeSnapshots(dsReq)
				if err == nil {
					stillExist = append(stillExist, snapshotId)
					continue
				}

				awsErr, ok := err.(awserr.Error)
				if !ok {
					checkErrors[snapshotId] = err
					continue
				}

				if awsErr.Code() != "InvalidSnapshot.NotFound" {
					checkErrors[snapshotId] = err
					continue
				}
			}

			if len(stillExist) > 0 || len(checkErrors) > 0 {
				errParts := []string{
					"Expected all snapshots to be gone, but:",
				}
				for _, snapshotId := range stillExist {
					errParts = append(
						errParts,
						fmt.Sprintf("- %v still exists", snapshotId),
					)
				}
				for snapshotId, err := range checkErrors {
					errParts = append(
						errParts,
						fmt.Sprintf("- checking %v gave error: %v", snapshotId, err),
					)
				}
				return errors.New(strings.Join(errParts, "\n"))
			}

			return nil
		},
	})
}

var testAccAWSAMICopyConfig = `
provider "aws" {
	region = "us-east-1"
}

// An AMI can't be directly copied from one account to another, and
// we can't rely on any particular AMI being available since anyone
// can run this test in whatever account they like.
// Therefore we jump through some hoops here:
//  - Spin up an EC2 instance based on a public AMI
//  - Create an AMI by snapshotting that EC2 instance, using
//    aws_ami_from_instance .
//  - Copy the new AMI using aws_ami_copy .
//
// Thus this test can only succeed if the aws_ami_from_instance resource
// is working. If it's misbehaving it will likely cause this test to fail too.

// Since we're booting a t2.micro HVM instance we need a VPC for it to boot
// up into.

resource "aws_vpc" "foo" {
	cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "foo" {
	cidr_block = "10.1.1.0/24"
	vpc_id = "${aws_vpc.foo.id}"
}

resource "aws_instance" "test" {
    // This AMI has one block device mapping, so we expect to have
    // one snapshot in our created AMI.
    // This is an Amazon Linux HVM AMI. A public HVM AMI is required
    // because paravirtual images cannot be copied between accounts.
    ami = "ami-5449393e"
    instance_type = "t2.micro"
    tags {
        Name = "terraform-acc-ami-copy-victim"
    }

    subnet_id = "${aws_subnet.foo.id}"
}

resource "aws_ami_from_instance" "test" {
    name = "terraform-acc-ami-copy-victim"
    description = "Testing Terraform aws_ami_from_instance resource"
    source_instance_id = "${aws_instance.test.id}"
}

resource "aws_ami_copy" "test" {
    name = "terraform-acc-ami-copy"
    description = "Testing Terraform aws_ami_copy resource"
    source_ami_id = "${aws_ami_from_instance.test.id}"
    source_ami_region = "us-east-1"
}
`
<commit_msg>provider/aws: switch test to use more accessible ami<commit_after>package aws

import (
	"errors"
	"fmt"
	"strings"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSAMICopy(t *testing.T) {
	var amiId string
	snapshots := []string{}

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSAMICopyConfig,
				Check: func(state *terraform.State) error {
					rs, ok := state.RootModule().Resources["aws_ami_copy.test"]
					if !ok {
						return fmt.Errorf("AMI resource not found")
					}

					amiId = rs.Primary.ID

					if amiId == "" {
						return fmt.Errorf("AMI id is not set")
					}

					conn := testAccProvider.Meta().(*AWSClient).ec2conn
					req := &ec2.DescribeImagesInput{
						ImageIds: []*string{aws.String(amiId)},
					}
					describe, err := conn.DescribeImages(req)
					if err != nil {
						return err
					}

					if len(describe.Images) != 1 ||
						*describe.Images[0].ImageId != rs.Primary.ID {
						return fmt.Errorf("AMI not found")
					}

					image := describe.Images[0]
					if expected := "available"; *image.State != expected {
						return fmt.Errorf("invalid image state; expected %v, got %v", expected, image.State)
					}
					if expected := "machine"; *image.ImageType != expected {
						return fmt.Errorf("wrong image type; expected %v, got %v", expected, image.ImageType)
					}
					if expected := "terraform-acc-ami-copy"; *image.Name != expected {
						return fmt.Errorf("wrong name; expected %v, got %v", expected, image.Name)
					}

					for _, bdm := range image.BlockDeviceMappings {
						if bdm.Ebs != nil && bdm.Ebs.SnapshotId != nil {
							snapshots = append(snapshots, *bdm.Ebs.SnapshotId)
						}
					}

					if expected := 1; len(snapshots) != expected {
						return fmt.Errorf("wrong number of snapshots; expected %v, got %v", expected, len(snapshots))
					}

					return nil
				},
			},
		},
		CheckDestroy: func(state *terraform.State) error {
			conn := testAccProvider.Meta().(*AWSClient).ec2conn
			diReq := &ec2.DescribeImagesInput{
				ImageIds: []*string{aws.String(amiId)},
			}
			diRes, err := conn.DescribeImages(diReq)
			if err != nil {
				return err
			}

			if len(diRes.Images) > 0 {
				state := diRes.Images[0].State
				return fmt.Errorf("AMI %v remains in state %v", amiId, state)
			}

			stillExist := make([]string, 0, len(snapshots))
			checkErrors := make(map[string]error)
			for _, snapshotId := range snapshots {
				dsReq := &ec2.DescribeSnapshotsInput{
					SnapshotIds: []*string{aws.String(snapshotId)},
				}
				_, err := conn.DescribeSnapshots(dsReq)
				if err == nil {
					stillExist = append(stillExist, snapshotId)
					continue
				}

				awsErr, ok := err.(awserr.Error)
				if !ok {
					checkErrors[snapshotId] = err
					continue
				}

				if awsErr.Code() != "InvalidSnapshot.NotFound" {
					checkErrors[snapshotId] = err
					continue
				}
			}

			if len(stillExist) > 0 || len(checkErrors) > 0 {
				errParts := []string{
					"Expected all snapshots to be gone, but:",
				}
				for _, snapshotId := range stillExist {
					errParts = append(
						errParts,
						fmt.Sprintf("- %v still exists", snapshotId),
					)
				}
				for snapshotId, err := range checkErrors {
					errParts = append(
						errParts,
						fmt.Sprintf("- checking %v gave error: %v", snapshotId, err),
					)
				}
				return errors.New(strings.Join(errParts, "\n"))
			}

			return nil
		},
	})
}

var testAccAWSAMICopyConfig = `
provider "aws" {
	region = "us-east-1"
}

// An AMI can't be directly copied from one account to another, and
// we can't rely on any particular AMI being available since anyone
// can run this test in whatever account they like.
// Therefore we jump through some hoops here:
//  - Spin up an EC2 instance based on a public AMI
//  - Create an AMI by snapshotting that EC2 instance, using
//    aws_ami_from_instance .
//  - Copy the new AMI using aws_ami_copy .
//
// Thus this test can only succeed if the aws_ami_from_instance resource
// is working. If it's misbehaving it will likely cause this test to fail too.

// Since we're booting a t2.micro HVM instance we need a VPC for it to boot
// up into.

resource "aws_vpc" "foo" {
	cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "foo" {
	cidr_block = "10.1.1.0/24"
	vpc_id = "${aws_vpc.foo.id}"
}

resource "aws_instance" "test" {
    // This AMI has one block device mapping, so we expect to have
    // one snapshot in our created AMI.
    // This is an Ubuntu Linux HVM AMI. A public HVM AMI is required
    // because paravirtual images cannot be copied between accounts.
    ami = "ami-0f8bce65"
    instance_type = "t2.micro"
    tags {
        Name = "terraform-acc-ami-copy-victim"
    }

    subnet_id = "${aws_subnet.foo.id}"
}

resource "aws_ami_from_instance" "test" {
    name = "terraform-acc-ami-copy-victim"
    description = "Testing Terraform aws_ami_from_instance resource"
    source_instance_id = "${aws_instance.test.id}"
}

resource "aws_ami_copy" "test" {
    name = "terraform-acc-ami-copy"
    description = "Testing Terraform aws_ami_copy resource"
    source_ami_id = "${aws_ami_from_instance.test.id}"
    source_ami_region = "us-east-1"
}
`
<|endoftext|>


<commit_before>package words

func rearrangeWords(text string) string {
    return text
}
<commit_msg>sovle 1451 use brute force<commit_after>package words

import (
    "strings"
    "unicode"
)

func rearrangeWords(text string) string {
    return useBruteForce(text)
}

// useBruteForce time complexity O(N), space complexity O(10000*MaxLen)
func useBruteForce(text string) string {
    arr := strings.Fields(text)
    set := make([][]string, 10_000)
    for i := range arr {
        set[len(arr[i])] = append(set[len(arr[i])], arr[i])
    }
    ans := make([]string, 0, len(arr))
    for i := range set {
        if len(set[i]) > 0 {
            ans = append(ans, set[i]...)
        }
    }
    // sort.Slice(arr, func(i, j int) bool {
    // return len(arr[i]) < len(arr[j])
    // })
    for i := range ans {
        bc := []byte(ans[i])
        if i == 0 {
            if bc[0] >= 'a' && bc[0] <= 'z' {
                up := byte(unicode.ToUpper(rune(bc[0])))
                bc[0] = up
                //arr[i] = string(bc)
            }
        } else {
            bc[0] = bc[0] | ('x' - 'X')
            //arr[i] = string(bc)
        }
        ans[i] = string(bc)
    }
    return strings.Join(ans, " ")
}
<|endoftext|>


<commit_before>// Copyright 2015 tsuru 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 router provides interfaces that need to be satisfied in order to
// implement a new router on tsuru.
package router

import (
	"errors"
	"fmt"

	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/db/storage"
	"github.com/tsuru/tsuru/log"
	"gopkg.in/mgo.v2/bson"
)

type routerFactory func(string) (Router, error)

var ErrRouteNotFound = errors.New("Route not found")

var routers = make(map[string]routerFactory)

// Register registers a new router.
func Register(name string, r routerFactory) {
	routers[name] = r
}

// Get gets the named router from the registry.
func Get(name string) (Router, error) {
	prefix := "routers:" + name
	routerType, err := config.GetString(prefix + ":type")
	if err != nil {
		log.Errorf("WARNING: config key '%s:type' not found, fallback to top level '%s:*' router config", prefix, name)
		routerType = name
		prefix = name
	}
	factory, ok := routers[routerType]
	if !ok {
		return nil, fmt.Errorf("Unknown router: %q.", routerType)
	}
	r, err := factory(prefix)
	if err != nil {
		return nil, err
	}
	return r, nil
}

// Router is the basic interface of this package. It provides methods for
// managing backends and routes. Each backend can have multiple routes.
type Router interface {
	AddBackend(name string) error
	RemoveBackend(name string) error
	AddRoute(name, address string) error
	RemoveRoute(name, address string) error
	SetCName(cname, name string) error
	UnsetCName(cname, name string) error
	Addr(name string) (string, error)

	// Swap change the router between two backends.
	Swap(string, string) error

	// Routes returns a list of routes of a backend.
	Routes(name string) ([]string, error)
}

type MessageRouter interface {
	StartupMessage() (string, error)
}

func collection() (*storage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, err
	}
	return conn.Collection("routers"), nil
}

// Store stores the app name related with the
// router name.
func Store(appName, routerName, kind string) error {
	coll, err := collection()
	if err != nil {
		return err
	}
	data := map[string]string{
		"app":    appName,
		"router": routerName,
		"kind":   kind,
	}
	return coll.Insert(&data)
}

func retrieveRouterData(appName string) (map[string]string, error) {
	data := map[string]string{}
	coll, err := collection()
	if err != nil {
		return data, err
	}
	err = coll.Find(bson.M{"app": appName}).One(&data)
	// Avoid need for data migrations, before kind existed we only supported
	// hipache as a router so we set is as default here.
	if data["kind"] == "" {
		data["kind"] = "hipache"
	}
	return data, err
}

func Retrieve(appName string) (string, error) {
	data, err := retrieveRouterData(appName)
	if err != nil {
		return "", err
	}
	return data["router"], nil
}

func Remove(appName string) error {
	coll, err := collection()
	if err != nil {
		return err
	}
	return coll.Remove(bson.M{"app": appName})
}

func swapBackendName(backend1, backend2 string) error {
	coll, err := collection()
	if err != nil {
		return err
	}
	router1, err := Retrieve(backend1)
	if err != nil {
		return err
	}
	router2, err := Retrieve(backend2)
	if err != nil {
		return err
	}
	update := bson.M{"$set": bson.M{"router": router2}}
	err = coll.Update(bson.M{"app": backend1}, update)
	if err != nil {
		return err
	}
	update = bson.M{"$set": bson.M{"router": router1}}
	return coll.Update(bson.M{"app": backend2}, update)
}

func Swap(r Router, backend1, backend2 string) error {
	data1, err := retrieveRouterData(backend1)
	if err != nil {
		return err
	}
	data2, err := retrieveRouterData(backend2)
	if err != nil {
		return err
	}
	if data1["kind"] != data2["kind"] {
		return fmt.Errorf("swap is only allowed between routers of the same kind. %q uses %q, %q uses %q",
			backend1, data1["kind"], backend2, data2["kind"])
	}
	routes1, err := r.Routes(backend1)
	if err != nil {
		return err
	}
	routes2, err := r.Routes(backend2)
	if err != nil {
		return err
	}
	for _, route := range routes1 {
		err = r.AddRoute(backend2, route)
		if err != nil {
			return err
		}
		err = r.RemoveRoute(backend1, route)
		if err != nil {
			return err
		}
	}
	for _, route := range routes2 {
		err = r.AddRoute(backend1, route)
		if err != nil {
			return err
		}
		err = r.RemoveRoute(backend2, route)
		if err != nil {
			return err
		}
	}
	return swapBackendName(backend1, backend2)
}
<commit_msg>router: add HealthCheckRouter<commit_after>// Copyright 2015 tsuru 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 router provides interfaces that need to be satisfied in order to
// implement a new router on tsuru.
package router

import (
	"errors"
	"fmt"

	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/db/storage"
	"github.com/tsuru/tsuru/log"
	"gopkg.in/mgo.v2/bson"
)

type routerFactory func(string) (Router, error)

var ErrRouteNotFound = errors.New("Route not found")

var routers = make(map[string]routerFactory)

// Register registers a new router.
func Register(name string, r routerFactory) {
	routers[name] = r
}

// Get gets the named router from the registry.
func Get(name string) (Router, error) {
	prefix := "routers:" + name
	routerType, err := config.GetString(prefix + ":type")
	if err != nil {
		log.Errorf("WARNING: config key '%s:type' not found, fallback to top level '%s:*' router config", prefix, name)
		routerType = name
		prefix = name
	}
	factory, ok := routers[routerType]
	if !ok {
		return nil, fmt.Errorf("Unknown router: %q.", routerType)
	}
	r, err := factory(prefix)
	if err != nil {
		return nil, err
	}
	return r, nil
}

// Router is the basic interface of this package. It provides methods for
// managing backends and routes. Each backend can have multiple routes.
type Router interface {
	AddBackend(name string) error
	RemoveBackend(name string) error
	AddRoute(name, address string) error
	RemoveRoute(name, address string) error
	SetCName(cname, name string) error
	UnsetCName(cname, name string) error
	Addr(name string) (string, error)

	// Swap change the router between two backends.
	Swap(string, string) error

	// Routes returns a list of routes of a backend.
	Routes(name string) ([]string, error)
}

type MessageRouter interface {
	StartupMessage() (string, error)
}

type HealthcheckRouter interface {
	Healthcheck() error
}

func collection() (*storage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, err
	}
	return conn.Collection("routers"), nil
}

// Store stores the app name related with the
// router name.
func Store(appName, routerName, kind string) error {
	coll, err := collection()
	if err != nil {
		return err
	}
	data := map[string]string{
		"app":    appName,
		"router": routerName,
		"kind":   kind,
	}
	return coll.Insert(&data)
}

func retrieveRouterData(appName string) (map[string]string, error) {
	data := map[string]string{}
	coll, err := collection()
	if err != nil {
		return data, err
	}
	err = coll.Find(bson.M{"app": appName}).One(&data)
	// Avoid need for data migrations, before kind existed we only supported
	// hipache as a router so we set is as default here.
	if data["kind"] == "" {
		data["kind"] = "hipache"
	}
	return data, err
}

func Retrieve(appName string) (string, error) {
	data, err := retrieveRouterData(appName)
	if err != nil {
		return "", err
	}
	return data["router"], nil
}

func Remove(appName string) error {
	coll, err := collection()
	if err != nil {
		return err
	}
	return coll.Remove(bson.M{"app": appName})
}

func swapBackendName(backend1, backend2 string) error {
	coll, err := collection()
	if err != nil {
		return err
	}
	router1, err := Retrieve(backend1)
	if err != nil {
		return err
	}
	router2, err := Retrieve(backend2)
	if err != nil {
		return err
	}
	update := bson.M{"$set": bson.M{"router": router2}}
	err = coll.Update(bson.M{"app": backend1}, update)
	if err != nil {
		return err
	}
	update = bson.M{"$set": bson.M{"router": router1}}
	return coll.Update(bson.M{"app": backend2}, update)
}

func Swap(r Router, backend1, backend2 string) error {
	data1, err := retrieveRouterData(backend1)
	if err != nil {
		return err
	}
	data2, err := retrieveRouterData(backend2)
	if err != nil {
		return err
	}
	if data1["kind"] != data2["kind"] {
		return fmt.Errorf("swap is only allowed between routers of the same kind. %q uses %q, %q uses %q",
			backend1, data1["kind"], backend2, data2["kind"])
	}
	routes1, err := r.Routes(backend1)
	if err != nil {
		return err
	}
	routes2, err := r.Routes(backend2)
	if err != nil {
		return err
	}
	for _, route := range routes1 {
		err = r.AddRoute(backend2, route)
		if err != nil {
			return err
		}
		err = r.RemoveRoute(backend1, route)
		if err != nil {
			return err
		}
	}
	for _, route := range routes2 {
		err = r.AddRoute(backend1, route)
		if err != nil {
			return err
		}
		err = r.RemoveRoute(backend2, route)
		if err != nil {
			return err
		}
	}
	return swapBackendName(backend1, backend2)
}
<|endoftext|>