Browse code

Ensure Host Network Service exists

If HNS does not exist on the Docker host, the daemon may fail with unexpected
and difficult to diagnose errors. This check prevents the daemon from starting
on a system that does not have the correct prerequisites.

Signed-off-by: Darren Stahl <darst@microsoft.com>

Darren Stahl authored on 2017/09/22 07:09:41
Showing 2 changed files
... ...
@@ -27,8 +27,10 @@ import (
27 27
 	"github.com/docker/libnetwork/netlabel"
28 28
 	"github.com/docker/libnetwork/options"
29 29
 	blkiodev "github.com/opencontainers/runc/libcontainer/configs"
30
+	"github.com/pkg/errors"
30 31
 	"github.com/sirupsen/logrus"
31 32
 	"golang.org/x/sys/windows"
33
+	"golang.org/x/sys/windows/svc/mgr"
32 34
 )
33 35
 
34 36
 const (
... ...
@@ -238,6 +240,29 @@ func checkSystem() error {
238 238
 		return fmt.Errorf("Failed to load vmcompute.dll. Ensure that the Containers role is installed.")
239 239
 	}
240 240
 
241
+	// Ensure that the required Host Network Service and vmcompute services
242
+	// are running. Docker will fail in unexpected ways if this is not present.
243
+	var requiredServices = []string{"hns", "vmcompute"}
244
+	if err := ensureServicesInstalled(requiredServices); err != nil {
245
+		return errors.Wrap(err, "a required service is not installed, ensure the Containers feature is installed")
246
+	}
247
+
248
+	return nil
249
+}
250
+
251
+func ensureServicesInstalled(services []string) error {
252
+	m, err := mgr.Connect()
253
+	if err != nil {
254
+		return err
255
+	}
256
+	defer m.Disconnect()
257
+	for _, service := range services {
258
+		s, err := m.OpenService(service)
259
+		if err != nil {
260
+			return errors.Wrapf(err, "failed to open service %s", service)
261
+		}
262
+		s.Close()
263
+	}
241 264
 	return nil
242 265
 }
243 266
 
244 267
new file mode 100644
... ...
@@ -0,0 +1,72 @@
0
+// +build windows
1
+
2
+package daemon
3
+
4
+import (
5
+	"strings"
6
+	"testing"
7
+
8
+	"golang.org/x/sys/windows/svc/mgr"
9
+)
10
+
11
+const existingService = "Power"
12
+
13
+func TestEnsureServicesExist(t *testing.T) {
14
+	m, err := mgr.Connect()
15
+	if err != nil {
16
+		t.Fatal("failed to connect to service manager, this test needs admin")
17
+	}
18
+	defer m.Disconnect()
19
+	s, err := m.OpenService(existingService)
20
+	if err != nil {
21
+		t.Fatalf("expected to find known inbox service %q, this test needs a known inbox service to run correctly", existingService)
22
+	}
23
+	defer s.Close()
24
+
25
+	input := []string{existingService}
26
+	err = ensureServicesInstalled(input)
27
+	if err != nil {
28
+		t.Fatalf("unexpected error for input %q: %q", input, err)
29
+	}
30
+}
31
+
32
+func TestEnsureServicesExistErrors(t *testing.T) {
33
+	m, err := mgr.Connect()
34
+	if err != nil {
35
+		t.Fatal("failed to connect to service manager, this test needs admin")
36
+	}
37
+	defer m.Disconnect()
38
+	s, err := m.OpenService(existingService)
39
+	if err != nil {
40
+		t.Fatalf("expected to find known inbox service %q, this test needs a known inbox service to run correctly", existingService)
41
+	}
42
+	defer s.Close()
43
+
44
+	for _, testcase := range []struct {
45
+		input         []string
46
+		expectedError string
47
+	}{
48
+		{
49
+			input:         []string{"daemon_windows_test_fakeservice"},
50
+			expectedError: "failed to open service daemon_windows_test_fakeservice",
51
+		},
52
+		{
53
+			input:         []string{"daemon_windows_test_fakeservice1", "daemon_windows_test_fakeservice2"},
54
+			expectedError: "failed to open service daemon_windows_test_fakeservice1",
55
+		},
56
+		{
57
+			input:         []string{existingService, "daemon_windows_test_fakeservice"},
58
+			expectedError: "failed to open service daemon_windows_test_fakeservice",
59
+		},
60
+	} {
61
+		t.Run(strings.Join(testcase.input, ";"), func(t *testing.T) {
62
+			err := ensureServicesInstalled(testcase.input)
63
+			if err == nil {
64
+				t.Fatalf("expected error for input %v", testcase.input)
65
+			}
66
+			if !strings.Contains(err.Error(), testcase.expectedError) {
67
+				t.Fatalf("expected error %q to contain %q", err.Error(), testcase.expectedError)
68
+			}
69
+		})
70
+	}
71
+}