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>
... | ... |
@@ -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 |
+} |