<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/containers/storage/storage"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/opencontainers/runc/libcontainer/label"
	"github.com/opencontainers/runtime-tools/generate"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

func (s *Server) runContainer(container *oci.Container) error {
	if err := s.runtime.CreateContainer(container); err != nil {
		return err
	}

	if err := s.runtime.UpdateStatus(container); err != nil {
		return err
	}

	if err := s.runtime.StartContainer(container); err != nil {
		return err
	}

	if err := s.runtime.UpdateStatus(container); err != nil {
		return err
	}

	return nil
}

// RunPodSandbox creates and runs a pod-level sandbox.
func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (resp *pb.RunPodSandboxResponse, err error) {
	logrus.Debugf("RunPodSandboxRequest %+v", req)
	var processLabel, mountLabel, netNsPath string
	// process req.Name
	name := req.GetConfig().GetMetadata().Name
	if name == "" {
		return nil, fmt.Errorf("PodSandboxConfig.Name should not be empty")
	}

	namespace := req.GetConfig().GetMetadata().Namespace
	attempt := req.GetConfig().GetMetadata().Attempt

	id, name, err := s.generatePodIDandName(name, namespace, attempt)
	if err != nil {
		return nil, err
	}
	_, containerName, err := s.generateContainerIDandName(name, "infra", attempt)
	if err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			s.releasePodName(name)
		}
	}()

	if err = s.podIDIndex.Add(id); err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			if err2 := s.podIDIndex.Delete(id); err2 != nil {
				logrus.Warnf("couldn't delete pod id %s from idIndex", id)
			}
		}
	}()

	podContainer, err := s.storage.CreatePodSandbox(s.imageContext,
		name, id,
		s.config.PauseImage, "",
		containerName,
		req.GetConfig().GetMetadata().Name,
		req.GetConfig().GetMetadata().Uid,
		namespace,
		attempt,
		nil)
	if err == storage.ErrDuplicateName {
		return nil, fmt.Errorf("pod sandbox with name %q already exists", name)
	}
	if err != nil {
		return nil, fmt.Errorf("error creating pod sandbox with name %q: %v", name, err)
	}
	defer func() {
		if err != nil {
			if err2 := s.storage.RemovePodSandbox(id); err2 != nil {
				logrus.Warnf("couldn't cleanup pod sandbox %q: %v", id, err2)
			}
		}
	}()

	// TODO: factor generating/updating the spec into something other projects can vendor

	// creates a spec Generator with the default spec.
	g := generate.New()

	// setup defaults for the pod sandbox
	g.SetRootReadonly(true)
	if s.config.PauseCommand == "" {
		if podContainer.Config != nil {
			g.SetProcessArgs(podContainer.Config.Config.Cmd)
		} else {
			g.SetProcessArgs([]string{podInfraCommand})
		}
	} else {
		g.SetProcessArgs([]string{s.config.PauseCommand})
	}

	// set hostname
	hostname := req.GetConfig().Hostname
	if hostname != "" {
		g.SetHostname(hostname)
	}

	// set log directory
	logDir := req.GetConfig().LogDirectory
	if logDir == "" {
		logDir = filepath.Join(s.config.LogDir, id)
	}

	// set DNS options
	if req.GetConfig().GetDnsConfig() != nil {
		dnsServers := req.GetConfig().GetDnsConfig().Servers
		dnsSearches := req.GetConfig().GetDnsConfig().Searches
		dnsOptions := req.GetConfig().GetDnsConfig().Options
		resolvPath := fmt.Sprintf("%s/resolv.conf", podContainer.RunDir)
		err = parseDNSOptions(dnsServers, dnsSearches, dnsOptions, resolvPath)
		if err != nil {
			err1 := removeFile(resolvPath)
			if err1 != nil {
				err = err1
				return nil, fmt.Errorf("%v; failed to remove %s: %v", err, resolvPath, err1)
			}
			return nil, err
		}
		g.AddBindMount(resolvPath, "/etc/resolv.conf", []string{"ro"})
	}

	// add metadata
	metadata := req.GetConfig().GetMetadata()
	metadataJSON, err := json.Marshal(metadata)
	if err != nil {
		return nil, err
	}

	// add labels
	labels := req.GetConfig().GetLabels()
	labelsJSON, err := json.Marshal(labels)
	if err != nil {
		return nil, err
	}

	// add annotations
	annotations := req.GetConfig().GetAnnotations()
	annotationsJSON, err := json.Marshal(annotations)
	if err != nil {
		return nil, err
	}

	// Don't use SELinux separation with Host Pid or IPC Namespace,
	if !req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostPid && !req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostIpc {
		processLabel, mountLabel, err = getSELinuxLabels(nil)
		if err != nil {
			return nil, err
		}
		g.SetProcessSelinuxLabel(processLabel)
	}

	// create shm mount for the pod containers.
	var shmPath string
	if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostIpc {
		shmPath = "/dev/shm"
	} else {
		shmPath, err = setupShm(podContainer.RunDir, mountLabel)
		if err != nil {
			return nil, err
		}
		defer func() {
			if err != nil {
				if err2 := syscall.Unmount(shmPath, syscall.MNT_DETACH); err2 != nil {
					logrus.Warnf("failed to unmount shm for pod: %v", err2)
				}
			}
		}()
	}

	err = s.setPodSandboxMountLabel(id, mountLabel)
	if err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			s.releaseContainerName(containerName)
		}
	}()

	if err = s.ctrIDIndex.Add(id); err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			if err2 := s.ctrIDIndex.Delete(id); err2 != nil {
				logrus.Warnf("couldn't delete ctr id %s from idIndex", id)
			}
		}
	}()

	g.AddAnnotation("ocid/metadata", string(metadataJSON))
	g.AddAnnotation("ocid/labels", string(labelsJSON))
	g.AddAnnotation("ocid/annotations", string(annotationsJSON))
	g.AddAnnotation("ocid/log_path", logDir)
	g.AddAnnotation("ocid/name", name)
	g.AddAnnotation("ocid/container_type", containerTypeSandbox)
	g.AddAnnotation("ocid/sandbox_id", id)
	g.AddAnnotation("ocid/container_name", containerName)
	g.AddAnnotation("ocid/container_id", id)
	g.AddAnnotation("ocid/shm_path", shmPath)

	sb := &sandbox{
		id:           id,
		name:         name,
		logDir:       logDir,
		labels:       labels,
		annotations:  annotations,
		containers:   oci.NewMemoryStore(),
		processLabel: processLabel,
		mountLabel:   mountLabel,
		metadata:     metadata,
		shmPath:      shmPath,
	}

	s.addSandbox(sb)

	for k, v := range annotations {
		g.AddAnnotation(k, v)
	}

	// extract linux sysctls from annotations and pass down to oci runtime
	safe, unsafe, err := SysctlsFromPodAnnotations(annotations)
	if err != nil {
		return nil, err
	}
	for _, sysctl := range safe {
		g.AddLinuxSysctl(sysctl.Name, sysctl.Value)
	}
	for _, sysctl := range unsafe {
		g.AddLinuxSysctl(sysctl.Name, sysctl.Value)
	}

	// setup cgroup settings
	cgroupParent := req.GetConfig().GetLinux().CgroupParent
	if cgroupParent != "" {
		if s.config.CgroupManager == "systemd" {
			cgPath := sb.cgroupParent + ":" + "ocid" + ":" + id
			g.SetLinuxCgroupsPath(cgPath)

		} else {
			g.SetLinuxCgroupsPath(sb.cgroupParent + "/" + id)

		}
		sb.cgroupParent = cgroupParent
	}

	hostNetwork := req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostNetwork

	// set up namespaces
	if hostNetwork {
		err = g.RemoveLinuxNamespace("network")
		if err != nil {
			return nil, err
		}

		netNsPath, err = hostNetNsPath()
		if err != nil {
			return nil, err
		}
	} else {
		// Create the sandbox network namespace
		if err = sb.netNsCreate(); err != nil {
			return nil, err
		}

		defer func() {
			if err == nil {
				return
			}

			if netnsErr := sb.netNsRemove(); netnsErr != nil {
				logrus.Warnf("Failed to remove networking namespace: %v", netnsErr)
			}
		}()

		// Pass the created namespace path to the runtime
		err = g.AddOrReplaceLinuxNamespace("network", sb.netNsPath())
		if err != nil {
			return nil, err
		}

		netNsPath = sb.netNsPath()
	}

	if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostPid {
		err = g.RemoveLinuxNamespace("pid")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostIpc {
		err = g.RemoveLinuxNamespace("ipc")
		if err != nil {
			return nil, err
		}
	}

	if !s.seccompEnabled {
		g.Spec().Linux.Seccomp = nil
	}

	saveOptions := generate.ExportOptions{}
	mountPoint, err := s.storage.StartContainer(id)
	if err != nil {
		return nil, fmt.Errorf("failed to mount container %s in pod sandbox %s(%s): %v", containerName, sb.name, id, err)
	}
	g.SetRootPath(mountPoint)
	err = g.SaveToFile(filepath.Join(podContainer.Dir, "config.json"), saveOptions)
	if err != nil {
		return nil, fmt.Errorf("failed to save template configuration for pod sandbox %s(%s): %v", sb.name, id, err)
	}
	if err = g.SaveToFile(filepath.Join(podContainer.RunDir, "config.json"), saveOptions); err != nil {
		return nil, fmt.Errorf("failed to write runtime configuration for pod sandbox %s(%s): %v", sb.name, id, err)
	}

	container, err := oci.NewContainer(id, containerName, podContainer.RunDir, logDir, sb.netNs(), labels, annotations, nil, nil, id, false)
	if err != nil {
		return nil, err
	}

	sb.infraContainer = container

	// setup the network
	if !hostNetwork {
		podNamespace := ""
		if err = s.netPlugin.SetUpPod(netNsPath, podNamespace, id, containerName); err != nil {
			return nil, fmt.Errorf("failed to create network for container %s in sandbox %s: %v", containerName, id, err)
		}
	}

	if err = s.runContainer(container); err != nil {
		return nil, err
	}

	resp = &pb.RunPodSandboxResponse{PodSandboxId: id}
	logrus.Debugf("RunPodSandboxResponse: %+v", resp)
	return resp, nil
}

func (s *Server) setPodSandboxMountLabel(id, mountLabel string) error {
	storageMetadata, err := s.storage.GetContainerMetadata(id)
	if err != nil {
		return err
	}
	storageMetadata.SetMountLabel(mountLabel)
	err = s.storage.SetContainerMetadata(id, storageMetadata)
	if err != nil {
		return err
	}
	return nil
}

func getSELinuxLabels(selinuxOptions *pb.SELinuxOption) (processLabel string, mountLabel string, err error) {
	processLabel = ""
	if selinuxOptions != nil {
		user := selinuxOptions.User
		if user == "" {
			return "", "", fmt.Errorf("SELinuxOption.User is empty")
		}

		role := selinuxOptions.Role
		if role == "" {
			return "", "", fmt.Errorf("SELinuxOption.Role is empty")
		}

		t := selinuxOptions.Type
		if t == "" {
			return "", "", fmt.Errorf("SELinuxOption.Type is empty")
		}

		level := selinuxOptions.Level
		if level == "" {
			return "", "", fmt.Errorf("SELinuxOption.Level is empty")
		}
		processLabel = fmt.Sprintf("%s:%s:%s:%s", user, role, t, level)
	}
	return label.InitLabels(label.DupSecOpt(processLabel))
}

func setupShm(podSandboxRunDir, mountLabel string) (shmPath string, err error) {
	shmPath = filepath.Join(podSandboxRunDir, "shm")
	if err = os.Mkdir(shmPath, 0700); err != nil {
		return "", err
	}
	shmOptions := "mode=1777,size=" + strconv.Itoa(defaultShmSize)
	if err = syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV),
		label.FormatMountLabel(shmOptions, mountLabel)); err != nil {
		return "", fmt.Errorf("failed to mount shm tmpfs for pod: %v", err)
	}
	return shmPath, nil
}
<commit_msg>Fix cgroup parent<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/containers/storage/storage"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/opencontainers/runc/libcontainer/label"
	"github.com/opencontainers/runtime-tools/generate"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

func (s *Server) runContainer(container *oci.Container) error {
	if err := s.runtime.CreateContainer(container); err != nil {
		return err
	}

	if err := s.runtime.UpdateStatus(container); err != nil {
		return err
	}

	if err := s.runtime.StartContainer(container); err != nil {
		return err
	}

	if err := s.runtime.UpdateStatus(container); err != nil {
		return err
	}

	return nil
}

// RunPodSandbox creates and runs a pod-level sandbox.
func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (resp *pb.RunPodSandboxResponse, err error) {
	logrus.Debugf("RunPodSandboxRequest %+v", req)
	var processLabel, mountLabel, netNsPath string
	// process req.Name
	name := req.GetConfig().GetMetadata().Name
	if name == "" {
		return nil, fmt.Errorf("PodSandboxConfig.Name should not be empty")
	}

	namespace := req.GetConfig().GetMetadata().Namespace
	attempt := req.GetConfig().GetMetadata().Attempt

	id, name, err := s.generatePodIDandName(name, namespace, attempt)
	if err != nil {
		return nil, err
	}
	_, containerName, err := s.generateContainerIDandName(name, "infra", attempt)
	if err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			s.releasePodName(name)
		}
	}()

	if err = s.podIDIndex.Add(id); err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			if err2 := s.podIDIndex.Delete(id); err2 != nil {
				logrus.Warnf("couldn't delete pod id %s from idIndex", id)
			}
		}
	}()

	podContainer, err := s.storage.CreatePodSandbox(s.imageContext,
		name, id,
		s.config.PauseImage, "",
		containerName,
		req.GetConfig().GetMetadata().Name,
		req.GetConfig().GetMetadata().Uid,
		namespace,
		attempt,
		nil)
	if err == storage.ErrDuplicateName {
		return nil, fmt.Errorf("pod sandbox with name %q already exists", name)
	}
	if err != nil {
		return nil, fmt.Errorf("error creating pod sandbox with name %q: %v", name, err)
	}
	defer func() {
		if err != nil {
			if err2 := s.storage.RemovePodSandbox(id); err2 != nil {
				logrus.Warnf("couldn't cleanup pod sandbox %q: %v", id, err2)
			}
		}
	}()

	// TODO: factor generating/updating the spec into something other projects can vendor

	// creates a spec Generator with the default spec.
	g := generate.New()

	// setup defaults for the pod sandbox
	g.SetRootReadonly(true)
	if s.config.PauseCommand == "" {
		if podContainer.Config != nil {
			g.SetProcessArgs(podContainer.Config.Config.Cmd)
		} else {
			g.SetProcessArgs([]string{podInfraCommand})
		}
	} else {
		g.SetProcessArgs([]string{s.config.PauseCommand})
	}

	// set hostname
	hostname := req.GetConfig().Hostname
	if hostname != "" {
		g.SetHostname(hostname)
	}

	// set log directory
	logDir := req.GetConfig().LogDirectory
	if logDir == "" {
		logDir = filepath.Join(s.config.LogDir, id)
	}

	// set DNS options
	if req.GetConfig().GetDnsConfig() != nil {
		dnsServers := req.GetConfig().GetDnsConfig().Servers
		dnsSearches := req.GetConfig().GetDnsConfig().Searches
		dnsOptions := req.GetConfig().GetDnsConfig().Options
		resolvPath := fmt.Sprintf("%s/resolv.conf", podContainer.RunDir)
		err = parseDNSOptions(dnsServers, dnsSearches, dnsOptions, resolvPath)
		if err != nil {
			err1 := removeFile(resolvPath)
			if err1 != nil {
				err = err1
				return nil, fmt.Errorf("%v; failed to remove %s: %v", err, resolvPath, err1)
			}
			return nil, err
		}
		g.AddBindMount(resolvPath, "/etc/resolv.conf", []string{"ro"})
	}

	// add metadata
	metadata := req.GetConfig().GetMetadata()
	metadataJSON, err := json.Marshal(metadata)
	if err != nil {
		return nil, err
	}

	// add labels
	labels := req.GetConfig().GetLabels()
	labelsJSON, err := json.Marshal(labels)
	if err != nil {
		return nil, err
	}

	// add annotations
	annotations := req.GetConfig().GetAnnotations()
	annotationsJSON, err := json.Marshal(annotations)
	if err != nil {
		return nil, err
	}

	// Don't use SELinux separation with Host Pid or IPC Namespace,
	if !req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostPid && !req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostIpc {
		processLabel, mountLabel, err = getSELinuxLabels(nil)
		if err != nil {
			return nil, err
		}
		g.SetProcessSelinuxLabel(processLabel)
	}

	// create shm mount for the pod containers.
	var shmPath string
	if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostIpc {
		shmPath = "/dev/shm"
	} else {
		shmPath, err = setupShm(podContainer.RunDir, mountLabel)
		if err != nil {
			return nil, err
		}
		defer func() {
			if err != nil {
				if err2 := syscall.Unmount(shmPath, syscall.MNT_DETACH); err2 != nil {
					logrus.Warnf("failed to unmount shm for pod: %v", err2)
				}
			}
		}()
	}

	err = s.setPodSandboxMountLabel(id, mountLabel)
	if err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			s.releaseContainerName(containerName)
		}
	}()

	if err = s.ctrIDIndex.Add(id); err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			if err2 := s.ctrIDIndex.Delete(id); err2 != nil {
				logrus.Warnf("couldn't delete ctr id %s from idIndex", id)
			}
		}
	}()

	g.AddAnnotation("ocid/metadata", string(metadataJSON))
	g.AddAnnotation("ocid/labels", string(labelsJSON))
	g.AddAnnotation("ocid/annotations", string(annotationsJSON))
	g.AddAnnotation("ocid/log_path", logDir)
	g.AddAnnotation("ocid/name", name)
	g.AddAnnotation("ocid/container_type", containerTypeSandbox)
	g.AddAnnotation("ocid/sandbox_id", id)
	g.AddAnnotation("ocid/container_name", containerName)
	g.AddAnnotation("ocid/container_id", id)
	g.AddAnnotation("ocid/shm_path", shmPath)

	sb := &sandbox{
		id:           id,
		name:         name,
		logDir:       logDir,
		labels:       labels,
		annotations:  annotations,
		containers:   oci.NewMemoryStore(),
		processLabel: processLabel,
		mountLabel:   mountLabel,
		metadata:     metadata,
		shmPath:      shmPath,
	}

	s.addSandbox(sb)

	for k, v := range annotations {
		g.AddAnnotation(k, v)
	}

	// extract linux sysctls from annotations and pass down to oci runtime
	safe, unsafe, err := SysctlsFromPodAnnotations(annotations)
	if err != nil {
		return nil, err
	}
	for _, sysctl := range safe {
		g.AddLinuxSysctl(sysctl.Name, sysctl.Value)
	}
	for _, sysctl := range unsafe {
		g.AddLinuxSysctl(sysctl.Name, sysctl.Value)
	}

	// setup cgroup settings
	cgroupParent := req.GetConfig().GetLinux().CgroupParent
	if cgroupParent != "" {
		if s.config.CgroupManager == "systemd" {
			cgPath := cgroupParent + ":" + "ocid" + ":" + id
			g.SetLinuxCgroupsPath(cgPath)

		} else {
			g.SetLinuxCgroupsPath(cgroupParent + "/" + id)

		}
		sb.cgroupParent = cgroupParent
	}

	hostNetwork := req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostNetwork

	// set up namespaces
	if hostNetwork {
		err = g.RemoveLinuxNamespace("network")
		if err != nil {
			return nil, err
		}

		netNsPath, err = hostNetNsPath()
		if err != nil {
			return nil, err
		}
	} else {
		// Create the sandbox network namespace
		if err = sb.netNsCreate(); err != nil {
			return nil, err
		}

		defer func() {
			if err == nil {
				return
			}

			if netnsErr := sb.netNsRemove(); netnsErr != nil {
				logrus.Warnf("Failed to remove networking namespace: %v", netnsErr)
			}
		}()

		// Pass the created namespace path to the runtime
		err = g.AddOrReplaceLinuxNamespace("network", sb.netNsPath())
		if err != nil {
			return nil, err
		}

		netNsPath = sb.netNsPath()
	}

	if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostPid {
		err = g.RemoveLinuxNamespace("pid")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().HostIpc {
		err = g.RemoveLinuxNamespace("ipc")
		if err != nil {
			return nil, err
		}
	}

	if !s.seccompEnabled {
		g.Spec().Linux.Seccomp = nil
	}

	saveOptions := generate.ExportOptions{}
	mountPoint, err := s.storage.StartContainer(id)
	if err != nil {
		return nil, fmt.Errorf("failed to mount container %s in pod sandbox %s(%s): %v", containerName, sb.name, id, err)
	}
	g.SetRootPath(mountPoint)
	err = g.SaveToFile(filepath.Join(podContainer.Dir, "config.json"), saveOptions)
	if err != nil {
		return nil, fmt.Errorf("failed to save template configuration for pod sandbox %s(%s): %v", sb.name, id, err)
	}
	if err = g.SaveToFile(filepath.Join(podContainer.RunDir, "config.json"), saveOptions); err != nil {
		return nil, fmt.Errorf("failed to write runtime configuration for pod sandbox %s(%s): %v", sb.name, id, err)
	}

	container, err := oci.NewContainer(id, containerName, podContainer.RunDir, logDir, sb.netNs(), labels, annotations, nil, nil, id, false)
	if err != nil {
		return nil, err
	}

	sb.infraContainer = container

	// setup the network
	if !hostNetwork {
		podNamespace := ""
		if err = s.netPlugin.SetUpPod(netNsPath, podNamespace, id, containerName); err != nil {
			return nil, fmt.Errorf("failed to create network for container %s in sandbox %s: %v", containerName, id, err)
		}
	}

	if err = s.runContainer(container); err != nil {
		return nil, err
	}

	resp = &pb.RunPodSandboxResponse{PodSandboxId: id}
	logrus.Debugf("RunPodSandboxResponse: %+v", resp)
	return resp, nil
}

func (s *Server) setPodSandboxMountLabel(id, mountLabel string) error {
	storageMetadata, err := s.storage.GetContainerMetadata(id)
	if err != nil {
		return err
	}
	storageMetadata.SetMountLabel(mountLabel)
	err = s.storage.SetContainerMetadata(id, storageMetadata)
	if err != nil {
		return err
	}
	return nil
}

func getSELinuxLabels(selinuxOptions *pb.SELinuxOption) (processLabel string, mountLabel string, err error) {
	processLabel = ""
	if selinuxOptions != nil {
		user := selinuxOptions.User
		if user == "" {
			return "", "", fmt.Errorf("SELinuxOption.User is empty")
		}

		role := selinuxOptions.Role
		if role == "" {
			return "", "", fmt.Errorf("SELinuxOption.Role is empty")
		}

		t := selinuxOptions.Type
		if t == "" {
			return "", "", fmt.Errorf("SELinuxOption.Type is empty")
		}

		level := selinuxOptions.Level
		if level == "" {
			return "", "", fmt.Errorf("SELinuxOption.Level is empty")
		}
		processLabel = fmt.Sprintf("%s:%s:%s:%s", user, role, t, level)
	}
	return label.InitLabels(label.DupSecOpt(processLabel))
}

func setupShm(podSandboxRunDir, mountLabel string) (shmPath string, err error) {
	shmPath = filepath.Join(podSandboxRunDir, "shm")
	if err = os.Mkdir(shmPath, 0700); err != nil {
		return "", err
	}
	shmOptions := "mode=1777,size=" + strconv.Itoa(defaultShmSize)
	if err = syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV),
		label.FormatMountLabel(shmOptions, mountLabel)); err != nil {
		return "", fmt.Errorf("failed to mount shm tmpfs for pod: %v", err)
	}
	return shmPath, nil
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"

	"github.com/fatih/color"
)

var warn = color.New(color.FgYellow).Add(color.Bold).Println

func procTestResponse(res *http.Response, t *testing.T, expectedCode int) {
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		t.Error(err)
	}
	fmt.Println(string(body) + "\n")
	if res.StatusCode != expectedCode {
		t.Error(res.Request.Method + ": " + res.Request.URL.Path + " returned " + strconv.Itoa(res.StatusCode) + ". Expected " + strconv.Itoa(expectedCode) + ".")
		return
	}
	if res.StatusCode == 204 {
		return
	}
	if res.StatusCode == 404 {
		return
	}
	var marsh interface{}
	err = json.Unmarshal(body, &marsh)
	if err != nil {
		t.Error(errors.New(res.Request.Method + ": failed to unmarshal response from " + res.Request.URL.Path + ". Status code was: " + strconv.Itoa(res.StatusCode)))
	}
	_, err = json.MarshalIndent(marsh, "", "  ")
	if err != nil {
		t.Error("failed to Marshal")
	}
}

func TestEndpoints(t *testing.T) {

	s := NewServer()
	r := s.NewRouter()
	server := httptest.NewServer(r)
	defer server.Close()

	// a few closures to save time below
	get := func(endpoint string, expectedCode int) {
		res, err := http.Get(server.URL + endpoint)
		if err != nil {
			t.Error(err)
			return
		}
		procTestResponse(res, t, expectedCode)
	}

	post := func(endpoint, msg string, expectedCode int) {
		res, err := http.Post(server.URL+endpoint, "application/json", bytes.NewBuffer([]byte(msg)))
		if err != nil {
			t.Error(err)
			return
		}
		procTestResponse(res, t, expectedCode)
	}

	put := func(endpoint, msg string, expectedCode int) {
		req, err := http.NewRequest("PUT", server.URL+endpoint, bytes.NewBuffer([]byte(msg)))
		if err != nil {
			t.Error(err)
			return
		}
		c := &http.Client{}
		res, err := c.Do(req)
		if err != nil {
			t.Error(err)
			return
		}
		procTestResponse(res, t, expectedCode)
	}

	del := func(endpoint string, expectedCode int) {
		req, err := http.NewRequest("DELETE", server.URL+endpoint, nil)
		if err != nil {
			t.Error(err)
			return
		}
		c := &http.Client{}
		res, err := c.Do(req)
		if err != nil {
			t.Error(err)
			return
		}
		procTestResponse(res, t, expectedCode)
	}

	// set up a group (1)
	post("/groups", `{"group":0}`, 200)

	// set up a + block (2)
	post("/blocks", `{"type":"+","group":1}`, 200)

	// label group 1
	put("/groups/1/label", `"The Best Group Ever"`, 204)

	// label the plus block
	put("/blocks/2/label", `"my bestest adder"`, 204)

	// move the plus block
	put("/blocks/2/position", `{"x":10,"y":10}`, 204)

	// get all the groups
	get("/groups", 200)

	// get group 1
	get("/groups/1", 200)

	// move group 1
	put("/groups/1/position", `{"x":20,"y":20}`, 204)

	// get all the blocks
	get("/blocks", 200)

	// get the + block
	get("/blocks/2", 200)

	// make a delay block (3)
	post("/blocks", `{"type":"delay", "group":1}`, 200)

	// make a log block (4)
	post("/blocks", `{"type":"log", "group":1}`, 200)

	// connect the + block to the delay block (5)
	post("/connections", `{"source":{"id":2, "Route":0}, "target":{"id":3, "Route":0}}`, 200)

	// connect the delay block to the log block (6)
	post("/connections", `{"source":{"id":3, "Route":0}, "target":{"id":4, "Route":0}}`, 200)

	// make a set block (7)
	post("/blocks", `{"type":"set", "group":1}`, 200)

	// disconnect the log block from the delay block
	del("/connections/6", 204)

	// connect the set block to the log block and delay block (8) (9)
	post("/connections", `{"source":{"id":7, "Route":0}, "target":{"id":4, "Route":0}}`, 200)
	post("/connections", `{"source":{"id":3, "Route":0}, "target":{"id":7, "Route":1}}`, 200)

	// list connections
	get("/connections", 200)
	// describe connection 8
	get("/connections/8", 200)

	// move log block to root group
	put("/groups/0/children/4", "", 204)
	// move + block to root group (we will generate some errors with this later)
	put("/groups/0/children/2", "", 204)

	// create a keyvalue source (10)
	post("/sources", `{"type":"key-value"}`, 200)

	// get the keyvalue source
	get("/sources/10", 200)

	// make a stream source (11)
	post("/sources", `{"type":"stream"}`, 200)

	// change a parameter in the stream
	put("/sources/11", `{"topic":"test"}`, 204)

	// get all the sources
	get("/sources", 200)

	// make a key value get block (12)
	post("/blocks", `{"type":"kvGet"}`, 200)

	// link the key value get block to the key value source (13)
	post("/links", `{"source":10,"block":12}`, 200)

	// list the links
	get("/links", 200)

	// this doesn't exist yet - TODO use case?
	// get the link
	// get("/links/13", 200)

	// delete the link
	del("/links/13", 204)

	// delete the keyvalue store
	del("/sources/10", 204)

	// export the pattern
	get("/groups/0/export", 200)

	// import a pattern
	pattern := `{"blocks":[{"label":"","type":"+","id":2,"inputs":[{"name":"addend","type":"fetch","value":"."},{"name":"addend","type":"fetch","value":"."}],"outputs":[{"name":"sum"}],"position":{"x":0,"y":0}},{"label":"","type":"delay","id":3,"inputs":[{"name":"passthrough","type":"fetch","value":"."},{"name":"duration","type":"const","value":"1s"}],"outputs":[{"name":"passthrough"}],"position":{"x":0,"y":0}}],"connections":[{"source":{"id":2,"route":0},"target":{"id":3,"route":0},"id":4}],"groups":[{"id":1,"label":"","children":[2,3],"position":{"x":0,"y":0}}]}`
	post("/groups/1/import", pattern, 204)

	// delete the log block
	del("/blocks/4", 204)

	// delete group 1
	del("/groups/1", 204)

	// get the blocks library
	get("/blocks/library", 200)

	// get the blocks library
	get("/sources/library", 200)

	// generate some errors
	del("/groups/1", 400)                                                                       // delete a group we've already deleted
	del("/groups/", 404)                                                                        // delete unspecified group
	del("/blocks/246", 400)                                                                     // delete an unknown block
	post("/groups/1/import", "{}", 400)                                                         // import empty
	post("/groups/1/import", "{bla}", 400)                                                      // import malformed
	get("/groups/6/export", 400)                                                                // export an unknown group
	post("/sources", `{"type":"GodHead"}`, 400)                                                 // create an unknown source
	get("/sources/45", 400)                                                                     // get an unknown source
	post("/links", `{"source":100,"block":12}`, 400)                                            // link to an unknown source
	post("/links", `{"source":10,"block":120}`, 400)                                            // link to an unknown block
	get("/links/450", 404)                                                                      // get an unknown link
	put("/groups/8/children/4", "", 400)                                                        // modify an unknown group
	put("/groups/0/children/34", "", 400)                                                       // move an unknown block to group 0
	post("/groups", `{"group":10}`, 400)                                                        // create a group with an unknown parent
	post("/groups", `{"group"10}`, 400)                                                         // create a group with malformed JSON
	post("/blocks", `{"type":"invalid", "group":0}`, 400)                                       // create a block of invalid type
	post("/blocks", `{"type"lid", "group":1}`, 400)                                             // create a block with malformed json
	post("/blocks", `{"type":"latch", "group":10}`, 400)                                        // create a block witha group that doesn't exist
	post("/connections", `{"source":{"id":700, "Route":0}, "target":{"id":2, "Route":0}}`, 400) //connect unknown source
	post("/connections", `{"source":{"id":2, "Route":0}, "target":{"id":200, "Route":0}}`, 400) //connect unknown target
	post("/connections", `{"source":{"i:0}, "ta200, "Route":0}}`, 400)                          //connect with malformed json
	post("/connections", `{}`, 400)                                                             //connect with empty json
	post("/connections", "", 400)                                                               //connect with empty string
	del("/connections/289", 400)                                                                //delete unknown connection
	del("/connections/", 404)                                                                   //delete unspecified connection
	del("/connections/invalid", 400)                                                            //delete malformed connection
}
<commit_msg>fixing tests<commit_after>package server

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"

	"github.com/fatih/color"
)

var warn = color.New(color.FgYellow).Add(color.Bold).Println

func procTestResponse(res *http.Response, t *testing.T, expectedCode int) {
	body, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		t.Error(err)
	}
	fmt.Println(string(body) + "\n")
	if res.StatusCode != expectedCode {
		t.Error(res.Request.Method + ": " + res.Request.URL.Path + " returned " + strconv.Itoa(res.StatusCode) + ". Expected " + strconv.Itoa(expectedCode) + ".")
		return
	}
	if res.StatusCode == 204 {
		return
	}
	if res.StatusCode == 404 {
		return
	}
	var marsh interface{}
	err = json.Unmarshal(body, &marsh)
	if err != nil {
		t.Error(errors.New(res.Request.Method + ": failed to unmarshal response from " + res.Request.URL.Path + ". Status code was: " + strconv.Itoa(res.StatusCode)))
	}
	_, err = json.MarshalIndent(marsh, "", "  ")
	if err != nil {
		t.Error("failed to Marshal")
	}
}

func TestEndpoints(t *testing.T) {

	s := NewServer()
	r := s.NewRouter()
	server := httptest.NewServer(r)
	defer server.Close()

	// a few closures to save time below
	get := func(endpoint string, expectedCode int) {
		res, err := http.Get(server.URL + endpoint)
		if err != nil {
			t.Error(err)
			return
		}
		procTestResponse(res, t, expectedCode)
	}

	post := func(endpoint, msg string, expectedCode int) {
		res, err := http.Post(server.URL+endpoint, "application/json", bytes.NewBuffer([]byte(msg)))
		if err != nil {
			t.Error(err)
			return
		}
		procTestResponse(res, t, expectedCode)
	}

	put := func(endpoint, msg string, expectedCode int) {
		req, err := http.NewRequest("PUT", server.URL+endpoint, bytes.NewBuffer([]byte(msg)))
		if err != nil {
			t.Error(err)
			return
		}
		c := &http.Client{}
		res, err := c.Do(req)
		if err != nil {
			t.Error(err)
			return
		}
		procTestResponse(res, t, expectedCode)
	}

	del := func(endpoint string, expectedCode int) {
		req, err := http.NewRequest("DELETE", server.URL+endpoint, nil)
		if err != nil {
			t.Error(err)
			return
		}
		c := &http.Client{}
		res, err := c.Do(req)
		if err != nil {
			t.Error(err)
			return
		}
		procTestResponse(res, t, expectedCode)
	}

	// set up a group (1)
	post("/groups", `{"group":0}`, 200)

	// set up a + block (2)
	post("/blocks", `{"type":"+","group":1}`, 200)

	// label group 1
	put("/groups/1/label", `"The Best Group Ever"`, 204)

	// label the plus block
	put("/blocks/2/label", `"my bestest adder"`, 204)

	// move the plus block
	put("/blocks/2/position", `{"x":10,"y":10}`, 204)

	// get all the groups
	get("/groups", 200)

	// get group 1
	get("/groups/1", 200)

	// move group 1
	put("/groups/1/position", `{"x":20,"y":20}`, 204)

	// get all the blocks
	get("/blocks", 200)

	// get the + block
	get("/blocks/2", 200)

	// make a delay block (3)
	post("/blocks", `{"type":"delay", "group":1}`, 200)

	// make a log block (4)
	post("/blocks", `{"type":"log", "group":1}`, 200)

	// connect the + block to the delay block (5)
	post("/connections", `{"source":{"id":2, "Route":0}, "target":{"id":3, "Route":0}}`, 200)

	// connect the delay block to the log block (6)
	post("/connections", `{"source":{"id":3, "Route":0}, "target":{"id":4, "Route":0}}`, 200)

	// make a set block (7)
	post("/blocks", `{"type":"set", "group":1}`, 200)

	// disconnect the log block from the delay block
	del("/connections/6", 204)

	// connect the set block to the log block and delay block (8) (9)
	post("/connections", `{"source":{"id":7, "Route":0}, "target":{"id":4, "Route":0}}`, 200)
	post("/connections", `{"source":{"id":3, "Route":0}, "target":{"id":7, "Route":1}}`, 200)

	// list connections
	get("/connections", 200)
	// describe connection 8
	get("/connections/8", 200)

	// move log block to root group
	put("/groups/0/children/4", "", 204)
	// move + block to root group (we will generate some errors with this later)
	put("/groups/0/children/2", "", 204)

	// create a keyvalue source (10)
	post("/sources", `{"type":"key-value"}`, 200)

	// get the keyvalue source
	get("/sources/10", 200)

	// make a stream source (11)
	post("/sources", `{"type":"stream"}`, 200)

	// change a parameter in the stream
	put("/sources/11/params", `{"topic":"test"}`, 204)

	// get all the sources
	get("/sources", 200)

	// make a key value get block (12)
	post("/blocks", `{"type":"kvGet"}`, 200)

	// link the key value get block to the key value source (13)
	post("/links", `{"source":10,"block":12}`, 200)

	// list the links
	get("/links", 200)

	// this doesn't exist yet - TODO use case?
	// get the link
	// get("/links/13", 200)

	// delete the link
	del("/links/13", 204)

	// delete the keyvalue store
	del("/sources/10", 204)

	// export the pattern
	get("/groups/0/export", 200)

	// import a pattern
	pattern := `{"blocks":[{"label":"","type":"+","id":2,"inputs":[{"name":"addend","type":"fetch","value":"."},{"name":"addend","type":"fetch","value":"."}],"outputs":[{"name":"sum"}],"position":{"x":0,"y":0}},{"label":"","type":"delay","id":3,"inputs":[{"name":"passthrough","type":"fetch","value":"."},{"name":"duration","type":"const","value":"1s"}],"outputs":[{"name":"passthrough"}],"position":{"x":0,"y":0}}],"connections":[{"source":{"id":2,"route":0},"target":{"id":3,"route":0},"id":4}],"groups":[{"id":1,"label":"","children":[2,3],"position":{"x":0,"y":0}}]}`
	post("/groups/1/import", pattern, 204)

	// delete the log block
	del("/blocks/4", 204)

	// delete group 1
	del("/groups/1", 204)

	// get the blocks library
	get("/blocks/library", 200)

	// get the blocks library
	get("/sources/library", 200)

	// generate some errors
	del("/groups/1", 400)                                                                       // delete a group we've already deleted
	del("/groups/", 404)                                                                        // delete unspecified group
	del("/blocks/246", 400)                                                                     // delete an unknown block
	post("/groups/1/import", "{}", 400)                                                         // import empty
	post("/groups/1/import", "{bla}", 400)                                                      // import malformed
	get("/groups/6/export", 400)                                                                // export an unknown group
	post("/sources", `{"type":"GodHead"}`, 400)                                                 // create an unknown source
	get("/sources/45", 400)                                                                     // get an unknown source
	post("/links", `{"source":100,"block":12}`, 400)                                            // link to an unknown source
	post("/links", `{"source":10,"block":120}`, 400)                                            // link to an unknown block
	get("/links/450", 404)                                                                      // get an unknown link
	put("/groups/8/children/4", "", 400)                                                        // modify an unknown group
	put("/groups/0/children/34", "", 400)                                                       // move an unknown block to group 0
	post("/groups", `{"group":10}`, 400)                                                        // create a group with an unknown parent
	post("/groups", `{"group"10}`, 400)                                                         // create a group with malformed JSON
	post("/blocks", `{"type":"invalid", "group":0}`, 400)                                       // create a block of invalid type
	post("/blocks", `{"type"lid", "group":1}`, 400)                                             // create a block with malformed json
	post("/blocks", `{"type":"latch", "group":10}`, 400)                                        // create a block witha group that doesn't exist
	post("/connections", `{"source":{"id":700, "Route":0}, "target":{"id":2, "Route":0}}`, 400) //connect unknown source
	post("/connections", `{"source":{"id":2, "Route":0}, "target":{"id":200, "Route":0}}`, 400) //connect unknown target
	post("/connections", `{"source":{"i:0}, "ta200, "Route":0}}`, 400)                          //connect with malformed json
	post("/connections", `{}`, 400)                                                             //connect with empty json
	post("/connections", "", 400)                                                               //connect with empty string
	del("/connections/289", 400)                                                                //delete unknown connection
	del("/connections/", 404)                                                                   //delete unspecified connection
	del("/connections/invalid", 400)                                                            //delete malformed connection
}
<|endoftext|>


<commit_before>//Package redis provides a cache implementation of onecache using redis as the backend
package redis

import (
	"time"

	"github.com/adelowo/onecache"
	"github.com/go-redis/redis"
)

//Default prefix to prevent collision with other key stored in redis
const defaultPrefix = "onecache:"

type RedisStore struct {
	client *redis.Client
	prefix string
}

func init() {
	onecache.Extend("redis", func() onecache.Store {
		//Default for most usage..
		//Can make use of NewRedisStore() for custom settings
		return NewRedisStore(&redis.Options{
			Addr:     "localhost:6379",
			Password: "",
			DB:       0,
		}, "")
	})
}

//Returns a new instance of the RedisStore
//If prefix is an empty string, the default cache prefix is used
func NewRedisStore(opts *redis.Options, prefix string) *RedisStore {

	var p string

	if prefix == "" {
		p = defaultPrefix
	} else {
		p = prefix
	}

	return &RedisStore{redis.NewClient(opts), p}
}

func (r *RedisStore) Set(k string, data []byte, expires time.Duration) error {
	return r.client.Set(r.key(k), data, expires).Err()
}

func (r *RedisStore) Get(key string) ([]byte, error) {

	val, err := r.client.Get(r.key(key)).Bytes()

	if err != nil {
		return nil, err
	}

	return val, nil
}

func (r *RedisStore) Delete(key string) error {
	return r.client.Del(r.key(key)).Err()
}

func (r *RedisStore) Flush() error {
	return r.client.FlushDb().Err()
}

func (r *RedisStore) Has(key string) bool {

	if _, err := r.Get(key); err != nil {
		return false
	}

	return true
}

func (r *RedisStore) key(k string) string {
	return r.prefix + k
}
<commit_msg>Use defaultPrefix for the redis client when registering the adapter<commit_after>//Package redis provides a cache implementation of onecache using redis as the backend
package redis

import (
	"time"

	"github.com/adelowo/onecache"
	"github.com/go-redis/redis"
)

//Default prefix to prevent collision with other key stored in redis
const defaultPrefix = "onecache:"

type RedisStore struct {
	client *redis.Client
	prefix string
}

func init() {
	onecache.Extend("redis", func() onecache.Store {
		//Default for most usage..
		//Can make use of NewRedisStore() for custom settings
		return NewRedisStore(&redis.Options{
			Addr:     "localhost:6379",
			Password: "",
			DB:       0,
		}, defaultPrefix)
	})
}

//Returns a new instance of the RedisStore
//If prefix is an empty string, the default cache prefix is used
func NewRedisStore(opts *redis.Options, prefix string) *RedisStore {

	var p string

	if prefix == "" {
		p = defaultPrefix
	} else {
		p = prefix
	}

	return &RedisStore{redis.NewClient(opts), p}
}

func (r *RedisStore) Set(k string, data []byte, expires time.Duration) error {
	return r.client.Set(r.key(k), data, expires).Err()
}

func (r *RedisStore) Get(key string) ([]byte, error) {

	val, err := r.client.Get(r.key(key)).Bytes()

	if err != nil {
		return nil, err
	}

	return val, nil
}

func (r *RedisStore) Delete(key string) error {
	return r.client.Del(r.key(key)).Err()
}

func (r *RedisStore) Flush() error {
	return r.client.FlushDb().Err()
}

func (r *RedisStore) Has(key string) bool {

	if _, err := r.Get(key); err != nil {
		return false
	}

	return true
}

func (r *RedisStore) key(k string) string {
	return r.prefix + k
}
<|endoftext|>


<commit_before>package redis

import (
	"github.com/Clever/leakybucket"
	"github.com/garyburd/redigo/redis"
	"time"
)

type bucket struct {
	name                string
	capacity, remaining uint
	reset               time.Time
	rate                time.Duration
	pool                *redis.Pool
}

func (b *bucket) Capacity() uint {
	return b.capacity
}

// Remaining space in the bucket.
func (b *bucket) Remaining() uint {
	return b.remaining
}

// Reset returns when the bucket will be drained.
func (b *bucket) Reset() time.Time {
	return b.reset
}

func (b *bucket) State() leakybucket.BucketState {
	return leakybucket.BucketState{Capacity: b.Capacity(), Remaining: b.Remaining(), Reset: b.Reset()}
}

var millisecond = int64(time.Millisecond)

func (b *bucket) updateOldReset() error {
	if b.reset.Unix() > time.Now().Unix() {
		return nil
	}

	conn := b.pool.Get()
	defer conn.Close()

	ttl, err := conn.Do("PTTL", b.name)
	if err != nil {
		return err
	}
	b.reset = time.Now().Add(time.Duration(ttl.(int64) * millisecond))
	return nil
}

// Add to the bucket.
func (b *bucket) Add(amount uint) (leakybucket.BucketState, error) {
	conn := b.pool.Get()
	defer conn.Close()

	if count, err := redis.Uint64(conn.Do("GET", b.name)); err != nil {
		// handle the key not being set
		if err == redis.ErrNil {
			b.remaining = b.capacity
		} else {
			return b.State(), err
		}
	} else {
		b.remaining = b.capacity - min(uint(count), b.capacity)
	}

	if amount > b.remaining {
		b.updateOldReset()
		return b.State(), leakybucket.ErrorFull
	}

	// Go y u no have Milliseconds method? Why only Seconds and Nanoseconds?
	expiry := int(b.rate.Nanoseconds() / millisecond)

	count, err := redis.Uint64(conn.Do("INCRBY", b.name, amount))
	if err != nil {
		return b.State(), err
	} else if uint(count) == amount {
		if _, err := conn.Do("PEXPIRE", b.name, expiry); err != nil {
			return b.State(), err
		}
	}

	b.updateOldReset()

	// Ensure we can't overflow
	b.remaining = b.capacity - min(uint(count), b.capacity)
	return b.State(), nil
}

// Storage is a redis-based, non thread-safe leaky bucket factory.
type Storage struct {
	pool *redis.Pool
}

// Create a bucket.
func (s *Storage) Create(name string, capacity uint, rate time.Duration) (leakybucket.Bucket, error) {
	conn := s.pool.Get()
	defer conn.Close()

	if count, err := redis.Uint64(conn.Do("GET", name)); err != nil {
		if err != redis.ErrNil {
			return nil, err
		}
		// return a standard bucket if key was not found
		return &bucket{
			name:      name,
			capacity:  capacity,
			remaining: capacity,
			reset:     time.Now().Add(rate),
			rate:      rate,
			pool:      s.pool,
		}, nil
	} else if ttl, err := redis.Int64(conn.Do("PTTL", name)); err != nil {
		return nil, err
	} else {
		b := &bucket{
			name:      name,
			capacity:  capacity,
			remaining: capacity - min(capacity, uint(count)),
			reset:     time.Now().Add(time.Duration(ttl * millisecond)),
			rate:      rate,
			pool:      s.pool,
		}
		return b, nil
	}
}

// New initializes the connection to redis.
func New(network, address string) (*Storage, error) {
	// If we find we need to change this timeout per application, we may want to expose
	// this as an extra config option
	timeout := time.Duration(5000 * millisecond) // 5 seconds
	s := &Storage{
		pool: redis.NewPool(func() (redis.Conn, error) {
			return redis.Dial(network, address, redis.DialReadTimeout(timeout))
		}, 5)}
	// When using a connection pool, you only get connection errors while trying to send commands.
	// Try to PING so we can fail-fast in the case of invalid address.
	conn := s.pool.Get()
	defer conn.Close()
	if _, err := conn.Do("PING"); err != nil {
		return nil, err
	}
	return s, nil
}

func min(a, b uint) uint {
	if a < b {
		return a
	}
	return b
}
<commit_msg>Add timeout for writes<commit_after>package redis

import (
	"github.com/Clever/leakybucket"
	"github.com/garyburd/redigo/redis"
	"time"
)

type bucket struct {
	name                string
	capacity, remaining uint
	reset               time.Time
	rate                time.Duration
	pool                *redis.Pool
}

func (b *bucket) Capacity() uint {
	return b.capacity
}

// Remaining space in the bucket.
func (b *bucket) Remaining() uint {
	return b.remaining
}

// Reset returns when the bucket will be drained.
func (b *bucket) Reset() time.Time {
	return b.reset
}

func (b *bucket) State() leakybucket.BucketState {
	return leakybucket.BucketState{Capacity: b.Capacity(), Remaining: b.Remaining(), Reset: b.Reset()}
}

var millisecond = int64(time.Millisecond)

func (b *bucket) updateOldReset() error {
	if b.reset.Unix() > time.Now().Unix() {
		return nil
	}

	conn := b.pool.Get()
	defer conn.Close()

	ttl, err := conn.Do("PTTL", b.name)
	if err != nil {
		return err
	}
	b.reset = time.Now().Add(time.Duration(ttl.(int64) * millisecond))
	return nil
}

// Add to the bucket.
func (b *bucket) Add(amount uint) (leakybucket.BucketState, error) {
	conn := b.pool.Get()
	defer conn.Close()

	if count, err := redis.Uint64(conn.Do("GET", b.name)); err != nil {
		// handle the key not being set
		if err == redis.ErrNil {
			b.remaining = b.capacity
		} else {
			return b.State(), err
		}
	} else {
		b.remaining = b.capacity - min(uint(count), b.capacity)
	}

	if amount > b.remaining {
		b.updateOldReset()
		return b.State(), leakybucket.ErrorFull
	}

	// Go y u no have Milliseconds method? Why only Seconds and Nanoseconds?
	expiry := int(b.rate.Nanoseconds() / millisecond)

	count, err := redis.Uint64(conn.Do("INCRBY", b.name, amount))
	if err != nil {
		return b.State(), err
	} else if uint(count) == amount {
		if _, err := conn.Do("PEXPIRE", b.name, expiry); err != nil {
			return b.State(), err
		}
	}

	b.updateOldReset()

	// Ensure we can't overflow
	b.remaining = b.capacity - min(uint(count), b.capacity)
	return b.State(), nil
}

// Storage is a redis-based, non thread-safe leaky bucket factory.
type Storage struct {
	pool *redis.Pool
}

// Create a bucket.
func (s *Storage) Create(name string, capacity uint, rate time.Duration) (leakybucket.Bucket, error) {
	conn := s.pool.Get()
	defer conn.Close()

	if count, err := redis.Uint64(conn.Do("GET", name)); err != nil {
		if err != redis.ErrNil {
			return nil, err
		}
		// return a standard bucket if key was not found
		return &bucket{
			name:      name,
			capacity:  capacity,
			remaining: capacity,
			reset:     time.Now().Add(rate),
			rate:      rate,
			pool:      s.pool,
		}, nil
	} else if ttl, err := redis.Int64(conn.Do("PTTL", name)); err != nil {
		return nil, err
	} else {
		b := &bucket{
			name:      name,
			capacity:  capacity,
			remaining: capacity - min(capacity, uint(count)),
			reset:     time.Now().Add(time.Duration(ttl * millisecond)),
			rate:      rate,
			pool:      s.pool,
		}
		return b, nil
	}
}

// New initializes the connection to redis.
func New(network, address string) (*Storage, error) {
	// If we find we need to change this timeout per application, we may want to expose
	// this as an extra config option
	timeout := time.Duration(5000 * millisecond) // 5 seconds
	s := &Storage{
		pool: redis.NewPool(func() (redis.Conn, error) {
			return redis.Dial(network, address, redis.DialReadTimeout(timeout), redis.DialWriteTimeout(timeout))
		}, 5)}
	// When using a connection pool, you only get connection errors while trying to send commands.
	// Try to PING so we can fail-fast in the case of invalid address.
	conn := s.pool.Get()
	defer conn.Close()
	if _, err := conn.Do("PING"); err != nil {
		return nil, err
	}
	return s, nil
}

func min(a, b uint) uint {
	if a < b {
		return a
	}
	return b
}
<|endoftext|>


<commit_before><commit_msg>Added Polarion ID's to 2 test cases (#2188)<commit_after><|endoftext|>


<commit_before>package dnstapio

import (
	"net"
	"sync/atomic"
	"time"

	clog "github.com/coredns/coredns/plugin/pkg/log"

	tap "github.com/dnstap/golang-dnstap"
	fs "github.com/farsightsec/golang-framestream"
)

var log = clog.NewWithPlugin("dnstap")

const (
	tcpWriteBufSize = 1024 * 1024
	tcpTimeout      = 4 * time.Second
	flushTimeout    = 1 * time.Second
	queueSize       = 10000
)

type dnstapIO struct {
	endpoint string
	socket   bool
	conn     net.Conn
	enc      *dnstapEncoder
	queue    chan tap.Dnstap
	dropped  uint32
	quit     chan struct{}
}

// New returns a new and initialized DnstapIO.
func New(endpoint string, socket bool) DnstapIO {
	return &dnstapIO{
		endpoint: endpoint,
		socket:   socket,
		enc: newDnstapEncoder(&fs.EncoderOptions{
			ContentType:   []byte("protobuf:dnstap.Dnstap"),
			Bidirectional: true,
		}),
		queue: make(chan tap.Dnstap, queueSize),
		quit:  make(chan struct{}),
	}
}

// DnstapIO interface
type DnstapIO interface {
	Connect()
	Dnstap(payload tap.Dnstap)
	Close()
}

func (dio *dnstapIO) newConnect() error {
	var err error
	if dio.socket {
		if dio.conn, err = net.Dial("unix", dio.endpoint); err != nil {
			return err
		}
	} else {
		if dio.conn, err = net.DialTimeout("tcp", dio.endpoint, tcpTimeout); err != nil {
			return err
		}
		if tcpConn, ok := dio.conn.(*net.TCPConn); ok {
			tcpConn.SetWriteBuffer(tcpWriteBufSize)
			tcpConn.SetNoDelay(false)
		}
	}

	return dio.enc.resetWriter(dio.conn)
}

// Connect connects to the dnstop endpoint.
func (dio *dnstapIO) Connect() {
	if err := dio.newConnect(); err != nil {
		log.Error("No connection to dnstap endpoint")
	}
	go dio.serve()
}

// Dnstap enqueues the payload for log.
func (dio *dnstapIO) Dnstap(payload tap.Dnstap) {
	select {
	case dio.queue <- payload:
	default:
		atomic.AddUint32(&dio.dropped, 1)
	}
}

func (dio *dnstapIO) closeConnection() {
	dio.enc.close()
	if dio.conn != nil {
		dio.conn.Close()
		dio.conn = nil
	}
}

// Close waits until the I/O routine is finished to return.
func (dio *dnstapIO) Close() {
	close(dio.quit)
}

func (dio *dnstapIO) flushBuffer() {
	if dio.conn == nil {
		if err := dio.newConnect(); err != nil {
			return
		}
		log.Info("Reconnected to dnstap")
	}

	if err := dio.enc.flushBuffer(); err != nil {
		log.Warningf("Connection lost: %s", err)
		dio.closeConnection()
		if err := dio.newConnect(); err != nil {
			log.Errorf("Cannot connect to dnstap: %s", err)
		} else {
			log.Info("Reconnected to dnstap")
		}
	}
}

func (dio *dnstapIO) write(payload *tap.Dnstap) {
	if err := dio.enc.writeMsg(payload); err != nil {
		atomic.AddUint32(&dio.dropped, 1)
	}
}

func (dio *dnstapIO) serve() {
	timeout := time.After(flushTimeout)
	for {
		select {
		case <-dio.quit:
			dio.flushBuffer()
			dio.closeConnection()
			return
		case payload := <-dio.queue:
			dio.write(&payload)
		case <-timeout:
			if dropped := atomic.SwapUint32(&dio.dropped, 0); dropped > 0 {
				log.Warningf("Dropped dnstap messages: %d", dropped)
			}
			dio.flushBuffer()
			timeout = time.After(flushTimeout)
		}
	}
}
<commit_msg>fix typo "dnstop" >> "dnstap" (#3170)<commit_after>package dnstapio

import (
	"net"
	"sync/atomic"
	"time"

	clog "github.com/coredns/coredns/plugin/pkg/log"

	tap "github.com/dnstap/golang-dnstap"
	fs "github.com/farsightsec/golang-framestream"
)

var log = clog.NewWithPlugin("dnstap")

const (
	tcpWriteBufSize = 1024 * 1024
	tcpTimeout      = 4 * time.Second
	flushTimeout    = 1 * time.Second
	queueSize       = 10000
)

type dnstapIO struct {
	endpoint string
	socket   bool
	conn     net.Conn
	enc      *dnstapEncoder
	queue    chan tap.Dnstap
	dropped  uint32
	quit     chan struct{}
}

// New returns a new and initialized DnstapIO.
func New(endpoint string, socket bool) DnstapIO {
	return &dnstapIO{
		endpoint: endpoint,
		socket:   socket,
		enc: newDnstapEncoder(&fs.EncoderOptions{
			ContentType:   []byte("protobuf:dnstap.Dnstap"),
			Bidirectional: true,
		}),
		queue: make(chan tap.Dnstap, queueSize),
		quit:  make(chan struct{}),
	}
}

// DnstapIO interface
type DnstapIO interface {
	Connect()
	Dnstap(payload tap.Dnstap)
	Close()
}

func (dio *dnstapIO) newConnect() error {
	var err error
	if dio.socket {
		if dio.conn, err = net.Dial("unix", dio.endpoint); err != nil {
			return err
		}
	} else {
		if dio.conn, err = net.DialTimeout("tcp", dio.endpoint, tcpTimeout); err != nil {
			return err
		}
		if tcpConn, ok := dio.conn.(*net.TCPConn); ok {
			tcpConn.SetWriteBuffer(tcpWriteBufSize)
			tcpConn.SetNoDelay(false)
		}
	}

	return dio.enc.resetWriter(dio.conn)
}

// Connect connects to the dnstap endpoint.
func (dio *dnstapIO) Connect() {
	if err := dio.newConnect(); err != nil {
		log.Error("No connection to dnstap endpoint")
	}
	go dio.serve()
}

// Dnstap enqueues the payload for log.
func (dio *dnstapIO) Dnstap(payload tap.Dnstap) {
	select {
	case dio.queue <- payload:
	default:
		atomic.AddUint32(&dio.dropped, 1)
	}
}

func (dio *dnstapIO) closeConnection() {
	dio.enc.close()
	if dio.conn != nil {
		dio.conn.Close()
		dio.conn = nil
	}
}

// Close waits until the I/O routine is finished to return.
func (dio *dnstapIO) Close() {
	close(dio.quit)
}

func (dio *dnstapIO) flushBuffer() {
	if dio.conn == nil {
		if err := dio.newConnect(); err != nil {
			return
		}
		log.Info("Reconnected to dnstap")
	}

	if err := dio.enc.flushBuffer(); err != nil {
		log.Warningf("Connection lost: %s", err)
		dio.closeConnection()
		if err := dio.newConnect(); err != nil {
			log.Errorf("Cannot connect to dnstap: %s", err)
		} else {
			log.Info("Reconnected to dnstap")
		}
	}
}

func (dio *dnstapIO) write(payload *tap.Dnstap) {
	if err := dio.enc.writeMsg(payload); err != nil {
		atomic.AddUint32(&dio.dropped, 1)
	}
}

func (dio *dnstapIO) serve() {
	timeout := time.After(flushTimeout)
	for {
		select {
		case <-dio.quit:
			dio.flushBuffer()
			dio.closeConnection()
			return
		case payload := <-dio.queue:
			dio.write(&payload)
		case <-timeout:
			if dropped := atomic.SwapUint32(&dio.dropped, 0); dropped > 0 {
				log.Warningf("Dropped dnstap messages: %d", dropped)
			}
			dio.flushBuffer()
			timeout = time.After(flushTimeout)
		}
	}
}
<|endoftext|>


<commit_before>package quickpgp

import (
	"fmt"
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/openpgp"
	openpgperrors "golang.org/x/crypto/openpgp/errors"
	"hostutils"
)

var _ = hostutils.Display

func Decrypt(privateKeyFileName string, publicKeyFileName string, file string) (err error) {

	var signer openpgp.EntityList
	if signer, err = readPublicKeyFile(publicKeyFileName); err != nil {
		return err
	}

	var recipient *openpgp.Entity
	if recipient, err = readPrivateKeyFile(privateKeyFileName); err != nil {
		return err
	}
	if recipient == nil {
		return fmt.Errorf("quickpgp: unable to read %s", privateKeyFileName)
	}

	var keyring openpgp.EntityList
	keyring = append(keyring, signer[0])
	keyring = append(keyring, recipient)

	var cipherTextFile *os.File
	if cipherTextFile, err = os.Open(file); err != nil {
		return err
	}

	md, err := openpgp.ReadMessage(cipherTextFile, keyring, nil, nil)
	if err != nil {
		return err
	}

	var plainTextOutput *os.File
	// Should use temp file here
	// Then rename to either file (without .pgp extension) or
	//  use md.LiteralData.FileName
	outfile := strings.TrimSuffix(file, ".pgp")
	if plainTextOutput, err = os.Create(outfile + ".new"); err != nil {
		return err
	}
	_, err = io.Copy(plainTextOutput, md.UnverifiedBody)
	if err != nil {
		return err
	}
	if md.SignatureError != nil {
		// TODO cleanup tmp file
		return err
	}
	if md.Signature == nil {
		return openpgperrors.ErrUnknownIssuer
	}

	return nil
}
<commit_msg>more consistent and less error prone file handling on decrypt<commit_after>package quickpgp

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"golang.org/x/crypto/openpgp"
	openpgperrors "golang.org/x/crypto/openpgp/errors"
	"hostutils"
)

var _ = hostutils.Display

func Decrypt(privateKeyFileName string, publicKeyFileName string, file string) (err error) {

	if filepath.Ext(file) != ".pgp" {
		return fmt.Errorf("quickpgp: filename to decrypt must end in .pgp")
	}

	var signer openpgp.EntityList
	if signer, err = readPublicKeyFile(publicKeyFileName); err != nil {
		return err
	}

	var recipient *openpgp.Entity
	if recipient, err = readPrivateKeyFile(privateKeyFileName); err != nil {
		return err
	}
	if recipient == nil {
		return fmt.Errorf("quickpgp: unable to read %s", privateKeyFileName)
	}

	var keyring openpgp.EntityList
	keyring = append(keyring, signer[0])
	keyring = append(keyring, recipient)

	var cipherTextFile *os.File
	if cipherTextFile, err = os.Open(file); err != nil {
		return err
	}

	md, err := openpgp.ReadMessage(cipherTextFile, keyring, nil, nil)
	if err != nil {
		return err
	}

	var cwd string
	if cwd, err = os.Getwd(); err != nil {
		return err
	}
	var plainTextOutput *os.File
	if plainTextOutput, err = ioutil.TempFile(cwd, ".quickpgp."); err != nil {
		return err
	}
	var cleanExit bool
	defer func() {
		if !cleanExit {
			_ = os.Remove(plainTextOutput.Name())
		}
	}()

	_, err = io.Copy(plainTextOutput, md.UnverifiedBody)
	if err != nil {
		return err
	}
	plainTextOutput.Close()
	if md.SignatureError != nil {
		return err
	}
	if md.Signature == nil {
		return openpgperrors.ErrUnknownIssuer
	}

	bareFilename := strings.TrimSuffix(file, filepath.Ext(file))
	if len(md.LiteralData.FileName) != 0 && md.LiteralData.FileName != bareFilename {
		fmt.Fprintf(os.Stderr, "quickpgp: suggested filename \"%s\"\n", md.LiteralData.FileName)
	}
	var finalFilename string
	if _, err := os.Stat(bareFilename); os.IsNotExist(err) {
		finalFilename = bareFilename
	} else {
		finalFilename = fmt.Sprintf("%s.%X", bareFilename, uint32(md.SignedByKeyId&0xffffffff))
		fmt.Fprintf(os.Stderr, "quickpgp: \"%s\" exists, writing to \"%s\"\n", bareFilename, finalFilename)
	}

	err = os.Rename(plainTextOutput.Name(), finalFilename)
	if err == nil {
		cleanExit = true
	}
	return err
}
<|endoftext|>


<commit_before>package docker

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/goreleaser/goreleaser/pipeline"
	"github.com/stretchr/testify/assert"
	"syscall"
)

func killAndRm(t *testing.T) {
	t.Log("killing registry")
	_ = exec.Command("docker", "kill", "registry").Run()
	_ = exec.Command("docker", "rm", "registry").Run()
}

func TestRunPipe(t *testing.T) {
	folder, err := ioutil.TempDir("", "archivetest")
	assert.NoError(t, err)
	var dist = filepath.Join(folder, "dist")
	assert.NoError(t, os.Mkdir(dist, 0755))
	assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
	var binPath = filepath.Join(dist, "mybin", "mybin")
	_, err = os.Create(binPath)
	assert.NoError(t, err)

	var table = map[string]struct {
		docker config.Docker
		err    string
	}{
		"valid": {
			docker: config.Docker{
				Image:       "localhost:5000/goreleaser/test_run_pipe",
				Goos:        "linux",
				Goarch:      "amd64",
				Dockerfile:  "testdata/Dockerfile",
				Binary:      "mybin",
				Latest:      true,
				TagTemplate: "{{.Tag}}-{{.Env.FOO}}",
			},
			err: "",
		},
		"invalid": {
			docker: config.Docker{
				Image:       "localhost:5000/goreleaser/test_run_pipe_nope",
				Goos:        "linux",
				Goarch:      "amd64",
				Dockerfile:  "testdata/Dockerfile",
				Binary:      "otherbin",
				TagTemplate: "{{.Version}}",
			},
			err: "",
		},
		"template_error": {
			docker: config.Docker{
				Image:       "localhost:5000/goreleaser/test_run_pipe_template_error",
				Goos:        "linux",
				Goarch:      "amd64",
				Dockerfile:  "testdata/Dockerfile",
				Binary:      "mybin",
				Latest:      true,
				TagTemplate: "{{.Tag}",
			},
			err: `template: tag:1: unexpected "}" in operand`,
		},
	}
	var images = []string{
		"localhost:5000/goreleaser/test_run_pipe:v1.0.0-123",
		"localhost:5000/goreleaser/test_run_pipe:latest",
	}
	// this might fail as the image doesnt exist yet, so lets ignore the error
	for _, img := range images {
		_ = exec.Command("docker", "rmi", img).Run()
	}

	killAndRm(t)
	if err := exec.Command(
		"docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2",
	).Run(); err != nil {
		t.Log("failed to start docker registry", err)
		t.FailNow()
	}
	defer killAndRm(t)

	for name, docker := range table {
		t.Run(name, func(tt *testing.T) {
			var ctx = &context.Context{
				Version:   "1.0.0",
				Publish:   true,
				Artifacts: artifact.New(),
				Git: context.GitInfo{
					CurrentTag: "v1.0.0",
				},
				Config: config.Project{
					ProjectName: "mybin",
					Dist:        dist,
					Dockers: []config.Docker{
						docker.docker,
					},
				},
				Env: map[string]string{"FOO": "123"},
			}
			for _, os := range []string{"linux", "darwin"} {
				for _, arch := range []string{"amd64", "386"} {
					ctx.Artifacts.Add(artifact.Artifact{
						Name:   "mybin",
						Path:   binPath,
						Goarch: arch,
						Goos:   os,
						Type:   artifact.Binary,
						Extra: map[string]string{
							"Binary": "mybin",
						},
					})
				}
			}
			if docker.err == "" {
				assert.NoError(tt, Pipe{}.Run(ctx))
			} else {
				assert.EqualError(tt, Pipe{}.Run(ctx), docker.err)
			}
		})
	}

	// this might should not fail as the image should have been created when
	// the step ran
	for _, img := range images {
		assert.NoError(t, exec.Command("docker", "rmi", img).Run())
	}
	// the test_run_pipe_nope image should not have been created, so deleting
	// it should fail
	assert.Error(t,
		exec.Command(
			"docker", "rmi", "localhost:5000/goreleaser/test_run_pipe_nope:1.0.0",
		).Run(),
	)
}

func TestDescription(t *testing.T) {
	assert.NotEmpty(t, Pipe{}.String())
}

func TestNoDockers(t *testing.T) {
	assert.True(t, pipeline.IsSkip(Pipe{}.Run(context.New(config.Project{}))))
}

func TestNoDockerWithoutImageName(t *testing.T) {
	assert.True(t, pipeline.IsSkip(Pipe{}.Run(context.New(config.Project{
		Dockers: []config.Docker{
			{
				Goos: "linux",
			},
		},
	}))))
}

func TestDockerNotInPath(t *testing.T) {
	var path = os.Getenv("PATH")
	defer func() {
		assert.NoError(t, os.Setenv("PATH", path))
	}()
	assert.NoError(t, os.Setenv("PATH", ""))
	var ctx = &context.Context{
		Version: "1.0.0",
		Config: config.Project{
			Dockers: []config.Docker{
				{
					Image: "a/b",
				},
			},
		},
	}
	assert.EqualError(t, Pipe{}.Run(ctx), ErrNoDocker.Error())
}

func TestDefault(t *testing.T) {
	var ctx = &context.Context{
		Config: config.Project{
			Builds: []config.Build{
				{
					Binary: "foo",
				},
			},
			Dockers: []config.Docker{
				{
					Latest: true,
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Default(ctx))
	assert.Len(t, ctx.Config.Dockers, 1)
	var docker = ctx.Config.Dockers[0]
	assert.Equal(t, "linux", docker.Goos)
	assert.Equal(t, "amd64", docker.Goarch)
	assert.Equal(t, ctx.Config.Builds[0].Binary, docker.Binary)
	assert.Equal(t, "Dockerfile", docker.Dockerfile)
	assert.Equal(t, "{{ .Version }}", docker.TagTemplate)
}

func TestDefaultNoDockers(t *testing.T) {
	var ctx = &context.Context{
		Config: config.Project{
			Dockers: []config.Docker{},
		},
	}
	assert.NoError(t, Pipe{}.Default(ctx))
	assert.Empty(t, ctx.Config.Dockers)
}

func TestDefaultSet(t *testing.T) {
	var ctx = &context.Context{
		Config: config.Project{
			Dockers: []config.Docker{
				{
					Goos:       "windows",
					Goarch:     "i386",
					Binary:     "bar",
					Dockerfile: "Dockerfile.foo",
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Default(ctx))
	assert.Len(t, ctx.Config.Dockers, 1)
	var docker = ctx.Config.Dockers[0]
	assert.Equal(t, "windows", docker.Goos)
	assert.Equal(t, "i386", docker.Goarch)
	assert.Equal(t, "bar", docker.Binary)
	assert.Equal(t, "{{ .Version }}", docker.TagTemplate)
	assert.Equal(t, "Dockerfile.foo", docker.Dockerfile)
}

func TestLinkFile(t *testing.T) {
	const srcFile = "/tmp/test"
	const dstFile = "/tmp/linked"
	err := ioutil.WriteFile(srcFile, []byte("foo"), 0644)
	if err != nil {
		t.Log("Cannot setup test file")
		t.Fail()
	}
	err = link(srcFile, dstFile)
	if err != nil {
		t.Log("Failed to link: ", err)
		t.Fail()
	}
	if inode(srcFile) != inode(dstFile) {
		t.Log("Inodes do not match, destination file is not a link")
		t.Fail()
	}
	// cleanup
	os.Remove(srcFile)
	os.Remove(dstFile)
}

func TestLinkDirectory(t *testing.T) {
	const srcDir = "/tmp/testdir"
	const testFile = "test"
	const dstDir = "/tmp/linkedDir"

	os.Mkdir(srcDir, 0755)
	err := ioutil.WriteFile(srcDir+"/"+testFile, []byte("foo"), 0644)
	if err != nil {
		t.Log("Cannot setup test file")
		t.Fail()
	}
	err = directoryLink(srcDir, dstDir, nil)
	if err != nil {
		t.Log("Failed to link: ", err)
		t.Fail()
	}
	if inode(srcDir+"/"+testFile) != inode(dstDir+"/"+testFile) {
		t.Log("Inodes do not match, destination file is not a link")
		t.Fail()
	}

	// cleanup
	os.RemoveAll(srcDir)
	os.RemoveAll(dstDir)
}

func TestLinkTwoLevelDirectory(t *testing.T) {
	const srcDir = "/tmp/testdir"
	const srcLevel2 = srcDir+"/level2"
	const testFile = "test"
	const dstDir = "/tmp/linkedDir"

	os.Mkdir(srcDir, 0755)
	os.Mkdir(srcLevel2, 0755)
	err := ioutil.WriteFile(srcDir+"/"+testFile, []byte("foo"), 0644)
	if err != nil {
		t.Log("Cannot setup test file")
		t.Fail()
	}
	err = ioutil.WriteFile(srcLevel2+"/"+testFile, []byte("foo"), 0644)
	if err != nil {
		t.Log("Cannot setup test file")
		t.Fail()
	}
	err = directoryLink(srcDir, dstDir, nil)
	if err != nil {
		t.Log("Failed to link: ", err)
		t.Fail()
	}
	if inode(srcDir+"/"+testFile) != inode(dstDir+"/"+testFile) {
		t.Log("Inodes do not match")
		t.Fail()
	}
	if inode(srcLevel2+"/"+testFile) != inode(dstDir+"/level2/"+testFile) {
		t.Log("Inodes do not match")
		t.Fail()
	}
	// cleanup
	os.RemoveAll(srcDir)
	os.RemoveAll(dstDir)
}

func inode(file string) uint64 {
	fileInfo, err := os.Stat(file)
	if err != nil {
		return 0
	}
	stat := fileInfo.Sys().(*syscall.Stat_t)
	return stat.Ino
}
<commit_msg>test: fixed docker link tests<commit_after>package docker

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/goreleaser/goreleaser/pipeline"
	"github.com/stretchr/testify/assert"
	"syscall"
)

func killAndRm(t *testing.T) {
	t.Log("killing registry")
	_ = exec.Command("docker", "kill", "registry").Run()
	_ = exec.Command("docker", "rm", "registry").Run()
}

func TestRunPipe(t *testing.T) {
	folder, err := ioutil.TempDir("", "archivetest")
	assert.NoError(t, err)
	var dist = filepath.Join(folder, "dist")
	assert.NoError(t, os.Mkdir(dist, 0755))
	assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
	var binPath = filepath.Join(dist, "mybin", "mybin")
	_, err = os.Create(binPath)
	assert.NoError(t, err)

	var table = map[string]struct {
		docker config.Docker
		err    string
	}{
		"valid": {
			docker: config.Docker{
				Image:       "localhost:5000/goreleaser/test_run_pipe",
				Goos:        "linux",
				Goarch:      "amd64",
				Dockerfile:  "testdata/Dockerfile",
				Binary:      "mybin",
				Latest:      true,
				TagTemplate: "{{.Tag}}-{{.Env.FOO}}",
			},
			err: "",
		},
		"invalid": {
			docker: config.Docker{
				Image:       "localhost:5000/goreleaser/test_run_pipe_nope",
				Goos:        "linux",
				Goarch:      "amd64",
				Dockerfile:  "testdata/Dockerfile",
				Binary:      "otherbin",
				TagTemplate: "{{.Version}}",
			},
			err: "",
		},
		"template_error": {
			docker: config.Docker{
				Image:       "localhost:5000/goreleaser/test_run_pipe_template_error",
				Goos:        "linux",
				Goarch:      "amd64",
				Dockerfile:  "testdata/Dockerfile",
				Binary:      "mybin",
				Latest:      true,
				TagTemplate: "{{.Tag}",
			},
			err: `template: tag:1: unexpected "}" in operand`,
		},
	}
	var images = []string{
		"localhost:5000/goreleaser/test_run_pipe:v1.0.0-123",
		"localhost:5000/goreleaser/test_run_pipe:latest",
	}
	// this might fail as the image doesnt exist yet, so lets ignore the error
	for _, img := range images {
		_ = exec.Command("docker", "rmi", img).Run()
	}

	killAndRm(t)
	if err := exec.Command(
		"docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2",
	).Run(); err != nil {
		t.Log("failed to start docker registry", err)
		t.FailNow()
	}
	defer killAndRm(t)

	for name, docker := range table {
		t.Run(name, func(tt *testing.T) {
			var ctx = &context.Context{
				Version:   "1.0.0",
				Publish:   true,
				Artifacts: artifact.New(),
				Git: context.GitInfo{
					CurrentTag: "v1.0.0",
				},
				Config: config.Project{
					ProjectName: "mybin",
					Dist:        dist,
					Dockers: []config.Docker{
						docker.docker,
					},
				},
				Env: map[string]string{"FOO": "123"},
			}
			for _, os := range []string{"linux", "darwin"} {
				for _, arch := range []string{"amd64", "386"} {
					ctx.Artifacts.Add(artifact.Artifact{
						Name:   "mybin",
						Path:   binPath,
						Goarch: arch,
						Goos:   os,
						Type:   artifact.Binary,
						Extra: map[string]string{
							"Binary": "mybin",
						},
					})
				}
			}
			if docker.err == "" {
				assert.NoError(tt, Pipe{}.Run(ctx))
			} else {
				assert.EqualError(tt, Pipe{}.Run(ctx), docker.err)
			}
		})
	}

	// this might should not fail as the image should have been created when
	// the step ran
	for _, img := range images {
		assert.NoError(t, exec.Command("docker", "rmi", img).Run())
	}
	// the test_run_pipe_nope image should not have been created, so deleting
	// it should fail
	assert.Error(t,
		exec.Command(
			"docker", "rmi", "localhost:5000/goreleaser/test_run_pipe_nope:1.0.0",
		).Run(),
	)
}

func TestDescription(t *testing.T) {
	assert.NotEmpty(t, Pipe{}.String())
}

func TestNoDockers(t *testing.T) {
	assert.True(t, pipeline.IsSkip(Pipe{}.Run(context.New(config.Project{}))))
}

func TestNoDockerWithoutImageName(t *testing.T) {
	assert.True(t, pipeline.IsSkip(Pipe{}.Run(context.New(config.Project{
		Dockers: []config.Docker{
			{
				Goos: "linux",
			},
		},
	}))))
}

func TestDockerNotInPath(t *testing.T) {
	var path = os.Getenv("PATH")
	defer func() {
		assert.NoError(t, os.Setenv("PATH", path))
	}()
	assert.NoError(t, os.Setenv("PATH", ""))
	var ctx = &context.Context{
		Version: "1.0.0",
		Config: config.Project{
			Dockers: []config.Docker{
				{
					Image: "a/b",
				},
			},
		},
	}
	assert.EqualError(t, Pipe{}.Run(ctx), ErrNoDocker.Error())
}

func TestDefault(t *testing.T) {
	var ctx = &context.Context{
		Config: config.Project{
			Builds: []config.Build{
				{
					Binary: "foo",
				},
			},
			Dockers: []config.Docker{
				{
					Latest: true,
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Default(ctx))
	assert.Len(t, ctx.Config.Dockers, 1)
	var docker = ctx.Config.Dockers[0]
	assert.Equal(t, "linux", docker.Goos)
	assert.Equal(t, "amd64", docker.Goarch)
	assert.Equal(t, ctx.Config.Builds[0].Binary, docker.Binary)
	assert.Equal(t, "Dockerfile", docker.Dockerfile)
	assert.Equal(t, "{{ .Version }}", docker.TagTemplate)
}

func TestDefaultNoDockers(t *testing.T) {
	var ctx = &context.Context{
		Config: config.Project{
			Dockers: []config.Docker{},
		},
	}
	assert.NoError(t, Pipe{}.Default(ctx))
	assert.Empty(t, ctx.Config.Dockers)
}

func TestDefaultSet(t *testing.T) {
	var ctx = &context.Context{
		Config: config.Project{
			Dockers: []config.Docker{
				{
					Goos:       "windows",
					Goarch:     "i386",
					Binary:     "bar",
					Dockerfile: "Dockerfile.foo",
				},
			},
		},
	}
	assert.NoError(t, Pipe{}.Default(ctx))
	assert.Len(t, ctx.Config.Dockers, 1)
	var docker = ctx.Config.Dockers[0]
	assert.Equal(t, "windows", docker.Goos)
	assert.Equal(t, "i386", docker.Goarch)
	assert.Equal(t, "bar", docker.Binary)
	assert.Equal(t, "{{ .Version }}", docker.TagTemplate)
	assert.Equal(t, "Dockerfile.foo", docker.Dockerfile)
}

func TestLinkFile(t *testing.T) {
	const srcFile = "/tmp/test"
	const dstFile = "/tmp/linked"
	err := ioutil.WriteFile(srcFile, []byte("foo"), 0644)
	if err != nil {
		t.Log("Cannot setup test file")
		t.Fail()
	}
	err = link(srcFile, dstFile)
	if err != nil {
		t.Log("Failed to link: ", err)
		t.Fail()
	}
	if inode(srcFile) != inode(dstFile) {
		t.Log("Inodes do not match, destination file is not a link")
		t.Fail()
	}
	// cleanup
	os.Remove(srcFile)
	os.Remove(dstFile)
}

func TestLinkDirectory(t *testing.T) {
	const srcDir = "/tmp/testdir"
	const testFile = "test"
	const dstDir = "/tmp/linkedDir"

	os.Mkdir(srcDir, 0755)
	err := ioutil.WriteFile(srcDir+"/"+testFile, []byte("foo"), 0644)
	if err != nil {
		t.Log("Cannot setup test file")
		t.Fail()
	}
	err = link(srcDir, dstDir)
	if err != nil {
		t.Log("Failed to link: ", err)
		t.Fail()
	}
	if inode(srcDir+"/"+testFile) != inode(dstDir+"/"+testFile) {
		t.Log("Inodes do not match, destination file is not a link")
		t.Fail()
	}

	// cleanup
	os.RemoveAll(srcDir)
	os.RemoveAll(dstDir)
}

func TestLinkTwoLevelDirectory(t *testing.T) {
	const srcDir = "/tmp/testdir"
	const srcLevel2 = srcDir+"/level2"
	const testFile = "test"
	const dstDir = "/tmp/linkedDir"

	os.Mkdir(srcDir, 0755)
	os.Mkdir(srcLevel2, 0755)
	err := ioutil.WriteFile(srcDir+"/"+testFile, []byte("foo"), 0644)
	if err != nil {
		t.Log("Cannot setup test file")
		t.Fail()
	}
	err = ioutil.WriteFile(srcLevel2+"/"+testFile, []byte("foo"), 0644)
	if err != nil {
		t.Log("Cannot setup test file")
		t.Fail()
	}
	err = link(srcDir, dstDir)
	if err != nil {
		t.Log("Failed to link: ", err)
		t.Fail()
	}
	if inode(srcDir+"/"+testFile) != inode(dstDir+"/"+testFile) {
		t.Log("Inodes do not match")
		t.Fail()
	}
	if inode(srcLevel2+"/"+testFile) != inode(dstDir+"/level2/"+testFile) {
		t.Log("Inodes do not match")
		t.Fail()
	}
	// cleanup
	os.RemoveAll(srcDir)
	os.RemoveAll(dstDir)
}

func inode(file string) uint64 {
	fileInfo, err := os.Stat(file)
	if err != nil {
		return 0
	}
	stat := fileInfo.Sys().(*syscall.Stat_t)
	return stat.Ino
}
<|endoftext|>


<commit_before>package vlabs

import (
	"testing"

	"github.com/Azure/acs-engine/pkg/api/common"
)

const (
	ValidKubernetesNodeStatusUpdateFrequency        = "10s"
	ValidKubernetesCtrlMgrNodeMonitorGracePeriod    = "40s"
	ValidKubernetesCtrlMgrPodEvictionTimeout        = "5m0s"
	ValidKubernetesCtrlMgrRouteReconciliationPeriod = "10s"
	ValidKubernetesCloudProviderBackoff             = false
	ValidKubernetesCloudProviderBackoffRetries      = 6
	ValidKubernetesCloudProviderBackoffJitter       = 1
	ValidKubernetesCloudProviderBackoffDuration     = 5
	ValidKubernetesCloudProviderBackoffExponent     = 1.5
	ValidKubernetesCloudProviderRateLimit           = false
	ValidKubernetesCloudProviderRateLimitQPS        = 3
	ValidKubernetesCloudProviderRateLimitBucket     = 10
)

func Test_OrchestratorProfile_Validate(t *testing.T) {
	o := &OrchestratorProfile{
		OrchestratorType: "DCOS",
		KubernetesConfig: &KubernetesConfig{},
	}

	if err := o.Validate(); err != nil {
		t.Errorf("should not error with empty object: %v", err)
	}

	o.KubernetesConfig.ClusterSubnet = "10.0.0.0/16"
	if err := o.Validate(); err == nil {
		t.Errorf("should error when KubernetesConfig populated for non-Kubernetes OrchestratorType")
	}
}

func Test_KubernetesConfig_Validate(t *testing.T) {
	// Tests that should pass across all releases
	for _, k8sRelease := range []string{common.KubernetesRelease1Dot5, common.KubernetesRelease1Dot6, common.KubernetesRelease1Dot7} {
		c := KubernetesConfig{}
		if err := c.Validate(k8sRelease); err != nil {
			t.Errorf("should not error on empty KubernetesConfig: %v, release %s", err, k8sRelease)
		}

		c = KubernetesConfig{
			ClusterSubnet:                    "10.120.0.0/16",
			DockerBridgeSubnet:               "10.120.1.0/16",
			MaxPods:                          42,
			NodeStatusUpdateFrequency:        ValidKubernetesNodeStatusUpdateFrequency,
			CtrlMgrNodeMonitorGracePeriod:    ValidKubernetesCtrlMgrNodeMonitorGracePeriod,
			CtrlMgrPodEvictionTimeout:        ValidKubernetesCtrlMgrPodEvictionTimeout,
			CtrlMgrRouteReconciliationPeriod: ValidKubernetesCtrlMgrRouteReconciliationPeriod,
			CloudProviderBackoff:             ValidKubernetesCloudProviderBackoff,
			CloudProviderBackoffRetries:      ValidKubernetesCloudProviderBackoffRetries,
			CloudProviderBackoffJitter:       ValidKubernetesCloudProviderBackoffJitter,
			CloudProviderBackoffDuration:     ValidKubernetesCloudProviderBackoffDuration,
			CloudProviderBackoffExponent:     ValidKubernetesCloudProviderBackoffExponent,
			CloudProviderRateLimit:           ValidKubernetesCloudProviderRateLimit,
			CloudProviderRateLimitQPS:        ValidKubernetesCloudProviderRateLimitQPS,
			CloudProviderRateLimitBucket:     ValidKubernetesCloudProviderRateLimitBucket,
		}
		if err := c.Validate(k8sRelease); err != nil {
			t.Errorf("should not error on a KubernetesConfig with valid param values: %v", err)
		}

		c = KubernetesConfig{
			ClusterSubnet: "10.16.x.0/invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid ClusterSubnet")
		}

		c = KubernetesConfig{
			DockerBridgeSubnet: "10.120.1.0/invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid DockerBridgeSubnet")
		}

		c = KubernetesConfig{
			MaxPods: KubernetesMinMaxPods - 1,
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid MaxPods")
		}

		c = KubernetesConfig{
			NodeStatusUpdateFrequency: "invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid NodeStatusUpdateFrequency")
		}

		c = KubernetesConfig{
			CtrlMgrNodeMonitorGracePeriod: "invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid CtrlMgrNodeMonitorGracePeriod")
		}

		c = KubernetesConfig{
			NodeStatusUpdateFrequency:     "10s",
			CtrlMgrNodeMonitorGracePeriod: "30s",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when CtrlMgrRouteReconciliationPeriod is not sufficiently larger than NodeStatusUpdateFrequency")
		}

		c = KubernetesConfig{
			CtrlMgrPodEvictionTimeout: "invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid CtrlMgrPodEvictionTimeout")
		}

		c = KubernetesConfig{
			CtrlMgrRouteReconciliationPeriod: "invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid CtrlMgrRouteReconciliationPeriod")
		}

		c = KubernetesConfig{
			DnsServiceIP: "192.168.0.10",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when DnsServiceIP but not ServiceCidr")
		}

		c = KubernetesConfig{
			ServiceCidr: "192.168.0.10/24",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when ServiceCidr but not DnsServiceIP")
		}

		c = KubernetesConfig{
			DnsServiceIP: "invalid",
			ServiceCidr:  "192.168.0.0/24",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when DnsServiceIP is invalid")
		}

		c = KubernetesConfig{
			DnsServiceIP: "192.168.1.10",
			ServiceCidr:  "192.168.0.0/not-a-len",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when ServiceCidr is invalid")
		}

		c = KubernetesConfig{
			DnsServiceIP: "192.168.1.10",
			ServiceCidr:  "192.168.0.0/24",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when DnsServiceIP is outside of ServiceCidr")
		}

		c = KubernetesConfig{
			DnsServiceIP: "172.99.255.255",
			ServiceCidr:  "172.99.0.1/16",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when DnsServiceIP is broadcast address of ServiceCidr")
		}

		c = KubernetesConfig{
			DnsServiceIP: "172.99.255.10",
			ServiceCidr:  "172.99.0.1/16",
		}
		if err := c.Validate(k8sRelease); err != nil {
			t.Error("should not error when DnsServiceIP and ServiceCidr are valid")
		}
	}

	// Tests that apply to pre-1.6 releases
	for _, k8sRelease := range []string{common.KubernetesRelease1Dot5} {
		c := KubernetesConfig{
			CloudProviderBackoff:   true,
			CloudProviderRateLimit: true,
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error because backoff and rate limiting are not available before v1.6.6")
		}
	}

	// Tests that apply to 1.6 and later releases
	for _, k8sRelease := range []string{common.KubernetesRelease1Dot6, common.KubernetesRelease1Dot7} {
		c := KubernetesConfig{
			CloudProviderBackoff:   true,
			CloudProviderRateLimit: true,
		}
		if err := c.Validate(k8sRelease); err != nil {
			t.Error("should not error when basic backoff and rate limiting are set to true with no options")
		}
	}
}

func Test_Properties_ValidateNetworkPolicy(t *testing.T) {
	p := &Properties{}
	p.OrchestratorProfile = &OrchestratorProfile{}
	p.OrchestratorProfile.OrchestratorType = Kubernetes

	for _, policy := range NetworkPolicyValues {
		p.OrchestratorProfile.KubernetesConfig = &KubernetesConfig{}
		p.OrchestratorProfile.KubernetesConfig.NetworkPolicy = policy
		if err := p.validateNetworkPolicy(); err != nil {
			t.Errorf(
				"should not error on networkPolicy=\"%s\"",
				policy,
			)
		}
	}

	p.OrchestratorProfile.KubernetesConfig.NetworkPolicy = "not-existing"
	if err := p.validateNetworkPolicy(); err == nil {
		t.Errorf(
			"should error on invalid networkPolicy",
		)
	}

	p.OrchestratorProfile.KubernetesConfig.NetworkPolicy = "calico"
	p.AgentPoolProfiles = []*AgentPoolProfile{
		{
			OSType: Windows,
		},
	}
	if err := p.validateNetworkPolicy(); err == nil {
		t.Errorf(
			"should error on calico for windows clusters",
		)
	}
}

func Test_ServicePrincipalProfile_ValidateSecretOrKeyvaultSecretRef(t *testing.T) {

	t.Run("ServicePrincipalProfile with secret should pass", func(t *testing.T) {
		p := getK8sDefaultProperties()

		if err := p.Validate(); err != nil {
			t.Errorf("should not error %v", err)
		}
	})

	t.Run("ServicePrincipalProfile with KeyvaultSecretRef (with version) should pass", func(t *testing.T) {
		p := getK8sDefaultProperties()
		p.ServicePrincipalProfile.Secret = ""
		p.ServicePrincipalProfile.KeyvaultSecretRef = &KeyvaultSecretRef{
			VaultID:       "/subscriptions/SUB-ID/resourceGroups/RG-NAME/providers/Microsoft.KeyVault/vaults/KV-NAME",
			SecretName:    "secret-name",
			SecretVersion: "version",
		}
		if err := p.Validate(); err != nil {
			t.Errorf("should not error %v", err)
		}
	})

	t.Run("ServicePrincipalProfile with KeyvaultSecretRef (without version) should pass", func(t *testing.T) {
		p := getK8sDefaultProperties()
		p.ServicePrincipalProfile.Secret = ""
		p.ServicePrincipalProfile.KeyvaultSecretRef = &KeyvaultSecretRef{
			VaultID:    "/subscriptions/SUB-ID/resourceGroups/RG-NAME/providers/Microsoft.KeyVault/vaults/KV-NAME",
			SecretName: "secret-name",
		}

		if err := p.Validate(); err != nil {
			t.Errorf("should not error %v", err)
		}
	})

	t.Run("ServicePrincipalProfile with Secret and KeyvaultSecretRef should NOT pass", func(t *testing.T) {
		p := getK8sDefaultProperties()
		p.ServicePrincipalProfile.Secret = "secret"
		p.ServicePrincipalProfile.KeyvaultSecretRef = &KeyvaultSecretRef{
			VaultID:    "/subscriptions/SUB-ID/resourceGroups/RG-NAME/providers/Microsoft.KeyVault/vaults/KV-NAME",
			SecretName: "secret-name",
		}

		if err := p.Validate(); err == nil {
			t.Error("error should have occurred")
		}
	})

	t.Run("ServicePrincipalProfile with incorrect KeyvaultSecretRef format should NOT pass", func(t *testing.T) {
		p := getK8sDefaultProperties()
		p.ServicePrincipalProfile.Secret = ""
		p.ServicePrincipalProfile.KeyvaultSecretRef = &KeyvaultSecretRef{
			VaultID:    "randomID",
			SecretName: "secret-name",
		}

		if err := p.Validate(); err == nil || err.Error() != "service principal client keyvault secret reference is of incorrect format" {
			t.Error("error should have occurred")
		}
	})
}

func getK8sDefaultProperties() *Properties {
	return &Properties{
		OrchestratorProfile: &OrchestratorProfile{
			OrchestratorType: Kubernetes,
		},
		MasterProfile: &MasterProfile{
			Count:     1,
			DNSPrefix: "foo",
			VMSize:    "Standard_DS2_v2",
		},
		AgentPoolProfiles: []*AgentPoolProfile{
			{
				Name:                "agentpool",
				VMSize:              "Standard_D2_v2",
				Count:               1,
				AvailabilityProfile: AvailabilitySet,
			},
		},
		LinuxProfile: &LinuxProfile{
			AdminUsername: "azureuser",
			SSH: struct {
				PublicKeys []PublicKey `json:"publicKeys" validate:"required,len=1"`
			}{
				PublicKeys: []PublicKey{{
					KeyData: "publickeydata",
				}},
			},
		},
		ServicePrincipalProfile: &ServicePrincipalProfile{
			ClientID: "clientID",
			Secret:   "clientSecret",
		},
	}
}
<commit_msg>K8s test validation DNS != first service ip (#1357)<commit_after>package vlabs

import (
	"testing"

	"github.com/Azure/acs-engine/pkg/api/common"
)

const (
	ValidKubernetesNodeStatusUpdateFrequency        = "10s"
	ValidKubernetesCtrlMgrNodeMonitorGracePeriod    = "40s"
	ValidKubernetesCtrlMgrPodEvictionTimeout        = "5m0s"
	ValidKubernetesCtrlMgrRouteReconciliationPeriod = "10s"
	ValidKubernetesCloudProviderBackoff             = false
	ValidKubernetesCloudProviderBackoffRetries      = 6
	ValidKubernetesCloudProviderBackoffJitter       = 1
	ValidKubernetesCloudProviderBackoffDuration     = 5
	ValidKubernetesCloudProviderBackoffExponent     = 1.5
	ValidKubernetesCloudProviderRateLimit           = false
	ValidKubernetesCloudProviderRateLimitQPS        = 3
	ValidKubernetesCloudProviderRateLimitBucket     = 10
)

func Test_OrchestratorProfile_Validate(t *testing.T) {
	o := &OrchestratorProfile{
		OrchestratorType: "DCOS",
		KubernetesConfig: &KubernetesConfig{},
	}

	if err := o.Validate(); err != nil {
		t.Errorf("should not error with empty object: %v", err)
	}

	o.KubernetesConfig.ClusterSubnet = "10.0.0.0/16"
	if err := o.Validate(); err == nil {
		t.Errorf("should error when KubernetesConfig populated for non-Kubernetes OrchestratorType")
	}
}

func Test_KubernetesConfig_Validate(t *testing.T) {
	// Tests that should pass across all releases
	for _, k8sRelease := range []string{common.KubernetesRelease1Dot5, common.KubernetesRelease1Dot6, common.KubernetesRelease1Dot7} {
		c := KubernetesConfig{}
		if err := c.Validate(k8sRelease); err != nil {
			t.Errorf("should not error on empty KubernetesConfig: %v, release %s", err, k8sRelease)
		}

		c = KubernetesConfig{
			ClusterSubnet:                    "10.120.0.0/16",
			DockerBridgeSubnet:               "10.120.1.0/16",
			MaxPods:                          42,
			NodeStatusUpdateFrequency:        ValidKubernetesNodeStatusUpdateFrequency,
			CtrlMgrNodeMonitorGracePeriod:    ValidKubernetesCtrlMgrNodeMonitorGracePeriod,
			CtrlMgrPodEvictionTimeout:        ValidKubernetesCtrlMgrPodEvictionTimeout,
			CtrlMgrRouteReconciliationPeriod: ValidKubernetesCtrlMgrRouteReconciliationPeriod,
			CloudProviderBackoff:             ValidKubernetesCloudProviderBackoff,
			CloudProviderBackoffRetries:      ValidKubernetesCloudProviderBackoffRetries,
			CloudProviderBackoffJitter:       ValidKubernetesCloudProviderBackoffJitter,
			CloudProviderBackoffDuration:     ValidKubernetesCloudProviderBackoffDuration,
			CloudProviderBackoffExponent:     ValidKubernetesCloudProviderBackoffExponent,
			CloudProviderRateLimit:           ValidKubernetesCloudProviderRateLimit,
			CloudProviderRateLimitQPS:        ValidKubernetesCloudProviderRateLimitQPS,
			CloudProviderRateLimitBucket:     ValidKubernetesCloudProviderRateLimitBucket,
		}
		if err := c.Validate(k8sRelease); err != nil {
			t.Errorf("should not error on a KubernetesConfig with valid param values: %v", err)
		}

		c = KubernetesConfig{
			ClusterSubnet: "10.16.x.0/invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid ClusterSubnet")
		}

		c = KubernetesConfig{
			DockerBridgeSubnet: "10.120.1.0/invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid DockerBridgeSubnet")
		}

		c = KubernetesConfig{
			MaxPods: KubernetesMinMaxPods - 1,
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid MaxPods")
		}

		c = KubernetesConfig{
			NodeStatusUpdateFrequency: "invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid NodeStatusUpdateFrequency")
		}

		c = KubernetesConfig{
			CtrlMgrNodeMonitorGracePeriod: "invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid CtrlMgrNodeMonitorGracePeriod")
		}

		c = KubernetesConfig{
			NodeStatusUpdateFrequency:     "10s",
			CtrlMgrNodeMonitorGracePeriod: "30s",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when CtrlMgrRouteReconciliationPeriod is not sufficiently larger than NodeStatusUpdateFrequency")
		}

		c = KubernetesConfig{
			CtrlMgrPodEvictionTimeout: "invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid CtrlMgrPodEvictionTimeout")
		}

		c = KubernetesConfig{
			CtrlMgrRouteReconciliationPeriod: "invalid",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error on invalid CtrlMgrRouteReconciliationPeriod")
		}

		c = KubernetesConfig{
			DnsServiceIP: "192.168.0.10",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when DnsServiceIP but not ServiceCidr")
		}

		c = KubernetesConfig{
			ServiceCidr: "192.168.0.10/24",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when ServiceCidr but not DnsServiceIP")
		}

		c = KubernetesConfig{
			DnsServiceIP: "invalid",
			ServiceCidr:  "192.168.0.0/24",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when DnsServiceIP is invalid")
		}

		c = KubernetesConfig{
			DnsServiceIP: "192.168.1.10",
			ServiceCidr:  "192.168.0.0/not-a-len",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when ServiceCidr is invalid")
		}

		c = KubernetesConfig{
			DnsServiceIP: "192.168.1.10",
			ServiceCidr:  "192.168.0.0/24",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when DnsServiceIP is outside of ServiceCidr")
		}

		c = KubernetesConfig{
			DnsServiceIP: "172.99.255.255",
			ServiceCidr:  "172.99.0.1/16",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when DnsServiceIP is broadcast address of ServiceCidr")
		}

		c = KubernetesConfig{
			DnsServiceIP: "172.99.0.1",
			ServiceCidr:  "172.99.0.1/16",
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error when DnsServiceIP is first IP of ServiceCidr")
		}

		c = KubernetesConfig{
			DnsServiceIP: "172.99.255.10",
			ServiceCidr:  "172.99.0.1/16",
		}
		if err := c.Validate(k8sRelease); err != nil {
			t.Error("should not error when DnsServiceIP and ServiceCidr are valid")
		}
	}

	// Tests that apply to pre-1.6 releases
	for _, k8sRelease := range []string{common.KubernetesRelease1Dot5} {
		c := KubernetesConfig{
			CloudProviderBackoff:   true,
			CloudProviderRateLimit: true,
		}
		if err := c.Validate(k8sRelease); err == nil {
			t.Error("should error because backoff and rate limiting are not available before v1.6.6")
		}
	}

	// Tests that apply to 1.6 and later releases
	for _, k8sRelease := range []string{common.KubernetesRelease1Dot6, common.KubernetesRelease1Dot7} {
		c := KubernetesConfig{
			CloudProviderBackoff:   true,
			CloudProviderRateLimit: true,
		}
		if err := c.Validate(k8sRelease); err != nil {
			t.Error("should not error when basic backoff and rate limiting are set to true with no options")
		}
	}
}

func Test_Properties_ValidateNetworkPolicy(t *testing.T) {
	p := &Properties{}
	p.OrchestratorProfile = &OrchestratorProfile{}
	p.OrchestratorProfile.OrchestratorType = Kubernetes

	for _, policy := range NetworkPolicyValues {
		p.OrchestratorProfile.KubernetesConfig = &KubernetesConfig{}
		p.OrchestratorProfile.KubernetesConfig.NetworkPolicy = policy
		if err := p.validateNetworkPolicy(); err != nil {
			t.Errorf(
				"should not error on networkPolicy=\"%s\"",
				policy,
			)
		}
	}

	p.OrchestratorProfile.KubernetesConfig.NetworkPolicy = "not-existing"
	if err := p.validateNetworkPolicy(); err == nil {
		t.Errorf(
			"should error on invalid networkPolicy",
		)
	}

	p.OrchestratorProfile.KubernetesConfig.NetworkPolicy = "calico"
	p.AgentPoolProfiles = []*AgentPoolProfile{
		{
			OSType: Windows,
		},
	}
	if err := p.validateNetworkPolicy(); err == nil {
		t.Errorf(
			"should error on calico for windows clusters",
		)
	}
}

func Test_ServicePrincipalProfile_ValidateSecretOrKeyvaultSecretRef(t *testing.T) {

	t.Run("ServicePrincipalProfile with secret should pass", func(t *testing.T) {
		p := getK8sDefaultProperties()

		if err := p.Validate(); err != nil {
			t.Errorf("should not error %v", err)
		}
	})

	t.Run("ServicePrincipalProfile with KeyvaultSecretRef (with version) should pass", func(t *testing.T) {
		p := getK8sDefaultProperties()
		p.ServicePrincipalProfile.Secret = ""
		p.ServicePrincipalProfile.KeyvaultSecretRef = &KeyvaultSecretRef{
			VaultID:       "/subscriptions/SUB-ID/resourceGroups/RG-NAME/providers/Microsoft.KeyVault/vaults/KV-NAME",
			SecretName:    "secret-name",
			SecretVersion: "version",
		}
		if err := p.Validate(); err != nil {
			t.Errorf("should not error %v", err)
		}
	})

	t.Run("ServicePrincipalProfile with KeyvaultSecretRef (without version) should pass", func(t *testing.T) {
		p := getK8sDefaultProperties()
		p.ServicePrincipalProfile.Secret = ""
		p.ServicePrincipalProfile.KeyvaultSecretRef = &KeyvaultSecretRef{
			VaultID:    "/subscriptions/SUB-ID/resourceGroups/RG-NAME/providers/Microsoft.KeyVault/vaults/KV-NAME",
			SecretName: "secret-name",
		}

		if err := p.Validate(); err != nil {
			t.Errorf("should not error %v", err)
		}
	})

	t.Run("ServicePrincipalProfile with Secret and KeyvaultSecretRef should NOT pass", func(t *testing.T) {
		p := getK8sDefaultProperties()
		p.ServicePrincipalProfile.Secret = "secret"
		p.ServicePrincipalProfile.KeyvaultSecretRef = &KeyvaultSecretRef{
			VaultID:    "/subscriptions/SUB-ID/resourceGroups/RG-NAME/providers/Microsoft.KeyVault/vaults/KV-NAME",
			SecretName: "secret-name",
		}

		if err := p.Validate(); err == nil {
			t.Error("error should have occurred")
		}
	})

	t.Run("ServicePrincipalProfile with incorrect KeyvaultSecretRef format should NOT pass", func(t *testing.T) {
		p := getK8sDefaultProperties()
		p.ServicePrincipalProfile.Secret = ""
		p.ServicePrincipalProfile.KeyvaultSecretRef = &KeyvaultSecretRef{
			VaultID:    "randomID",
			SecretName: "secret-name",
		}

		if err := p.Validate(); err == nil || err.Error() != "service principal client keyvault secret reference is of incorrect format" {
			t.Error("error should have occurred")
		}
	})
}

func getK8sDefaultProperties() *Properties {
	return &Properties{
		OrchestratorProfile: &OrchestratorProfile{
			OrchestratorType: Kubernetes,
		},
		MasterProfile: &MasterProfile{
			Count:     1,
			DNSPrefix: "foo",
			VMSize:    "Standard_DS2_v2",
		},
		AgentPoolProfiles: []*AgentPoolProfile{
			{
				Name:                "agentpool",
				VMSize:              "Standard_D2_v2",
				Count:               1,
				AvailabilityProfile: AvailabilitySet,
			},
		},
		LinuxProfile: &LinuxProfile{
			AdminUsername: "azureuser",
			SSH: struct {
				PublicKeys []PublicKey `json:"publicKeys" validate:"required,len=1"`
			}{
				PublicKeys: []PublicKey{{
					KeyData: "publickeydata",
				}},
			},
		},
		ServicePrincipalProfile: &ServicePrincipalProfile{
			ClientID: "clientID",
			Secret:   "clientSecret",
		},
	}
}
<|endoftext|>