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