Browse code

add optional fields in daemon.json to enable buildkit

Signed-off-by: Anda Xu <anda.xu@docker.com>

Anda Xu authored on 2018/08/06 10:52:35
Showing 10 changed files
... ...
@@ -1,17 +1,21 @@
1 1
 package build // import "github.com/docker/docker/api/server/router/build"
2 2
 
3
-import "github.com/docker/docker/api/server/router"
3
+import (
4
+	"github.com/docker/docker/api/server/router"
5
+	"github.com/docker/docker/api/types"
6
+)
4 7
 
5 8
 // buildRouter is a router to talk with the build controller
6 9
 type buildRouter struct {
7
-	backend Backend
8
-	daemon  experimentalProvider
9
-	routes  []router.Route
10
+	backend        Backend
11
+	daemon         experimentalProvider
12
+	routes         []router.Route
13
+	builderVersion types.BuilderVersion
10 14
 }
11 15
 
12 16
 // NewRouter initializes a new build router
13
-func NewRouter(b Backend, d experimentalProvider) router.Router {
14
-	r := &buildRouter{backend: b, daemon: d}
17
+func NewRouter(b Backend, d experimentalProvider, bv types.BuilderVersion) router.Router {
18
+	r := &buildRouter{backend: b, daemon: d, builderVersion: bv}
15 19
 	r.initRoutes()
16 20
 	return r
17 21
 }
... ...
@@ -230,8 +230,10 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
230 230
 		return errdefs.InvalidParameter(errors.New("squash is only supported with experimental mode"))
231 231
 	}
232 232
 
233
-	if buildOptions.Version == types.BuilderBuildKit && !br.daemon.HasExperimental() {
234
-		return errdefs.InvalidParameter(errors.New("buildkit is only supported with experimental mode"))
233
+	// check if the builder feature has been enabled from daemon as well.
234
+	if buildOptions.Version == types.BuilderBuildKit &&
235
+		(br.builderVersion != types.BuilderBuildKit || !br.daemon.HasExperimental()) {
236
+		return errdefs.InvalidParameter(errors.New("buildkit is not enabled on daemon"))
235 237
 	}
236 238
 
237 239
 	out := io.Writer(output)
... ...
@@ -2,6 +2,7 @@ package system // import "github.com/docker/docker/api/server/router/system"
2 2
 
3 3
 import (
4 4
 	"github.com/docker/docker/api/server/router"
5
+	"github.com/docker/docker/api/types"
5 6
 	buildkit "github.com/docker/docker/builder/builder-next"
6 7
 	"github.com/docker/docker/builder/fscache"
7 8
 )
... ...
@@ -9,25 +10,27 @@ import (
9 9
 // systemRouter provides information about the Docker system overall.
10 10
 // It gathers information about host, daemon and container events.
11 11
 type systemRouter struct {
12
-	backend Backend
13
-	cluster ClusterBackend
14
-	routes  []router.Route
15
-	fscache *fscache.FSCache // legacy
16
-	builder *buildkit.Builder
12
+	backend        Backend
13
+	cluster        ClusterBackend
14
+	routes         []router.Route
15
+	fscache        *fscache.FSCache // legacy
16
+	builder        *buildkit.Builder
17
+	builderVersion types.BuilderVersion
17 18
 }
18 19
 
19 20
 // NewRouter initializes a new system router
20
-func NewRouter(b Backend, c ClusterBackend, fscache *fscache.FSCache, builder *buildkit.Builder) router.Router {
21
+func NewRouter(b Backend, c ClusterBackend, fscache *fscache.FSCache, builder *buildkit.Builder, bv types.BuilderVersion) router.Router {
21 22
 	r := &systemRouter{
22
-		backend: b,
23
-		cluster: c,
24
-		fscache: fscache,
25
-		builder: builder,
23
+		backend:        b,
24
+		cluster:        c,
25
+		fscache:        fscache,
26
+		builder:        builder,
27
+		builderVersion: bv,
26 28
 	}
27 29
 
28 30
 	r.routes = []router.Route{
29 31
 		router.NewOptionsRoute("/{anyroute:.*}", optionsHandler),
30
-		router.NewGetRoute("/_ping", pingHandler),
32
+		router.NewGetRoute("/_ping", r.pingHandler),
31 33
 		router.NewGetRoute("/events", r.getEvents, router.WithCancel),
32 34
 		router.NewGetRoute("/info", r.getInfo),
33 35
 		router.NewGetRoute("/version", r.getVersion),
... ...
@@ -25,7 +25,10 @@ func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request,
25 25
 	return nil
26 26
 }
27 27
 
28
-func pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
28
+func (s *systemRouter) pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
29
+	if bv := s.builderVersion; bv != "" {
30
+		w.Header().Set("Builder-Version", string(bv))
31
+	}
29 32
 	_, err := w.Write([]byte{'O', 'K'})
30 33
 	return err
31 34
 }
... ...
@@ -6972,6 +6972,9 @@ paths:
6972 6972
             API-Version:
6973 6973
               type: "string"
6974 6974
               description: "Max API Version the server supports"
6975
+            BuildKit-Version:
6976
+              type: "string"
6977
+              description: "Default version of docker image builder"
6975 6978
             Docker-Experimental:
6976 6979
               type: "boolean"
6977 6980
               description: "If the server is running with experimental mode enabled"
... ...
@@ -102,9 +102,10 @@ type ContainerStats struct {
102 102
 // Ping contains response of Engine API:
103 103
 // GET "/_ping"
104 104
 type Ping struct {
105
-	APIVersion   string
106
-	OSType       string
107
-	Experimental bool
105
+	APIVersion     string
106
+	OSType         string
107
+	Experimental   bool
108
+	BuilderVersion BuilderVersion
108 109
 }
109 110
 
110 111
 // ComponentVersion describes the version information for a specific component.
... ...
@@ -7,7 +7,7 @@ import (
7 7
 	"github.com/docker/docker/api/types"
8 8
 )
9 9
 
10
-// Ping pings the server and returns the value of the "Docker-Experimental", "OS-Type" & "API-Version" headers
10
+// Ping pings the server and returns the value of the "Docker-Experimental", "Builder-Version", "OS-Type" & "API-Version" headers
11 11
 func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
12 12
 	var ping types.Ping
13 13
 	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) {
27 27
 			ping.Experimental = true
28 28
 		}
29 29
 		ping.OSType = serverResp.header.Get("OSType")
30
+		if bv := serverResp.header.Get("Builder-Version"); bv != "" {
31
+			ping.BuilderVersion = types.BuilderVersion(bv)
32
+		}
30 33
 	}
31 34
 	return ping, cli.checkResponseErr(serverResp)
32 35
 }
... ...
@@ -65,7 +65,6 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
65 65
 
66 66
 	flags.StringVar(&conf.SwarmDefaultAdvertiseAddr, "swarm-default-advertise-addr", "", "Set default address or interface for swarm advertised address")
67 67
 	flags.BoolVar(&conf.Experimental, "experimental", false, "Enable experimental features")
68
-
69 68
 	flags.StringVar(&conf.MetricsAddress, "metrics-addr", "", "Set default address and port to serve the metrics api on")
70 69
 
71 70
 	flags.Var(opts.NewNamedListOptsRef("node-generic-resources", &conf.NodeGenericResources, opts.ValidateSingleGenericResource), "node-generic-resource", "Advertise user-defined resource")
... ...
@@ -27,6 +27,7 @@ import (
27 27
 	swarmrouter "github.com/docker/docker/api/server/router/swarm"
28 28
 	systemrouter "github.com/docker/docker/api/server/router/system"
29 29
 	"github.com/docker/docker/api/server/router/volume"
30
+	"github.com/docker/docker/api/types"
30 31
 	buildkit "github.com/docker/docker/builder/builder-next"
31 32
 	"github.com/docker/docker/builder/dockerfile"
32 33
 	"github.com/docker/docker/builder/fscache"
... ...
@@ -253,6 +254,7 @@ type routerOptions struct {
253 253
 	buildBackend   *buildbackend.Backend
254 254
 	buildCache     *fscache.FSCache // legacy
255 255
 	buildkit       *buildkit.Builder
256
+	builderVersion types.BuilderVersion
256 257
 	daemon         *daemon.Daemon
257 258
 	api            *apiserver.Server
258 259
 	cluster        *cluster.Cluster
... ...
@@ -283,8 +285,7 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio
283 283
 	if err != nil {
284 284
 		return opts, err
285 285
 	}
286
-
287
-	buildkit, err := buildkit.New(buildkit.Opt{
286
+	bk, err := buildkit.New(buildkit.Opt{
288 287
 		SessionManager: sm,
289 288
 		Root:           filepath.Join(config.Root, "buildkit"),
290 289
 		Dist:           daemon.DistributionServices(),
... ...
@@ -293,16 +294,24 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio
293 293
 		return opts, err
294 294
 	}
295 295
 
296
-	bb, err := buildbackend.NewBackend(daemon.ImageService(), manager, buildCache, buildkit)
296
+	bb, err := buildbackend.NewBackend(daemon.ImageService(), manager, buildCache, bk)
297 297
 	if err != nil {
298 298
 		return opts, errors.Wrap(err, "failed to create buildmanager")
299 299
 	}
300
-
300
+	var bv types.BuilderVersion
301
+	if v, ok := config.Features["buildkit"]; ok {
302
+		if v {
303
+			bv = types.BuilderBuildKit
304
+		} else {
305
+			bv = types.BuilderV1
306
+		}
307
+	}
301 308
 	return routerOptions{
302 309
 		sessionManager: sm,
303 310
 		buildBackend:   bb,
304 311
 		buildCache:     buildCache,
305
-		buildkit:       buildkit,
312
+		buildkit:       bk,
313
+		builderVersion: bv,
306 314
 		daemon:         daemon,
307 315
 	}, nil
308 316
 }
... ...
@@ -476,9 +485,9 @@ func initRouter(opts routerOptions) {
476 476
 		checkpointrouter.NewRouter(opts.daemon, decoder),
477 477
 		container.NewRouter(opts.daemon, decoder),
478 478
 		image.NewRouter(opts.daemon.ImageService()),
479
-		systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache, opts.buildkit),
479
+		systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache, opts.buildkit, opts.builderVersion),
480 480
 		volume.NewRouter(opts.daemon.VolumesService()),
481
-		build.NewRouter(opts.buildBackend, opts.daemon),
481
+		build.NewRouter(opts.buildBackend, opts.daemon, opts.builderVersion),
482 482
 		sessionrouter.NewRouter(opts.sessionManager),
483 483
 		swarmrouter.NewRouter(opts.cluster),
484 484
 		pluginrouter.NewRouter(opts.daemon.PluginManager()),
... ...
@@ -56,6 +56,13 @@ var flatOptions = map[string]bool{
56 56
 	"default-ulimits":    true,
57 57
 }
58 58
 
59
+// skipValidateOptions contains configuration keys
60
+// that will be skipped from findConfigurationConflicts
61
+// for unknown flag validation.
62
+var skipValidateOptions = map[string]bool{
63
+	"features": true,
64
+}
65
+
59 66
 // LogConfig represents the default log configuration.
60 67
 // It includes json tags to deserialize configuration from a file
61 68
 // using the same names that the flags in the command line use.
... ...
@@ -203,6 +210,10 @@ type CommonConfig struct {
203 203
 	// should be configured with the CRI plugin enabled. This allows using
204 204
 	// Docker's containerd instance directly with a Kubernetes kubelet.
205 205
 	CriContainerd bool `json:"cri-containerd,omitempty"`
206
+
207
+	// Features contains a list of feature key value pairs indicating what features are enabled or disabled.
208
+	// If a certain feature doesn't appear in this list then it's unset (i.e. neither true nor false).
209
+	Features map[string]bool `json:"features,omitempty"`
206 210
 }
207 211
 
208 212
 // IsValueSet returns true if a configuration value
... ...
@@ -444,7 +455,7 @@ func findConfigurationConflicts(config map[string]interface{}, flags *pflag.Flag
444 444
 	// 1. Search keys from the file that we don't recognize as flags.
445 445
 	unknownKeys := make(map[string]interface{})
446 446
 	for key, value := range config {
447
-		if flag := flags.Lookup(key); flag == nil {
447
+		if flag := flags.Lookup(key); flag == nil && !skipValidateOptions[key] {
448 448
 			unknownKeys[key] = value
449 449
 		}
450 450
 	}