// +build !windows

package main

import (
	"bytes"
	"io/ioutil"
	"os/exec"
	"strings"

	"github.com/docker/docker/pkg/parsers/kernel"
	"github.com/docker/docker/pkg/sysinfo"
)

var (
	// SysInfo stores information about which features a kernel supports.
	SysInfo      *sysinfo.SysInfo
	cpuCfsPeriod = testRequirement{
		func() bool {
			return SysInfo.CPUCfsPeriod
		},
		"Test requires an environment that supports cgroup cfs period.",
	}
	cpuCfsQuota = testRequirement{
		func() bool {
			return SysInfo.CPUCfsQuota
		},
		"Test requires an environment that supports cgroup cfs quota.",
	}
	cpuShare = testRequirement{
		func() bool {
			return SysInfo.CPUShares
		},
		"Test requires an environment that supports cgroup cpu shares.",
	}
	oomControl = testRequirement{
		func() bool {
			return SysInfo.OomKillDisable
		},
		"Test requires Oom control enabled.",
	}
	pidsLimit = testRequirement{
		func() bool {
			return SysInfo.PidsLimit
		},
		"Test requires pids limit enabled.",
	}
	kernelMemorySupport = testRequirement{
		func() bool {
			return SysInfo.KernelMemory
		},
		"Test requires an environment that supports cgroup kernel memory.",
	}
	memoryLimitSupport = testRequirement{
		func() bool {
			return SysInfo.MemoryLimit
		},
		"Test requires an environment that supports cgroup memory limit.",
	}
	memoryReservationSupport = testRequirement{
		func() bool {
			return SysInfo.MemoryReservation
		},
		"Test requires an environment that supports cgroup memory reservation.",
	}
	swapMemorySupport = testRequirement{
		func() bool {
			return SysInfo.SwapLimit
		},
		"Test requires an environment that supports cgroup swap memory limit.",
	}
	memorySwappinessSupport = testRequirement{
		func() bool {
			return SysInfo.MemorySwappiness
		},
		"Test requires an environment that supports cgroup memory swappiness.",
	}
	blkioWeight = testRequirement{
		func() bool {
			return SysInfo.BlkioWeight
		},
		"Test requires an environment that supports blkio weight.",
	}
	cgroupCpuset = testRequirement{
		func() bool {
			return SysInfo.Cpuset
		},
		"Test requires an environment that supports cgroup cpuset.",
	}
	seccompEnabled = testRequirement{
		func() bool {
			return supportsSeccomp && SysInfo.Seccomp
		},
		"Test requires that seccomp support be enabled in the daemon.",
	}
	bridgeNfIptables = testRequirement{
		func() bool {
			return !SysInfo.BridgeNFCallIPTablesDisabled
		},
		"Test requires that bridge-nf-call-iptables support be enabled in the daemon.",
	}
	bridgeNfIP6tables = testRequirement{
		func() bool {
			return !SysInfo.BridgeNFCallIP6TablesDisabled
		},
		"Test requires that bridge-nf-call-ip6tables support be enabled in the daemon.",
	}
	unprivilegedUsernsClone = testRequirement{
		func() bool {
			content, err := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
			if err == nil && strings.Contains(string(content), "0") {
				return false
			}
			return true
		},
		"Test cannot be run with 'sysctl kernel.unprivileged_userns_clone' = 0",
	}
	ambientCapabilities = testRequirement{
		func() bool {
			content, err := ioutil.ReadFile("/proc/self/status")
			if err == nil && strings.Contains(string(content), "CapAmb:") {
				return true
			}
			return false
		},
		"Test cannot be run without a kernel (4.3+) supporting ambient capabilities",
	}
	overlayFSSupported = testRequirement{
		func() bool {
			cmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "/bin/sh", "-c", "cat /proc/filesystems")
			out, err := cmd.CombinedOutput()
			if err != nil {
				return false
			}
			return bytes.Contains(out, []byte("overlay\n"))
		},
		"Test cannot be run without suppport for overlayfs",
	}
	overlay2Supported = testRequirement{
		func() bool {
			if !overlayFSSupported.Condition() {
				return false
			}

			daemonV, err := kernel.ParseRelease(daemonKernelVersion)
			if err != nil {
				return false
			}
			requiredV := kernel.VersionInfo{Kernel: 4}
			return kernel.CompareKernelVersion(*daemonV, requiredV) > -1

		},
		"Test cannot be run without overlay2 support (kernel 4.0+)",
	}
)

func init() {
	SysInfo = sysinfo.New(true)
}