Browse Source

Merge pull request #43254 from thaJeztah/windows_rs5_minimum

Windows: require Windows Server RS5 / ltsc2019 (build 17763) as minimum
Sebastiaan van Stijn 3 years ago
parent
commit
dc8fb8f03b

+ 1 - 10
cmd/dockerd/service_windows.go

@@ -12,7 +12,6 @@ import (
 	"time"
 	"unsafe"
 
-	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/pflag"
 	"golang.org/x/sys/windows"
@@ -167,19 +166,11 @@ func registerService() error {
 	}
 	defer m.Disconnect()
 
-	depends := []string{}
-
-	// This dependency is required on build 14393 (RS1)
-	// it is added to the platform in newer builds
-	if osversion.Build() == osversion.RS1 {
-		depends = append(depends, "ConDrv")
-	}
-
 	c := mgr.Config{
 		ServiceType:  windows.SERVICE_WIN32_OWN_PROCESS,
 		StartType:    mgr.StartAutomatic,
 		ErrorControl: mgr.ErrorNormal,
-		Dependencies: depends,
+		Dependencies: []string{},
 		DisplayName:  "Docker Engine",
 	}
 

+ 8 - 38
daemon/daemon_windows.go

@@ -118,15 +118,6 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, isHyp
 		return warnings, fmt.Errorf("range of CPUs is from 0.01 to %d.00, as there are only %d CPUs available", sysinfo.NumCPU(), sysinfo.NumCPU())
 	}
 
-	if resources.NanoCPUs > 0 && isHyperv && osversion.Build() < osversion.RS3 {
-		leftoverNanoCPUs := resources.NanoCPUs % 1e9
-		if leftoverNanoCPUs != 0 && resources.NanoCPUs > 1e9 {
-			resources.NanoCPUs = ((resources.NanoCPUs + 1e9/2) / 1e9) * 1e9
-			warningString := fmt.Sprintf("Your current OS version does not support Hyper-V containers with NanoCPUs greater than 1000000000 but not divisible by 1000000000. NanoCPUs rounded to %d", resources.NanoCPUs)
-			warnings = append(warnings, warningString)
-		}
-	}
-
 	if len(resources.BlkioDeviceReadBps) > 0 {
 		return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceReadBps")
 	}
@@ -187,19 +178,7 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
 	if hostConfig == nil {
 		return nil, nil
 	}
-	hyperv := daemon.runAsHyperVContainer(hostConfig)
-
-	// On RS5, we allow (but don't strictly support) process isolation on Client SKUs.
-	// Prior to RS5, we don't allow process isolation on Client SKUs.
-	// @engine maintainers. This block should not be removed. It partially enforces licensing
-	// restrictions on Windows. Ping Microsoft folks if there are concerns or PRs to change this.
-	if !hyperv && system.IsWindowsClient() && osversion.Build() < osversion.RS5 {
-		return warnings, fmt.Errorf("Windows client operating systems earlier than version 1809 can only run Hyper-V containers")
-	}
-
-	w, err := verifyPlatformContainerResources(&hostConfig.Resources, hyperv)
-	warnings = append(warnings, w...)
-	return warnings, err
+	return verifyPlatformContainerResources(&hostConfig.Resources, daemon.runAsHyperVContainer(hostConfig))
 }
 
 // verifyDaemonSettings performs validation of daemon config struct
@@ -211,11 +190,8 @@ func verifyDaemonSettings(config *config.Config) error {
 func checkSystem() error {
 	// Validate the OS version. Note that dockerd.exe must be manifested for this
 	// call to return the correct version.
-	if osversion.Get().MajorVersion < 10 {
-		return fmt.Errorf("This version of Windows does not support the docker daemon")
-	}
-	if osversion.Build() < osversion.RS1 {
-		return fmt.Errorf("The docker daemon requires build 14393 or later of Windows Server 2016 or Windows 10")
+	if osversion.Get().MajorVersion < 10 || osversion.Build() < osversion.RS5 {
+		return fmt.Errorf("this version of Windows does not support the docker daemon (Windows build %d or higher is required)", osversion.RS5)
 	}
 
 	vmcompute := windows.NewLazySystemDLL("vmcompute.dll")
@@ -574,13 +550,13 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
 // setDefaultIsolation determine the default isolation mode for the
 // daemon to run in. This is only applicable on Windows
 func (daemon *Daemon) setDefaultIsolation() error {
-	daemon.defaultIsolation = containertypes.Isolation("process")
-
 	// On client SKUs, default to Hyper-V. @engine maintainers. This
 	// should not be removed. Ping Microsoft folks is there are PRs to
 	// to change this.
 	if system.IsWindowsClient() {
-		daemon.defaultIsolation = containertypes.Isolation("hyperv")
+		daemon.defaultIsolation = containertypes.IsolationHyperV
+	} else {
+		daemon.defaultIsolation = containertypes.IsolationProcess
 	}
 	for _, option := range daemon.configStore.ExecOptions {
 		key, val, err := parsers.ParseKeyValueOpt(option)
@@ -595,16 +571,10 @@ func (daemon *Daemon) setDefaultIsolation() error {
 				return fmt.Errorf("Invalid exec-opt value for 'isolation':'%s'", val)
 			}
 			if containertypes.Isolation(val).IsHyperV() {
-				daemon.defaultIsolation = containertypes.Isolation("hyperv")
+				daemon.defaultIsolation = containertypes.IsolationHyperV
 			}
 			if containertypes.Isolation(val).IsProcess() {
-				if system.IsWindowsClient() && osversion.Build() < osversion.RS5 {
-					// On RS5, we allow (but don't strictly support) process isolation on Client SKUs.
-					// @engine maintainers. This block should not be removed. It partially enforces licensing
-					// restrictions on Windows. Ping Microsoft folks if there are concerns or PRs to change this.
-					return fmt.Errorf("Windows client operating systems earlier than version 1809 can only run Hyper-V containers")
-				}
-				daemon.defaultIsolation = containertypes.Isolation("process")
+				daemon.defaultIsolation = containertypes.IsolationProcess
 			}
 		default:
 			return fmt.Errorf("Unrecognised exec-opt '%s'\n", key)

+ 5 - 8
daemon/info.go

@@ -253,14 +253,11 @@ func operatingSystem() (operatingSystem string) {
 	} else {
 		operatingSystem = s
 	}
-	// Don't do containerized check on Windows
-	if runtime.GOOS != "windows" {
-		if inContainer, err := operatingsystem.IsContainerized(); err != nil {
-			logrus.Errorf("Could not determine if daemon is containerized: %v", err)
-			operatingSystem += " (error determining if containerized)"
-		} else if inContainer {
-			operatingSystem += " (containerized)"
-		}
+	if inContainer, err := operatingsystem.IsContainerized(); err != nil {
+		logrus.Errorf("Could not determine if daemon is containerized: %v", err)
+		operatingSystem += " (error determining if containerized)"
+	} else if inContainer {
+		operatingSystem += " (containerized)"
 	}
 
 	return operatingSystem

+ 0 - 4
daemon/oci_windows.go

@@ -7,7 +7,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/Microsoft/hcsshim/osversion"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
@@ -260,9 +259,6 @@ func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.S
 		if isHyperV {
 			return errors.New("device assignment is not supported for HyperV containers")
 		}
-		if osversion.Build() < osversion.RS5 {
-			return errors.New("device assignment requires Windows builds RS5 (17763+) or later")
-		}
 		for _, deviceMapping := range c.HostConfig.Devices {
 			srcParts := strings.SplitN(deviceMapping.PathOnHost, "/", 2)
 			if len(srcParts) != 2 {

+ 0 - 3
integration-cli/docker_api_containers_windows_test.go

@@ -12,7 +12,6 @@ import (
 	"testing"
 
 	winio "github.com/Microsoft/go-winio"
-	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/mount"
@@ -22,8 +21,6 @@ import (
 )
 
 func (s *DockerSuite) TestContainersAPICreateMountsBindNamedPipe(c *testing.T) {
-	testRequires(c, testEnv.IsLocalDaemon, DaemonIsWindowsAtLeastBuild(osversion.RS3)) // Named pipe support was added in RS3
-
 	// Create a host pipe to map into the container
 	hostPipeName := fmt.Sprintf(`\\.\pipe\docker-cli-test-pipe-%x`, rand.Uint64())
 	pc := &winio.PipeConfig{

+ 0 - 26
integration-cli/docker_api_images_test.go

@@ -4,18 +4,14 @@ import (
 	"context"
 	"net/http"
 	"net/http/httptest"
-	"runtime"
-	"strconv"
 	"strings"
 	"testing"
 
-	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/docker/docker/pkg/parsers/kernel"
 	"github.com/docker/docker/testutil/request"
 	"gotest.tools/v3/assert"
 )
@@ -59,17 +55,6 @@ func (s *DockerSuite) TestAPIImagesFilter(c *testing.T) {
 }
 
 func (s *DockerSuite) TestAPIImagesSaveAndLoad(c *testing.T) {
-	if runtime.GOOS == "windows" {
-		// Note we parse kernel.GetKernelVersion rather than osversion.Build()
-		// as test binaries aren't manifested, so would otherwise report build 9200.
-		v, err := kernel.GetKernelVersion()
-		assert.NilError(c, err)
-		buildNumber, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
-		if buildNumber <= osversion.RS3 {
-			c.Skip("Temporarily disabled on RS3 and older because they are too slow. See #39909")
-		}
-	}
-
 	testRequires(c, Network)
 	buildImageSuccessfully(c, "saveandload", build.WithDockerfile("FROM busybox\nENV FOO bar"))
 	id := getIDByName(c, "saveandload")
@@ -141,17 +126,6 @@ func (s *DockerSuite) TestAPIImagesHistory(c *testing.T) {
 }
 
 func (s *DockerSuite) TestAPIImagesImportBadSrc(c *testing.T) {
-	if runtime.GOOS == "windows" {
-		// Note we parse kernel.GetKernelVersion rather than osversion.Build()
-		// as test binaries aren't manifested, so would otherwise report build 9200.
-		v, err := kernel.GetKernelVersion()
-		assert.NilError(c, err)
-		buildNumber, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
-		if buildNumber == osversion.RS3 {
-			c.Skip("Temporarily disabled on RS3 builds")
-		}
-	}
-
 	testRequires(c, Network, testEnv.IsLocalDaemon)
 
 	server := httptest.NewServer(http.NewServeMux())

+ 1 - 1
integration-cli/docker_cli_create_test.go

@@ -303,7 +303,7 @@ func (s *DockerSuite) TestCreateWithWorkdir(c *testing.T) {
 	// Windows does not create the workdir until the container is started
 	if testEnv.OSType == "windows" {
 		dockerCmd(c, "start", name)
-		if IsolationIsHyperv() {
+		if testEnv.DaemonInfo.Isolation.IsHyperV() {
 			// Hyper-V isolated containers do not allow file-operations on a
 			// running container. This test currently uses `docker cp` to verify
 			// that the WORKDIR was automatically created, which cannot be done

+ 2 - 2
integration-cli/docker_cli_restart_test.go

@@ -171,7 +171,7 @@ func (s *DockerSuite) TestRestartContainerSuccess(c *testing.T) {
 	// such that it assumes there is a host process to kill. In Hyper-V
 	// containers, the process is inside the utility VM, not on the host.
 	if DaemonIsWindows() {
-		testRequires(c, IsolationIsProcess)
+		testRequires(c, testEnv.DaemonInfo.Isolation.IsProcess)
 	}
 
 	out := runSleepingContainer(c, "-d", "--restart=always")
@@ -247,7 +247,7 @@ func (s *DockerSuite) TestRestartPolicyAfterRestart(c *testing.T) {
 	// such that it assumes there is a host process to kill. In Hyper-V
 	// containers, the process is inside the utility VM, not on the host.
 	if DaemonIsWindows() {
-		testRequires(c, IsolationIsProcess)
+		testRequires(c, testEnv.DaemonInfo.Isolation.IsProcess)
 	}
 
 	out := runSleepingContainer(c, "-d", "--restart=always")

+ 3 - 9
integration-cli/docker_cli_run_test.go

@@ -22,7 +22,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
@@ -1877,11 +1876,6 @@ func (s *DockerSuite) TestRunBindMounts(c *testing.T) {
 		testRequires(c, DaemonIsLinux, NotUserNamespace)
 	}
 
-	if testEnv.OSType == "windows" {
-		// Disabled prior to RS5 due to how volumes are mapped
-		testRequires(c, DaemonIsWindowsAtLeastBuild(osversion.RS5))
-	}
-
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 
 	tmpDir, err := os.MkdirTemp("", "docker-test-container")
@@ -4105,7 +4099,7 @@ func (s *DockerSuite) TestRunWindowsWithCPUPercent(c *testing.T) {
 }
 
 func (s *DockerSuite) TestRunProcessIsolationWithCPUCountCPUSharesAndCPUPercent(c *testing.T) {
-	testRequires(c, IsolationIsProcess)
+	testRequires(c, DaemonIsWindows, testEnv.DaemonInfo.Isolation.IsProcess)
 
 	out, _ := dockerCmd(c, "run", "--cpu-count=1", "--cpu-shares=1000", "--cpu-percent=80", "--name", "test", "busybox", "echo", "testing")
 	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "WARNING: Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded"))
@@ -4122,7 +4116,7 @@ func (s *DockerSuite) TestRunProcessIsolationWithCPUCountCPUSharesAndCPUPercent(
 }
 
 func (s *DockerSuite) TestRunHypervIsolationWithCPUCountCPUSharesAndCPUPercent(c *testing.T) {
-	testRequires(c, IsolationIsHyperv)
+	testRequires(c, DaemonIsWindows, testEnv.DaemonInfo.Isolation.IsHyperV)
 
 	out, _ := dockerCmd(c, "run", "--cpu-count=1", "--cpu-shares=1000", "--cpu-percent=80", "--name", "test", "busybox", "echo", "testing")
 	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "testing"))
@@ -4413,7 +4407,7 @@ func (s *DockerSuite) TestRunAddDeviceCgroupRule(c *testing.T) {
 
 // Verifies that running as local system is operating correctly on Windows
 func (s *DockerSuite) TestWindowsRunAsSystem(c *testing.T) {
-	testRequires(c, DaemonIsWindowsAtLeastBuild(osversion.RS3))
+	testRequires(c, DaemonIsWindows)
 	out, _ := dockerCmd(c, "run", "--net=none", `--user=nt authority\system`, "--hostname=XYZZY", minimalBaseImage(), "cmd", "/c", `@echo %USERNAME%`)
 	assert.Equal(c, strings.TrimSpace(out), "XYZZY$")
 }

+ 0 - 16
integration-cli/docker_cli_start_test.go

@@ -2,15 +2,11 @@ package main
 
 import (
 	"fmt"
-	"runtime"
-	"strconv"
 	"strings"
 	"testing"
 	"time"
 
-	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/docker/docker/integration-cli/cli"
-	"github.com/docker/docker/pkg/parsers/kernel"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/icmd"
 )
@@ -190,18 +186,6 @@ func (s *DockerSuite) TestStartAttachWithRename(c *testing.T) {
 }
 
 func (s *DockerSuite) TestStartReturnCorrectExitCode(c *testing.T) {
-	// Note we parse kernel.GetKernelVersion rather than system.GetOSVersion
-	// as test binaries aren't manifested, so would otherwise report the wrong
-	// build number.
-	if runtime.GOOS == "windows" {
-		v, err := kernel.GetKernelVersion()
-		assert.NilError(c, err)
-		build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
-		if build < osversion.RS3 {
-			c.Skip("FLAKY on Windows RS1, see #38521")
-		}
-	}
-
 	dockerCmd(c, "create", "--restart=on-failure:2", "--name", "withRestart", "busybox", "sh", "-c", "exit 11")
 	dockerCmd(c, "create", "--rm", "--name", "withRm", "busybox", "sh", "-c", "exit 12")
 

+ 1 - 25
integration-cli/requirements_test.go

@@ -6,7 +6,6 @@ import (
 	"net/http"
 	"os"
 	"os/exec"
-	"strconv"
 	"strings"
 	"testing"
 	"time"
@@ -27,17 +26,6 @@ func DaemonIsWindows() bool {
 	return testEnv.OSType == "windows"
 }
 
-func DaemonIsWindowsAtLeastBuild(buildNumber int) func() bool {
-	return func() bool {
-		if testEnv.OSType != "windows" {
-			return false
-		}
-		version := testEnv.DaemonInfo.KernelVersion
-		numVersion, _ := strconv.Atoi(strings.Split(version, " ")[1])
-		return numVersion >= buildNumber
-	}
-}
-
 func DaemonIsLinux() bool {
 	return testEnv.OSType == "linux"
 }
@@ -154,23 +142,11 @@ func UserNamespaceInKernel() bool {
 
 func IsPausable() bool {
 	if testEnv.OSType == "windows" {
-		return testEnv.DaemonInfo.Isolation == "hyperv"
+		return testEnv.DaemonInfo.Isolation.IsHyperV()
 	}
 	return true
 }
 
-func IsolationIs(expectedIsolation string) bool {
-	return testEnv.OSType == "windows" && string(testEnv.DaemonInfo.Isolation) == expectedIsolation
-}
-
-func IsolationIsHyperv() bool {
-	return IsolationIs("hyperv")
-}
-
-func IsolationIsProcess() bool {
-	return IsolationIs("process")
-}
-
 // RegistryHosting returns whether the host can host a registry (v2) or not
 func RegistryHosting() bool {
 	// for now registry binary is built only if we're running inside

+ 0 - 7
libcontainerd/local/local_windows.go

@@ -16,7 +16,6 @@ import (
 	"time"
 
 	"github.com/Microsoft/hcsshim"
-	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/containerd/containerd"
 	"github.com/containerd/containerd/cio"
 	containerderrdefs "github.com/containerd/containerd/errdefs"
@@ -305,9 +304,6 @@ func (c *client) createWindows(id string, spec *specs.Spec, runtimeOptions inter
 		}
 	}
 	configuration.MappedDirectories = mds
-	if len(mps) > 0 && osversion.Build() < osversion.RS3 {
-		return errors.New("named pipe mounts are not supported on this version of Windows")
-	}
 	configuration.MappedPipes = mps
 
 	if len(spec.Windows.Devices) > 0 {
@@ -315,9 +311,6 @@ func (c *client) createWindows(id string, spec *specs.Spec, runtimeOptions inter
 		if configuration.HvPartition {
 			return errors.New("device assignment is not supported for HyperV containers")
 		}
-		if osversion.Build() < osversion.RS5 {
-			return errors.New("device assignment requires Windows builds RS5 (17763+) or later")
-		}
 		for _, d := range spec.Windows.Devices {
 			configuration.AssignedDevices = append(configuration.AssignedDevices, hcsshim.AssignedDevice{InterfaceClassGUID: d.ID})
 		}

+ 25 - 27
libnetwork/drivers/windows/overlay/ov_endpoint_windows.go

@@ -155,42 +155,40 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
 
 	hnsEndpoint.Policies = append(hnsEndpoint.Policies, paPolicy)
 
-	if osversion.Build() > 16236 {
-		natPolicy, err := json.Marshal(hcsshim.PaPolicy{
-			Type: "OutBoundNAT",
-		})
-
-		if err != nil {
-			return err
-		}
+	natPolicy, err := json.Marshal(hcsshim.PaPolicy{
+		Type: "OutBoundNAT",
+	})
 
-		hnsEndpoint.Policies = append(hnsEndpoint.Policies, natPolicy)
+	if err != nil {
+		return err
+	}
 
-		epConnectivity, err := windows.ParseEndpointConnectivity(epOptions)
-		if err != nil {
-			return err
-		}
+	hnsEndpoint.Policies = append(hnsEndpoint.Policies, natPolicy)
 
-		ep.portMapping = epConnectivity.PortBindings
-		ep.portMapping, err = windows.AllocatePorts(n.portMapper, ep.portMapping, ep.addr.IP)
-		if err != nil {
-			return err
-		}
+	epConnectivity, err := windows.ParseEndpointConnectivity(epOptions)
+	if err != nil {
+		return err
+	}
 
-		defer func() {
-			if err != nil {
-				windows.ReleasePorts(n.portMapper, ep.portMapping)
-			}
-		}()
+	ep.portMapping = epConnectivity.PortBindings
+	ep.portMapping, err = windows.AllocatePorts(n.portMapper, ep.portMapping, ep.addr.IP)
+	if err != nil {
+		return err
+	}
 
-		pbPolicy, err := windows.ConvertPortBindings(ep.portMapping)
+	defer func() {
 		if err != nil {
-			return err
+			windows.ReleasePorts(n.portMapper, ep.portMapping)
 		}
-		hnsEndpoint.Policies = append(hnsEndpoint.Policies, pbPolicy...)
+	}()
 
-		ep.disablegateway = true
+	pbPolicy, err := windows.ConvertPortBindings(ep.portMapping)
+	if err != nil {
+		return err
 	}
+	hnsEndpoint.Policies = append(hnsEndpoint.Policies, pbPolicy...)
+
+	ep.disablegateway = true
 
 	configurationb, err := json.Marshal(hnsEndpoint)
 	if err != nil {

+ 0 - 4
libnetwork/drivers/windows/windows.go

@@ -21,7 +21,6 @@ import (
 	"sync"
 
 	"github.com/Microsoft/hcsshim"
-	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/discoverapi"
 	"github.com/docker/docker/libnetwork/driverapi"
@@ -218,9 +217,6 @@ func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string
 			}
 			config.VSID = uint(vsid)
 		case EnableOutboundNat:
-			if osversion.Build() <= 16236 {
-				return nil, fmt.Errorf("Invalid network option. OutboundNat is not supported on this OS version")
-			}
 			b, err := strconv.ParseBool(value)
 			if err != nil {
 				return nil, err

+ 90 - 95
libnetwork/service_windows.go

@@ -4,7 +4,6 @@ import (
 	"net"
 
 	"github.com/Microsoft/hcsshim"
-	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/sirupsen/logrus"
 )
 
@@ -23,98 +22,96 @@ func (n *network) addLBBackend(ip net.IP, lb *loadBalancer) {
 	vip := lb.vip
 	ingressPorts := lb.service.ingressPorts
 
-	if osversion.Build() > 16236 {
-		lb.Lock()
-		defer lb.Unlock()
-		//find the load balancer IP for the network.
-		var sourceVIP string
-		for _, e := range n.Endpoints() {
-			epInfo := e.Info()
-			if epInfo == nil {
-				continue
-			}
-			if epInfo.LoadBalancer() {
-				sourceVIP = epInfo.Iface().Address().IP.String()
-				break
-			}
+	lb.Lock()
+	defer lb.Unlock()
+	//find the load balancer IP for the network.
+	var sourceVIP string
+	for _, e := range n.Endpoints() {
+		epInfo := e.Info()
+		if epInfo == nil {
+			continue
 		}
-
-		if sourceVIP == "" {
-			logrus.Errorf("Failed to find load balancer IP for network %s", n.Name())
-			return
+		if epInfo.LoadBalancer() {
+			sourceVIP = epInfo.Iface().Address().IP.String()
+			break
 		}
+	}
 
-		var endpoints []hcsshim.HNSEndpoint
+	if sourceVIP == "" {
+		logrus.Errorf("Failed to find load balancer IP for network %s", n.Name())
+		return
+	}
 
-		for eid, be := range lb.backEnds {
-			if be.disabled {
-				continue
-			}
-			//Call HNS to get back ID (GUID) corresponding to the endpoint.
-			hnsEndpoint, err := hcsshim.GetHNSEndpointByName(eid)
-			if err != nil {
-				logrus.Errorf("Failed to find HNS ID for endpoint %v: %v", eid, err)
-				return
-			}
+	var endpoints []hcsshim.HNSEndpoint
 
-			endpoints = append(endpoints, *hnsEndpoint)
+	for eid, be := range lb.backEnds {
+		if be.disabled {
+			continue
+		}
+		//Call HNS to get back ID (GUID) corresponding to the endpoint.
+		hnsEndpoint, err := hcsshim.GetHNSEndpointByName(eid)
+		if err != nil {
+			logrus.Errorf("Failed to find HNS ID for endpoint %v: %v", eid, err)
+			return
 		}
 
-		if policies, ok := lbPolicylistMap[lb]; ok {
+		endpoints = append(endpoints, *hnsEndpoint)
+	}
 
-			if policies.ilb != nil {
-				policies.ilb.Delete()
-				policies.ilb = nil
-			}
+	if policies, ok := lbPolicylistMap[lb]; ok {
 
-			if policies.elb != nil {
-				policies.elb.Delete()
-				policies.elb = nil
-			}
-			delete(lbPolicylistMap, lb)
+		if policies.ilb != nil {
+			policies.ilb.Delete()
+			policies.ilb = nil
 		}
 
-		ilbPolicy, err := hcsshim.AddLoadBalancer(endpoints, true, sourceVIP, vip.String(), 0, 0, 0)
-		if err != nil {
-			logrus.Errorf("Failed to add ILB policy for service %s (%s) with endpoints %v using load balancer IP %s on network %s: %v",
-				lb.service.name, vip.String(), endpoints, sourceVIP, n.Name(), err)
-			return
+		if policies.elb != nil {
+			policies.elb.Delete()
+			policies.elb = nil
 		}
+		delete(lbPolicylistMap, lb)
+	}
 
-		lbPolicylistMap[lb] = &policyLists{
-			ilb: ilbPolicy,
-		}
+	ilbPolicy, err := hcsshim.AddLoadBalancer(endpoints, true, sourceVIP, vip.String(), 0, 0, 0)
+	if err != nil {
+		logrus.Errorf("Failed to add ILB policy for service %s (%s) with endpoints %v using load balancer IP %s on network %s: %v",
+			lb.service.name, vip.String(), endpoints, sourceVIP, n.Name(), err)
+		return
+	}
 
-		publishedPorts := make(map[uint32]uint32)
+	lbPolicylistMap[lb] = &policyLists{
+		ilb: ilbPolicy,
+	}
 
-		for i, port := range ingressPorts {
-			protocol := uint16(6)
+	publishedPorts := make(map[uint32]uint32)
 
-			// Skip already published port
-			if publishedPorts[port.PublishedPort] == port.TargetPort {
-				continue
-			}
+	for i, port := range ingressPorts {
+		protocol := uint16(6)
 
-			if port.Protocol == ProtocolUDP {
-				protocol = 17
-			}
+		// Skip already published port
+		if publishedPorts[port.PublishedPort] == port.TargetPort {
+			continue
+		}
 
-			// check if already has udp matching to add wild card publishing
-			for j := i + 1; j < len(ingressPorts); j++ {
-				if ingressPorts[j].TargetPort == port.TargetPort &&
-					ingressPorts[j].PublishedPort == port.PublishedPort {
-					protocol = 0
-				}
+		if port.Protocol == ProtocolUDP {
+			protocol = 17
+		}
+
+		// check if already has udp matching to add wild card publishing
+		for j := i + 1; j < len(ingressPorts); j++ {
+			if ingressPorts[j].TargetPort == port.TargetPort &&
+				ingressPorts[j].PublishedPort == port.PublishedPort {
+				protocol = 0
 			}
+		}
 
-			publishedPorts[port.PublishedPort] = port.TargetPort
+		publishedPorts[port.PublishedPort] = port.TargetPort
 
-			lbPolicylistMap[lb].elb, err = hcsshim.AddLoadBalancer(endpoints, false, sourceVIP, "", protocol, uint16(port.TargetPort), uint16(port.PublishedPort))
-			if err != nil {
-				logrus.Errorf("Failed to add ELB policy for service %s (ip:%s target port:%v published port:%v) with endpoints %v using load balancer IP %s on network %s: %v",
-					lb.service.name, vip.String(), uint16(port.TargetPort), uint16(port.PublishedPort), endpoints, sourceVIP, n.Name(), err)
-				return
-			}
+		lbPolicylistMap[lb].elb, err = hcsshim.AddLoadBalancer(endpoints, false, sourceVIP, "", protocol, uint16(port.TargetPort), uint16(port.PublishedPort))
+		if err != nil {
+			logrus.Errorf("Failed to add ELB policy for service %s (ip:%s target port:%v published port:%v) with endpoints %v using load balancer IP %s on network %s: %v",
+				lb.service.name, vip.String(), uint16(port.TargetPort), uint16(port.PublishedPort), endpoints, sourceVIP, n.Name(), err)
+			return
 		}
 	}
 }
@@ -124,30 +121,28 @@ func (n *network) rmLBBackend(ip net.IP, lb *loadBalancer, rmService bool, fullR
 		return
 	}
 
-	if osversion.Build() > 16236 {
-		if numEnabledBackends(lb) > 0 {
-			//Reprogram HNS (actually VFP) with the existing backends.
-			n.addLBBackend(ip, lb)
-		} else {
-			lb.Lock()
-			defer lb.Unlock()
-			logrus.Debugf("No more backends for service %s (ip:%s).  Removing all policies", lb.service.name, lb.vip.String())
-
-			if policyLists, ok := lbPolicylistMap[lb]; ok {
-				if policyLists.ilb != nil {
-					policyLists.ilb.Delete()
-					policyLists.ilb = nil
-				}
-
-				if policyLists.elb != nil {
-					policyLists.elb.Delete()
-					policyLists.elb = nil
-				}
-				delete(lbPolicylistMap, lb)
-
-			} else {
-				logrus.Errorf("Failed to find policies for service %s (%s)", lb.service.name, lb.vip.String())
+	if numEnabledBackends(lb) > 0 {
+		// Reprogram HNS (actually VFP) with the existing backends.
+		n.addLBBackend(ip, lb)
+	} else {
+		lb.Lock()
+		defer lb.Unlock()
+		logrus.Debugf("No more backends for service %s (ip:%s).  Removing all policies", lb.service.name, lb.vip.String())
+
+		if policyLists, ok := lbPolicylistMap[lb]; ok {
+			if policyLists.ilb != nil {
+				policyLists.ilb.Delete()
+				policyLists.ilb = nil
 			}
+
+			if policyLists.elb != nil {
+				policyLists.elb.Delete()
+				policyLists.elb = nil
+			}
+			delete(lbPolicylistMap, lb)
+
+		} else {
+			logrus.Errorf("Failed to find policies for service %s (%s)", lb.service.name, lb.vip.String())
 		}
 	}
 }

+ 1 - 2
pkg/parsers/operatingsystem/operatingsystem_windows.go

@@ -52,8 +52,7 @@ func withCurrentVersionRegistryKey(f func(registry.Key) (string, error)) (string
 
 // GetOperatingSystemVersion gets the version of the current operating system, as a string.
 func GetOperatingSystemVersion() (string, error) {
-	version := osversion.Get()
-	return fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build), nil
+	return osversion.Get().ToString(), nil
 }
 
 // IsContainerized returns true if we are running inside a container.