Browse Source

Make experimental a runtime flag

Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
Kenfe-Mickael Laventure 8 năm trước cách đây
mục cha
commit
7781a1bf0f
100 tập tin đã thay đổi với 319 bổ sung446 xóa
  1. 29 0
      api/server/middleware/experimental.go
  2. 0 2
      api/server/router/checkpoint/backend.go
  3. 8 0
      api/server/router/checkpoint/checkpoint.go
  4. 0 15
      api/server/router/checkpoint/checkpoint_experimental.go
  5. 0 8
      api/server/router/checkpoint/checkpoint_regular.go
  6. 0 2
      api/server/router/checkpoint/checkpoint_routes.go
  7. 0 2
      api/server/router/plugin/backend.go
  8. 13 0
      api/server/router/plugin/plugin.go
  9. 0 20
      api/server/router/plugin/plugin_experimental.go
  10. 0 9
      api/server/router/plugin/plugin_regular.go
  11. 0 2
      api/server/router/plugin/plugin_routes.go
  12. 0 2
      cli/command/bundlefile/bundlefile.go
  13. 0 2
      cli/command/bundlefile/bundlefile_test.go
  14. 17 3
      cli/command/checkpoint/cmd.go
  15. 0 30
      cli/command/checkpoint/cmd_experimental.go
  16. 0 2
      cli/command/checkpoint/create.go
  17. 0 2
      cli/command/checkpoint/list.go
  18. 0 2
      cli/command/checkpoint/remove.go
  19. 22 6
      cli/command/cli.go
  20. 10 4
      cli/command/commands/commands.go
  21. 3 1
      cli/command/container/start.go
  22. 0 8
      cli/command/container/start_utils.go
  23. 0 9
      cli/command/container/start_utils_experimental.go
  24. 23 3
      cli/command/plugin/cmd.go
  25. 0 35
      cli/command/plugin/cmd_experimental.go
  26. 0 2
      cli/command/plugin/disable.go
  27. 0 2
      cli/command/plugin/enable.go
  28. 0 2
      cli/command/plugin/inspect.go
  29. 0 2
      cli/command/plugin/install.go
  30. 0 2
      cli/command/plugin/list.go
  31. 0 2
      cli/command/plugin/push.go
  32. 0 2
      cli/command/plugin/remove.go
  33. 0 2
      cli/command/plugin/set.go
  34. 26 6
      cli/command/stack/cmd.go
  35. 0 40
      cli/command/stack/cmd_experimental.go
  36. 0 2
      cli/command/stack/common.go
  37. 0 2
      cli/command/stack/config.go
  38. 0 2
      cli/command/stack/deploy.go
  39. 0 2
      cli/command/stack/list.go
  40. 0 2
      cli/command/stack/opts.go
  41. 0 2
      cli/command/stack/ps.go
  42. 0 2
      cli/command/stack/remove.go
  43. 0 2
      cli/command/stack/services.go
  44. 1 1
      cli/command/system/info.go
  45. 10 13
      cli/command/system/version.go
  46. 1 0
      client/interface.go
  47. 1 8
      client/interface_experimental.go
  48. 1 2
      client/interface_stable.go
  49. 19 0
      client/ping.go
  50. 0 2
      client/plugin_disable.go
  51. 0 2
      client/plugin_disable_test.go
  52. 0 2
      client/plugin_enable.go
  53. 0 2
      client/plugin_enable_test.go
  54. 0 2
      client/plugin_inspect.go
  55. 0 2
      client/plugin_inspect_test.go
  56. 0 2
      client/plugin_install.go
  57. 0 2
      client/plugin_list.go
  58. 0 2
      client/plugin_list_test.go
  59. 0 2
      client/plugin_push.go
  60. 0 2
      client/plugin_push_test.go
  61. 0 2
      client/plugin_remove.go
  62. 0 2
      client/plugin_remove_test.go
  63. 0 2
      client/plugin_set.go
  64. 0 2
      client/plugin_set_test.go
  65. 1 5
      cmd/docker/docker.go
  66. 4 1
      cmd/dockerd/daemon.go
  67. 1 6
      cmd/dockerd/docker.go
  68. 0 13
      cmd/dockerd/routes.go
  69. 3 2
      cmd/dockerd/routes_experimental.go
  70. 3 0
      daemon/config.go
  71. 0 2
      daemon/config_experimental.go
  72. 0 10
      daemon/config_stub.go
  73. 10 3
      daemon/daemon.go
  74. 9 5
      daemon/daemon_experimental.go
  75. 0 19
      daemon/daemon_stub.go
  76. 0 2
      daemon/graphdriver/plugin.go
  77. 0 9
      daemon/graphdriver/plugin_unsupported.go
  78. 0 2
      daemon/graphdriver/proxy.go
  79. 2 2
      daemon/info.go
  80. 1 1
      docs/reference/api/docker_remote_api.md
  81. 3 0
      docs/reference/commandline/dockerd.md
  82. 0 6
      hack/make.sh
  83. 5 0
      hack/make/.integration-daemon-start
  84. 0 4
      hack/make/build-deb
  85. 1 2
      hack/make/build-rpm
  86. 1 1
      hack/make/release-deb
  87. 1 1
      hack/make/release-rpm
  88. 5 1
      integration-cli/check_test.go
  89. 3 0
      integration-cli/daemon.go
  90. 13 7
      integration-cli/docker_cli_daemon_experimental_test.go
  91. 22 7
      integration-cli/docker_cli_experimental_test.go
  92. 5 3
      integration-cli/docker_cli_external_graphdriver_unix_test.go
  93. 1 2
      integration-cli/docker_cli_help_test.go
  94. 3 2
      integration-cli/docker_cli_info_test.go
  95. 5 2
      integration-cli/docker_cli_stack_test.go
  96. 20 19
      integration-cli/docker_experimental_network_test.go
  97. 7 1
      integration-cli/docker_test_vars.go
  98. 2 3
      integration-cli/requirements.go
  99. 4 0
      man/dockerd.8.md
  100. 0 2
      plugin/backend.go

+ 29 - 0
api/server/middleware/experimental.go

@@ -0,0 +1,29 @@
+package middleware
+
+import (
+	"net/http"
+
+	"golang.org/x/net/context"
+)
+
+// ExperimentalMiddleware is a the middleware in charge of adding the
+// 'Docker-Experimental' header to every outgoing request
+type ExperimentalMiddleware struct {
+	experimental string
+}
+
+// NewExperimentalMiddleware creates a new ExperimentalMiddleware
+func NewExperimentalMiddleware(experimentalEnabled bool) ExperimentalMiddleware {
+	if experimentalEnabled {
+		return ExperimentalMiddleware{"true"}
+	}
+	return ExperimentalMiddleware{"false"}
+}
+
+// WrapHandler returns a new handler function wrapping the previous one in the request chain.
+func (e ExperimentalMiddleware) WrapHandler(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+		w.Header().Set("Docker-Experimental", e.experimental)
+		return handler(ctx, w, r, vars)
+	}
+}

+ 0 - 2
api/server/router/checkpoint/backend.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package checkpoint
 
 import "github.com/docker/docker/api/types"

+ 8 - 0
api/server/router/checkpoint/checkpoint.go

@@ -26,3 +26,11 @@ func NewRouter(b Backend, decoder httputils.ContainerDecoder) router.Router {
 func (r *checkpointRouter) Routes() []router.Route {
 	return r.routes
 }
+
+func (r *checkpointRouter) initRoutes() {
+	r.routes = []router.Route{
+		router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints),
+		router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint),
+		router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint),
+	}
+}

+ 0 - 15
api/server/router/checkpoint/checkpoint_experimental.go

@@ -1,15 +0,0 @@
-// +build experimental
-
-package checkpoint
-
-import (
-	"github.com/docker/docker/api/server/router"
-)
-
-func (r *checkpointRouter) initRoutes() {
-	r.routes = []router.Route{
-		router.NewGetRoute("/containers/{name:.*}/checkpoints", r.getContainerCheckpoints),
-		router.NewPostRoute("/containers/{name:.*}/checkpoints", r.postContainerCheckpoint),
-		router.NewDeleteRoute("/containers/{name}/checkpoints/{checkpoint}", r.deleteContainerCheckpoint),
-	}
-}

+ 0 - 8
api/server/router/checkpoint/checkpoint_regular.go

@@ -1,8 +0,0 @@
-// +build !experimental
-
-package checkpoint
-
-func (r *checkpointRouter) initRoutes() {}
-
-// Backend is empty so that the package can compile in non-experimental
-type Backend interface{}

+ 0 - 2
api/server/router/checkpoint/checkpoint_routes.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package checkpoint
 
 import (

+ 0 - 2
api/server/router/plugin/backend.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 13 - 0
api/server/router/plugin/plugin.go

@@ -21,3 +21,16 @@ func NewRouter(b Backend) router.Router {
 func (r *pluginRouter) Routes() []router.Route {
 	return r.routes
 }
+
+func (r *pluginRouter) initRoutes() {
+	r.routes = []router.Route{
+		router.NewGetRoute("/plugins", r.listPlugins),
+		router.NewGetRoute("/plugins/{name:.*}", r.inspectPlugin),
+		router.NewDeleteRoute("/plugins/{name:.*}", r.removePlugin),
+		router.NewPostRoute("/plugins/{name:.*}/enable", r.enablePlugin), // PATCH?
+		router.NewPostRoute("/plugins/{name:.*}/disable", r.disablePlugin),
+		router.NewPostRoute("/plugins/pull", r.pullPlugin),
+		router.NewPostRoute("/plugins/{name:.*}/push", r.pushPlugin),
+		router.NewPostRoute("/plugins/{name:.*}/set", r.setPlugin),
+	}
+}

+ 0 - 20
api/server/router/plugin/plugin_experimental.go

@@ -1,20 +0,0 @@
-// +build experimental
-
-package plugin
-
-import (
-	"github.com/docker/docker/api/server/router"
-)
-
-func (r *pluginRouter) initRoutes() {
-	r.routes = []router.Route{
-		router.NewGetRoute("/plugins", r.listPlugins),
-		router.NewGetRoute("/plugins/{name:.*}", r.inspectPlugin),
-		router.NewDeleteRoute("/plugins/{name:.*}", r.removePlugin),
-		router.NewPostRoute("/plugins/{name:.*}/enable", r.enablePlugin), // PATCH?
-		router.NewPostRoute("/plugins/{name:.*}/disable", r.disablePlugin),
-		router.NewPostRoute("/plugins/pull", r.pullPlugin),
-		router.NewPostRoute("/plugins/{name:.*}/push", r.pushPlugin),
-		router.NewPostRoute("/plugins/{name:.*}/set", r.setPlugin),
-	}
-}

+ 0 - 9
api/server/router/plugin/plugin_regular.go

@@ -1,9 +0,0 @@
-// +build !experimental
-
-package plugin
-
-func (r *pluginRouter) initRoutes() {}
-
-// Backend is empty so that the package can compile in non-experimental
-// (Needed by volume driver)
-type Backend interface{}

+ 0 - 2
api/server/router/plugin/plugin_routes.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 0 - 2
cli/command/bundlefile/bundlefile.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package bundlefile
 
 import (

+ 0 - 2
cli/command/bundlefile/bundlefile_test.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package bundlefile
 
 import (

+ 17 - 3
cli/command/checkpoint/cmd.go

@@ -1,13 +1,27 @@
-// +build !experimental
-
 package checkpoint
 
 import (
+	"fmt"
+
+	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/spf13/cobra"
 )
 
 // NewCheckpointCommand returns the `checkpoint` subcommand (only in experimental)
 func NewCheckpointCommand(dockerCli *command.DockerCli) *cobra.Command {
-	return &cobra.Command{}
+	cmd := &cobra.Command{
+		Use:   "checkpoint",
+		Short: "Manage checkpoints",
+		Args:  cli.NoArgs,
+		Run: func(cmd *cobra.Command, args []string) {
+			fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
+		},
+	}
+	cmd.AddCommand(
+		newCreateCommand(dockerCli),
+		newListCommand(dockerCli),
+		newRemoveCommand(dockerCli),
+	)
+	return cmd
 }

+ 0 - 30
cli/command/checkpoint/cmd_experimental.go

@@ -1,30 +0,0 @@
-// +build experimental
-
-package checkpoint
-
-import (
-	"fmt"
-
-	"github.com/spf13/cobra"
-
-	"github.com/docker/docker/cli"
-	"github.com/docker/docker/cli/command"
-)
-
-// NewCheckpointCommand returns the `checkpoint` subcommand (only in experimental)
-func NewCheckpointCommand(dockerCli *command.DockerCli) *cobra.Command {
-	cmd := &cobra.Command{
-		Use:   "checkpoint",
-		Short: "Manage checkpoints",
-		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
-		},
-	}
-	cmd.AddCommand(
-		newCreateCommand(dockerCli),
-		newListCommand(dockerCli),
-		newRemoveCommand(dockerCli),
-	)
-	return cmd
-}

+ 0 - 2
cli/command/checkpoint/create.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package checkpoint
 
 import (

+ 0 - 2
cli/command/checkpoint/list.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package checkpoint
 
 import (

+ 0 - 2
cli/command/checkpoint/remove.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package checkpoint
 
 import (

+ 22 - 6
cli/command/cli.go

@@ -19,6 +19,7 @@ import (
 	dopts "github.com/docker/docker/opts"
 	"github.com/docker/go-connections/sockets"
 	"github.com/docker/go-connections/tlsconfig"
+	"golang.org/x/net/context"
 )
 
 // Streams is an interface which exposes the standard input and output streams
@@ -31,12 +32,27 @@ type Streams interface {
 // DockerCli represents the docker command line client.
 // Instances of the client can be returned from NewDockerCli.
 type DockerCli struct {
-	configFile *configfile.ConfigFile
-	in         *InStream
-	out        *OutStream
-	err        io.Writer
-	keyFile    string
-	client     client.APIClient
+	configFile      *configfile.ConfigFile
+	in              *InStream
+	out             *OutStream
+	err             io.Writer
+	keyFile         string
+	client          client.APIClient
+	hasExperimental *bool
+}
+
+// HasExperimental returns true if experimental features are accessible
+func (cli *DockerCli) HasExperimental() bool {
+	if cli.hasExperimental == nil {
+		if cli.client == nil {
+			cli.Initialize(cliflags.NewClientOptions())
+		}
+		enabled := false
+		cli.hasExperimental = &enabled
+		enabled, _ = cli.client.Ping(context.Background())
+	}
+
+	return *cli.hasExperimental
 }
 
 // Client returns the APIClient

+ 10 - 4
cli/command/commands/commands.go

@@ -24,8 +24,6 @@ func AddCommands(cmd *cobra.Command, dockerCli *command.DockerCli) {
 	cmd.AddCommand(
 		node.NewNodeCommand(dockerCli),
 		service.NewServiceCommand(dockerCli),
-		stack.NewStackCommand(dockerCli),
-		stack.NewTopLevelDeployCommand(dockerCli),
 		swarm.NewSwarmCommand(dockerCli),
 		container.NewContainerCommand(dockerCli),
 		image.NewImageCommand(dockerCli),
@@ -72,9 +70,17 @@ func AddCommands(cmd *cobra.Command, dockerCli *command.DockerCli) {
 		hide(image.NewSaveCommand(dockerCli)),
 		hide(image.NewTagCommand(dockerCli)),
 		hide(system.NewInspectCommand(dockerCli)),
-		checkpoint.NewCheckpointCommand(dockerCli),
-		plugin.NewPluginCommand(dockerCli),
 	)
+
+	if dockerCli.HasExperimental() {
+		cmd.AddCommand(
+			stack.NewStackCommand(dockerCli),
+			stack.NewTopLevelDeployCommand(dockerCli),
+			checkpoint.NewCheckpointCommand(dockerCli),
+			plugin.NewPluginCommand(dockerCli),
+		)
+	}
+
 }
 
 func hide(cmd *cobra.Command) *cobra.Command {

+ 3 - 1
cli/command/container/start.go

@@ -44,7 +44,9 @@ func NewStartCommand(dockerCli *command.DockerCli) *cobra.Command {
 	flags.BoolVarP(&opts.openStdin, "interactive", "i", false, "Attach container's STDIN")
 	flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
 
-	addExperimentalStartFlags(flags, &opts)
+	if dockerCli.HasExperimental() {
+		flags.StringVar(&opts.checkpoint, "checkpoint", "", "Restore from this checkpoint")
+	}
 
 	return cmd
 }

+ 0 - 8
cli/command/container/start_utils.go

@@ -1,8 +0,0 @@
-// +build !experimental
-
-package container
-
-import "github.com/spf13/pflag"
-
-func addExperimentalStartFlags(flags *pflag.FlagSet, opts *startOptions) {
-}

+ 0 - 9
cli/command/container/start_utils_experimental.go

@@ -1,9 +0,0 @@
-// +build experimental
-
-package container
-
-import "github.com/spf13/pflag"
-
-func addExperimentalStartFlags(flags *pflag.FlagSet, opts *startOptions) {
-	flags.StringVar(&opts.checkpoint, "checkpoint", "", "Restore from this checkpoint")
-}

+ 23 - 3
cli/command/plugin/cmd.go

@@ -1,13 +1,33 @@
-// +build !experimental
-
 package plugin
 
 import (
+	"fmt"
+
+	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/spf13/cobra"
 )
 
 // NewPluginCommand returns a cobra command for `plugin` subcommands
 func NewPluginCommand(dockerCli *command.DockerCli) *cobra.Command {
-	return &cobra.Command{}
+	cmd := &cobra.Command{
+		Use:   "plugin",
+		Short: "Manage plugins",
+		Args:  cli.NoArgs,
+		Run: func(cmd *cobra.Command, args []string) {
+			fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
+		},
+	}
+
+	cmd.AddCommand(
+		newDisableCommand(dockerCli),
+		newEnableCommand(dockerCli),
+		newInspectCommand(dockerCli),
+		newInstallCommand(dockerCli),
+		newListCommand(dockerCli),
+		newRemoveCommand(dockerCli),
+		newSetCommand(dockerCli),
+		newPushCommand(dockerCli),
+	)
+	return cmd
 }

+ 0 - 35
cli/command/plugin/cmd_experimental.go

@@ -1,35 +0,0 @@
-// +build experimental
-
-package plugin
-
-import (
-	"fmt"
-
-	"github.com/docker/docker/cli"
-	"github.com/docker/docker/cli/command"
-	"github.com/spf13/cobra"
-)
-
-// NewPluginCommand returns a cobra command for `plugin` subcommands
-func NewPluginCommand(dockerCli *command.DockerCli) *cobra.Command {
-	cmd := &cobra.Command{
-		Use:   "plugin",
-		Short: "Manage plugins",
-		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
-		},
-	}
-
-	cmd.AddCommand(
-		newDisableCommand(dockerCli),
-		newEnableCommand(dockerCli),
-		newInspectCommand(dockerCli),
-		newInstallCommand(dockerCli),
-		newListCommand(dockerCli),
-		newRemoveCommand(dockerCli),
-		newSetCommand(dockerCli),
-		newPushCommand(dockerCli),
-	)
-	return cmd
-}

+ 0 - 2
cli/command/plugin/disable.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 0 - 2
cli/command/plugin/enable.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 0 - 2
cli/command/plugin/inspect.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 0 - 2
cli/command/plugin/install.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 0 - 2
cli/command/plugin/list.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 0 - 2
cli/command/plugin/push.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 0 - 2
cli/command/plugin/remove.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 0 - 2
cli/command/plugin/set.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

+ 26 - 6
cli/command/stack/cmd.go

@@ -1,18 +1,38 @@
-// +build !experimental
-
 package stack
 
 import (
+	"fmt"
+
+	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/spf13/cobra"
 )
 
-// NewStackCommand returns no command
+// NewStackCommand returns a cobra command for `stack` subcommands
 func NewStackCommand(dockerCli *command.DockerCli) *cobra.Command {
-	return &cobra.Command{}
+	cmd := &cobra.Command{
+		Use:   "stack",
+		Short: "Manage Docker stacks",
+		Args:  cli.NoArgs,
+		Run: func(cmd *cobra.Command, args []string) {
+			fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
+		},
+	}
+	cmd.AddCommand(
+		newConfigCommand(dockerCli),
+		newDeployCommand(dockerCli),
+		newListCommand(dockerCli),
+		newRemoveCommand(dockerCli),
+		newServicesCommand(dockerCli),
+		newPsCommand(dockerCli),
+	)
+	return cmd
 }
 
-// NewTopLevelDeployCommand returns no command
+// NewTopLevelDeployCommand returns a command for `docker deploy`
 func NewTopLevelDeployCommand(dockerCli *command.DockerCli) *cobra.Command {
-	return &cobra.Command{}
+	cmd := newDeployCommand(dockerCli)
+	// Remove the aliases at the top level
+	cmd.Aliases = []string{}
+	return cmd
 }

+ 0 - 40
cli/command/stack/cmd_experimental.go

@@ -1,40 +0,0 @@
-// +build experimental
-
-package stack
-
-import (
-	"fmt"
-
-	"github.com/docker/docker/cli"
-	"github.com/docker/docker/cli/command"
-	"github.com/spf13/cobra"
-)
-
-// NewStackCommand returns a cobra command for `stack` subcommands
-func NewStackCommand(dockerCli *command.DockerCli) *cobra.Command {
-	cmd := &cobra.Command{
-		Use:   "stack",
-		Short: "Manage Docker stacks",
-		Args:  cli.NoArgs,
-		Run: func(cmd *cobra.Command, args []string) {
-			fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
-		},
-	}
-	cmd.AddCommand(
-		newConfigCommand(dockerCli),
-		newDeployCommand(dockerCli),
-		newListCommand(dockerCli),
-		newRemoveCommand(dockerCli),
-		newServicesCommand(dockerCli),
-		newPsCommand(dockerCli),
-	)
-	return cmd
-}
-
-// NewTopLevelDeployCommand returns a command for `docker deploy`
-func NewTopLevelDeployCommand(dockerCli *command.DockerCli) *cobra.Command {
-	cmd := newDeployCommand(dockerCli)
-	// Remove the aliases at the top level
-	cmd.Aliases = []string{}
-	return cmd
-}

+ 0 - 2
cli/command/stack/common.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package stack
 
 import (

+ 0 - 2
cli/command/stack/config.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package stack
 
 import (

+ 0 - 2
cli/command/stack/deploy.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package stack
 
 import (

+ 0 - 2
cli/command/stack/list.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package stack
 
 import (

+ 0 - 2
cli/command/stack/opts.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package stack
 
 import (

+ 0 - 2
cli/command/stack/ps.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package stack
 
 import (

+ 0 - 2
cli/command/stack/remove.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package stack
 
 import (

+ 0 - 2
cli/command/stack/services.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package stack
 
 import (

+ 1 - 1
cli/command/system/info.go

@@ -225,7 +225,7 @@ func prettyPrintInfo(dockerCli *command.DockerCli, info types.Info) error {
 		}
 	}
 
-	ioutils.FprintfIfTrue(dockerCli.Out(), "Experimental: %v\n", info.ExperimentalBuild)
+	fmt.Fprintf(dockerCli.Out(), "Experimental: %v\n", info.ExperimentalBuild)
 	if info.ClusterStore != "" {
 		fmt.Fprintf(dockerCli.Out(), "Cluster Store: %s\n", info.ClusterStore)
 	}

+ 10 - 13
cli/command/system/version.go

@@ -10,7 +10,6 @@ import (
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/dockerversion"
-	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils/templates"
 	"github.com/spf13/cobra"
 )
@@ -21,8 +20,7 @@ var versionTemplate = `Client:
  Go version:   {{.Client.GoVersion}}
  Git commit:   {{.Client.GitCommit}}
  Built:        {{.Client.BuildTime}}
- OS/Arch:      {{.Client.Os}}/{{.Client.Arch}}{{if .Client.Experimental}}
- Experimental: {{.Client.Experimental}}{{end}}{{if .ServerOK}}
+ OS/Arch:      {{.Client.Os}}/{{.Client.Arch}}{{if .ServerOK}}
 
 Server:
  Version:      {{.Server.Version}}
@@ -30,8 +28,8 @@ Server:
  Go version:   {{.Server.GoVersion}}
  Git commit:   {{.Server.GitCommit}}
  Built:        {{.Server.BuildTime}}
- OS/Arch:      {{.Server.Os}}/{{.Server.Arch}}{{if .Server.Experimental}}
- Experimental: {{.Server.Experimental}}{{end}}{{end}}`
+ OS/Arch:      {{.Server.Os}}/{{.Server.Arch}}
+ Experimental: {{.Server.Experimental}}{{end}}`
 
 type versionOptions struct {
 	format string
@@ -73,14 +71,13 @@ func runVersion(dockerCli *command.DockerCli, opts *versionOptions) error {
 
 	vd := types.VersionResponse{
 		Client: &types.Version{
-			Version:      dockerversion.Version,
-			APIVersion:   dockerCli.Client().ClientVersion(),
-			GoVersion:    runtime.Version(),
-			GitCommit:    dockerversion.GitCommit,
-			BuildTime:    dockerversion.BuildTime,
-			Os:           runtime.GOOS,
-			Arch:         runtime.GOARCH,
-			Experimental: utils.ExperimentalBuild(),
+			Version:    dockerversion.Version,
+			APIVersion: dockerCli.Client().ClientVersion(),
+			GoVersion:  runtime.Version(),
+			GitCommit:  dockerversion.GitCommit,
+			BuildTime:  dockerversion.BuildTime,
+			Os:         runtime.GOOS,
+			Arch:       runtime.GOARCH,
 		},
 	}
 

+ 1 - 0
client/interface.go

@@ -127,6 +127,7 @@ type SystemAPIClient interface {
 	Info(ctx context.Context) (types.Info, error)
 	RegistryLogin(ctx context.Context, auth types.AuthConfig) (types.AuthResponse, error)
 	DiskUsage(ctx context.Context) (types.DiskUsage, error)
+	Ping(ctx context.Context) (bool, error)
 }
 
 // VolumeAPIClient defines API client methods for the volumes

+ 1 - 8
client/interface_experimental.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (
@@ -7,9 +5,7 @@ import (
 	"golang.org/x/net/context"
 )
 
-// APIClient is an interface that clients that talk with a docker server must implement.
-type APIClient interface {
-	CommonAPIClient
+type apiClientExperimental interface {
 	CheckpointAPIClient
 	PluginAPIClient
 }
@@ -32,6 +28,3 @@ type PluginAPIClient interface {
 	PluginSet(ctx context.Context, name string, args []string) error
 	PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error)
 }
-
-// Ensure that Client always implements APIClient.
-var _ APIClient = &Client{}

+ 1 - 2
client/interface_stable.go

@@ -1,10 +1,9 @@
-// +build !experimental
-
 package client
 
 // APIClient is an interface that clients that talk with a docker server must implement.
 type APIClient interface {
 	CommonAPIClient
+	apiClientExperimental
 }
 
 // Ensure that Client always implements APIClient.

+ 19 - 0
client/ping.go

@@ -0,0 +1,19 @@
+package client
+
+import "golang.org/x/net/context"
+
+// Ping pings the server and return the value of the "Docker-Experimental" header
+func (cli *Client) Ping(ctx context.Context) (bool, error) {
+	serverResp, err := cli.get(ctx, "/_ping", nil, nil)
+	if err != nil {
+		return false, err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	exp := serverResp.header.Get("Docker-Experimental")
+	if exp != "true" {
+		return false, nil
+	}
+
+	return true, nil
+}

+ 0 - 2
client/plugin_disable.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_disable_test.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_enable.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_enable_test.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_inspect.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_inspect_test.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_install.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_list.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_list_test.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_push.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_push_test.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_remove.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_remove_test.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_set.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 0 - 2
client/plugin_set_test.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package client
 
 import (

+ 1 - 5
cmd/docker/docker.go

@@ -91,11 +91,7 @@ func main() {
 }
 
 func showVersion() {
-	if utils.ExperimentalBuild() {
-		fmt.Printf("Docker version %s, build %s, experimental\n", dockerversion.Version, dockerversion.GitCommit)
-	} else {
-		fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit)
-	}
+	fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit)
 }
 
 func dockerPreRun(opts *cliflags.ClientOptions) {

+ 4 - 1
cmd/dockerd/daemon.go

@@ -129,7 +129,7 @@ func (cli *DaemonCli) start(opts daemonOptions) (err error) {
 		utils.EnableDebug()
 	}
 
-	if utils.ExperimentalBuild() {
+	if cli.Config.Experimental {
 		logrus.Warn("Running experimental build")
 	}
 
@@ -435,6 +435,9 @@ func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) {
 func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config) {
 	v := cfg.Version
 
+	exp := middleware.NewExperimentalMiddleware(cli.d.HasExperimental())
+	s.UseMiddleware(exp)
+
 	vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion)
 	s.UseMiddleware(vm)
 

+ 1 - 6
cmd/dockerd/docker.go

@@ -13,7 +13,6 @@ import (
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/docker/docker/pkg/term"
-	"github.com/docker/docker/utils"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 )
@@ -85,11 +84,7 @@ func runDaemon(opts daemonOptions) error {
 }
 
 func showVersion() {
-	if utils.ExperimentalBuild() {
-		fmt.Printf("Docker version %s, build %s, experimental\n", dockerversion.Version, dockerversion.GitCommit)
-	} else {
-		fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit)
-	}
+	fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit)
 }
 
 func main() {

+ 0 - 13
cmd/dockerd/routes.go

@@ -1,13 +0,0 @@
-// +build !experimental
-
-package main
-
-import (
-	"github.com/docker/docker/api/server/httputils"
-	"github.com/docker/docker/api/server/router"
-	"github.com/docker/docker/daemon"
-)
-
-func addExperimentalRouters(routers []router.Router, d *daemon.Daemon, decoder httputils.ContainerDecoder) []router.Router {
-	return routers
-}

+ 3 - 2
cmd/dockerd/routes_experimental.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package main
 
 import (
@@ -12,5 +10,8 @@ import (
 )
 
 func addExperimentalRouters(routers []router.Router, d *daemon.Daemon, decoder httputils.ContainerDecoder) []router.Router {
+	if !d.HasExperimental() {
+		return []router.Router{}
+	}
 	return append(routers, checkpointrouter.NewRouter(d, decoder), pluginrouter.NewRouter(plugin.GetManager()))
 }

+ 3 - 0
daemon/config.go

@@ -153,6 +153,8 @@ type CommonConfig struct {
 
 	reloadLock sync.Mutex
 	valuesSet  map[string]interface{}
+
+	Experimental bool `json:"experimental"` // Experimental indicates whether experimental features should be exposed or not
 }
 
 // InstallCommonFlags adds flags to the pflag.FlagSet to configure the daemon
@@ -187,6 +189,7 @@ func (config *Config) InstallCommonFlags(flags *pflag.FlagSet) {
 	flags.IntVar(&config.ShutdownTimeout, "shutdown-timeout", defaultShutdownTimeout, "Set the default shutdown timeout")
 
 	flags.StringVar(&config.SwarmDefaultAdvertiseAddr, "swarm-default-advertise-addr", "", "Set default address or interface for swarm advertised address")
+	flags.BoolVar(&config.Experimental, "experimental", false, "Enable experimental features")
 
 	config.MaxConcurrentDownloads = &maxConcurrentDownloads
 	config.MaxConcurrentUploads = &maxConcurrentUploads

+ 0 - 2
daemon/config_experimental.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package daemon
 
 import (

+ 0 - 10
daemon/config_stub.go

@@ -1,10 +0,0 @@
-// +build !experimental
-
-package daemon
-
-import (
-	"github.com/spf13/pflag"
-)
-
-func (config *Config) attachExperimentalFlags(cmd *pflag.FlagSet) {
-}

+ 10 - 3
daemon/daemon.go

@@ -104,6 +104,14 @@ type Daemon struct {
 	clusterProvider           cluster.Provider
 }
 
+// HasExperimental returns whether the experimental features of the daemon are enabled or not
+func (daemon *Daemon) HasExperimental() bool {
+	if daemon.configStore != nil && daemon.configStore.Experimental {
+		return true
+	}
+	return false
+}
+
 func (daemon *Daemon) restore() error {
 	var (
 		currentDriver = daemon.GraphDriverName()
@@ -667,7 +675,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 	}
 
 	// Plugin system initialization should happen before restore. Do not change order.
-	if err := pluginInit(d, config, containerdRemote); err != nil {
+	if err := d.pluginInit(config, containerdRemote); err != nil {
 		return nil, err
 	}
 
@@ -775,7 +783,7 @@ func (daemon *Daemon) Shutdown() error {
 	}
 
 	// Shutdown plugins after containers. Dont change the order.
-	pluginShutdown()
+	daemon.pluginShutdown()
 
 	// trigger libnetwork Stop only if it's initialized
 	if daemon.netController != nil {
@@ -1022,7 +1030,6 @@ func (daemon *Daemon) Reload(config *Config) (err error) {
 		if err := daemon.containerdRemote.UpdateOptions(libcontainerd.WithLiveRestore(config.LiveRestoreEnabled)); err != nil {
 			return err
 		}
-
 	}
 
 	// If no value is set for max-concurrent-downloads we assume it is the default value

+ 9 - 5
daemon/daemon_experimental.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package daemon
 
 import (
@@ -12,11 +10,17 @@ func (daemon *Daemon) verifyExperimentalContainerSettings(hostConfig *container.
 	return nil, nil
 }
 
-func pluginInit(d *Daemon, cfg *Config, remote libcontainerd.Remote) error {
-	return plugin.Init(cfg.Root, d.PluginStore, remote, d.RegistryService, cfg.LiveRestoreEnabled, d.LogPluginEvent)
+func (daemon *Daemon) pluginInit(cfg *Config, remote libcontainerd.Remote) error {
+	if !daemon.HasExperimental() {
+		return nil
+	}
+	return plugin.Init(cfg.Root, daemon.PluginStore, remote, daemon.RegistryService, cfg.LiveRestoreEnabled, daemon.LogPluginEvent)
 }
 
-func pluginShutdown() {
+func (daemon *Daemon) pluginShutdown() {
+	if !daemon.HasExperimental() {
+		return
+	}
 	manager := plugin.GetManager()
 	// Check for a valid manager object. In error conditions, daemon init can fail
 	// and shutdown called, before plugin manager is initialized.

+ 0 - 19
daemon/daemon_stub.go

@@ -1,19 +0,0 @@
-// +build !experimental
-
-package daemon
-
-import (
-	"github.com/docker/docker/api/types/container"
-	"github.com/docker/docker/libcontainerd"
-)
-
-func (daemon *Daemon) verifyExperimentalContainerSettings(hostConfig *container.HostConfig, config *container.Config) ([]string, error) {
-	return nil, nil
-}
-
-func pluginInit(d *Daemon, config *Config, remote libcontainerd.Remote) error {
-	return nil
-}
-
-func pluginShutdown() {
-}

+ 0 - 2
daemon/graphdriver/plugin.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package graphdriver
 
 import (

+ 0 - 9
daemon/graphdriver/plugin_unsupported.go

@@ -1,9 +0,0 @@
-// +build !experimental
-
-package graphdriver
-
-import "github.com/docker/docker/pkg/plugingetter"
-
-func lookupPlugin(name, home string, opts []string, pg plugingetter.PluginGetter) (Driver, error) {
-	return nil, ErrNotSupported
-}

+ 0 - 2
daemon/graphdriver/proxy.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package graphdriver
 
 import (

+ 2 - 2
daemon/info.go

@@ -109,7 +109,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
 		MemTotal:           meminfo.MemTotal,
 		DockerRootDir:      daemon.configStore.Root,
 		Labels:             daemon.configStore.Labels,
-		ExperimentalBuild:  utils.ExperimentalBuild(),
+		ExperimentalBuild:  daemon.configStore.Experimental,
 		ServerVersion:      dockerversion.Version,
 		ClusterStore:       daemon.configStore.ClusterStore,
 		ClusterAdvertise:   daemon.configStore.ClusterAdvertise,
@@ -158,7 +158,7 @@ func (daemon *Daemon) SystemVersion() types.Version {
 		Os:           runtime.GOOS,
 		Arch:         runtime.GOARCH,
 		BuildTime:    dockerversion.BuildTime,
-		Experimental: utils.ExperimentalBuild(),
+		Experimental: daemon.configStore.Experimental,
 	}
 
 	kernelVersion := "<unknown>"

+ 1 - 1
docs/reference/api/docker_remote_api.md

@@ -157,7 +157,7 @@ This section lists each version from latest to oldest.  Each listing includes a
 * `POST /containers/prune` prunes stopped containers.
 * `POST /images/prune` prunes unused images.
 * `POST /volumes/prune` prunes unused volumes.
-
+* Every API response now includes a `Docker-Experimental` header specifying if experimental features are enabled (value can be `true` or `false`).
 
 ### v1.24 API changes
 

+ 3 - 0
docs/reference/commandline/dockerd.md

@@ -45,6 +45,7 @@ Options:
       --dns-search value                      DNS search domains to use (default [])
       --exec-opt value                        Runtime execution options (default [])
       --exec-root string                      Root directory for execution state files (default "/var/run/docker")
+      --experimental                          Enable experimental features
       --fixed-cidr string                     IPv4 subnet for fixed IPs
       --fixed-cidr-v6 string                  IPv6 subnet for fixed IPs
   -g, --graph string                          Root of the Docker runtime (default "/var/lib/docker")
@@ -1114,6 +1115,7 @@ This is a full example of the allowed configuration options on Linux:
 	"dns-search": [],
 	"exec-opts": [],
 	"exec-root": "",
+	"experimental": false,
 	"storage-driver": "",
 	"storage-opts": [],
 	"labels": [],
@@ -1195,6 +1197,7 @@ This is a full example of the allowed configuration options on Windows:
     "dns-opts": [],
     "dns-search": [],
     "exec-opts": [],
+    "experimental": false,
     "storage-driver": "",
     "storage-opts": [],
     "labels": [],

+ 0 - 6
hack/make.sh

@@ -126,12 +126,6 @@ if [ ! "$GOPATH" ]; then
 	exit 1
 fi
 
-if [ "$DOCKER_EXPERIMENTAL" ]; then
-	echo >&2 '# WARNING! DOCKER_EXPERIMENTAL is set: building experimental features'
-	echo >&2
-	DOCKER_BUILDTAGS+=" experimental"
-fi
-
 DOCKER_BUILDTAGS+=" daemon"
 if ${PKG_CONFIG} 'libsystemd >= 209' 2> /dev/null ; then
 	DOCKER_BUILDTAGS+=" journald"

+ 5 - 0
hack/make/.integration-daemon-start

@@ -50,6 +50,11 @@ if [ "$DOCKER_REMAP_ROOT" ]; then
 	extra_params="--userns-remap $DOCKER_REMAP_ROOT"
 fi
 
+if [  "$DOCKER_EXPERIMENTAL" ]; then
+	echo >&2 '# DOCKER_EXPERIMENTAL is set: starting daemon with experimental features enabled! '
+	extra_params="$extra_params --experimental"
+fi
+
 if [ -z "$DOCKER_TEST_HOST" ]; then
 	# Start apparmor if it is enabled
 	if [ -e "/sys/module/apparmor/parameters/enabled" ] && [ "$(cat /sys/module/apparmor/parameters/enabled)" == "Y" ]; then

+ 0 - 4
hack/make/build-deb

@@ -76,10 +76,6 @@ set -e
 			# Install runc and containerd
 			RUN ./hack/dockerfile/install-binaries.sh runc-dynamic containerd-dynamic
 		EOF
-
-		if [ "$DOCKER_EXPERIMENTAL" ]; then
-			echo 'ENV DOCKER_EXPERIMENTAL 1' >> "$DEST/$version/Dockerfile.build"
-		fi
 		cat >> "$DEST/$version/Dockerfile.build" <<-EOF
 			RUN cp -aL hack/make/.build-deb debian
 			RUN { echo '$debSource (${debVersion}-0~${suite}) $suite; urgency=low'; echo; echo '  * Version: $VERSION'; echo; echo " -- $debMaintainer  $debDate"; } > debian/changelog && cat >&2 debian/changelog

+ 1 - 2
hack/make/build-rpm

@@ -96,8 +96,7 @@ set -e
 			# Install runc and containerd
 			RUN ./hack/dockerfile/install-binaries.sh runc-dynamic containerd-dynamic
 		EOF
-
-		if [ "$DOCKER_EXPERIMENTAL" ]; then
+		if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then
 			echo 'ENV DOCKER_EXPERIMENTAL 1' >> "$DEST/$version/Dockerfile.build"
 		fi
 		cat >> "$DEST/$version/Dockerfile.build" <<-EOF

+ 1 - 1
hack/make/release-deb

@@ -39,7 +39,7 @@ if [[ "$VERSION" == *-rc* ]]; then
 	component="testing"
 fi
 
-if [ "$DOCKER_EXPERIMENTAL" ] || [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then
+if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then
 	component="experimental"
 fi
 

+ 1 - 1
hack/make/release-rpm

@@ -25,7 +25,7 @@ if [[ "$VERSION" == *-rc* ]]; then
 	release="testing"
 fi
 
-if [ $DOCKER_EXPERIMENTAL ] || [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then
+if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then
 	release="experimental"
 fi
 

+ 5 - 1
integration-cli/check_test.go

@@ -266,7 +266,11 @@ func (s *DockerSwarmSuite) AddDaemon(c *check.C, joinSwarm, manager bool) *Swarm
 		port:   defaultSwarmPort + s.portIndex,
 	}
 	d.listenAddr = fmt.Sprintf("0.0.0.0:%d", d.port)
-	err := d.StartWithBusybox("--iptables=false", "--swarm-default-advertise-addr=lo") // avoid networking conflicts
+	args := []string{"--iptables=false", "--swarm-default-advertise-addr=lo"} // avoid networking conflicts
+	if experimentalDaemon {
+		args = append(args, "--experimental")
+	}
+	err := d.StartWithBusybox(args...)
 	c.Assert(err, check.IsNil)
 
 	if joinSwarm == true {

+ 3 - 0
integration-cli/daemon.go

@@ -153,6 +153,9 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
 		"--pidfile", fmt.Sprintf("%s/docker.pid", d.folder),
 		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
 	)
+	if experimentalDaemon {
+		args = append(args, "--experimental")
+	}
 	if !(d.useDefaultHost || d.useDefaultTLSHost) {
 		args = append(args, []string{"--host", d.sock()}...)
 	}

+ 13 - 7
integration-cli/docker_cli_daemon_experimental_test.go

@@ -1,4 +1,4 @@
-// +build linux, experimental
+// +build linux
 
 package main
 
@@ -17,7 +17,8 @@ var pluginName = "tiborvass/no-remove"
 
 // TestDaemonRestartWithPluginEnabled tests state restore for an enabled plugin
 func (s *DockerDaemonSuite) TestDaemonRestartWithPluginEnabled(c *check.C) {
-	testRequires(c, Network)
+	testRequires(c, Network, ExperimentalDaemon)
+
 	if err := s.d.Start(); err != nil {
 		c.Fatalf("Could not start daemon: %v", err)
 	}
@@ -49,7 +50,8 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPluginEnabled(c *check.C) {
 
 // TestDaemonRestartWithPluginDisabled tests state restore for a disabled plugin
 func (s *DockerDaemonSuite) TestDaemonRestartWithPluginDisabled(c *check.C) {
-	testRequires(c, Network)
+	testRequires(c, Network, ExperimentalDaemon)
+
 	if err := s.d.Start(); err != nil {
 		c.Fatalf("Could not start daemon: %v", err)
 	}
@@ -79,7 +81,8 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPluginDisabled(c *check.C) {
 // TestDaemonKillLiveRestoreWithPlugins SIGKILLs daemon started with --live-restore.
 // Plugins should continue to run.
 func (s *DockerDaemonSuite) TestDaemonKillLiveRestoreWithPlugins(c *check.C) {
-	testRequires(c, Network)
+	testRequires(c, Network, ExperimentalDaemon)
+
 	if err := s.d.Start("--live-restore"); err != nil {
 		c.Fatalf("Could not start daemon: %v", err)
 	}
@@ -111,7 +114,8 @@ func (s *DockerDaemonSuite) TestDaemonKillLiveRestoreWithPlugins(c *check.C) {
 // TestDaemonShutdownLiveRestoreWithPlugins SIGTERMs daemon started with --live-restore.
 // Plugins should continue to run.
 func (s *DockerDaemonSuite) TestDaemonShutdownLiveRestoreWithPlugins(c *check.C) {
-	testRequires(c, Network)
+	testRequires(c, Network, ExperimentalDaemon)
+
 	if err := s.d.Start("--live-restore"); err != nil {
 		c.Fatalf("Could not start daemon: %v", err)
 	}
@@ -142,7 +146,8 @@ func (s *DockerDaemonSuite) TestDaemonShutdownLiveRestoreWithPlugins(c *check.C)
 
 // TestDaemonShutdownWithPlugins shuts down running plugins.
 func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
-	testRequires(c, Network)
+	testRequires(c, Network, ExperimentalDaemon)
+
 	if err := s.d.Start(); err != nil {
 		c.Fatalf("Could not start daemon: %v", err)
 	}
@@ -180,7 +185,8 @@ func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
 
 // TestVolumePlugin tests volume creation using a plugin.
 func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
-	testRequires(c, Network)
+	testRequires(c, Network, ExperimentalDaemon)
+
 	volName := "plugin-volume"
 	volRoot := "/data"
 	destDir := "/tmp/data/"

+ 22 - 7
integration-cli/docker_cli_experimental_test.go

@@ -1,21 +1,36 @@
-// +build experimental
-
 package main
 
 import (
+	"strings"
+
 	"github.com/docker/docker/pkg/integration/checker"
 	"github.com/go-check/check"
-	"strings"
 )
 
-func (s *DockerSuite) TestExperimentalVersion(c *check.C) {
+func (s *DockerSuite) TestExperimentalVersionTrue(c *check.C) {
+	testRequires(c, ExperimentalDaemon)
+
 	out, _ := dockerCmd(c, "version")
 	for _, line := range strings.Split(out, "\n") {
-		if strings.HasPrefix(line, "Experimental (client):") || strings.HasPrefix(line, "Experimental (server):") {
+		if strings.HasPrefix(strings.TrimSpace(line), "Experimental:") {
 			c.Assert(line, checker.Matches, "*true")
+			return
+		}
+	}
+
+	c.Fatal(`"Experimental" not found in version output`)
+}
+
+func (s *DockerSuite) TestExperimentalVersionFalse(c *check.C) {
+	testRequires(c, NotExperimentalDaemon)
+
+	out, _ := dockerCmd(c, "version")
+	for _, line := range strings.Split(out, "\n") {
+		if strings.HasPrefix(strings.TrimSpace(line), "Experimental:") {
+			c.Assert(line, checker.Matches, "*false")
+			return
 		}
 	}
 
-	out, _ = dockerCmd(c, "-v")
-	c.Assert(out, checker.Contains, ", experimental", check.Commentf("docker version did not contain experimental"))
+	c.Fatal(`"Experimental" not found in version output`)
 }

+ 5 - 3
integration-cli/docker_cli_external_graphdriver_unix_test.go

@@ -1,4 +1,3 @@
-// +build experimental
 // +build !windows
 
 package main
@@ -287,7 +286,7 @@ func (s *DockerExternalGraphdriverSuite) setUpPlugin(c *check.C, name string, ex
 
 	mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) {
 		s.ec[ext].applydiff++
-		var diff io.Reader = r.Body
+		diff := r.Body
 		defer r.Body.Close()
 
 		id := r.URL.Query().Get("id")
@@ -338,6 +337,8 @@ func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) {
 }
 
 func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
+	testRequires(c, ExperimentalDaemon)
+
 	s.testExternalGraphDriver("test-external-graph-driver", "spec", c)
 	s.testExternalGraphDriver("json-external-graph-driver", "json", c)
 }
@@ -388,7 +389,8 @@ func (s *DockerExternalGraphdriverSuite) testExternalGraphDriver(name string, ex
 }
 
 func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriverPull(c *check.C) {
-	testRequires(c, Network)
+	testRequires(c, Network, ExperimentalDaemon)
+
 	c.Assert(s.d.Start(), check.IsNil)
 
 	out, err := s.d.Cmd("pull", "busybox:latest")

+ 1 - 2
integration-cli/docker_cli_help_test.go

@@ -10,7 +10,6 @@ import (
 	"github.com/docker/docker/pkg/homedir"
 	"github.com/docker/docker/pkg/integration/checker"
 	icmd "github.com/docker/docker/pkg/integration/cmd"
-	"github.com/docker/docker/utils"
 	"github.com/go-check/check"
 )
 
@@ -117,7 +116,7 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 		cmdsToTest = append(cmdsToTest, "network ls")
 		cmdsToTest = append(cmdsToTest, "network rm")
 
-		if utils.ExperimentalBuild() {
+		if experimentalDaemon {
 			cmdsToTest = append(cmdsToTest, "checkpoint create")
 			cmdsToTest = append(cmdsToTest, "checkpoint ls")
 			cmdsToTest = append(cmdsToTest, "checkpoint rm")

+ 3 - 2
integration-cli/docker_cli_info_test.go

@@ -7,7 +7,6 @@ import (
 	"strings"
 
 	"github.com/docker/docker/pkg/integration/checker"
-	"github.com/docker/docker/utils"
 	"github.com/go-check/check"
 )
 
@@ -44,8 +43,10 @@ func (s *DockerSuite) TestInfoEnsureSucceeds(c *check.C) {
 		stringsToCheck = append(stringsToCheck, "Runtimes:", "Default Runtime: runc")
 	}
 
-	if utils.ExperimentalBuild() {
+	if experimentalDaemon {
 		stringsToCheck = append(stringsToCheck, "Experimental: true")
+	} else {
+		stringsToCheck = append(stringsToCheck, "Experimental: false")
 	}
 
 	for _, linePrefix := range stringsToCheck {

+ 5 - 2
integration-cli/docker_cli_stack_test.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package main
 
 import (
@@ -11,6 +9,7 @@ import (
 )
 
 func (s *DockerSwarmSuite) TestStackRemove(c *check.C) {
+	testRequires(c, ExperimentalDaemon)
 	d := s.AddDaemon(c, true, true)
 
 	stackArgs := append([]string{"stack", "remove", "UNKNOWN_STACK"})
@@ -21,6 +20,7 @@ func (s *DockerSwarmSuite) TestStackRemove(c *check.C) {
 }
 
 func (s *DockerSwarmSuite) TestStackTasks(c *check.C) {
+	testRequires(c, ExperimentalDaemon)
 	d := s.AddDaemon(c, true, true)
 
 	stackArgs := append([]string{"stack", "ps", "UNKNOWN_STACK"})
@@ -31,6 +31,7 @@ func (s *DockerSwarmSuite) TestStackTasks(c *check.C) {
 }
 
 func (s *DockerSwarmSuite) TestStackServices(c *check.C) {
+	testRequires(c, ExperimentalDaemon)
 	d := s.AddDaemon(c, true, true)
 
 	stackArgs := append([]string{"stack", "services", "UNKNOWN_STACK"})
@@ -59,6 +60,7 @@ const testDAB = `{
 }`
 
 func (s *DockerSwarmSuite) TestStackWithDAB(c *check.C) {
+	testRequires(c, ExperimentalDaemon)
 	// setup
 	testStackName := "test"
 	testDABFileName := testStackName + ".dab"
@@ -92,6 +94,7 @@ func (s *DockerSwarmSuite) TestStackWithDAB(c *check.C) {
 }
 
 func (s *DockerSwarmSuite) TestStackWithDABExtension(c *check.C) {
+	testRequires(c, ExperimentalDaemon)
 	// setup
 	testStackName := "test.dab"
 	testDABFileName := testStackName

+ 20 - 19
integration-cli/docker_experimental_network_test.go

@@ -1,4 +1,4 @@
-// +build experimental
+// +build !windows
 
 package main
 
@@ -50,7 +50,8 @@ var (
 
 func (s *DockerNetworkSuite) TestDockerNetworkMacvlanPersistance(c *check.C) {
 	// verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
-	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
+
 	// master dummy interface 'dm' abbreviation represents 'docker macvlan'
 	master := "dm-dummy0"
 	// simulate the master link the vlan tagged subinterface parent link will use
@@ -69,7 +70,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanPersistance(c *check.C) {
 
 func (s *DockerNetworkSuite) TestDockerNetworkIpvlanPersistance(c *check.C) {
 	// verify the driver automatically provisions the 802.1q link (di-dummy0.70)
-	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	// master dummy interface 'di' notation represent 'docker ipvlan'
 	master := "di-dummy0"
 	// simulate the master link the vlan tagged subinterface parent link will use
@@ -88,7 +89,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanPersistance(c *check.C) {
 
 func (s *DockerNetworkSuite) TestDockerNetworkMacvlanSubIntCreate(c *check.C) {
 	// verify the driver automatically provisions the 802.1q link (dm-dummy0.50)
-	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	// master dummy interface 'dm' abbreviation represents 'docker macvlan'
 	master := "dm-dummy0"
 	// simulate the master link the vlan tagged subinterface parent link will use
@@ -103,7 +104,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanSubIntCreate(c *check.C) {
 
 func (s *DockerNetworkSuite) TestDockerNetworkIpvlanSubIntCreate(c *check.C) {
 	// verify the driver automatically provisions the 802.1q link (di-dummy0.50)
-	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	// master dummy interface 'dm' abbreviation represents 'docker ipvlan'
 	master := "di-dummy0"
 	// simulate the master link the vlan tagged subinterface parent link will use
@@ -118,7 +119,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanSubIntCreate(c *check.C) {
 
 func (s *DockerNetworkSuite) TestDockerNetworkMacvlanOverlapParent(c *check.C) {
 	// verify the same parent interface cannot be used if already in use by an existing network
-	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	// master dummy interface 'dm' abbreviation represents 'docker macvlan'
 	master := "dm-dummy0"
 	out, err := createMasterDummy(c, master)
@@ -138,7 +139,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanOverlapParent(c *check.C) {
 
 func (s *DockerNetworkSuite) TestDockerNetworkIpvlanOverlapParent(c *check.C) {
 	// verify the same parent interface cannot be used if already in use by an existing network
-	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	// master dummy interface 'dm' abbreviation represents 'docker ipvlan'
 	master := "di-dummy0"
 	out, err := createMasterDummy(c, master)
@@ -158,7 +159,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanOverlapParent(c *check.C) {
 
 func (s *DockerNetworkSuite) TestDockerNetworkMacvlanMultiSubnet(c *check.C) {
 	// create a dual stack multi-subnet Macvlan bridge mode network and validate connectivity between four containers, two on each subnet
-	testRequires(c, DaemonIsLinux, IPv6, MacvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IPv6, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=macvlan", "--ipv6", "--subnet=172.28.100.0/24", "--subnet=172.28.102.0/24", "--gateway=172.28.102.254",
 		"--subnet=2001:db8:abc2::/64", "--subnet=2001:db8:abc4::/64", "--gateway=2001:db8:abc4::254", "dualstackbridge")
 	// Ensure the network was created
@@ -213,7 +214,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanMultiSubnet(c *check.C) {
 
 func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL2MultiSubnet(c *check.C) {
 	// create a dual stack multi-subnet Ipvlan L2 network and validate connectivity within the subnets, two on each subnet
-	testRequires(c, DaemonIsLinux, IPv6, IpvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IPv6, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=ipvlan", "--ipv6", "--subnet=172.28.200.0/24", "--subnet=172.28.202.0/24", "--gateway=172.28.202.254",
 		"--subnet=2001:db8:abc8::/64", "--subnet=2001:db8:abc6::/64", "--gateway=2001:db8:abc6::254", "dualstackl2")
 	// Ensure the network was created
@@ -267,7 +268,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL2MultiSubnet(c *check.C) {
 
 func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL3MultiSubnet(c *check.C) {
 	// create a dual stack multi-subnet Ipvlan L3 network and validate connectivity between all four containers per L3 mode
-	testRequires(c, DaemonIsLinux, IPv6, IpvlanKernelSupport, NotUserNamespace, NotArm, IPv6)
+	testRequires(c, DaemonIsLinux, IPv6, IpvlanKernelSupport, NotUserNamespace, NotArm, IPv6, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=ipvlan", "--ipv6", "--subnet=172.28.10.0/24", "--subnet=172.28.12.0/24", "--gateway=172.28.12.254",
 		"--subnet=2001:db8:abc9::/64", "--subnet=2001:db8:abc7::/64", "--gateway=2001:db8:abc7::254", "-o", "ipvlan_mode=l3", "dualstackl3")
 	// Ensure the network was created
@@ -326,7 +327,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL3MultiSubnet(c *check.C) {
 
 func (s *DockerNetworkSuite) TestDockerNetworkIpvlanAddressing(c *check.C) {
 	// Ensure the default gateways, next-hops and default dev devices are properly set
-	testRequires(c, DaemonIsLinux, IPv6, IpvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IPv6, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=macvlan", "--ipv6", "--subnet=172.28.130.0/24",
 		"--subnet=2001:db8:abca::/64", "--gateway=2001:db8:abca::254", "-o", "macvlan_mode=bridge", "dualstackbridge")
 	assertNwIsAvailable(c, "dualstackbridge")
@@ -372,7 +373,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanAddressing(c *check.C) {
 
 func (s *DockerSuite) TestDockerNetworkMacVlanBridgeNilParent(c *check.C) {
 	// macvlan bridge mode - dummy parent interface is provisioned dynamically
-	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=macvlan", "dm-nil-parent")
 	assertNwIsAvailable(c, "dm-nil-parent")
 
@@ -389,7 +390,7 @@ func (s *DockerSuite) TestDockerNetworkMacVlanBridgeNilParent(c *check.C) {
 
 func (s *DockerSuite) TestDockerNetworkMacVlanBridgeInternalMode(c *check.C) {
 	// macvlan bridge mode --internal containers can communicate inside the network but not externally
-	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=macvlan", "--internal", "dm-internal")
 	assertNwIsAvailable(c, "dm-internal")
 	nr := getNetworkResource(c, "dm-internal")
@@ -412,7 +413,7 @@ func (s *DockerSuite) TestDockerNetworkMacVlanBridgeInternalMode(c *check.C) {
 
 func (s *DockerSuite) TestDockerNetworkIpvlanL2NilParent(c *check.C) {
 	// ipvlan l2 mode - dummy parent interface is provisioned dynamically
-	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=ipvlan", "di-nil-parent")
 	assertNwIsAvailable(c, "di-nil-parent")
 
@@ -429,7 +430,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL2NilParent(c *check.C) {
 
 func (s *DockerSuite) TestDockerNetworkIpvlanL2InternalMode(c *check.C) {
 	// ipvlan l2 mode --internal containers can communicate inside the network but not externally
-	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=ipvlan", "--internal", "di-internal")
 	assertNwIsAvailable(c, "di-internal")
 	nr := getNetworkResource(c, "di-internal")
@@ -451,7 +452,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL2InternalMode(c *check.C) {
 
 func (s *DockerSuite) TestDockerNetworkIpvlanL3NilParent(c *check.C) {
 	// ipvlan l3 mode - dummy parent interface is provisioned dynamically
-	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.230.0/24",
 		"--subnet=172.28.220.0/24", "-o", "ipvlan_mode=l3", "di-nil-parent-l3")
 	assertNwIsAvailable(c, "di-nil-parent-l3")
@@ -469,7 +470,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL3NilParent(c *check.C) {
 
 func (s *DockerSuite) TestDockerNetworkIpvlanL3InternalMode(c *check.C) {
 	// ipvlan l3 mode --internal containers can communicate inside the network but not externally
-	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.230.0/24",
 		"--subnet=172.28.220.0/24", "-o", "ipvlan_mode=l3", "--internal", "di-internal-l3")
 	assertNwIsAvailable(c, "di-internal-l3")
@@ -492,7 +493,7 @@ func (s *DockerSuite) TestDockerNetworkIpvlanL3InternalMode(c *check.C) {
 
 func (s *DockerSuite) TestDockerNetworkMacVlanExistingParent(c *check.C) {
 	// macvlan bridge mode - empty parent interface containers can reach each other internally but not externally
-	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	netName := "dm-parent-exists"
 	out, err := createMasterDummy(c, "dm-dummy0")
 	//out, err := createVlanInterface(c, "dm-parent", "dm-slave", "macvlan", "bridge")
@@ -512,7 +513,7 @@ func (s *DockerSuite) TestDockerNetworkMacVlanExistingParent(c *check.C) {
 
 func (s *DockerSuite) TestDockerNetworkMacVlanSubinterface(c *check.C) {
 	// macvlan bridge mode -  empty parent interface containers can reach each other internally but not externally
-	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon)
 	netName := "dm-subinterface"
 	out, err := createMasterDummy(c, "dm-dummy0")
 	c.Assert(err, check.IsNil, check.Commentf(out))

+ 7 - 1
integration-cli/docker_test_vars.go

@@ -61,6 +61,10 @@ var (
 	volumesConfigPath    string
 	containerStoragePath string
 
+	// experimentalDaemon tell whether the main daemon has
+	// experimental features enabled or not
+	experimentalDaemon bool
+
 	// daemonStorageDriver is held globally so that tests can know the storage
 	// driver of the daemon. This is initialized in docker_utils by sending
 	// a version call to the daemon and examining the response header.
@@ -128,13 +132,15 @@ func init() {
 	// /info endpoint for the specific root dir
 	dockerBasePath = "/var/lib/docker"
 	type Info struct {
-		DockerRootDir string
+		DockerRootDir     string
+		ExperimentalBuild bool
 	}
 	var i Info
 	status, b, err := sockRequest("GET", "/info", nil)
 	if err == nil && status == 200 {
 		if err = json.Unmarshal(b, &i); err == nil {
 			dockerBasePath = i.DockerRootDir
+			experimentalDaemon = i.ExperimentalBuild
 		}
 	}
 	volumesConfigPath = dockerBasePath + "/volumes"

+ 2 - 3
integration-cli/requirements.go

@@ -9,7 +9,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/docker/docker/utils"
 	"github.com/go-check/check"
 )
 
@@ -31,11 +30,11 @@ var (
 		"Test requires a Linux daemon",
 	}
 	ExperimentalDaemon = testRequirement{
-		func() bool { return utils.ExperimentalBuild() },
+		func() bool { return experimentalDaemon },
 		"Test requires an experimental daemon",
 	}
 	NotExperimentalDaemon = testRequirement{
-		func() bool { return !utils.ExperimentalBuild() },
+		func() bool { return !experimentalDaemon },
 		"Test requires a non experimental daemon",
 	}
 	IsAmd64 = testRequirement{

+ 4 - 0
man/dockerd.8.md

@@ -27,6 +27,7 @@ dockerd - Enable daemon mode
 [**--dns-search**[=*[]*]]
 [**--exec-opt**[=*[]*]]
 [**--exec-root**[=*/var/run/docker*]]
+[**--experimental**[=*false*]]
 [**--fixed-cidr**[=*FIXED-CIDR*]]
 [**--fixed-cidr-v6**[=*FIXED-CIDR-V6*]]
 [**-G**|**--group**[=*docker*]]
@@ -146,6 +147,9 @@ format.
 **--exec-root**=""
   Path to use as the root of the Docker execution state files. Default is `/var/run/docker`.
 
+**--experimental**=""
+  Enable the daemon experimental features.
+
 **--fixed-cidr**=""
   IPv4 subnet for fixed IPs (e.g., 10.20.0.0/16); this subnet must be nested in the bridge subnet (which is defined by \-b or \-\-bip)
 

+ 0 - 2
plugin/backend.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package plugin
 
 import (

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác