<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package network

import (
	"fmt"
	"strconv"
	"time"

	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"

	batchv1 "k8s.io/api/batch/v1"
	k8sv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/kubevirt/tests"
	"kubevirt.io/kubevirt/tests/libvmi"
)

var _ = SIGDescribe("[Serial]Services", func() {
	var virtClient kubecli.KubevirtClient

	runTCPClientExpectingHelloWorldFromServer := func(host, port, namespace string) *batchv1.Job {
		job := tests.NewHelloWorldJob(host, port)
		job, err := virtClient.BatchV1().Jobs(namespace).Create(job)
		ExpectWithOffset(1, err).ToNot(HaveOccurred())
		return job
	}

	exposeExistingVMISpec := func(vmi *v1.VirtualMachineInstance, subdomain string, hostname string, selectorLabelKey string, selectorLabelValue string) *v1.VirtualMachineInstance {
		vmi.Labels = map[string]string{selectorLabelKey: selectorLabelValue}
		vmi.Spec.Subdomain = subdomain
		vmi.Spec.Hostname = hostname

		return vmi
	}

	readyVMI := func(vmi *v1.VirtualMachineInstance) *v1.VirtualMachineInstance {
		createdVMI, err := virtClient.VirtualMachineInstance(tests.NamespaceTestDefault).Create(vmi)
		Expect(err).ToNot(HaveOccurred())

		return tests.WaitUntilVMIReady(createdVMI, tests.LoggedInCirrosExpecter)
	}

	cleanupVMI := func(virtClient kubecli.KubevirtClient, vmi *v1.VirtualMachineInstance) {
		By("Deleting the VMI")
		Expect(virtClient.VirtualMachineInstance(tests.NamespaceTestDefault).Delete(vmi.GetName(), &k8smetav1.DeleteOptions{})).To(Succeed())

		By("Waiting for the VMI to be gone")
		Eventually(func() error {
			_, err := virtClient.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vmi.GetName(), &k8smetav1.GetOptions{})
			return err
		}, 2*time.Minute, time.Second).Should(SatisfyAll(HaveOccurred(), WithTransform(errors.IsNotFound, BeTrue())), "The VMI should be gone within the given timeout")
	}

	cleanupService := func(namespace string, serviceName string) error {
		return virtClient.CoreV1().Services(namespace).Delete(serviceName, &k8smetav1.DeleteOptions{})
	}

	assertConnectivityToService := func(serviceName, namespace string, servicePort int) func() error {
		serviceFQDN := fmt.Sprintf("%s.%s", serviceName, namespace)

		By(fmt.Sprintf("starting a job which tries to reach the vmi via service %s", serviceFQDN))
		job := runTCPClientExpectingHelloWorldFromServer(serviceFQDN, strconv.Itoa(servicePort), namespace)

		By(fmt.Sprintf("waiting for the job to report a SUCCESSFUL connection attempt to service %s on port %d", serviceFQDN, servicePort))
		tests.WaitForJobToSucceed(job, 90)
		return func() error {
			return virtClient.BatchV1().Jobs(tests.NamespaceTestDefault).Delete(job.Name, &k8smetav1.DeleteOptions{})
		}
	}

	assertNoConnectivityToService := func(serviceName, namespace string, servicePort int) func() error {
		serviceFQDN := fmt.Sprintf("%s.%s", serviceName, namespace)

		By(fmt.Sprintf("starting a job which tries to reach the vmi via service %s", serviceFQDN))
		job := runTCPClientExpectingHelloWorldFromServer(serviceFQDN, strconv.Itoa(servicePort), namespace)

		By(fmt.Sprintf("waiting for the job to report a FAILED connection attempt to service %s on port %d", serviceFQDN, servicePort))
		tests.WaitForJobToFail(job, 90)
		return func() error {
			return virtClient.BatchV1().Jobs(tests.NamespaceTestDefault).Delete(job.Name, &k8smetav1.DeleteOptions{})
		}
	}

	BeforeEach(func() {
		var err error
		virtClient, err = kubecli.GetKubevirtClient()
		Expect(err).NotTo(HaveOccurred(), "Should successfully initialize an API client")
	})

	Context("bridge interface binding", func() {
		var inboundVMI *v1.VirtualMachineInstance
		var serviceName string

		const (
			selectorLabelKey   = "expose"
			selectorLabelValue = "me"
			servicePort        = 1500
		)

		createVMISpecWithBridgeInterface := func() *v1.VirtualMachineInstance {
			return libvmi.NewCirros(
				libvmi.WithInterface(libvmi.InterfaceDeviceWithBridgeBinding()),
				libvmi.WithNetwork(v1.DefaultPodNetwork()))
		}

		createReadyVMIWithBridgeBindingAndExposedService := func(hostname string, subdomain string) *v1.VirtualMachineInstance {
			return readyVMI(
				exposeExistingVMISpec(
					createVMISpecWithBridgeInterface(), subdomain, hostname, selectorLabelKey, selectorLabelValue))
		}

		BeforeEach(func() {
			subdomain := "vmi"
			hostname := "inbound"

			inboundVMI = createReadyVMIWithBridgeBindingAndExposedService(hostname, subdomain)
			tests.StartTCPServer(inboundVMI, servicePort)
		})

		AfterEach(func() {
			Expect(inboundVMI).NotTo(BeNil(), "the VMI object must exist in order to be deleted.")
			cleanupVMI(virtClient, inboundVMI)
		})

		Context("with a service matching the vmi exposed", func() {
			var jobsToCleanup []func() error

			BeforeEach(func() {
				serviceName = "myservice"

				service := buildServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue)
				_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service)
				Expect(err).ToNot(HaveOccurred())

				// reset the job cleanup functions list
				jobsToCleanup = nil
			})

			AfterEach(func() {
				Expect(virtClient.CoreV1().Services(inboundVMI.Namespace).Delete(serviceName, &k8smetav1.DeleteOptions{})).To(Succeed())
			})

			AfterEach(func() {
				cleanupJobs(jobsToCleanup)
			})

			It("[test_id:1547] should be able to reach the vmi based on labels specified on the vmi", func() {
				jobCleanupFunction := assertConnectivityToService(serviceName, inboundVMI.Namespace, servicePort)
				jobsToCleanup = append(jobsToCleanup, jobCleanupFunction)
			})

			It("[test_id:1548] should fail to reach the vmi if an invalid servicename is used", func() {
				jobCleanupFunction := assertNoConnectivityToService("wrongservice", inboundVMI.Namespace, servicePort)
				jobsToCleanup = append(jobsToCleanup, jobCleanupFunction)
			})
		})

		Context("with a subdomain and a headless service given", func() {
			var jobsToCleanup []func() error

			BeforeEach(func() {
				serviceName = inboundVMI.Spec.Subdomain

				service := buildHeadlessServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue)
				_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service)
				Expect(err).ToNot(HaveOccurred())

				// reset the job cleanup functions list
				jobsToCleanup = nil
			})

			AfterEach(func() {
				Expect(virtClient.CoreV1().Services(inboundVMI.Namespace).Delete(serviceName, &k8smetav1.DeleteOptions{})).To(Succeed())
			})

			AfterEach(func() {
				cleanupJobs(jobsToCleanup)
			})

			It("[test_id:1549]should be able to reach the vmi via its unique fully qualified domain name", func() {
				serviceHostnameWithSubdomain := fmt.Sprintf("%s.%s", inboundVMI.Spec.Hostname, inboundVMI.Spec.Subdomain)
				jobCleanupFunction := assertConnectivityToService(serviceHostnameWithSubdomain, inboundVMI.Namespace, servicePort)
				jobsToCleanup = append(jobsToCleanup, jobCleanupFunction)
			})
		})
	})

	Context("Masquerade interface binding", func() {
		var inboundVMI *v1.VirtualMachineInstance

		const (
			selectorLabelKey   = "expose"
			selectorLabelValue = "me"
			servicePort        = 1500
		)

		createReadyVMIWithMasqueradeBindingAndExposedService := func(hostname string, subdomain string) *v1.VirtualMachineInstance {
			vmi := libvmi.NewCirros(
				libvmi.WithInterface(libvmi.InterfaceDeviceWithMasqueradeBinding()),
				libvmi.WithNetwork(v1.DefaultPodNetwork()))
			return readyVMI(
				exposeExistingVMISpec(vmi, subdomain, hostname, selectorLabelKey, selectorLabelValue))
		}

		BeforeEach(func() {
			subdomain := "vmi"
			hostname := "inbound"

			inboundVMI = createReadyVMIWithMasqueradeBindingAndExposedService(hostname, subdomain)
			tests.StartTCPServer(inboundVMI, servicePort)
		})

		AfterEach(func() {
			Expect(inboundVMI).NotTo(BeNil(), "the VMI object must exist in order to be deleted.")
			cleanupVMI(virtClient, inboundVMI)
		})

		Context("with a service matching the vmi exposed", func() {
			var jobToCleanup func() error
			var service *k8sv1.Service

			AfterEach(func() {
				Expect(jobToCleanup).NotTo(BeNil(), "a k8sv1.Job cleaning up function should exist")
				Expect(jobToCleanup()).To(Succeed(), "cleaning up the k8sv1.Job entity should have succeeded.")
				jobToCleanup = nil
			})

			AfterEach(func() {
				Expect(cleanupService(inboundVMI.Namespace, service.Name)).To(Succeed(), "cleaning up the k8sv1.Service entity should have succeeded.")
			})

			table.DescribeTable("[Conformance] should be able to reach the vmi based on labels specified on the vmi", func(ipFamily k8sv1.IPFamily) {
				serviceName := "myservice"
				if ipFamily == k8sv1.IPv6Protocol {
					serviceName = serviceName + "v6"
					service = buildIPv6ServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue)
				} else {
					service = buildServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue)
				}

				_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service)
				Expect(err).NotTo(HaveOccurred(), "the k8sv1.Service entity should have been created.")

				jobToCleanup = assertConnectivityToService(serviceName, inboundVMI.Namespace, servicePort)
			},
				table.Entry("when the service is exposed by an IPv4 address.", k8sv1.IPv4Protocol),
				table.Entry("when the service is exposed by an IPv6 address.", k8sv1.IPv6Protocol),
			)
		})

		Context("*without* a service matching the vmi exposed", func() {
			var jobToCleanup func() error
			var serviceName string

			AfterEach(func() {
				Expect(jobToCleanup).NotTo(BeNil(), "a k8sv1.Job cleaning up function should exist")
				Expect(jobToCleanup()).To(Succeed(), "cleaning up the k8sv1.Job entity should have succeeded.")
				jobToCleanup = nil
			})

			It("should fail to reach the vmi", func() {
				serviceName = "missingservice"
				jobToCleanup = assertNoConnectivityToService(serviceName, inboundVMI.Namespace, servicePort)
			})
		})
	})
})

func cleanupJobs(jobsCleanupFunctions []func() error) {
	var errorBucket []error
	for _, jobCleanupFunc := range jobsCleanupFunctions {
		err := jobCleanupFunc()
		if err != nil {
			errorBucket = append(errorBucket, err)
		}
	}
	Expect(errorBucket).To(BeEmpty(), "Removing the `Job`s should be successful.")
}

func buildHeadlessServiceSpec(serviceName string, exposedPort int, portToExpose int, selectorKey string, selectorValue string) *k8sv1.Service {
	service := buildServiceSpec(serviceName, exposedPort, portToExpose, selectorKey, selectorValue)
	service.Spec.ClusterIP = k8sv1.ClusterIPNone
	return service
}

func buildIPv6ServiceSpec(serviceName string, exposedPort int, portToExpose int, selectorKey string, selectorValue string) *k8sv1.Service {
	service := buildServiceSpec(serviceName, exposedPort, portToExpose, selectorKey, selectorValue)
	ipv6Family := k8sv1.IPv6Protocol
	service.Spec.IPFamily = &ipv6Family

	return service
}

func buildServiceSpec(serviceName string, exposedPort int, portToExpose int, selectorKey string, selectorValue string) *k8sv1.Service {
	return &k8sv1.Service{
		ObjectMeta: k8smetav1.ObjectMeta{
			Name: serviceName,
		},
		Spec: k8sv1.ServiceSpec{
			Selector: map[string]string{
				selectorKey: selectorValue,
			},
			Ports: []k8sv1.ServicePort{
				{Protocol: k8sv1.ProtocolTCP, Port: int32(portToExpose), TargetPort: intstr.FromInt(exposedPort)},
			},
		},
	}
}
<commit_msg>dual stack, services, tests: unify the `Job` cleanup solution<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package network

import (
	"fmt"
	"strconv"
	"time"

	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"

	batchv1 "k8s.io/api/batch/v1"
	k8sv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	k8smetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/kubevirt/tests"
	"kubevirt.io/kubevirt/tests/libvmi"
)

var _ = SIGDescribe("[Serial]Services", func() {
	var virtClient kubecli.KubevirtClient

	runTCPClientExpectingHelloWorldFromServer := func(host, port, namespace string) *batchv1.Job {
		job := tests.NewHelloWorldJob(host, port)
		job, err := virtClient.BatchV1().Jobs(namespace).Create(job)
		ExpectWithOffset(1, err).ToNot(HaveOccurred())
		return job
	}

	exposeExistingVMISpec := func(vmi *v1.VirtualMachineInstance, subdomain string, hostname string, selectorLabelKey string, selectorLabelValue string) *v1.VirtualMachineInstance {
		vmi.Labels = map[string]string{selectorLabelKey: selectorLabelValue}
		vmi.Spec.Subdomain = subdomain
		vmi.Spec.Hostname = hostname

		return vmi
	}

	readyVMI := func(vmi *v1.VirtualMachineInstance) *v1.VirtualMachineInstance {
		createdVMI, err := virtClient.VirtualMachineInstance(tests.NamespaceTestDefault).Create(vmi)
		Expect(err).ToNot(HaveOccurred())

		return tests.WaitUntilVMIReady(createdVMI, tests.LoggedInCirrosExpecter)
	}

	cleanupVMI := func(virtClient kubecli.KubevirtClient, vmi *v1.VirtualMachineInstance) {
		By("Deleting the VMI")
		Expect(virtClient.VirtualMachineInstance(tests.NamespaceTestDefault).Delete(vmi.GetName(), &k8smetav1.DeleteOptions{})).To(Succeed())

		By("Waiting for the VMI to be gone")
		Eventually(func() error {
			_, err := virtClient.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vmi.GetName(), &k8smetav1.GetOptions{})
			return err
		}, 2*time.Minute, time.Second).Should(SatisfyAll(HaveOccurred(), WithTransform(errors.IsNotFound, BeTrue())), "The VMI should be gone within the given timeout")
	}

	cleanupService := func(namespace string, serviceName string) error {
		return virtClient.CoreV1().Services(namespace).Delete(serviceName, &k8smetav1.DeleteOptions{})
	}

	assertConnectivityToService := func(serviceName, namespace string, servicePort int) func() error {
		serviceFQDN := fmt.Sprintf("%s.%s", serviceName, namespace)

		By(fmt.Sprintf("starting a job which tries to reach the vmi via service %s", serviceFQDN))
		job := runTCPClientExpectingHelloWorldFromServer(serviceFQDN, strconv.Itoa(servicePort), namespace)

		By(fmt.Sprintf("waiting for the job to report a SUCCESSFUL connection attempt to service %s on port %d", serviceFQDN, servicePort))
		tests.WaitForJobToSucceed(job, 90)
		return func() error {
			return virtClient.BatchV1().Jobs(tests.NamespaceTestDefault).Delete(job.Name, &k8smetav1.DeleteOptions{})
		}
	}

	assertNoConnectivityToService := func(serviceName, namespace string, servicePort int) func() error {
		serviceFQDN := fmt.Sprintf("%s.%s", serviceName, namespace)

		By(fmt.Sprintf("starting a job which tries to reach the vmi via service %s", serviceFQDN))
		job := runTCPClientExpectingHelloWorldFromServer(serviceFQDN, strconv.Itoa(servicePort), namespace)

		By(fmt.Sprintf("waiting for the job to report a FAILED connection attempt to service %s on port %d", serviceFQDN, servicePort))
		tests.WaitForJobToFail(job, 90)
		return func() error {
			return virtClient.BatchV1().Jobs(tests.NamespaceTestDefault).Delete(job.Name, &k8smetav1.DeleteOptions{})
		}
	}

	BeforeEach(func() {
		var err error
		virtClient, err = kubecli.GetKubevirtClient()
		Expect(err).NotTo(HaveOccurred(), "Should successfully initialize an API client")
	})

	Context("bridge interface binding", func() {
		var inboundVMI *v1.VirtualMachineInstance
		var serviceName string

		const (
			selectorLabelKey   = "expose"
			selectorLabelValue = "me"
			servicePort        = 1500
		)

		createVMISpecWithBridgeInterface := func() *v1.VirtualMachineInstance {
			return libvmi.NewCirros(
				libvmi.WithInterface(libvmi.InterfaceDeviceWithBridgeBinding()),
				libvmi.WithNetwork(v1.DefaultPodNetwork()))
		}

		createReadyVMIWithBridgeBindingAndExposedService := func(hostname string, subdomain string) *v1.VirtualMachineInstance {
			return readyVMI(
				exposeExistingVMISpec(
					createVMISpecWithBridgeInterface(), subdomain, hostname, selectorLabelKey, selectorLabelValue))
		}

		BeforeEach(func() {
			subdomain := "vmi"
			hostname := "inbound"

			inboundVMI = createReadyVMIWithBridgeBindingAndExposedService(hostname, subdomain)
			tests.StartTCPServer(inboundVMI, servicePort)
		})

		AfterEach(func() {
			Expect(inboundVMI).NotTo(BeNil(), "the VMI object must exist in order to be deleted.")
			cleanupVMI(virtClient, inboundVMI)
		})

		Context("with a service matching the vmi exposed", func() {
			var jobCleanup func() error

			BeforeEach(func() {
				serviceName = "myservice"

				service := buildServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue)
				_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service)
				Expect(err).ToNot(HaveOccurred())
			})

			AfterEach(func() {
				Expect(cleanupService(inboundVMI.GetNamespace(), serviceName)).To(Succeed(), "cleaning up the k8sv1.Service entity should have succeeded.")
			})

			AfterEach(func() {
				Expect(jobCleanup).NotTo(BeNil(), "a k8sv1.Job cleaning up function should exist")
				Expect(jobCleanup()).To(Succeed(), "cleaning up the k8sv1.Job entity should have succeeded.")
				jobCleanup = nil
			})

			It("[test_id:1547] should be able to reach the vmi based on labels specified on the vmi", func() {
				jobCleanup = assertConnectivityToService(serviceName, inboundVMI.Namespace, servicePort)
			})

			It("[test_id:1548] should fail to reach the vmi if an invalid servicename is used", func() {
				jobCleanup = assertNoConnectivityToService("wrongservice", inboundVMI.Namespace, servicePort)
			})
		})

		Context("with a subdomain and a headless service given", func() {
			var jobCleanup func() error

			BeforeEach(func() {
				serviceName = inboundVMI.Spec.Subdomain

				service := buildHeadlessServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue)
				_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service)
				Expect(err).ToNot(HaveOccurred())
			})

			AfterEach(func() {
				Expect(virtClient.CoreV1().Services(inboundVMI.Namespace).Delete(serviceName, &k8smetav1.DeleteOptions{})).To(Succeed())
			})

			AfterEach(func() {
				Expect(jobCleanup()).To(Succeed(), "cleaning up the k8sv1.Service entity should have succeeded.")
			})

			It("[test_id:1549]should be able to reach the vmi via its unique fully qualified domain name", func() {
				serviceHostnameWithSubdomain := fmt.Sprintf("%s.%s", inboundVMI.Spec.Hostname, inboundVMI.Spec.Subdomain)
				jobCleanup = assertConnectivityToService(serviceHostnameWithSubdomain, inboundVMI.Namespace, servicePort)
			})
		})
	})

	Context("Masquerade interface binding", func() {
		var inboundVMI *v1.VirtualMachineInstance

		const (
			selectorLabelKey   = "expose"
			selectorLabelValue = "me"
			servicePort        = 1500
		)

		createReadyVMIWithMasqueradeBindingAndExposedService := func(hostname string, subdomain string) *v1.VirtualMachineInstance {
			vmi := libvmi.NewCirros(
				libvmi.WithInterface(libvmi.InterfaceDeviceWithMasqueradeBinding()),
				libvmi.WithNetwork(v1.DefaultPodNetwork()))
			return readyVMI(
				exposeExistingVMISpec(vmi, subdomain, hostname, selectorLabelKey, selectorLabelValue))
		}

		BeforeEach(func() {
			subdomain := "vmi"
			hostname := "inbound"

			inboundVMI = createReadyVMIWithMasqueradeBindingAndExposedService(hostname, subdomain)
			tests.StartTCPServer(inboundVMI, servicePort)
		})

		AfterEach(func() {
			Expect(inboundVMI).NotTo(BeNil(), "the VMI object must exist in order to be deleted.")
			cleanupVMI(virtClient, inboundVMI)
		})

		Context("with a service matching the vmi exposed", func() {
			var jobCleanup func() error
			var service *k8sv1.Service

			AfterEach(func() {
				Expect(jobCleanup).NotTo(BeNil(), "a k8sv1.Job cleaning up function should exist")
				Expect(jobCleanup()).To(Succeed(), "cleaning up the k8sv1.Job entity should have succeeded.")
				jobCleanup = nil
			})

			AfterEach(func() {
				Expect(cleanupService(inboundVMI.GetNamespace(), service.Name)).To(Succeed(), "cleaning up the k8sv1.Service entity should have succeeded.")
			})

			table.DescribeTable("[Conformance] should be able to reach the vmi based on labels specified on the vmi", func(ipFamily k8sv1.IPFamily) {
				serviceName := "myservice"
				if ipFamily == k8sv1.IPv6Protocol {
					serviceName = serviceName + "v6"
					service = buildIPv6ServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue)
				} else {
					service = buildServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue)
				}

				_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service)
				Expect(err).NotTo(HaveOccurred(), "the k8sv1.Service entity should have been created.")

				jobCleanup = assertConnectivityToService(serviceName, inboundVMI.Namespace, servicePort)
			},
				table.Entry("when the service is exposed by an IPv4 address.", k8sv1.IPv4Protocol),
				table.Entry("when the service is exposed by an IPv6 address.", k8sv1.IPv6Protocol),
			)
		})

		Context("*without* a service matching the vmi exposed", func() {
			var jobCleanup func() error
			var serviceName string

			AfterEach(func() {
				Expect(jobCleanup).NotTo(BeNil(), "a k8sv1.Job cleaning up function should exist")
				Expect(jobCleanup()).To(Succeed(), "cleaning up the k8sv1.Job entity should have succeeded.")
				jobCleanup = nil
			})

			It("should fail to reach the vmi", func() {
				serviceName = "missingservice"
				jobCleanup = assertNoConnectivityToService(serviceName, inboundVMI.Namespace, servicePort)
			})
		})
	})
})

func buildHeadlessServiceSpec(serviceName string, exposedPort int, portToExpose int, selectorKey string, selectorValue string) *k8sv1.Service {
	service := buildServiceSpec(serviceName, exposedPort, portToExpose, selectorKey, selectorValue)
	service.Spec.ClusterIP = k8sv1.ClusterIPNone
	return service
}

func buildIPv6ServiceSpec(serviceName string, exposedPort int, portToExpose int, selectorKey string, selectorValue string) *k8sv1.Service {
	service := buildServiceSpec(serviceName, exposedPort, portToExpose, selectorKey, selectorValue)
	ipv6Family := k8sv1.IPv6Protocol
	service.Spec.IPFamily = &ipv6Family

	return service
}

func buildServiceSpec(serviceName string, exposedPort int, portToExpose int, selectorKey string, selectorValue string) *k8sv1.Service {
	return &k8sv1.Service{
		ObjectMeta: k8smetav1.ObjectMeta{
			Name: serviceName,
		},
		Spec: k8sv1.ServiceSpec{
			Selector: map[string]string{
				selectorKey: selectorValue,
			},
			Ports: []k8sv1.ServicePort{
				{Protocol: k8sv1.ProtocolTCP, Port: int32(portToExpose), TargetPort: intstr.FromInt(exposedPort)},
			},
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libgit

import (
	"context"
	"os"
	"path"
	"strings"
	"sync"

	"github.com/keybase/client/go/kbfs/data"
	"github.com/keybase/client/go/kbfs/libfs"
	"github.com/keybase/client/go/kbfs/libkbfs"
	"github.com/keybase/client/go/protocol/keybase1"
	billy "gopkg.in/src-d/go-billy.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
)

// This file contains libkbfs.Node wrappers for implementing the
// .kbfs_autogit directory structure. It breaks down like this:
//
// * `rootWrapper.wrap()` is installed as a root node wrapper, and wraps
//   the root node for each TLF in a `rootNode` instance.
// * `rootNode` allows .kbfs_autogit to be auto-created when it is
//   looked up, and wraps it two ways, as both a `libkbfs.ReadonlyNode`, and
//   an `autogitRootNode`.
// * `autogitRootNode` lists all the git repos associated with this
//   folder-branch.  It wraps child nodes two ways, as both a
//   `libkbfs.ReadonlyNode` (inherited from `rootNode`), and an
//   `repoDirNode`.
// * `repoDirNode` returns a `*Browser` object when `GetFS()` is
//   called, which is configured to access the corresponding
//   subdirectory within the git repository.  It wraps all children as
//   a `libkbfs.ReadonlyNode` (inherited from `rootNode`); it also
//   wraps subdirectories in `repoDirNode`, and file entries in
//   `repoFileNode`.
// * `repoFileNode` returns a `*browserFile` object when `GetFile()`
//   is called, which is expected to be closed by the caller.
//
// The `*Browser` objects returned are cached in the AutogitManager
// instance, in an LRU-cache, and are cleared whenever the underlying
// repo is updated.  However, this means that the log debug tags that
// are in use may be from the original request that caused the
// `*Browser` to be cached, rather than from the request that is using
// the cached browser.  If this becomes a problem when trying to debug
// stuff, we can modify the go-git `Tree` code to be able to replace
// the underlying storage layer with one that uses the right context.

const (
	// AutogitRoot is the subdirectory name within a TLF where autogit
	// can be accessed.
	AutogitRoot = ".kbfs_autogit"
	// AutogitBranchPrefix is a prefix of a subdirectory name
	// containing one element of a git reference name.
	AutogitBranchPrefix = ".kbfs_autogit_branch_"
	// AutogitCommitPrefix is a prefix of a file name
	// containing the full commit hash.
	AutogitCommitPrefix = ".kbfs_autogit_commit_"
	// branchSlash can substitute for slashes in branch names,
	// following `AutogitBranchPrefix`.
	branchSlash = "^"
)

type repoFileNode struct {
	libkbfs.Node
	am        *AutogitManager
	gitRootFS *libfs.FS
	repo      string
	subdir    string
	branch    plumbing.ReferenceName
	filePath  string
}

var _ libkbfs.Node = (*repoFileNode)(nil)

func (rfn repoFileNode) GetFile(ctx context.Context) billy.File {
	ctx = libkbfs.CtxWithRandomIDReplayable(
		ctx, ctxAutogitIDKey, ctxAutogitOpID, rfn.am.log)
	_, b, err := rfn.am.GetBrowserForRepo(
		ctx, rfn.gitRootFS, rfn.repo, rfn.branch, rfn.subdir)
	if err != nil {
		rfn.am.log.CDebugf(nil, "Error getting browser: %+v", err)
		return nil
	}

	f, err := b.Open(rfn.filePath)
	if err != nil {
		rfn.am.log.CDebugf(ctx, "Error opening file: %+v", err)
		return nil
	}
	return f
}

type repoCommitNode struct {
	libkbfs.Node
	am        *AutogitManager
	gitRootFS *libfs.FS
	repo      string
	hash      plumbing.Hash
}

var _ libkbfs.Node = (*repoCommitNode)(nil)

func (rcn repoCommitNode) GetFile(ctx context.Context) billy.File {
	ctx = libkbfs.CtxWithRandomIDReplayable(
		ctx, ctxAutogitIDKey, ctxAutogitOpID, rcn.am.log)
	_, b, err := rcn.am.GetBrowserForRepo(ctx, rcn.gitRootFS, rcn.repo, "", "")
	if err != nil {
		rcn.am.log.CDebugf(nil, "Error getting browser: %+v", err)
		return nil
	}

	f, err := b.getCommitFile(ctx, rcn.hash)
	if err != nil {
		rcn.am.log.CDebugf(ctx, "Error opening file: %+v", err)
		return nil
	}
	return f
}

type repoDirNode struct {
	libkbfs.Node
	am        *AutogitManager
	gitRootFS *libfs.FS
	repo      string
	subdir    string
	branch    plumbing.ReferenceName
	once      sync.Once
}

var _ libkbfs.Node = (*repoDirNode)(nil)

// ShouldCreateMissedLookup implements the Node interface for
// repoDirNode.
func (rdn *repoDirNode) ShouldCreateMissedLookup(
	ctx context.Context, name string) (
	bool, context.Context, data.EntryType, os.FileInfo, string) {
	switch {
	case strings.HasPrefix(name, AutogitBranchPrefix):
		branchName := strings.TrimPrefix(name, AutogitBranchPrefix)
		if len(branchName) == 0 {
			return rdn.Node.ShouldCreateMissedLookup(ctx, name)
		}
		// It's difficult to tell if a given name is a legitimate
		// prefix for a branch name or not, so just accept everything.
		// If it's not legit, trying to read the data will error out.
		return true, ctx, data.FakeDir, nil, ""
	case strings.HasPrefix(name, AutogitCommitPrefix):
		commit := strings.TrimPrefix(name, AutogitCommitPrefix)
		if len(commit) == 0 {
			return rdn.Node.ShouldCreateMissedLookup(ctx, name)
		}

		rcn := &repoCommitNode{
			Node:      nil,
			am:        rdn.am,
			gitRootFS: rdn.gitRootFS,
			repo:      rdn.repo,
			hash:      plumbing.NewHash(commit),
		}
		f := rcn.GetFile(ctx)
		if f == nil {
			rdn.am.log.CDebugf(ctx, "Error getting commit file")
			return rdn.Node.ShouldCreateMissedLookup(ctx, name)
		}
		return true, ctx, data.FakeFile, f.(*diffFile).GetInfo(), ""
	default:
		return rdn.Node.ShouldCreateMissedLookup(ctx, name)
	}

}

func (rdn *repoDirNode) GetFS(ctx context.Context) billy.Filesystem {
	ctx = libkbfs.CtxWithRandomIDReplayable(
		ctx, ctxAutogitIDKey, ctxAutogitOpID, rdn.am.log)
	_, b, err := rdn.am.GetBrowserForRepo(
		ctx, rdn.gitRootFS, rdn.repo, rdn.branch, rdn.subdir)
	if err != nil {
		rdn.am.log.CDebugf(ctx, "Error getting browser: %+v", err)
		return nil
	}

	if rdn.subdir == "" {
		// If this is the root node for the repo, register it exactly once.
		rdn.once.Do(func() {
			billyFS, err := rdn.gitRootFS.Chroot(rdn.repo)
			if err != nil {
				rdn.am.log.CDebugf(nil, "Error getting repo FS: %+v", err)
				return
			}
			repoFS := billyFS.(*libfs.FS)
			rdn.am.registerRepoNode(repoFS.RootNode(), rdn)
		})
	}

	return b
}

func (rdn *repoDirNode) WrapChild(child libkbfs.Node) libkbfs.Node {
	child = rdn.Node.WrapChild(child)
	name := child.GetBasename()

	if rdn.subdir == "" && strings.HasPrefix(name, AutogitBranchPrefix) &&
		rdn.gitRootFS != nil {
		newBranchPart := strings.TrimPrefix(name, AutogitBranchPrefix)
		branch := plumbing.ReferenceName(path.Join(
			string(rdn.branch),
			strings.Replace(newBranchPart, branchSlash, "/", -1)))

		return &repoDirNode{
			Node:      child,
			am:        rdn.am,
			gitRootFS: rdn.gitRootFS,
			repo:      rdn.repo,
			subdir:    "",
			branch:    branch,
		}
	} else if strings.HasPrefix(name, AutogitCommitPrefix) {
		commit := strings.TrimPrefix(name, AutogitCommitPrefix)
		return &repoCommitNode{
			Node:      child,
			am:        rdn.am,
			gitRootFS: rdn.gitRootFS,
			repo:      rdn.repo,
			hash:      plumbing.NewHash(commit),
		}
	}

	if child.EntryType() == data.Dir {
		return &repoDirNode{
			Node:      child,
			am:        rdn.am,
			gitRootFS: rdn.gitRootFS,
			repo:      rdn.repo,
			subdir:    path.Join(rdn.subdir, name),
			branch:    rdn.branch,
		}
	}
	return &repoFileNode{
		Node:      child,
		am:        rdn.am,
		gitRootFS: rdn.gitRootFS,
		repo:      rdn.repo,
		subdir:    rdn.subdir,
		branch:    rdn.branch,
		filePath:  name,
	}
}

type wrappedRepoList struct {
	*libfs.FS
}

func (wrl *wrappedRepoList) Stat(repoName string) (os.FileInfo, error) {
	return wrl.FS.Stat(normalizeRepoName(repoName))
}

func (wrl *wrappedRepoList) Lstat(repoName string) (os.FileInfo, error) {
	return wrl.FS.Lstat(normalizeRepoName(repoName))
}

// autogitRootNode represents the .kbfs_autogit folder, and lists all
// the git repos associated with the member Node's TLF.
type autogitRootNode struct {
	libkbfs.Node
	am *AutogitManager
	fs *libfs.FS
}

var _ libkbfs.Node = (*autogitRootNode)(nil)

func (arn autogitRootNode) GetFS(ctx context.Context) billy.Filesystem {
	ctx = libkbfs.CtxWithRandomIDReplayable(
		context.Background(), ctxAutogitIDKey, ctxAutogitOpID, arn.am.log)
	return &wrappedRepoList{arn.fs.WithContext(ctx)}
}

// WrapChild implements the Node interface for autogitRootNode.
func (arn autogitRootNode) WrapChild(child libkbfs.Node) libkbfs.Node {
	child = arn.Node.WrapChild(child)
	repo := normalizeRepoName(child.GetBasename())
	return &repoDirNode{
		Node:      child,
		am:        arn.am,
		gitRootFS: arn.fs,
		repo:      repo,
		subdir:    "",
		branch:    "",
	}
}

// rootNode is a Node wrapper around a TLF root node, that causes the
// autogit root to be created when it is accessed.
type rootNode struct {
	libkbfs.Node
	am *AutogitManager

	lock sync.RWMutex
	fs   *libfs.FS
}

var _ libkbfs.Node = (*rootNode)(nil)

// ShouldCreateMissedLookup implements the Node interface for
// rootNode.
func (rn *rootNode) ShouldCreateMissedLookup(ctx context.Context, name string) (
	bool, context.Context, data.EntryType, os.FileInfo, string) {
	if name != AutogitRoot {
		return rn.Node.ShouldCreateMissedLookup(ctx, name)
	}

	rn.lock.Lock()
	defer rn.lock.Unlock()
	if rn.fs == nil {
		// Make the FS once, in a place where we know the NodeCache
		// won't be locked (to avoid deadlock).

		h, err := rn.am.config.KBFSOps().GetTLFHandle(ctx, rn)
		if err != nil {
			rn.am.log.CDebugf(ctx, "Error getting handle: %+v", err)
			return rn.Node.ShouldCreateMissedLookup(ctx, name)
		}

		// Wrap this child so that it will show all the repos.
		ctx := libkbfs.CtxWithRandomIDReplayable(
			context.Background(), ctxAutogitIDKey, ctxAutogitOpID, rn.am.log)
		fs, err := libfs.NewReadonlyFS(
			ctx, rn.am.config, h, rn.GetFolderBranch().Branch, kbfsRepoDir, "",
			keybase1.MDPriorityNormal)
		if err != nil {
			rn.am.log.CDebugf(ctx, "Error making repo FS: %+v", err)
			return rn.Node.ShouldCreateMissedLookup(ctx, name)
		}
		rn.fs = fs
	}
	return true, ctx, data.FakeDir, nil, ""
}

// WrapChild implements the Node interface for rootNode.
func (rn *rootNode) WrapChild(child libkbfs.Node) libkbfs.Node {
	child = rn.Node.WrapChild(child)
	if child.GetBasename() != AutogitRoot {
		return child
	}

	rn.lock.RLock()
	defer rn.lock.RUnlock()
	if rn.fs == nil {
		rn.am.log.CDebugf(nil, "FS not available on WrapChild")
		return child
	}

	rn.am.log.CDebugf(nil, "Making autogit root node")
	return &autogitRootNode{
		Node: &libkbfs.ReadonlyNode{Node: child},
		am:   rn.am,
		fs:   rn.fs,
	}
}

// rootWrapper is a struct that manages wrapping root nodes with
// autogit-related context.
type rootWrapper struct {
	am *AutogitManager
}

func (rw rootWrapper) wrap(node libkbfs.Node) libkbfs.Node {
	return &rootNode{
		Node: node,
		am:   rw.am,
	}
}
<commit_msg>libgit: extra debugging for issue where browser tree goes nil<commit_after>// Copyright 2018 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libgit

import (
	"context"
	"os"
	"path"
	"strings"
	"sync"

	"github.com/keybase/client/go/kbfs/data"
	"github.com/keybase/client/go/kbfs/libfs"
	"github.com/keybase/client/go/kbfs/libkbfs"
	"github.com/keybase/client/go/protocol/keybase1"
	billy "gopkg.in/src-d/go-billy.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
)

// This file contains libkbfs.Node wrappers for implementing the
// .kbfs_autogit directory structure. It breaks down like this:
//
// * `rootWrapper.wrap()` is installed as a root node wrapper, and wraps
//   the root node for each TLF in a `rootNode` instance.
// * `rootNode` allows .kbfs_autogit to be auto-created when it is
//   looked up, and wraps it two ways, as both a `libkbfs.ReadonlyNode`, and
//   an `autogitRootNode`.
// * `autogitRootNode` lists all the git repos associated with this
//   folder-branch.  It wraps child nodes two ways, as both a
//   `libkbfs.ReadonlyNode` (inherited from `rootNode`), and an
//   `repoDirNode`.
// * `repoDirNode` returns a `*Browser` object when `GetFS()` is
//   called, which is configured to access the corresponding
//   subdirectory within the git repository.  It wraps all children as
//   a `libkbfs.ReadonlyNode` (inherited from `rootNode`); it also
//   wraps subdirectories in `repoDirNode`, and file entries in
//   `repoFileNode`.
// * `repoFileNode` returns a `*browserFile` object when `GetFile()`
//   is called, which is expected to be closed by the caller.
//
// The `*Browser` objects returned are cached in the AutogitManager
// instance, in an LRU-cache, and are cleared whenever the underlying
// repo is updated.  However, this means that the log debug tags that
// are in use may be from the original request that caused the
// `*Browser` to be cached, rather than from the request that is using
// the cached browser.  If this becomes a problem when trying to debug
// stuff, we can modify the go-git `Tree` code to be able to replace
// the underlying storage layer with one that uses the right context.

const (
	// AutogitRoot is the subdirectory name within a TLF where autogit
	// can be accessed.
	AutogitRoot = ".kbfs_autogit"
	// AutogitBranchPrefix is a prefix of a subdirectory name
	// containing one element of a git reference name.
	AutogitBranchPrefix = ".kbfs_autogit_branch_"
	// AutogitCommitPrefix is a prefix of a file name
	// containing the full commit hash.
	AutogitCommitPrefix = ".kbfs_autogit_commit_"
	// branchSlash can substitute for slashes in branch names,
	// following `AutogitBranchPrefix`.
	branchSlash = "^"
)

type repoFileNode struct {
	libkbfs.Node
	am        *AutogitManager
	gitRootFS *libfs.FS
	repo      string
	subdir    string
	branch    plumbing.ReferenceName
	filePath  string
}

var _ libkbfs.Node = (*repoFileNode)(nil)

func (rfn repoFileNode) GetFile(ctx context.Context) billy.File {
	ctx = libkbfs.CtxWithRandomIDReplayable(
		ctx, ctxAutogitIDKey, ctxAutogitOpID, rfn.am.log)
	_, b, err := rfn.am.GetBrowserForRepo(
		ctx, rfn.gitRootFS, rfn.repo, rfn.branch, rfn.subdir)
	if err != nil {
		rfn.am.log.CDebugf(nil, "Error getting browser: %+v", err)
		return nil
	}

	f, err := b.Open(rfn.filePath)
	if err != nil {
		rfn.am.log.CDebugf(ctx, "Error opening file: %+v", err)
		return nil
	}
	return f
}

type repoCommitNode struct {
	libkbfs.Node
	am        *AutogitManager
	gitRootFS *libfs.FS
	repo      string
	hash      plumbing.Hash
}

var _ libkbfs.Node = (*repoCommitNode)(nil)

func (rcn repoCommitNode) GetFile(ctx context.Context) billy.File {
	ctx = libkbfs.CtxWithRandomIDReplayable(
		ctx, ctxAutogitIDKey, ctxAutogitOpID, rcn.am.log)
	_, b, err := rcn.am.GetBrowserForRepo(ctx, rcn.gitRootFS, rcn.repo, "", "")
	if err != nil {
		rcn.am.log.CDebugf(nil, "Error getting browser: %+v", err)
		return nil
	}

	f, err := b.getCommitFile(ctx, rcn.hash)
	if err != nil {
		rcn.am.log.CDebugf(ctx, "Error opening file: %+v", err)
		return nil
	}
	return f
}

type repoDirNode struct {
	libkbfs.Node
	am        *AutogitManager
	gitRootFS *libfs.FS
	repo      string
	subdir    string
	branch    plumbing.ReferenceName
	once      sync.Once
}

var _ libkbfs.Node = (*repoDirNode)(nil)

// ShouldCreateMissedLookup implements the Node interface for
// repoDirNode.
func (rdn *repoDirNode) ShouldCreateMissedLookup(
	ctx context.Context, name string) (
	bool, context.Context, data.EntryType, os.FileInfo, string) {
	switch {
	case strings.HasPrefix(name, AutogitBranchPrefix):
		branchName := strings.TrimPrefix(name, AutogitBranchPrefix)
		if len(branchName) == 0 {
			return rdn.Node.ShouldCreateMissedLookup(ctx, name)
		}
		// It's difficult to tell if a given name is a legitimate
		// prefix for a branch name or not, so just accept everything.
		// If it's not legit, trying to read the data will error out.
		return true, ctx, data.FakeDir, nil, ""
	case strings.HasPrefix(name, AutogitCommitPrefix):
		commit := strings.TrimPrefix(name, AutogitCommitPrefix)
		if len(commit) == 0 {
			return rdn.Node.ShouldCreateMissedLookup(ctx, name)
		}

		rcn := &repoCommitNode{
			Node:      nil,
			am:        rdn.am,
			gitRootFS: rdn.gitRootFS,
			repo:      rdn.repo,
			hash:      plumbing.NewHash(commit),
		}
		f := rcn.GetFile(ctx)
		if f == nil {
			rdn.am.log.CDebugf(ctx, "Error getting commit file")
			return rdn.Node.ShouldCreateMissedLookup(ctx, name)
		}
		return true, ctx, data.FakeFile, f.(*diffFile).GetInfo(), ""
	default:
		return rdn.Node.ShouldCreateMissedLookup(ctx, name)
	}

}

func (rdn *repoDirNode) GetFS(ctx context.Context) billy.Filesystem {
	ctx = libkbfs.CtxWithRandomIDReplayable(
		ctx, ctxAutogitIDKey, ctxAutogitOpID, rdn.am.log)
	_, b, err := rdn.am.GetBrowserForRepo(
		ctx, rdn.gitRootFS, rdn.repo, rdn.branch, rdn.subdir)
	if err != nil {
		rdn.am.log.CDebugf(ctx, "Error getting browser: %+v", err)
		return nil
	}

	if rdn.subdir == "" {
		// If this is the root node for the repo, register it exactly once.
		rdn.once.Do(func() {
			// TODO(KBFS-4077): remove this debugging when we find the bug
			// where b.tree seems to be disappearing.
			rdn.am.log.CDebugf(
				ctx, "Got browser %p for repo=%s, branch=%s, subdir=%s, "+
					"with tree %p", b, rdn.repo, rdn.branch, rdn.subdir,
				b.tree)
			billyFS, err := rdn.gitRootFS.Chroot(rdn.repo)
			if err != nil {
				rdn.am.log.CDebugf(ctx, "Error getting repo FS: %+v", err)
				return
			}
			repoFS := billyFS.(*libfs.FS)
			rdn.am.registerRepoNode(repoFS.RootNode(), rdn)
		})
	}

	return b
}

func (rdn *repoDirNode) WrapChild(child libkbfs.Node) libkbfs.Node {
	child = rdn.Node.WrapChild(child)
	name := child.GetBasename()

	if rdn.subdir == "" && strings.HasPrefix(name, AutogitBranchPrefix) &&
		rdn.gitRootFS != nil {
		newBranchPart := strings.TrimPrefix(name, AutogitBranchPrefix)
		branch := plumbing.ReferenceName(path.Join(
			string(rdn.branch),
			strings.Replace(newBranchPart, branchSlash, "/", -1)))

		return &repoDirNode{
			Node:      child,
			am:        rdn.am,
			gitRootFS: rdn.gitRootFS,
			repo:      rdn.repo,
			subdir:    "",
			branch:    branch,
		}
	} else if strings.HasPrefix(name, AutogitCommitPrefix) {
		commit := strings.TrimPrefix(name, AutogitCommitPrefix)
		return &repoCommitNode{
			Node:      child,
			am:        rdn.am,
			gitRootFS: rdn.gitRootFS,
			repo:      rdn.repo,
			hash:      plumbing.NewHash(commit),
		}
	}

	if child.EntryType() == data.Dir {
		return &repoDirNode{
			Node:      child,
			am:        rdn.am,
			gitRootFS: rdn.gitRootFS,
			repo:      rdn.repo,
			subdir:    path.Join(rdn.subdir, name),
			branch:    rdn.branch,
		}
	}
	return &repoFileNode{
		Node:      child,
		am:        rdn.am,
		gitRootFS: rdn.gitRootFS,
		repo:      rdn.repo,
		subdir:    rdn.subdir,
		branch:    rdn.branch,
		filePath:  name,
	}
}

type wrappedRepoList struct {
	*libfs.FS
}

func (wrl *wrappedRepoList) Stat(repoName string) (os.FileInfo, error) {
	return wrl.FS.Stat(normalizeRepoName(repoName))
}

func (wrl *wrappedRepoList) Lstat(repoName string) (os.FileInfo, error) {
	return wrl.FS.Lstat(normalizeRepoName(repoName))
}

// autogitRootNode represents the .kbfs_autogit folder, and lists all
// the git repos associated with the member Node's TLF.
type autogitRootNode struct {
	libkbfs.Node
	am *AutogitManager
	fs *libfs.FS
}

var _ libkbfs.Node = (*autogitRootNode)(nil)

func (arn autogitRootNode) GetFS(ctx context.Context) billy.Filesystem {
	ctx = libkbfs.CtxWithRandomIDReplayable(
		context.Background(), ctxAutogitIDKey, ctxAutogitOpID, arn.am.log)
	return &wrappedRepoList{arn.fs.WithContext(ctx)}
}

// WrapChild implements the Node interface for autogitRootNode.
func (arn autogitRootNode) WrapChild(child libkbfs.Node) libkbfs.Node {
	child = arn.Node.WrapChild(child)
	repo := normalizeRepoName(child.GetBasename())
	return &repoDirNode{
		Node:      child,
		am:        arn.am,
		gitRootFS: arn.fs,
		repo:      repo,
		subdir:    "",
		branch:    "",
	}
}

// rootNode is a Node wrapper around a TLF root node, that causes the
// autogit root to be created when it is accessed.
type rootNode struct {
	libkbfs.Node
	am *AutogitManager

	lock sync.RWMutex
	fs   *libfs.FS
}

var _ libkbfs.Node = (*rootNode)(nil)

// ShouldCreateMissedLookup implements the Node interface for
// rootNode.
func (rn *rootNode) ShouldCreateMissedLookup(ctx context.Context, name string) (
	bool, context.Context, data.EntryType, os.FileInfo, string) {
	if name != AutogitRoot {
		return rn.Node.ShouldCreateMissedLookup(ctx, name)
	}

	rn.lock.Lock()
	defer rn.lock.Unlock()
	if rn.fs == nil {
		// Make the FS once, in a place where we know the NodeCache
		// won't be locked (to avoid deadlock).

		h, err := rn.am.config.KBFSOps().GetTLFHandle(ctx, rn)
		if err != nil {
			rn.am.log.CDebugf(ctx, "Error getting handle: %+v", err)
			return rn.Node.ShouldCreateMissedLookup(ctx, name)
		}

		// Wrap this child so that it will show all the repos.
		ctx := libkbfs.CtxWithRandomIDReplayable(
			context.Background(), ctxAutogitIDKey, ctxAutogitOpID, rn.am.log)
		fs, err := libfs.NewReadonlyFS(
			ctx, rn.am.config, h, rn.GetFolderBranch().Branch, kbfsRepoDir, "",
			keybase1.MDPriorityNormal)
		if err != nil {
			rn.am.log.CDebugf(ctx, "Error making repo FS: %+v", err)
			return rn.Node.ShouldCreateMissedLookup(ctx, name)
		}
		rn.fs = fs
	}
	return true, ctx, data.FakeDir, nil, ""
}

// WrapChild implements the Node interface for rootNode.
func (rn *rootNode) WrapChild(child libkbfs.Node) libkbfs.Node {
	child = rn.Node.WrapChild(child)
	if child.GetBasename() != AutogitRoot {
		return child
	}

	rn.lock.RLock()
	defer rn.lock.RUnlock()
	if rn.fs == nil {
		rn.am.log.CDebugf(nil, "FS not available on WrapChild")
		return child
	}

	rn.am.log.CDebugf(nil, "Making autogit root node")
	return &autogitRootNode{
		Node: &libkbfs.ReadonlyNode{Node: child},
		am:   rn.am,
		fs:   rn.fs,
	}
}

// rootWrapper is a struct that manages wrapping root nodes with
// autogit-related context.
type rootWrapper struct {
	am *AutogitManager
}

func (rw rootWrapper) wrap(node libkbfs.Node) libkbfs.Node {
	return &rootNode{
		Node: node,
		am:   rw.am,
	}
}
<|endoftext|>


<commit_before>package db

import (
	"fmt"
	"strings"

	"github.com/pborman/uuid"

	"github.com/starkandwayne/shield/timespec"
)

type Schedule struct {
	UUID    string `json:"uuid"`
	Name    string `json:"name"`
	Summary string `json:"summary"`
	When    string `json:"when"`
}

type ScheduleFilter struct {
	SkipUsed   bool
	SkipUnused bool

	SearchName string
}

func (f *ScheduleFilter) Query() (string, []interface{}) {
	var wheres []string = []string{"s.uuid = s.uuid"}
	var args []interface{}
	n := 1

	if f.SearchName != "" {
		wheres = append(wheres, fmt.Sprintf("s.name LIKE $%d", n))
		args = append(args, Pattern(f.SearchName))
		n++
	}

	if !f.SkipUsed && !f.SkipUnused {
		return `
			SELECT s.uuid, s.name, s.summary, s.timespec, -1 AS n
				FROM schedules s
				WHERE ` + strings.Join(wheres, " AND ") + `
				ORDER BY s.name, s.uuid ASC
		`, args
	}

	// by default, show schedules with no attached jobs (unused)
	having := `HAVING COUNT(j.uuid) = 0`
	if f.SkipUnused {
		// otherwise, only show schedules that have attached jobs
		having = `HAVING COUNT(j.uuid) > 0`
	}

	return `
		SELECT DISTINCT s.uuid, s.name, s.summary, s.timespec, COUNT(j.uuid) AS n
			FROM schedules s
				LEFT JOIN jobs j
					ON j.schedule_uuid = s.uuid
			WHERE ` + strings.Join(wheres, " AND ") + `
			GROUP BY s.uuid
			` + having + `
			ORDER BY s.name, s.uuid ASC
	`, args
}

func (db *DB) GetAllSchedules(filter *ScheduleFilter) ([]*Schedule, error) {
	l := []*Schedule{}
	query, args := filter.Query()
	r, err := db.Query(query, args...)
	if err != nil {
		return l, err
	}
	defer r.Close()

	for r.Next() {
		ann := &Schedule{}
		var n int

		if err = r.Scan(&ann.UUID, &ann.Name, &ann.Summary, &ann.When, &n); err != nil {
			return l, err
		}

		l = append(l, ann)
	}

	return l, nil
}

func (db *DB) GetSchedule(id uuid.UUID) (*Schedule, error) {
	r, err := db.Query(`
		SELECT uuid, name, summary, timespec
			FROM schedules WHERE uuid = $1`, id.String())
	if err != nil {
		return nil, err
	}
	defer r.Close()

	if !r.Next() {
		return nil, nil
	}

	ann := &Schedule{}

	if err = r.Scan(&ann.UUID, &ann.Name, &ann.Summary, &ann.When); err != nil {
		return nil, err
	}

	return ann, nil
}

func (db *DB) AnnotateSchedule(id uuid.UUID, name string, summary string) error {
	return db.Exec(
		`UPDATE schedules SET name = $1, summary = $2 WHERE uuid = $3`,
		name, summary, id.String(),
	)
}

func (db *DB) CreateSchedule(ts string) (uuid.UUID, error) {
	id := uuid.NewRandom()

	_, err := timespec.Parse(ts)
	if err != nil {
		return id, err
	}
	return id, db.Exec(
		`INSERT INTO schedules (uuid, timespec) VALUES ($1, $2)`,
		id.String(), ts,
	)
}

func (db *DB) UpdateSchedule(id uuid.UUID, ts string) error {
	_, err := timespec.Parse(ts)
	if err != nil {
		return err
	}
	return db.Exec(
		`UPDATE schedules SET timespec = $1 WHERE uuid = $2`,
		ts, id.String(),
	)
}

func (db *DB) DeleteSchedule(id uuid.UUID) (bool, error) {
	r, err := db.Query(
		`SELECT COUNT(uuid) FROM jobs WHERE jobs.schedule_uuid = $1`,
		id.String(),
	)
	if err != nil {
		return false, err
	}
	defer r.Close()

	// already deleted
	if !r.Next() {
		return true, nil
	}

	var numJobs int
	if err = r.Scan(&numJobs); err != nil {
		return false, err
	}

	if numJobs < 0 {
		return false, fmt.Errorf("Schedule %s is in used by %d (negative) Jobs", id.String(), numJobs)
	}
	if numJobs > 0 {
		return false, nil
	}

	r.Close()
	return true, db.Exec(
		`DELETE FROM schedules WHERE uuid = $1`,
		id.String(),
	)
}
<commit_msg>Make db.Schedule UUIDs real UUIDs<commit_after>package db

import (
	"fmt"
	"strings"

	"github.com/pborman/uuid"

	"github.com/starkandwayne/shield/timespec"
)

type Schedule struct {
	UUID    uuid.UUID `json:"uuid"`
	Name    string    `json:"name"`
	Summary string    `json:"summary"`
	When    string    `json:"when"`
}

type ScheduleFilter struct {
	SkipUsed   bool
	SkipUnused bool

	SearchName string
}

func (f *ScheduleFilter) Query() (string, []interface{}) {
	var wheres []string = []string{"s.uuid = s.uuid"}
	var args []interface{}
	n := 1

	if f.SearchName != "" {
		wheres = append(wheres, fmt.Sprintf("s.name LIKE $%d", n))
		args = append(args, Pattern(f.SearchName))
		n++
	}

	if !f.SkipUsed && !f.SkipUnused {
		return `
			SELECT s.uuid, s.name, s.summary, s.timespec, -1 AS n
				FROM schedules s
				WHERE ` + strings.Join(wheres, " AND ") + `
				ORDER BY s.name, s.uuid ASC
		`, args
	}

	// by default, show schedules with no attached jobs (unused)
	having := `HAVING COUNT(j.uuid) = 0`
	if f.SkipUnused {
		// otherwise, only show schedules that have attached jobs
		having = `HAVING COUNT(j.uuid) > 0`
	}

	return `
		SELECT DISTINCT s.uuid, s.name, s.summary, s.timespec, COUNT(j.uuid) AS n
			FROM schedules s
				LEFT JOIN jobs j
					ON j.schedule_uuid = s.uuid
			WHERE ` + strings.Join(wheres, " AND ") + `
			GROUP BY s.uuid
			` + having + `
			ORDER BY s.name, s.uuid ASC
	`, args
}

func (db *DB) GetAllSchedules(filter *ScheduleFilter) ([]*Schedule, error) {
	l := []*Schedule{}
	query, args := filter.Query()
	r, err := db.Query(query, args...)
	if err != nil {
		return l, err
	}
	defer r.Close()

	for r.Next() {
		ann := &Schedule{}
		var n int
		var thisUUID string
		if err = r.Scan(&thisUUID, &ann.Name, &ann.Summary, &ann.When, &n); err != nil {
			return l, err
		}
		ann.UUID = uuid.Parse(thisUUID)

		l = append(l, ann)
	}

	return l, nil
}

func (db *DB) GetSchedule(id uuid.UUID) (*Schedule, error) {
	r, err := db.Query(`
		SELECT uuid, name, summary, timespec
			FROM schedules WHERE uuid = $1`, id.String())
	if err != nil {
		return nil, err
	}
	defer r.Close()

	if !r.Next() {
		return nil, nil
	}

	ann := &Schedule{}
	var thisUUID string
	if err = r.Scan(&thisUUID, &ann.Name, &ann.Summary, &ann.When); err != nil {
		return nil, err
	}
	ann.UUID = uuid.Parse(thisUUID)

	return ann, nil
}

func (db *DB) AnnotateSchedule(id uuid.UUID, name string, summary string) error {
	return db.Exec(
		`UPDATE schedules SET name = $1, summary = $2 WHERE uuid = $3`,
		name, summary, id.String(),
	)
}

func (db *DB) CreateSchedule(ts string) (uuid.UUID, error) {
	id := uuid.NewRandom()

	_, err := timespec.Parse(ts)
	if err != nil {
		return id, err
	}
	return id, db.Exec(
		`INSERT INTO schedules (uuid, timespec) VALUES ($1, $2)`,
		id.String(), ts,
	)
}

func (db *DB) UpdateSchedule(id uuid.UUID, ts string) error {
	_, err := timespec.Parse(ts)
	if err != nil {
		return err
	}
	return db.Exec(
		`UPDATE schedules SET timespec = $1 WHERE uuid = $2`,
		ts, id.String(),
	)
}

func (db *DB) DeleteSchedule(id uuid.UUID) (bool, error) {
	r, err := db.Query(
		`SELECT COUNT(uuid) FROM jobs WHERE jobs.schedule_uuid = $1`,
		id.String(),
	)
	if err != nil {
		return false, err
	}
	defer r.Close()

	// already deleted
	if !r.Next() {
		return true, nil
	}

	var numJobs int
	if err = r.Scan(&numJobs); err != nil {
		return false, err
	}

	if numJobs < 0 {
		return false, fmt.Errorf("Schedule %s is in used by %d (negative) Jobs", id.String(), numJobs)
	}
	if numJobs > 0 {
		return false, nil
	}

	r.Close()
	return true, db.Exec(
		`DELETE FROM schedules WHERE uuid = $1`,
		id.String(),
	)
}
<|endoftext|>


<commit_before>package dns

import (
	"context"
	"fmt"
	"net"

	"github.com/hashicorp/go-hclog"
	"github.com/miekg/dns"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/peer"
	"google.golang.org/grpc/status"

	"github.com/hashicorp/consul/proto-public/pbdns"
)

type LocalAddr struct {
	IP   net.IP
	Port int
}

type Config struct {
	Logger      hclog.Logger
	DNSServeMux *dns.ServeMux
	LocalAddr   LocalAddr
}

type Server struct {
	Config
}

func NewServer(cfg Config) *Server {
	return &Server{cfg}
}

func (s *Server) Register(grpcServer *grpc.Server) {
	pbdns.RegisterDNSServiceServer(grpcServer, s)
}

// BufferResponseWriter writes a DNS response to a byte buffer.
type BufferResponseWriter struct {
	responseBuffer []byte
	LocalAddress   net.Addr
	RemoteAddress  net.Addr
	Logger         hclog.Logger
}

// LocalAddr returns the net.Addr of the server
func (b *BufferResponseWriter) LocalAddr() net.Addr {
	return b.LocalAddress
}

// RemoteAddr returns the net.Addr of the client that sent the current request.
func (b *BufferResponseWriter) RemoteAddr() net.Addr {
	return b.RemoteAddress
}

// WriteMsg writes a reply back to the client.
func (b *BufferResponseWriter) WriteMsg(m *dns.Msg) error {
	// Pack message to bytes first.
	msgBytes, err := m.Pack()
	if err != nil {
		b.Logger.Error("error packing message", "err", err)
		return err
	}
	b.responseBuffer = msgBytes
	return nil
}

// Write writes a raw buffer back to the client.
func (b *BufferResponseWriter) Write(m []byte) (int, error) {
	b.Logger.Debug("Write was called")
	return copy(b.responseBuffer, m), nil
}

// Close closes the connection.
func (b *BufferResponseWriter) Close() error {
	// There's nothing for us to do here as we don't handle the connection.
	return nil
}

// TsigStatus returns the status of the Tsig.
func (b *BufferResponseWriter) TsigStatus() error {
	// TSIG doesn't apply to this response writer.
	return nil
}

// TsigTimersOnly sets the tsig timers only boolean.
func (b *BufferResponseWriter) TsigTimersOnly(bool) {}

// Hijack lets the caller take over the connection.
// After a call to Hijack(), the DNS package will not do anything with the connection. {
func (b *BufferResponseWriter) Hijack() {}

func (s *Server) Query(ctx context.Context, req *pbdns.QueryRequest) (*pbdns.QueryResponse, error) {
	pr, ok := peer.FromContext(ctx)
	if !ok {
		return nil, fmt.Errorf("error retrieving peer information from context")
	}

	var local net.Addr
	var remote net.Addr
	// We do this so that we switch to udp/tcp when handling the request since it will be proxied
	// through consul through gRPC and we need to 'fake' the protocol so that the message is trimmed
	// according to wether it is UDP or TCP.
	switch req.GetProtocol() {
	case pbdns.Protocol_PROTOCOL_TCP:
		remote = pr.Addr
		local = &net.TCPAddr{IP: s.LocalAddr.IP, Port: s.LocalAddr.Port}
	case pbdns.Protocol_PROTOCOL_UDP:
		remoteAddr := pr.Addr.(*net.TCPAddr)
		remote = &net.UDPAddr{IP: remoteAddr.IP, Port: remoteAddr.Port}
		local = &net.UDPAddr{IP: s.LocalAddr.IP, Port: s.LocalAddr.Port}
	default:
		return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("error protocol type not set: %v", req.GetProtocol()))
	}

	respWriter := &BufferResponseWriter{
		LocalAddress:  local,
		RemoteAddress: remote,
		Logger:        s.Logger,
	}

	msg := &dns.Msg{}
	err := msg.Unpack(req.Msg)
	if err != nil {
		s.Logger.Error("error unpacking message", "err", err)
		return nil, status.Error(codes.Internal, fmt.Sprintf("failure decoding dns request: %s", err.Error()))
	}
	s.DNSServeMux.ServeDNS(respWriter, msg)

	queryResponse := &pbdns.QueryResponse{Msg: respWriter.responseBuffer}

	return queryResponse, nil
}
<commit_msg>Making suggested comments<commit_after>package dns

import (
	"context"
	"fmt"
	"net"

	"github.com/hashicorp/go-hclog"
	"github.com/miekg/dns"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/peer"
	"google.golang.org/grpc/status"

	"github.com/hashicorp/consul/proto-public/pbdns"
)

type LocalAddr struct {
	IP   net.IP
	Port int
}

type Config struct {
	Logger      hclog.Logger
	DNSServeMux *dns.ServeMux
	LocalAddr   LocalAddr
}

type Server struct {
	Config
}

func NewServer(cfg Config) *Server {
	return &Server{cfg}
}

func (s *Server) Register(grpcServer *grpc.Server) {
	pbdns.RegisterDNSServiceServer(grpcServer, s)
}

// BufferResponseWriter writes a DNS response to a byte buffer.
type BufferResponseWriter struct {
	responseBuffer []byte
	LocalAddress   net.Addr
	RemoteAddress  net.Addr
	Logger         hclog.Logger
}

// LocalAddr returns the net.Addr of the server
func (b *BufferResponseWriter) LocalAddr() net.Addr {
	return b.LocalAddress
}

// RemoteAddr returns the net.Addr of the client that sent the current request.
func (b *BufferResponseWriter) RemoteAddr() net.Addr {
	return b.RemoteAddress
}

// WriteMsg writes a reply back to the client.
func (b *BufferResponseWriter) WriteMsg(m *dns.Msg) error {
	// Pack message to bytes first.
	msgBytes, err := m.Pack()
	if err != nil {
		b.Logger.Error("error packing message", "err", err)
		return err
	}
	b.responseBuffer = msgBytes
	return nil
}

// Write writes a raw buffer back to the client.
func (b *BufferResponseWriter) Write(m []byte) (int, error) {
	b.Logger.Debug("Write was called")
	return copy(b.responseBuffer, m), nil
}

// Close closes the connection.
func (b *BufferResponseWriter) Close() error {
	// There's nothing for us to do here as we don't handle the connection.
	return nil
}

// TsigStatus returns the status of the Tsig.
func (b *BufferResponseWriter) TsigStatus() error {
	// TSIG doesn't apply to this response writer.
	return nil
}

// TsigTimersOnly sets the tsig timers only boolean.
func (b *BufferResponseWriter) TsigTimersOnly(bool) {}

// Hijack lets the caller take over the connection.
// After a call to Hijack(), the DNS package will not do anything with the connection. {
func (b *BufferResponseWriter) Hijack() {}

// Query is a gRPC endpoint that will serve dns requests. It will be consumed primarily by the
// consul dataplane to proxy dns requests to consul.
func (s *Server) Query(ctx context.Context, req *pbdns.QueryRequest) (*pbdns.QueryResponse, error) {
	pr, ok := peer.FromContext(ctx)
	if !ok {
		return nil, fmt.Errorf("error retrieving peer information from context")
	}

	var local net.Addr
	var remote net.Addr
	// We do this so that we switch to udp/tcp when handling the request since it will be proxied
	// through consul through gRPC and we need to 'fake' the protocol so that the message is trimmed
	// according to wether it is UDP or TCP.
	switch req.GetProtocol() {
	case pbdns.Protocol_PROTOCOL_TCP:
		remote = pr.Addr
		local = &net.TCPAddr{IP: s.LocalAddr.IP, Port: s.LocalAddr.Port}
	case pbdns.Protocol_PROTOCOL_UDP:
		remoteAddr := pr.Addr.(*net.TCPAddr)
		remote = &net.UDPAddr{IP: remoteAddr.IP, Port: remoteAddr.Port}
		local = &net.UDPAddr{IP: s.LocalAddr.IP, Port: s.LocalAddr.Port}
	default:
		return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("error protocol type not set: %v", req.GetProtocol()))
	}

	respWriter := &BufferResponseWriter{
		LocalAddress:  local,
		RemoteAddress: remote,
		Logger:        s.Logger,
	}

	msg := &dns.Msg{}
	err := msg.Unpack(req.Msg)
	if err != nil {
		s.Logger.Error("error unpacking message", "err", err)
		return nil, status.Error(codes.Internal, fmt.Sprintf("failure decoding dns request: %s", err.Error()))
	}
	s.DNSServeMux.ServeDNS(respWriter, msg)

	queryResponse := &pbdns.QueryResponse{Msg: respWriter.responseBuffer}

	return queryResponse, nil
}
<|endoftext|>


<commit_before>package redis

import (
	"errors"
	"fmt"
	"strconv"
	"time"

	"github.com/garyburd/redigo/redis"
)

type RedisSession struct {
	pool   *redis.Pool
	prefix string
}

func NewRedisSession(server string) (*RedisSession, error) {
	s := &RedisSession{}

	pool := &redis.Pool{
		MaxIdle:     3,
		MaxActive:   1000,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server)
			if err != nil {
				return nil, err
			}
			return c, err
		},
	}
	s.pool = pool
	// when we use connection pooling
	// dialing and returning an error will be
	// with the request
	return s, nil
}

// Close closes the connection pool for redis
func (r *RedisSession) Close() error {
	return r.pool.Close()
}

// SetPrefix is used to add a prefix to all keys to be used. It is useful for
// creating namespaces for each different application
func (r *RedisSession) SetPrefix(name string) {
	r.prefix = name + ":"
}

func (r *RedisSession) addPrefix(name string) string {
	return r.prefix + name
}

// Do is a wrapper around redigo's redis.Do method that executes any redis
// command. Do does not support prefix support. Example usage: redis.Do("INCR",
// "counter").
func (r *RedisSession) Do(cmd string, args ...interface{}) (interface{}, error) {
	conn := r.pool.Get()
	// conn.Close() returns an error but we are allready returning regarding error
	// while returning the Do(..) response
	defer conn.Close()
	return conn.Do(cmd, args...)
}

// Send is a wrapper around redigo's redis.Send method that writes the
// command to the client's output buffer.
func (r *RedisSession) Send(cmd string, args ...interface{}) error {
	conn := r.pool.Get()
	// conn.Close() returns an error but we are allready returning regarding error
	// while returning the Do(..) response
	defer conn.Close()
	return conn.Send(cmd, args...)
}

// Set is used to hold the string value. If key already holds a value, it is
// overwritten, regardless of its type. A return of nil means successfull.
// Example usage: redis.Set("arslan:name", "fatih")
func (r *RedisSession) Set(key, value string) error {
	reply, err := r.Do("SET", r.addPrefix(key), value)
	if err != nil {
		return err
	}

	if reply != "OK" {
		return fmt.Errorf("reply string is wrong!: %s", reply)

	}
	return nil
}

// Get is used to get the value of key. If the key does not exist an empty
// string is returned. Usage: redis.Get("arslan")
func (r *RedisSession) Get(key string) (string, error) {
	reply, err := redis.String(r.Do("GET", r.addPrefix(key)))
	if err != nil {
		return "", err
	}
	return reply, nil
}

// GetInt is used the value of key as an integer. If the key does not exist or
// the stored value is a non-integer, zero is returned. Example usage:
// redis.GetInt("counter")
func (r *RedisSession) GetInt(key string) (int, error) {
	reply, err := redis.Int(r.Do("GET", r.addPrefix(key)))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// Del is used to remove the specified keys. Key is ignored if it does not
// exist. It returns the number of keys that were removed. Example usage:
// redis.Del("counter", "arslan:name")
func (r *RedisSession) Del(args ...interface{}) (int, error) {
	prefixed := make([]interface{}, 0)
	for _, arg := range args {
		prefixed = append(prefixed, r.addPrefix(arg.(string)))
	}

	reply, err := redis.Int(r.Do("DEL", prefixed...))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// Incr increments the number stored at key by one. If the key does not exist,
// it is set to 0 before performing the operation. An error is returned if the
// key contains a value of the wrong type or contains a string that can not be
// represented as integer
func (r *RedisSession) Incr(key string) (int, error) {
	reply, err := redis.Int(r.Do("INCR", r.addPrefix(key)))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// Expire sets a timeout on a key. After the timeout has expired, the key will
// automatically be deleted. Calling Expire on a key that has already expire
// set will update the expire value.
func (r *RedisSession) Expire(key string, timeout time.Duration) error {
	seconds := strconv.Itoa(int(timeout.Seconds()))
	reply, err := redis.Int(r.Do("EXPIRE", r.addPrefix(key), seconds))
	if err != nil {
		return err
	}

	if reply != 1 {
		return errors.New("key does not exist or the timeout could not be set")
	}

	return nil
}

// Exists returns true if key exists or false if not.
func (r *RedisSession) Exists(key string) bool {
	// does not have any err message to be checked, it return either 1 or 0
	reply, _ := redis.Int(r.Do("EXISTS", r.addPrefix(key)))

	if reply == 1 {
		return true
	}

	return false // means reply is 0, key does not exist
}

// Ping pings the redis server to check if it is alive or not
// If the server is not alive will return a proper error
func (r *RedisSession) Ping() error {
	reply, err := redis.String(r.Do("PING"))
	if err != nil {
		return err
	}

	if reply != "PONG" {
		return fmt.Errorf("reply string is wrong!: %s", reply)
	}

	return nil
}

// Scard gets the member count of a Set with given key
func (r *RedisSession) Scard(key string) (int, error) {
	reply, err := redis.Int(r.Do("SCARD", key))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// SortedSetIncrBy increments the value of a member
// in a sorted set
//
// This function tries to return last floating value of the item,
// if it fails to parse reply to float64, returns parsing error along with
// Reply it self
func (r *RedisSession) SortedSetIncrBy(key string, incrBy, item interface{}) (float64, error) {
	prefixed := make([]interface{}, 0)
	// add key
	prefixed = append(prefixed, r.addPrefix(key))

	// add incrBy
	prefixed = append(prefixed, incrBy)

	// add item
	prefixed = append(prefixed, item)

	reply, err := r.Do("ZINCRBY", prefixed...)
	if err != nil {
		return float64(0.0), err
	}

	// An error is returned when key exists but does not hold a sorted set.
	// Reply is Bulk string: the new score of member
	// (a double precision floating point number), represented as string.
	val, err := strconv.ParseFloat(string(reply.([]uint8)), 64)
	if err != nil {
		return float64(0.0), fmt.Errorf(
			"Error while parsing %v, reply from Redis is: %s",
			err.Error(),
			reply,
		)
	}
	return val, nil
}
<commit_msg>Redis: add a new function for ZREVRANGE method<commit_after>package redis

import (
	"errors"
	"fmt"
	"strconv"
	"time"

	"github.com/garyburd/redigo/redis"
)

type RedisSession struct {
	pool   *redis.Pool
	prefix string
}

func NewRedisSession(server string) (*RedisSession, error) {
	s := &RedisSession{}

	pool := &redis.Pool{
		MaxIdle:     3,
		MaxActive:   1000,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", server)
			if err != nil {
				return nil, err
			}
			return c, err
		},
	}
	s.pool = pool
	// when we use connection pooling
	// dialing and returning an error will be
	// with the request
	return s, nil
}

// Close closes the connection pool for redis
func (r *RedisSession) Close() error {
	return r.pool.Close()
}

// SetPrefix is used to add a prefix to all keys to be used. It is useful for
// creating namespaces for each different application
func (r *RedisSession) SetPrefix(name string) {
	r.prefix = name + ":"
}

func (r *RedisSession) addPrefix(name string) string {
	return r.prefix + name
}

// Do is a wrapper around redigo's redis.Do method that executes any redis
// command. Do does not support prefix support. Example usage: redis.Do("INCR",
// "counter").
func (r *RedisSession) Do(cmd string, args ...interface{}) (interface{}, error) {
	conn := r.pool.Get()
	// conn.Close() returns an error but we are allready returning regarding error
	// while returning the Do(..) response
	defer conn.Close()
	return conn.Do(cmd, args...)
}

// Send is a wrapper around redigo's redis.Send method that writes the
// command to the client's output buffer.
func (r *RedisSession) Send(cmd string, args ...interface{}) error {
	conn := r.pool.Get()
	// conn.Close() returns an error but we are allready returning regarding error
	// while returning the Do(..) response
	defer conn.Close()
	return conn.Send(cmd, args...)
}

// Set is used to hold the string value. If key already holds a value, it is
// overwritten, regardless of its type. A return of nil means successfull.
// Example usage: redis.Set("arslan:name", "fatih")
func (r *RedisSession) Set(key, value string) error {
	reply, err := r.Do("SET", r.addPrefix(key), value)
	if err != nil {
		return err
	}

	if reply != "OK" {
		return fmt.Errorf("reply string is wrong!: %s", reply)

	}
	return nil
}

// Get is used to get the value of key. If the key does not exist an empty
// string is returned. Usage: redis.Get("arslan")
func (r *RedisSession) Get(key string) (string, error) {
	reply, err := redis.String(r.Do("GET", r.addPrefix(key)))
	if err != nil {
		return "", err
	}
	return reply, nil
}

// GetInt is used the value of key as an integer. If the key does not exist or
// the stored value is a non-integer, zero is returned. Example usage:
// redis.GetInt("counter")
func (r *RedisSession) GetInt(key string) (int, error) {
	reply, err := redis.Int(r.Do("GET", r.addPrefix(key)))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// Del is used to remove the specified keys. Key is ignored if it does not
// exist. It returns the number of keys that were removed. Example usage:
// redis.Del("counter", "arslan:name")
func (r *RedisSession) Del(args ...interface{}) (int, error) {
	prefixed := make([]interface{}, 0)
	for _, arg := range args {
		prefixed = append(prefixed, r.addPrefix(arg.(string)))
	}

	reply, err := redis.Int(r.Do("DEL", prefixed...))
	if err != nil {
		return 0, err
	}
	return reply, nil
}

// Incr increments the number stored at key by one. If the key does not exist,
// it is set to 0 before performing the operation. An error is returned if the
// key contains a value of the wrong type or contains a string that can not be
// represented as integer
func (r *RedisSession) Incr(key string) (int, error) {
	reply, err := redis.Int(r.Do("INCR", r.addPrefix(key)))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// Expire sets a timeout on a key. After the timeout has expired, the key will
// automatically be deleted. Calling Expire on a key that has already expire
// set will update the expire value.
func (r *RedisSession) Expire(key string, timeout time.Duration) error {
	seconds := strconv.Itoa(int(timeout.Seconds()))
	reply, err := redis.Int(r.Do("EXPIRE", r.addPrefix(key), seconds))
	if err != nil {
		return err
	}

	if reply != 1 {
		return errors.New("key does not exist or the timeout could not be set")
	}

	return nil
}

// Exists returns true if key exists or false if not.
func (r *RedisSession) Exists(key string) bool {
	// does not have any err message to be checked, it return either 1 or 0
	reply, _ := redis.Int(r.Do("EXISTS", r.addPrefix(key)))

	if reply == 1 {
		return true
	}

	return false // means reply is 0, key does not exist
}

// Ping pings the redis server to check if it is alive or not
// If the server is not alive will return a proper error
func (r *RedisSession) Ping() error {
	reply, err := redis.String(r.Do("PING"))
	if err != nil {
		return err
	}

	if reply != "PONG" {
		return fmt.Errorf("reply string is wrong!: %s", reply)
	}

	return nil
}

// Scard gets the member count of a Set with given key
func (r *RedisSession) Scard(key string) (int, error) {
	reply, err := redis.Int(r.Do("SCARD", key))
	if err != nil {
		return 0, err
	}

	return reply, nil
}

// SortedSetIncrBy increments the value of a member
// in a sorted set
//
// This function tries to return last floating value of the item,
// if it fails to parse reply to float64, returns parsing error along with
// Reply it self
func (r *RedisSession) SortedSetIncrBy(key string, incrBy, item interface{}) (float64, error) {
	prefixed := make([]interface{}, 0)
	// add key
	prefixed = append(prefixed, r.addPrefix(key))

	// add incrBy
	prefixed = append(prefixed, incrBy)

	// add item
	prefixed = append(prefixed, item)

	return redis.Float64(r.Do("ZINCRBY", prefixed...))
}

// ZREVRANGE key start stop [WITHSCORES]
// Returns the specified range of elements in the sorted set stored at key.
// The elements are considered to be ordered from the highest
// to the lowest score. Descending lexicographical order is used
// for elements with equal score.
//
// Apart from the reversed ordering, ZREVRANGE is similar to ZRANGE.
func (r *RedisSession) SortedSetReverseRange(key string, rest ...interface{}) ([]interface{}, error) {
	// create a slice with rest length +1
	// because we are gonna prepend key to it
	prefixedReq := make([]interface{}, len(rest)+1)

	// prepend prefixed key
	prefixedReq[0] = r.addPrefix(key)

	for key, el := range rest {
		prefixedReq[key+1] = el
	}

	return redis.Values(r.Do("ZREVRANGE", prefixedReq...))
}
<|endoftext|>


<commit_before>package vagrantkite

import (
	"koding/kites/kloud/klient"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/koding/kite"
	"github.com/koding/kite/dnode"
)

// this signals the end of a watch command when we listen from Klient
const magicEnd = "guCnvNVedAQT8DiNpcP3pVqzseJvLY"

type vagrantCreateReq struct {
	FilePath string
	Hostname string
	Box      string
	Memory   int
	Cpus     int
}

type vagrantUpReq struct {
	FilePath string
	Watch    dnode.Function
}

func resourceVagrantKiteBuild() *schema.Resource {
	return &schema.Resource{
		Create: resourceMachineCreate,
		Read:   resourceMachineNoop,
		Update: resourceMachineNoop,
		Delete: resourceMachineNoop,

		Schema: map[string]*schema.Schema{
			// Full path of the file for Vagrantfile
			"queryString": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				Description: "Kite Query string for finding which klient to send the commands",
			},
			"filePath": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				Description: "Full path of the file for Vagrantfile",
			},
			"box": &schema.Schema{
				Type:        schema.TypeString,
				Optional:    true,
				Description: "Box type of underlying Vagrant machine. By default ubuntu/trusty64",
			},
			"hostname": &schema.Schema{
				Type:        schema.TypeString,
				Optional:    true,
				Description: "Hostname of the Vagrant machine. Defaults to klient's username",
			},
			"memory": &schema.Schema{
				Type:        schema.TypeInt,
				Optional:    true,
				Description: "Memory(MB) of the underlying Vagrant box. Defaults to 1024",
			},
			"cpus": &schema.Schema{
				Type:        schema.TypeInt,
				Optional:    true,
				Description: "Number of CPU's to be used for the underlying Vagrant box. Defaults to 1",
			},
		},
	}
}

// resourceMachineCreate creates a new vagrant machine in remote klient host
func resourceMachineCreate(d *schema.ResourceData, meta interface{}) error {
	// TODO(arslan): cache the client, it reads from kite.key every single time
	// it tries to populate the config
	c, err := NewClient()
	if err != nil {
		return err
	}
	defer c.Close()

	queryString := d.Get("queryString").(string)

	klientRef, err := klient.ConnectTimeout(c.Kite, queryString, time.Second*10)
	if err != nil {
		if err == klient.ErrDialingFailed || err == kite.ErrNoKitesAvailable {
			c.Log.Error(
				"[%s] Klient is not registered to Kontrol. Err: %s",
				queryString,
				err.Error(),
			)

			return nil // if the machine is not open, we cant do anything
		}

		return err
	}

	defer klientRef.Close()

	args := &vagrantCreateReq{
		FilePath: d.Get("filePath").(string),
		Box:      d.Get("box").(string),
		Hostname: d.Get("hostname").(string),
		Memory:   d.Get("memory").(int),
		Cpus:     d.Get("cpus").(int),
	}

	if _, err = klientRef.Client.Tell("vagrant.create", queryString, args); err != nil {
		return err
	}

	done := make(chan bool)

	watch := dnode.Callback(func(r *dnode.Partial) {
		msg := r.One().MustString()
		log.Println("[DEBUG] Vagrant up msg:", msg)
		if msg == magicEnd {
			close(done)
		}
	})

	upArgs := &vagrantUpReq{
		FilePath: d.Get("filePath").(string),
		Watch:    watch,
	}

	if _, err = klientRef.Client.Tell("vagrant.up", queryString, upArgs); err != nil {
		return err
	}

	// TODO(arslan): timeout with a select statement
	<-done

	d.SetId(queryString)

	return nil
}

func resourceMachineNoop(d *schema.ResourceData, meta interface{}) error { return nil }
<commit_msg>vagrantkite: fix sending arguments<commit_after>package vagrantkite

import (
	"koding/kites/kloud/klient"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/koding/kite"
	"github.com/koding/kite/dnode"
)

// this signals the end of a watch command when we listen from Klient
const magicEnd = "guCnvNVedAQT8DiNpcP3pVqzseJvLY"

type vagrantCreateReq struct {
	FilePath string
	Hostname string
	Box      string
	Memory   int
	Cpus     int
}

type vagrantUpReq struct {
	FilePath string
	Watch    dnode.Function
}

func resourceVagrantKiteBuild() *schema.Resource {
	return &schema.Resource{
		Create: resourceMachineCreate,
		Read:   resourceMachineNoop,
		Update: resourceMachineNoop,
		Delete: resourceMachineNoop,

		Schema: map[string]*schema.Schema{
			// Full path of the file for Vagrantfile
			"queryString": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				Description: "Kite Query string for finding which klient to send the commands",
			},
			"filePath": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				Description: "Full path of the file for Vagrantfile",
			},
			"box": &schema.Schema{
				Type:        schema.TypeString,
				Optional:    true,
				Description: "Box type of underlying Vagrant machine. By default ubuntu/trusty64",
			},
			"hostname": &schema.Schema{
				Type:        schema.TypeString,
				Optional:    true,
				Description: "Hostname of the Vagrant machine. Defaults to klient's username",
			},
			"memory": &schema.Schema{
				Type:        schema.TypeInt,
				Optional:    true,
				Description: "Memory(MB) of the underlying Vagrant box. Defaults to 1024",
			},
			"cpus": &schema.Schema{
				Type:        schema.TypeInt,
				Optional:    true,
				Description: "Number of CPU's to be used for the underlying Vagrant box. Defaults to 1",
			},
		},
	}
}

// resourceMachineCreate creates a new vagrant machine in remote klient host
func resourceMachineCreate(d *schema.ResourceData, meta interface{}) error {
	// TODO(arslan): cache the client, it reads from kite.key every single time
	// it tries to populate the config
	c, err := NewClient()
	if err != nil {
		return err
	}
	defer c.Close()

	queryString := d.Get("queryString").(string)

	klientRef, err := klient.ConnectTimeout(c.Kite, queryString, time.Second*10)
	if err != nil {
		if err == klient.ErrDialingFailed || err == kite.ErrNoKitesAvailable {
			c.Log.Error(
				"[%s] Klient is not registered to Kontrol. Err: %s",
				queryString,
				err.Error(),
			)

			return nil // if the machine is not open, we cant do anything
		}

		return err
	}
	defer klientRef.Close()

	args := &vagrantCreateReq{
		FilePath: d.Get("filePath").(string),
		Box:      d.Get("box").(string),
		Hostname: d.Get("hostname").(string),
		Memory:   d.Get("memory").(int),
		Cpus:     d.Get("cpus").(int),
	}

	if _, err = klientRef.Client.Tell("vagrant.create", args); err != nil {
		return err
	}

	done := make(chan bool)

	watch := dnode.Callback(func(r *dnode.Partial) {
		msg := r.One().MustString()
		log.Println("[DEBUG] Vagrant up msg:", msg)
		if msg == magicEnd {
			close(done)
		}
	})

	upArgs := &vagrantUpReq{
		FilePath: d.Get("filePath").(string),
		Watch:    watch,
	}

	if _, err = klientRef.Client.Tell("vagrant.up", upArgs); err != nil {
		return err
	}

	// TODO(arslan): timeout with a select statement
	<-done

	d.SetId(queryString)

	return nil
}

func resourceMachineNoop(d *schema.ResourceData, meta interface{}) error { return nil }
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"os"
	"os/exec"
	"syscall"
	"github.com/docker/docker/pkg/reexec"
	"path/filepath"
	"github.com/getsentry/raven-go"
	"../../models"
	"../../config"
)

func init() {
	reexec.Register("justice_init", justice_init)
	if reexec.Init() {
		os.Exit(0)
	}
}

func pivot_root(new_root string) error {
	put_old := filepath.Join(new_root, "/.pivot_root")

	// bind mount new_root to itself - this is a slight hack needed to satisfy requirement (2)
	//
	// The following restrictions apply to new_root and put_old:
	// 1.  They must be directories.
	// 2.  new_root and put_old must not be on the same filesystem as the current root.
	// 3.  put_old must be underneath new_root, that is, adding a nonzero
	//     number of /.. to the string pointed to by put_old must yield the same directory as new_root.
	// 4.  No other filesystem may be mounted on put_old.
	if err := syscall.Mount(new_root, new_root, "", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
		return err
	}

	// create put_old directory
	if err := os.MkdirAll(put_old, 0700); err != nil {
		return err
	}

	// call pivot_root
	if err := syscall.PivotRoot(new_root, put_old); err != nil {
		return err
	}

	// Note that this also applies to the calling process: pivot_root() may
	// or may not affect its current working directory.  It is therefore
	// recommended to call chdir("/") immediately after pivot_root().
	if err := os.Chdir("/"); err != nil {
		return err
	}

	// umount put_old, which now lives at /.pivot_root
	put_old = "/.pivot_root"
	if err := syscall.Unmount(put_old, syscall.MNT_DETACH); err != nil {
		return err
	}

	// remove put_old
	if err := os.RemoveAll(put_old); err != nil {
		return err
	}

	return nil
}

func mount_proc(new_root string) error {
	target := filepath.Join(new_root, "/proc")
	os.MkdirAll(target, 0755)
	return syscall.Mount("proc", target, "proc", uintptr(0), "")
}

func justice_init() {
	new_root_path := "/root/busybox"

	raven.SetDSN(config.SENTRY_DSN)
	task_result := &models.TaskResult{
		Status: models.STATUS_RE,
		Error:  "Runtime Error",
	}
	result, _ := json.Marshal(task_result)

	if err := mount_proc(new_root_path); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}

	if err := pivot_root(new_root_path); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}

	if err := syscall.Sethostname([]byte("justice")); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}

	justice_run()
}

func justice_run() {
	raven.SetDSN(config.SENTRY_DSN)
	task_result := &models.TaskResult{
		Status: models.STATUS_RE,
		Error:  "Runtime Error",
	}
	result, _ := json.Marshal(task_result)

	cmd := exec.Command("/bin/sh")
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Env = []string{"PS1=[justice] # "}

	if err := cmd.Run(); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "ContainerRunTimeError"})
		os.Stdout.Write(result)
		os.Exit(1)
	}
}

func main() {
	raven.SetDSN(config.SENTRY_DSN)
	task_result := &models.TaskResult{
		Status: models.STATUS_RE,
		Error:  "Runtime Error",
	}
	result, _ := json.Marshal(task_result)

	cmd := reexec.Command("justice_init")
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWNS |
			syscall.CLONE_NEWUTS |
			syscall.CLONE_NEWIPC |
			syscall.CLONE_NEWPID |
			syscall.CLONE_NEWNET |
			syscall.CLONE_NEWUSER,
		UidMappings: []syscall.SysProcIDMap{
			{
				ContainerID: 0,
				HostID:      os.Getuid(),
				Size:        1,
			},
		},
		GidMappings: []syscall.SysProcIDMap{
			{
				ContainerID: 0,
				HostID:      os.Getgid(),
				Size:        1,
			},
		},
	}

	if err := cmd.Start(); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}

	if err := cmd.Wait(); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}

	os.Stdout.Write(result)
}
<commit_msg>1. add constructors to TaskResult 2. add judge logic to justice_run()<commit_after>package main

import (
	"encoding/json"
	"os"
	"os/exec"
	"syscall"
	"github.com/docker/docker/pkg/reexec"
	"path/filepath"
	"github.com/getsentry/raven-go"
	"../../models"
	"../../config"
	"flag"
	"strings"
	"bytes"
)

func init() {
	reexec.Register("justice_init", justice_init)
	if reexec.Init() {
		os.Exit(0)
	}
}

func pivot_root(new_root string) error {
	put_old := filepath.Join(new_root, "/.pivot_root")

	// bind mount new_root to itself - this is a slight hack needed to satisfy requirement (2)
	//
	// The following restrictions apply to new_root and put_old:
	// 1.  They must be directories.
	// 2.  new_root and put_old must not be on the same filesystem as the current root.
	// 3.  put_old must be underneath new_root, that is, adding a nonzero
	//     number of /.. to the string pointed to by put_old must yield the same directory as new_root.
	// 4.  No other filesystem may be mounted on put_old.
	if err := syscall.Mount(new_root, new_root, "", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
		return err
	}

	// create put_old directory
	if err := os.MkdirAll(put_old, 0700); err != nil {
		return err
	}

	// call pivot_root
	if err := syscall.PivotRoot(new_root, put_old); err != nil {
		return err
	}

	// Note that this also applies to the calling process: pivot_root() may
	// or may not affect its current working directory.  It is therefore
	// recommended to call chdir("/") immediately after pivot_root().
	if err := os.Chdir("/"); err != nil {
		return err
	}

	// umount put_old, which now lives at /.pivot_root
	put_old = "/.pivot_root"
	if err := syscall.Unmount(put_old, syscall.MNT_DETACH); err != nil {
		return err
	}

	// remove put_old
	if err := os.RemoveAll(put_old); err != nil {
		return err
	}

	return nil
}

func mount_proc(new_root string) error {
	target := filepath.Join(new_root, "/proc")
	os.MkdirAll(target, 0755)
	return syscall.Mount("proc", target, "proc", uintptr(0), "")
}

func justice_init() {
	new_root_path := os.Args[1]
	input := os.Args[2]
	expected := os.Args[3]

	raven.SetDSN(config.SENTRY_DSN)
	task_result := &models.TaskResult{
		Status: models.STATUS_RE,
		Error:  "Runtime Error",
	}
	result, _ := json.Marshal(task_result)

	if err := mount_proc(new_root_path); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}

	if err := pivot_root(new_root_path); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}

	if err := syscall.Sethostname([]byte("justice")); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}

	justice_run(input, expected)
}

func justice_run(input, expected string) {
	raven.SetDSN(config.SENTRY_DSN)

	// for c programs, compiled binary with name [Main] will be located in "/"
	var o bytes.Buffer
	cmd := exec.Command("/Main")
	cmd.Stdin = strings.NewReader(input)
	cmd.Stdout = &o
	cmd.Stderr = os.Stderr
	cmd.Env = []string{"PS1=[justice] # "}

	if err := cmd.Run(); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "ContainerRunTimeError"})
		result, _ := json.Marshal(models.GetRuntimeErrorTaskResult())
		os.Stdout.Write(result)
		os.Exit(2)
	}

	output := string(o)
	if output == expected {
		result, _ := json.Marshal(models.GetAccepptedTaskResult(13,456))
		os.Stdout.Write(result)
	} else {
		result, _ := json.Marshal(models.GetWrongAnswerTaskResult(input, output, expected))
		os.Stdout.Write(result)
	}
	os.Exit(0)
}

func main() {
	basedir := flag.String("basedir", "/tmp", "basedir of tmp C binary")
	input := flag.String("input", "", "test case input")
	expected := flag.String("expected", "", "test case expected")
	flag.Parse()

	raven.SetDSN(config.SENTRY_DSN)
	task_result := &models.TaskResult{
		Status: models.STATUS_RE,
		Error:  "Runtime Error",
	}
	result, _ := json.Marshal(task_result)

	cmd := reexec.Command("justice_init", *basedir, *input, *expected)
	cmd.Stdin = os.Stdin
	//cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWNS |
			syscall.CLONE_NEWUTS |
			syscall.CLONE_NEWIPC |
			syscall.CLONE_NEWPID |
			syscall.CLONE_NEWNET |
			syscall.CLONE_NEWUSER,
		UidMappings: []syscall.SysProcIDMap{
			{
				ContainerID: 0,
				HostID:      os.Getuid(),
				Size:        1,
			},
		},
		GidMappings: []syscall.SysProcIDMap{
			{
				ContainerID: 0,
				HostID:      os.Getgid(),
				Size:        1,
			},
		},
	}

	if err := cmd.Start(); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}

	if err := cmd.Wait(); err != nil {
		raven.CaptureErrorAndWait(err, map[string]string{"error": "InitContainerFailed"})
		os.Stdout.Write(result)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

type treeNode struct {
	children []*treeNode
	parent   *treeNode
	expanded bool
	index    int
	data     interface{}
}

func (n *treeNode) isLast() bool {
	if n.parent == nil {
		return true
	} else {
		return n.index == len(n.parent.children)-1
	}
}

func (n *treeNode) nextSibling() *treeNode {
	if n.isLast() {
		return nil
	} else {
		return n.parent.children[n.index+1]
	}
}

func (n *treeNode) prevSibling() *treeNode {
	if n.index == 0 {
		return nil
	} else {
		return n.parent.children[n.index-1]
	}
}

func (n *treeNode) deepestNode() *treeNode {
	if n.children != nil {
		lastChild := n.children[len(n.children)-1]
		return lastChild.deepestNode()
	}
	return n
}

func (node *treeNode) nextNode() *treeNode {

	// this node has children? then next node is first child.
	// TODO check to see if node is expanded.
	if node.children != nil && len(node.children) > 0 {
		return node.children[0]
	}

	// if we are last sibling - taverse up tree till we find a
	// parent with a sibling.
	if node.isLast() {
		for node.parent != nil {
			node = node.parent
			if ps := node.nextSibling(); ps != nil {
				return ps
			}
		}
		// no parents with siblings
		return nil
	}

	return node.nextSibling()
}

func (node *treeNode) prevNode() *treeNode {

	if node.index == 0 {
		if node.parent != nil {
			return node.parent
		}

		return nil
	}

	prevSib := node.prevSibling()

	return prevSib.deepestNode()
}

func iabs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

func (node *treeNode) traverseNodes(count int) (*treeNode, int) {
	for n := 0; n < iabs(count); n++ {
		var next *treeNode
		if count < 0 {
			next = node.prevNode()
		} else {
			next = node.nextNode()
		}
		if next == nil {
			return node, n
		}
		node = next
	}
	return node, count
}
<commit_msg>refactoring<commit_after>package main

type treeNode struct {
	children []*treeNode
	parent   *treeNode
	expanded bool
	index    int
	data     interface{}
}

func (n *treeNode) isLast() bool {
	if n.parent == nil {
		return true
	} else {
		return n.index == len(n.parent.children)-1
	}
}

func (n *treeNode) nextSibling() *treeNode {
	if n.isLast() {
		return nil
	} else {
		return n.parent.children[n.index+1]
	}
}

func (n *treeNode) prevSibling() *treeNode {
	if n.index == 0 {
		return nil
	} else {
		return n.parent.children[n.index-1]
	}
}

func (n *treeNode) deepestNode() *treeNode {
	if n.children != nil {
		lastChild := n.children[len(n.children)-1]
		return lastChild.deepestNode()
	}
	return n
}

func (node *treeNode) nextNode() *treeNode {

	// this node has children? then next node is first child.
	// TODO check to see if node is expanded.
	if node.children != nil && len(node.children) > 0 {
		return node.children[0]
	}

	// if we are last sibling - taverse up tree till we find a
	// parent with a sibling.
	if node.isLast() {
		for node.parent != nil {
			node = node.parent
			if ps := node.nextSibling(); ps != nil {
				return ps
			}
		}
		// no parents with siblings
		return nil
	}

	return node.nextSibling()
}

func (node *treeNode) prevNode() *treeNode {
	// if first sibling then return parent
	if node.index == 0 {
		if node.parent != nil {
			return node.parent
		}

		return nil
	}

	// return the deepest node of previous sibling
	return node.prevSibling().deepestNode()
}

func Iabs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

func (node *treeNode) traverseNodes(count int) (*treeNode, int) {
	for n := 0; n < Iabs(count); n++ {
		var next *treeNode
		if count < 0 {
			next = node.prevNode()
		} else {
			next = node.nextNode()
		}
		if next == nil {
			return node, n
		}
		node = next
	}
	return node, count
}
<|endoftext|>


<commit_before>// +build linux

package fscommon

import (
	"io/ioutil"
	"os"
	"syscall"

	securejoin "github.com/cyphar/filepath-securejoin"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

func WriteFile(dir, file, data string) error {
	if dir == "" {
		return errors.Errorf("no directory specified for %s", file)
	}
	path, err := securejoin.SecureJoin(dir, file)
	if err != nil {
		return err
	}
	if err := retryingWriteFile(path, []byte(data), 0700); err != nil {
		return errors.Wrapf(err, "failed to write %q to %q", data, path)
	}
	return nil
}

func ReadFile(dir, file string) (string, error) {
	if dir == "" {
		return "", errors.Errorf("no directory specified for %s", file)
	}
	path, err := securejoin.SecureJoin(dir, file)
	if err != nil {
		return "", err
	}
	data, err := ioutil.ReadFile(path)
	return string(data), err
}

func retryingWriteFile(filename string, data []byte, perm os.FileMode) error {
	for {
		err := ioutil.WriteFile(filename, data, perm)
		if isInterruptedWriteFile(err) {
			logrus.Infof("interrupted while writing %s to %s", string(data), filename)
			continue
		}
		return err
	}
}

func isInterruptedWriteFile(err error) bool {
	if patherr, ok := err.(*os.PathError); ok {
		errno, ok2 := patherr.Err.(syscall.Errno)
		if ok2 && errno == syscall.EINTR {
			return true
		}
	}
	return false
}
<commit_msg>cgroups/fscommon: use errors.Is<commit_after>// +build linux

package fscommon

import (
	"io/ioutil"
	"os"

	securejoin "github.com/cyphar/filepath-securejoin"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/sys/unix"
)

func WriteFile(dir, file, data string) error {
	if dir == "" {
		return errors.Errorf("no directory specified for %s", file)
	}
	path, err := securejoin.SecureJoin(dir, file)
	if err != nil {
		return err
	}
	if err := retryingWriteFile(path, []byte(data), 0700); err != nil {
		return errors.Wrapf(err, "failed to write %q to %q", data, path)
	}
	return nil
}

func ReadFile(dir, file string) (string, error) {
	if dir == "" {
		return "", errors.Errorf("no directory specified for %s", file)
	}
	path, err := securejoin.SecureJoin(dir, file)
	if err != nil {
		return "", err
	}
	data, err := ioutil.ReadFile(path)
	return string(data), err
}

func retryingWriteFile(filename string, data []byte, perm os.FileMode) error {
	for {
		err := ioutil.WriteFile(filename, data, perm)
		if errors.Is(err, unix.EINTR) {
			logrus.Infof("interrupted while writing %s to %s", string(data), filename)
			continue
		}
		return err
	}
}
<|endoftext|>