Browse Source

add optional fields in daemon.json to enable buildkit

Signed-off-by: Anda Xu <anda.xu@docker.com>
Anda Xu 7 years ago
parent
commit
2be17666b4

+ 10 - 6
api/server/router/build/build.go

@@ -1,17 +1,21 @@
 package build // import "github.com/docker/docker/api/server/router/build"
 package build // import "github.com/docker/docker/api/server/router/build"
 
 
-import "github.com/docker/docker/api/server/router"
+import (
+	"github.com/docker/docker/api/server/router"
+	"github.com/docker/docker/api/types"
+)
 
 
 // buildRouter is a router to talk with the build controller
 // buildRouter is a router to talk with the build controller
 type buildRouter struct {
 type buildRouter struct {
-	backend Backend
-	daemon  experimentalProvider
-	routes  []router.Route
+	backend        Backend
+	daemon         experimentalProvider
+	routes         []router.Route
+	builderVersion types.BuilderVersion
 }
 }
 
 
 // NewRouter initializes a new build router
 // NewRouter initializes a new build router
-func NewRouter(b Backend, d experimentalProvider) router.Router {
-	r := &buildRouter{backend: b, daemon: d}
+func NewRouter(b Backend, d experimentalProvider, bv types.BuilderVersion) router.Router {
+	r := &buildRouter{backend: b, daemon: d, builderVersion: bv}
 	r.initRoutes()
 	r.initRoutes()
 	return r
 	return r
 }
 }

+ 4 - 2
api/server/router/build/build_routes.go

@@ -230,8 +230,10 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
 		return errdefs.InvalidParameter(errors.New("squash is only supported with experimental mode"))
 		return errdefs.InvalidParameter(errors.New("squash is only supported with experimental mode"))
 	}
 	}
 
 
-	if buildOptions.Version == types.BuilderBuildKit && !br.daemon.HasExperimental() {
-		return errdefs.InvalidParameter(errors.New("buildkit is only supported with experimental mode"))
+	// check if the builder feature has been enabled from daemon as well.
+	if buildOptions.Version == types.BuilderBuildKit &&
+		(br.builderVersion != types.BuilderBuildKit || !br.daemon.HasExperimental()) {
+		return errdefs.InvalidParameter(errors.New("buildkit is not enabled on daemon"))
 	}
 	}
 
 
 	out := io.Writer(output)
 	out := io.Writer(output)

+ 14 - 11
api/server/router/system/system.go

@@ -2,6 +2,7 @@ package system // import "github.com/docker/docker/api/server/router/system"
 
 
 import (
 import (
 	"github.com/docker/docker/api/server/router"
 	"github.com/docker/docker/api/server/router"
+	"github.com/docker/docker/api/types"
 	buildkit "github.com/docker/docker/builder/builder-next"
 	buildkit "github.com/docker/docker/builder/builder-next"
 	"github.com/docker/docker/builder/fscache"
 	"github.com/docker/docker/builder/fscache"
 )
 )
@@ -9,25 +10,27 @@ import (
 // systemRouter provides information about the Docker system overall.
 // systemRouter provides information about the Docker system overall.
 // It gathers information about host, daemon and container events.
 // It gathers information about host, daemon and container events.
 type systemRouter struct {
 type systemRouter struct {
-	backend Backend
-	cluster ClusterBackend
-	routes  []router.Route
-	fscache *fscache.FSCache // legacy
-	builder *buildkit.Builder
+	backend        Backend
+	cluster        ClusterBackend
+	routes         []router.Route
+	fscache        *fscache.FSCache // legacy
+	builder        *buildkit.Builder
+	builderVersion types.BuilderVersion
 }
 }
 
 
 // NewRouter initializes a new system router
 // NewRouter initializes a new system router
-func NewRouter(b Backend, c ClusterBackend, fscache *fscache.FSCache, builder *buildkit.Builder) router.Router {
+func NewRouter(b Backend, c ClusterBackend, fscache *fscache.FSCache, builder *buildkit.Builder, bv types.BuilderVersion) router.Router {
 	r := &systemRouter{
 	r := &systemRouter{
-		backend: b,
-		cluster: c,
-		fscache: fscache,
-		builder: builder,
+		backend:        b,
+		cluster:        c,
+		fscache:        fscache,
+		builder:        builder,
+		builderVersion: bv,
 	}
 	}
 
 
 	r.routes = []router.Route{
 	r.routes = []router.Route{
 		router.NewOptionsRoute("/{anyroute:.*}", optionsHandler),
 		router.NewOptionsRoute("/{anyroute:.*}", optionsHandler),
-		router.NewGetRoute("/_ping", pingHandler),
+		router.NewGetRoute("/_ping", r.pingHandler),
 		router.NewGetRoute("/events", r.getEvents, router.WithCancel),
 		router.NewGetRoute("/events", r.getEvents, router.WithCancel),
 		router.NewGetRoute("/info", r.getInfo),
 		router.NewGetRoute("/info", r.getInfo),
 		router.NewGetRoute("/version", r.getVersion),
 		router.NewGetRoute("/version", r.getVersion),

+ 4 - 1
api/server/router/system/system_routes.go

@@ -25,7 +25,10 @@ func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request,
 	return nil
 	return nil
 }
 }
 
 
-func pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+func (s *systemRouter) pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	if bv := s.builderVersion; bv != "" {
+		w.Header().Set("Builder-Version", string(bv))
+	}
 	_, err := w.Write([]byte{'O', 'K'})
 	_, err := w.Write([]byte{'O', 'K'})
 	return err
 	return err
 }
 }

+ 3 - 0
api/swagger.yaml

@@ -6972,6 +6972,9 @@ paths:
             API-Version:
             API-Version:
               type: "string"
               type: "string"
               description: "Max API Version the server supports"
               description: "Max API Version the server supports"
+            BuildKit-Version:
+              type: "string"
+              description: "Default version of docker image builder"
             Docker-Experimental:
             Docker-Experimental:
               type: "boolean"
               type: "boolean"
               description: "If the server is running with experimental mode enabled"
               description: "If the server is running with experimental mode enabled"

+ 4 - 3
api/types/types.go

@@ -102,9 +102,10 @@ type ContainerStats struct {
 // Ping contains response of Engine API:
 // Ping contains response of Engine API:
 // GET "/_ping"
 // GET "/_ping"
 type Ping struct {
 type Ping struct {
-	APIVersion   string
-	OSType       string
-	Experimental bool
+	APIVersion     string
+	OSType         string
+	Experimental   bool
+	BuilderVersion BuilderVersion
 }
 }
 
 
 // ComponentVersion describes the version information for a specific component.
 // ComponentVersion describes the version information for a specific component.

+ 4 - 1
client/ping.go

@@ -7,7 +7,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 )
 )
 
 
-// Ping pings the server and returns the value of the "Docker-Experimental", "OS-Type" & "API-Version" headers
+// Ping pings the server and returns the value of the "Docker-Experimental", "Builder-Version", "OS-Type" & "API-Version" headers
 func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
 func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
 	var ping types.Ping
 	var ping types.Ping
 	req, err := cli.buildRequest("GET", path.Join(cli.basePath, "/_ping"), nil, nil)
 	req, err := cli.buildRequest("GET", path.Join(cli.basePath, "/_ping"), nil, nil)
@@ -27,6 +27,9 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
 			ping.Experimental = true
 			ping.Experimental = true
 		}
 		}
 		ping.OSType = serverResp.header.Get("OSType")
 		ping.OSType = serverResp.header.Get("OSType")
+		if bv := serverResp.header.Get("Builder-Version"); bv != "" {
+			ping.BuilderVersion = types.BuilderVersion(bv)
+		}
 	}
 	}
 	return ping, cli.checkResponseErr(serverResp)
 	return ping, cli.checkResponseErr(serverResp)
 }
 }

+ 0 - 1
cmd/dockerd/config.go

@@ -65,7 +65,6 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
 
 
 	flags.StringVar(&conf.SwarmDefaultAdvertiseAddr, "swarm-default-advertise-addr", "", "Set default address or interface for swarm advertised address")
 	flags.StringVar(&conf.SwarmDefaultAdvertiseAddr, "swarm-default-advertise-addr", "", "Set default address or interface for swarm advertised address")
 	flags.BoolVar(&conf.Experimental, "experimental", false, "Enable experimental features")
 	flags.BoolVar(&conf.Experimental, "experimental", false, "Enable experimental features")
-
 	flags.StringVar(&conf.MetricsAddress, "metrics-addr", "", "Set default address and port to serve the metrics api on")
 	flags.StringVar(&conf.MetricsAddress, "metrics-addr", "", "Set default address and port to serve the metrics api on")
 
 
 	flags.Var(opts.NewNamedListOptsRef("node-generic-resources", &conf.NodeGenericResources, opts.ValidateSingleGenericResource), "node-generic-resource", "Advertise user-defined resource")
 	flags.Var(opts.NewNamedListOptsRef("node-generic-resources", &conf.NodeGenericResources, opts.ValidateSingleGenericResource), "node-generic-resource", "Advertise user-defined resource")

+ 16 - 7
cmd/dockerd/daemon.go

@@ -27,6 +27,7 @@ import (
 	swarmrouter "github.com/docker/docker/api/server/router/swarm"
 	swarmrouter "github.com/docker/docker/api/server/router/swarm"
 	systemrouter "github.com/docker/docker/api/server/router/system"
 	systemrouter "github.com/docker/docker/api/server/router/system"
 	"github.com/docker/docker/api/server/router/volume"
 	"github.com/docker/docker/api/server/router/volume"
+	"github.com/docker/docker/api/types"
 	buildkit "github.com/docker/docker/builder/builder-next"
 	buildkit "github.com/docker/docker/builder/builder-next"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/builder/fscache"
 	"github.com/docker/docker/builder/fscache"
@@ -253,6 +254,7 @@ type routerOptions struct {
 	buildBackend   *buildbackend.Backend
 	buildBackend   *buildbackend.Backend
 	buildCache     *fscache.FSCache // legacy
 	buildCache     *fscache.FSCache // legacy
 	buildkit       *buildkit.Builder
 	buildkit       *buildkit.Builder
+	builderVersion types.BuilderVersion
 	daemon         *daemon.Daemon
 	daemon         *daemon.Daemon
 	api            *apiserver.Server
 	api            *apiserver.Server
 	cluster        *cluster.Cluster
 	cluster        *cluster.Cluster
@@ -283,8 +285,7 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio
 	if err != nil {
 	if err != nil {
 		return opts, err
 		return opts, err
 	}
 	}
-
-	buildkit, err := buildkit.New(buildkit.Opt{
+	bk, err := buildkit.New(buildkit.Opt{
 		SessionManager: sm,
 		SessionManager: sm,
 		Root:           filepath.Join(config.Root, "buildkit"),
 		Root:           filepath.Join(config.Root, "buildkit"),
 		Dist:           daemon.DistributionServices(),
 		Dist:           daemon.DistributionServices(),
@@ -293,16 +294,24 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio
 		return opts, err
 		return opts, err
 	}
 	}
 
 
-	bb, err := buildbackend.NewBackend(daemon.ImageService(), manager, buildCache, buildkit)
+	bb, err := buildbackend.NewBackend(daemon.ImageService(), manager, buildCache, bk)
 	if err != nil {
 	if err != nil {
 		return opts, errors.Wrap(err, "failed to create buildmanager")
 		return opts, errors.Wrap(err, "failed to create buildmanager")
 	}
 	}
-
+	var bv types.BuilderVersion
+	if v, ok := config.Features["buildkit"]; ok {
+		if v {
+			bv = types.BuilderBuildKit
+		} else {
+			bv = types.BuilderV1
+		}
+	}
 	return routerOptions{
 	return routerOptions{
 		sessionManager: sm,
 		sessionManager: sm,
 		buildBackend:   bb,
 		buildBackend:   bb,
 		buildCache:     buildCache,
 		buildCache:     buildCache,
-		buildkit:       buildkit,
+		buildkit:       bk,
+		builderVersion: bv,
 		daemon:         daemon,
 		daemon:         daemon,
 	}, nil
 	}, nil
 }
 }
@@ -476,9 +485,9 @@ func initRouter(opts routerOptions) {
 		checkpointrouter.NewRouter(opts.daemon, decoder),
 		checkpointrouter.NewRouter(opts.daemon, decoder),
 		container.NewRouter(opts.daemon, decoder),
 		container.NewRouter(opts.daemon, decoder),
 		image.NewRouter(opts.daemon.ImageService()),
 		image.NewRouter(opts.daemon.ImageService()),
-		systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache, opts.buildkit),
+		systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache, opts.buildkit, opts.builderVersion),
 		volume.NewRouter(opts.daemon.VolumesService()),
 		volume.NewRouter(opts.daemon.VolumesService()),
-		build.NewRouter(opts.buildBackend, opts.daemon),
+		build.NewRouter(opts.buildBackend, opts.daemon, opts.builderVersion),
 		sessionrouter.NewRouter(opts.sessionManager),
 		sessionrouter.NewRouter(opts.sessionManager),
 		swarmrouter.NewRouter(opts.cluster),
 		swarmrouter.NewRouter(opts.cluster),
 		pluginrouter.NewRouter(opts.daemon.PluginManager()),
 		pluginrouter.NewRouter(opts.daemon.PluginManager()),

+ 12 - 1
daemon/config/config.go

@@ -56,6 +56,13 @@ var flatOptions = map[string]bool{
 	"default-ulimits":    true,
 	"default-ulimits":    true,
 }
 }
 
 
+// skipValidateOptions contains configuration keys
+// that will be skipped from findConfigurationConflicts
+// for unknown flag validation.
+var skipValidateOptions = map[string]bool{
+	"features": true,
+}
+
 // LogConfig represents the default log configuration.
 // LogConfig represents the default log configuration.
 // It includes json tags to deserialize configuration from a file
 // It includes json tags to deserialize configuration from a file
 // using the same names that the flags in the command line use.
 // using the same names that the flags in the command line use.
@@ -203,6 +210,10 @@ type CommonConfig struct {
 	// should be configured with the CRI plugin enabled. This allows using
 	// should be configured with the CRI plugin enabled. This allows using
 	// Docker's containerd instance directly with a Kubernetes kubelet.
 	// Docker's containerd instance directly with a Kubernetes kubelet.
 	CriContainerd bool `json:"cri-containerd,omitempty"`
 	CriContainerd bool `json:"cri-containerd,omitempty"`
+
+	// Features contains a list of feature key value pairs indicating what features are enabled or disabled.
+	// If a certain feature doesn't appear in this list then it's unset (i.e. neither true nor false).
+	Features map[string]bool `json:"features,omitempty"`
 }
 }
 
 
 // IsValueSet returns true if a configuration value
 // IsValueSet returns true if a configuration value
@@ -444,7 +455,7 @@ func findConfigurationConflicts(config map[string]interface{}, flags *pflag.Flag
 	// 1. Search keys from the file that we don't recognize as flags.
 	// 1. Search keys from the file that we don't recognize as flags.
 	unknownKeys := make(map[string]interface{})
 	unknownKeys := make(map[string]interface{})
 	for key, value := range config {
 	for key, value := range config {
-		if flag := flags.Lookup(key); flag == nil {
+		if flag := flags.Lookup(key); flag == nil && !skipValidateOptions[key] {
 			unknownKeys[key] = value
 			unknownKeys[key] = value
 		}
 		}
 	}
 	}