1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #42683 from thaJeztah/remove_lcow_step6

Remove LCOW (step 6)
This commit is contained in:
Brian Goff 2021-07-29 11:34:29 -07:00 committed by GitHub
commit 51b06c6795
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 170 additions and 308 deletions

View file

@ -116,7 +116,7 @@ func TestFromScratch(t *testing.T) {
}
err := initializeStage(sb, cmd)
if runtime.GOOS == "windows" && !system.LCOWSupported() {
if runtime.GOOS == "windows" {
assert.Check(t, is.Error(err, "Linux containers are not supported on this system"))
return
}

View file

@ -264,12 +264,6 @@ func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error
// SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
func (container *Container) SetupWorkingDirectory(rootIdentity idtools.Identity) error {
// TODO: LCOW Support. This will need revisiting.
// We will need to do remote filesystem operations here.
if container.OS != runtime.GOOS {
return nil
}
if container.Config.WorkingDir == "" {
return nil
}
@ -727,14 +721,14 @@ func getConfigTargetPath(r *swarmtypes.ConfigReference) string {
// CreateDaemonEnvironment creates a new environment variable slice for this container.
func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string {
// Setup environment
os := container.OS
if os == "" {
os = runtime.GOOS
ctrOS := container.OS
if ctrOS == "" {
ctrOS = runtime.GOOS
}
// Figure out what size slice we need so we can allocate this all at once.
envSize := len(container.Config.Env)
if runtime.GOOS != "windows" || (runtime.GOOS == "windows" && os == "linux") {
if runtime.GOOS != "windows" {
envSize += 2 + len(linkedEnv)
}
if tty {
@ -743,7 +737,7 @@ func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string
env := make([]string, 0, envSize)
if runtime.GOOS != "windows" {
env = append(env, "PATH="+system.DefaultPathEnv(os))
env = append(env, "PATH="+system.DefaultPathEnv(ctrOS))
env = append(env, "HOSTNAME="+container.Config.Hostname)
if tty {
env = append(env, "TERM=xterm")

View file

@ -16,6 +16,7 @@ import (
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/runconfig"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/selinux/go-selinux"
@ -113,19 +114,17 @@ func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr
img *image.Image
imgID image.ID
err error
os = runtime.GOOS
)
os := runtime.GOOS
if opts.params.Config.Image != "" {
img, err = daemon.imageService.GetImage(opts.params.Config.Image, opts.params.Platform)
if err != nil {
return nil, err
}
if img.OS != "" {
os = img.OS
}
os = img.OperatingSystem()
imgID = img.ID()
if isWindows && img.OS == "linux" {
if !system.IsOSSupported(os) {
return nil, errors.New("operating system on which parent image was created is not Windows")
}
} else if isWindows {

View file

@ -3,7 +3,6 @@ package daemon // import "github.com/docker/docker/daemon"
import (
"context"
"fmt"
"runtime"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/container"
@ -14,19 +13,9 @@ import (
// createContainerOSSpecificSettings performs host-OS specific container create functionality
func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
if container.OS == runtime.GOOS {
if containertypes.Isolation.IsDefault(hostConfig.Isolation) {
// Make sure the host config has the default daemon isolation if not specified by caller.
if containertypes.Isolation.IsDefault(containertypes.Isolation(hostConfig.Isolation)) {
hostConfig.Isolation = daemon.defaultIsolation
}
} else {
// LCOW must be a Hyper-V container as you can't run a shared kernel when one
// is a Windows kernel, the other is a Linux kernel.
if containertypes.Isolation.IsProcess(containertypes.Isolation(hostConfig.Isolation)) {
return fmt.Errorf("process isolation is invalid for Linux containers on Windows")
}
hostConfig.Isolation = "hyperv"
hostConfig.Isolation = daemon.defaultIsolation
}
parser := volumemounts.NewParser()
for spec := range config.Volumes {

View file

@ -510,17 +510,11 @@ func (daemon *Daemon) conditionalMountOnStart(container *container.Container) er
// conditionalUnmountOnCleanup is a platform specific helper function called
// during the cleanup of a container to unmount.
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
// Bail out now for Linux containers
if system.LCOWSupported() && container.OS != "windows" {
if daemon.runAsHyperVContainer(container.HostConfig) {
// We do not unmount if a Hyper-V container
return nil
}
// We do not unmount if a Hyper-V container
if !daemon.runAsHyperVContainer(container.HostConfig) {
return daemon.Unmount(container)
}
return nil
return daemon.Unmount(container)
}
func driverOptions(config *config.Config) []nwconfig.Option {

View file

@ -5,7 +5,6 @@ import (
"fmt"
"io/ioutil"
"path/filepath"
"runtime"
"strings"
"github.com/Microsoft/hcsshim/osversion"
@ -13,7 +12,6 @@ import (
"github.com/docker/docker/container"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/oci"
"github.com/docker/docker/oci/caps"
"github.com/docker/docker/pkg/sysinfo"
"github.com/docker/docker/pkg/system"
specs "github.com/opencontainers/runtime-spec/specs-go"
@ -33,8 +31,11 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
if err != nil {
return nil, err
}
if !system.IsOSSupported(img.OperatingSystem()) {
return nil, system.ErrNotSupportedOperatingSystem
}
s := oci.DefaultOSSpec(img.OS)
s := oci.DefaultSpec()
linkedEnv, err := daemon.setupLinkedContainers(c)
if err != nil {
@ -116,11 +117,6 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
if !mount.Writable {
m.Options = append(m.Options, "ro")
}
if img.OS != runtime.GOOS {
m.Type = "bind"
m.Options = append(m.Options, "rbind")
m.Options = append(m.Options, fmt.Sprintf("uvmpath=/tmp/gcs/%s/binds", c.ID))
}
s.Mounts = append(s.Mounts, m)
}
@ -200,20 +196,8 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
NetworkSharedContainerName: networkSharedContainerID,
}
switch img.OS {
case "windows":
if err := daemon.createSpecWindowsFields(c, &s, isHyperV); err != nil {
return nil, err
}
case "linux":
if !system.LCOWSupported() {
return nil, fmt.Errorf("Linux containers on Windows are not supported")
}
if err := daemon.createSpecLinuxFields(c, &s); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("Unsupported platform %q", img.OS)
if err := daemon.createSpecWindowsFields(c, &s, isHyperV); err != nil {
return nil, err
}
if logrus.IsLevelEnabled(logrus.DebugLevel) {
@ -222,7 +206,7 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
}
}
return (*specs.Spec)(&s), nil
return &s, nil
}
// Sets the Windows-specific fields of the OCI spec
@ -370,41 +354,6 @@ func (daemon *Daemon) setWindowsCredentialSpec(c *container.Container, s *specs.
return nil
}
// Sets the Linux-specific fields of the OCI spec
// TODO: LCOW Support. We need to do a lot more pulling in what can
// be pulled in from oci_linux.go.
func (daemon *Daemon) createSpecLinuxFields(c *container.Container, s *specs.Spec) error {
s.Root = &specs.Root{
Path: "rootfs",
Readonly: c.HostConfig.ReadonlyRootfs,
}
s.Hostname = c.Config.Hostname
setLinuxDomainname(c, s)
if len(s.Process.Cwd) == 0 {
s.Process.Cwd = `/`
}
s.Process.Args = append([]string{c.Path}, c.Args...)
// Note these are against the UVM.
setResourcesInSpec(c, s, true) // LCOW is Hyper-V only
capabilities, err := caps.TweakCapabilities(caps.DefaultCapabilities(), c.HostConfig.CapAdd, c.HostConfig.CapDrop, c.HostConfig.Privileged)
if err != nil {
return fmt.Errorf("linux spec capabilities: %v", err)
}
if err := oci.SetCapabilities(s, capabilities); err != nil {
return fmt.Errorf("linux spec capabilities: %v", err)
}
devPermissions, err := oci.AppendDevicePermissionsFromCgroupRules(nil, c.HostConfig.DeviceCgroupRules)
if err != nil {
return fmt.Errorf("linux runtime spec devices: %v", err)
}
s.Linux.Resources.Devices = devPermissions
return nil
}
func setResourcesInSpec(c *container.Container, s *specs.Spec, isHyperV bool) {
// In s.Windows.Resources
cpuShares := uint16(c.HostConfig.CPUShares)

View file

@ -3,7 +3,6 @@ package distribution // import "github.com/docker/docker/distribution"
import (
"context"
"encoding/json"
"fmt"
"io"
"runtime"
@ -21,6 +20,7 @@ import (
"github.com/docker/libtrust"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// Config stores configuration for communicating
@ -155,20 +155,12 @@ func (s *imageConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error)
return nil, err
}
// fail immediately on Windows when downloading a non-Windows image
// and vice versa. Exception on Windows if Linux Containers are enabled.
if runtime.GOOS == "windows" && unmarshalledConfig.OS == "linux" && !system.LCOWSupported() {
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
} else if runtime.GOOS != "windows" && unmarshalledConfig.OS == "windows" {
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
}
os := unmarshalledConfig.OS
if os == "" {
os = runtime.GOOS
}
if !system.IsOSSupported(os) {
return nil, system.ErrNotSupportedOperatingSystem
return nil, errors.Wrapf(system.ErrNotSupportedOperatingSystem, "image operating system %q cannot be used on this platform", os)
}
return &specs.Platform{OS: os, Architecture: unmarshalledConfig.Architecture, Variant: unmarshalledConfig.Variant, OSVersion: unmarshalledConfig.OSVersion}, nil
}

View file

@ -8,7 +8,6 @@ import (
"io/ioutil"
"os"
"runtime"
"strings"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/platforms"
@ -487,6 +486,14 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform
}
func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
if platform != nil {
// Early bath if the requested OS doesn't match that of the configuration.
// This avoids doing the download, only to potentially fail later.
if !system.IsOSSupported(platform.OS) {
return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", runtime.GOOS, platform.OS)
}
}
var verifiedManifest *schema1.Manifest
verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
if err != nil {
@ -541,44 +548,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
descriptors = append(descriptors, layerDescriptor)
}
// The v1 manifest itself doesn't directly contain an OS. However,
// the history does, but unfortunately that's a string, so search through
// all the history until hopefully we find one which indicates the OS.
// supertest2014/nyan is an example of a registry image with schemav1.
configOS := runtime.GOOS
if system.LCOWSupported() {
type config struct {
Os string `json:"os,omitempty"`
}
for _, v := range verifiedManifest.History {
var c config
if err := json.Unmarshal([]byte(v.V1Compatibility), &c); err == nil {
if c.Os != "" {
configOS = c.Os
break
}
}
}
}
// In the situation that the API call didn't specify an OS explicitly, but
// we support the operating system, switch to that operating system.
// eg FROM supertest2014/nyan with no platform specifier, and docker build
// with no --platform= flag under LCOW.
requestedOS := ""
if platform != nil {
requestedOS = platform.OS
} else if system.IsOSSupported(configOS) {
requestedOS = configOS
}
// Early bath if the requested OS doesn't match that of the configuration.
// This avoids doing the download, only to potentially fail later.
if !strings.EqualFold(configOS, requestedOS) {
return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS)
}
resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, configOS, descriptors, p.config.ProgressOutput)
resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, runtime.GOOS, descriptors, p.config.ProgressOutput)
if err != nil {
return "", "", err
}

View file

@ -61,7 +61,6 @@ Write-Host -ForegroundColor Red "-----------------------------------------------
# DOCKER_STORAGE_OPTS comma-separated list of optional storage driver options for the daemon under test
# examples:
# DOCKER_STORAGE_OPTS="size=40G"
# DOCKER_STORAGE_OPTS="lcow.globalmode=false,lcow.kernel=kernel.efi"
#
# SKIP_VALIDATION_TESTS if defined skips the validation tests
#
@ -191,7 +190,7 @@ Function Nuke-Everything {
Stop-Process -name "tail" -Force -ErrorAction SilentlyContinue 2>&1 | Out-Null
# Detach any VHDs
gwmi msvm_mountedstorageimage -namespace root/virtualization/v2 -ErrorAction SilentlyContinue | foreach-object {$_.DetachVirtualHardDisk() }
gwmi msvm_mountedstorageimage -namespace root/virtualization/v2 -ErrorAction SilentlyContinue | foreach-Object {$_.DetachVirtualHardDisk() }
# Stop any compute processes
Get-ComputeProcess | Stop-ComputeProcess -Force
@ -602,10 +601,10 @@ Try {
}
# Arguments: Allow setting optional storage-driver options
# example usage: DOCKER_STORAGE_OPTS="lcow.globalmode=false,lcow.kernel=kernel.efi"
# example usage: DDOCKER_STORAGE_OPTS="size=40G"
if (-not ("$env:DOCKER_STORAGE_OPTS" -eq "")) {
Write-Host -ForegroundColor Green "INFO: Running the daemon under test with storage-driver options ${env:DOCKER_STORAGE_OPTS}"
$env:DOCKER_STORAGE_OPTS.Split(",") | ForEach {
$env:DOCKER_STORAGE_OPTS.Split(",") | ForEach-Object {
$dutArgs += "--storage-opt $_"
}
}

View file

@ -217,9 +217,7 @@ func (s *saveSession) save(outStream io.Writer) error {
for _, l := range imageDescr.layers {
// IMPORTANT: We use path, not filepath here to ensure the layers
// in the manifest use Unix-style forward-slashes. Otherwise, a
// Linux image saved from LCOW won't be able to be imported on
// LCOL.
// in the manifest use Unix-style forward-slashes.
layers = append(layers, path.Join(l, legacyLayerFileName))
}

View file

@ -14,12 +14,7 @@ func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
// DefaultSpec returns the default spec used by docker for the current Platform
func DefaultSpec() specs.Spec {
return DefaultOSSpec(runtime.GOOS)
}
// DefaultOSSpec returns the spec for a given OS
func DefaultOSSpec(osName string) specs.Spec {
if osName == "windows" {
if runtime.GOOS == "windows" {
return DefaultWindowsSpec()
}
return DefaultLinuxSpec()
@ -37,7 +32,7 @@ func DefaultWindowsSpec() specs.Spec {
// DefaultLinuxSpec create a default spec for running Linux containers
func DefaultLinuxSpec() specs.Spec {
s := specs.Spec{
return specs.Spec{
Version: specs.Version,
Process: &specs.Process{
Capabilities: &specs.LinuxCapabilities{
@ -48,147 +43,139 @@ func DefaultLinuxSpec() specs.Spec {
},
},
Root: &specs.Root{},
}
s.Mounts = []specs.Mount{
{
Destination: "/proc",
Type: "proc",
Source: "proc",
Options: []string{"nosuid", "noexec", "nodev"},
Mounts: []specs.Mount{
{
Destination: "/proc",
Type: "proc",
Source: "proc",
Options: []string{"nosuid", "noexec", "nodev"},
},
{
Destination: "/dev",
Type: "tmpfs",
Source: "tmpfs",
Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
},
{
Destination: "/dev/pts",
Type: "devpts",
Source: "devpts",
Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
},
{
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
Options: []string{"nosuid", "noexec", "nodev", "ro"},
},
{
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Source: "cgroup",
Options: []string{"ro", "nosuid", "noexec", "nodev"},
},
{
Destination: "/dev/mqueue",
Type: "mqueue",
Source: "mqueue",
Options: []string{"nosuid", "noexec", "nodev"},
},
{
Destination: "/dev/shm",
Type: "tmpfs",
Source: "shm",
Options: []string{"nosuid", "noexec", "nodev", "mode=1777"},
},
},
{
Destination: "/dev",
Type: "tmpfs",
Source: "tmpfs",
Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
},
{
Destination: "/dev/pts",
Type: "devpts",
Source: "devpts",
Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
},
{
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
Options: []string{"nosuid", "noexec", "nodev", "ro"},
},
{
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Source: "cgroup",
Options: []string{"ro", "nosuid", "noexec", "nodev"},
},
{
Destination: "/dev/mqueue",
Type: "mqueue",
Source: "mqueue",
Options: []string{"nosuid", "noexec", "nodev"},
},
{
Destination: "/dev/shm",
Type: "tmpfs",
Source: "shm",
Options: []string{"nosuid", "noexec", "nodev", "mode=1777"},
},
}
s.Linux = &specs.Linux{
MaskedPaths: []string{
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware",
},
ReadonlyPaths: []string{
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger",
},
Namespaces: []specs.LinuxNamespace{
{Type: "mount"},
{Type: "network"},
{Type: "uts"},
{Type: "pid"},
{Type: "ipc"},
},
// Devices implicitly contains the following devices:
// null, zero, full, random, urandom, tty, console, and ptmx.
// ptmx is a bind mount or symlink of the container's ptmx.
// See also: https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#default-devices
Devices: []specs.LinuxDevice{},
Resources: &specs.LinuxResources{
Devices: []specs.LinuxDeviceCgroup{
{
Allow: false,
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(5),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(3),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(9),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(8),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(5),
Minor: iPtr(0),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(5),
Minor: iPtr(1),
Access: "rwm",
},
{
Allow: false,
Type: "c",
Major: iPtr(10),
Minor: iPtr(229),
Access: "rwm",
Linux: &specs.Linux{
MaskedPaths: []string{
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware",
},
ReadonlyPaths: []string{
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger",
},
Namespaces: []specs.LinuxNamespace{
{Type: "mount"},
{Type: "network"},
{Type: "uts"},
{Type: "pid"},
{Type: "ipc"},
},
// Devices implicitly contains the following devices:
// null, zero, full, random, urandom, tty, console, and ptmx.
// ptmx is a bind mount or symlink of the container's ptmx.
// See also: https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#default-devices
Devices: []specs.LinuxDevice{},
Resources: &specs.LinuxResources{
Devices: []specs.LinuxDeviceCgroup{
{
Allow: false,
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(5),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(3),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(9),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(8),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(5),
Minor: iPtr(0),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(5),
Minor: iPtr(1),
Access: "rwm",
},
{
Allow: false,
Type: "c",
Major: iPtr(10),
Minor: iPtr(229),
Access: "rwm",
},
},
},
},
}
// For LCOW support, populate a blank Windows spec
if runtime.GOOS == "windows" {
s.Windows = &specs.Windows{}
}
return s
}

View file

@ -374,9 +374,6 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read
return rebased
}
// TODO @gupta-ak. These might have to be changed in the future to be
// continuity driver aware as well to support LCOW.
// CopyResource performs an archive copy from the given source path to the
// given destination path. The source path MUST exist and the destination
// path's parent directory must exist.

View file

@ -28,8 +28,7 @@ type ContainerFS interface {
// Driver combines both continuity's Driver and PathDriver interfaces with a Platform
// field to determine the OS.
type Driver interface {
// OS returns the OS where the rootfs is located. Essentially,
// runtime.GOOS for everything aside from LCOW, which is "linux"
// OS returns the OS where the rootfs is located. Essentially, runtime.GOOS.
OS() string
// Architecture returns the hardware architecture where the

View file

@ -4,11 +4,6 @@ import (
"strings"
)
// LCOWSupported returns true if Linux containers on Windows are supported.
func LCOWSupported() bool {
return false
}
// IsOSSupported determines if an operating system is supported by the host.
func IsOSSupported(os string) bool {
return strings.EqualFold(runtime.GOOS, os)