浏览代码

builder: add graceful cancellation endpoint

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Tonis Tiigi 7 年之前
父节点
当前提交
0bddd4ccfe

+ 8 - 2
api/server/backend/build/backend.go

@@ -79,8 +79,10 @@ func (b *Backend) Build(ctx context.Context, config backend.BuildConfig) (string
 		}
 		}
 	}
 	}
 
 
-	stdout := config.ProgressWriter.StdoutFormatter
-	fmt.Fprintf(stdout, "Successfully built %s\n", stringid.TruncateID(imageID))
+	if !useBuildKit {
+		stdout := config.ProgressWriter.StdoutFormatter
+		fmt.Fprintf(stdout, "Successfully built %s\n", stringid.TruncateID(imageID))
+	}
 	err = tagger.TagImages(image.ID(imageID))
 	err = tagger.TagImages(image.ID(imageID))
 	return imageID, err
 	return imageID, err
 }
 }
@@ -94,6 +96,10 @@ func (b *Backend) PruneCache(ctx context.Context) (*types.BuildCachePruneReport,
 	return &types.BuildCachePruneReport{SpaceReclaimed: size}, nil
 	return &types.BuildCachePruneReport{SpaceReclaimed: size}, nil
 }
 }
 
 
+func (b *Backend) Cancel(ctx context.Context, id string) error {
+	return b.buildkit.Cancel(ctx, id)
+}
+
 func squashBuild(build *builder.Result, imageComponent ImageComponent) (string, error) {
 func squashBuild(build *builder.Result, imageComponent ImageComponent) (string, error) {
 	var fromID string
 	var fromID string
 	if build.FromImage != nil {
 	if build.FromImage != nil {

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

@@ -15,6 +15,8 @@ type Backend interface {
 
 
 	// Prune build cache
 	// Prune build cache
 	PruneCache(context.Context) (*types.BuildCachePruneReport, error)
 	PruneCache(context.Context) (*types.BuildCachePruneReport, error)
+
+	Cancel(context.Context, string) error
 }
 }
 
 
 type experimentalProvider interface {
 type experimentalProvider interface {

+ 1 - 0
api/server/router/build/build.go

@@ -25,5 +25,6 @@ func (r *buildRouter) initRoutes() {
 	r.routes = []router.Route{
 	r.routes = []router.Route{
 		router.NewPostRoute("/build", r.postBuild, router.WithCancel),
 		router.NewPostRoute("/build", r.postBuild, router.WithCancel),
 		router.NewPostRoute("/build/prune", r.postPrune, router.WithCancel),
 		router.NewPostRoute("/build/prune", r.postPrune, router.WithCancel),
+		router.NewPostRoute("/build/cancel", r.postCancel),
 	}
 	}
 }
 }

+ 12 - 0
api/server/router/build/build_routes.go

@@ -145,6 +145,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
 		options.CacheFrom = cacheFrom
 		options.CacheFrom = cacheFrom
 	}
 	}
 	options.SessionID = r.FormValue("session")
 	options.SessionID = r.FormValue("session")
+	options.BuildID = r.FormValue("buildid")
 
 
 	return options, nil
 	return options, nil
 }
 }
@@ -157,6 +158,17 @@ func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r *
 	return httputils.WriteJSON(w, http.StatusOK, report)
 	return httputils.WriteJSON(w, http.StatusOK, report)
 }
 }
 
 
+func (br *buildRouter) postCancel(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
+	w.Header().Set("Content-Type", "application/json")
+
+	id := r.FormValue("id")
+	if id == "" {
+		return errors.Errorf("build ID not provided")
+	}
+
+	return br.backend.Cancel(ctx, id)
+}
+
 func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	var (
 	var (
 		notVerboseBuffer = bytes.NewBuffer(nil)
 		notVerboseBuffer = bytes.NewBuffer(nil)

+ 1 - 0
api/types/client.go

@@ -181,6 +181,7 @@ type ImageBuildOptions struct {
 	Target      string
 	Target      string
 	SessionID   string
 	SessionID   string
 	Platform    string
 	Platform    string
+	BuildID     string
 }
 }
 
 
 // ImageBuildResponse holds information
 // ImageBuildResponse holds information

+ 22 - 0
builder/builder-next/builder.go

@@ -59,6 +59,9 @@ type Opt struct {
 type Builder struct {
 type Builder struct {
 	controller *control.Controller
 	controller *control.Controller
 	results    *results
 	results    *results
+
+	mu   sync.Mutex
+	jobs map[string]func()
 }
 }
 
 
 func New(opt Opt) (*Builder, error) {
 func New(opt Opt) (*Builder, error) {
@@ -71,11 +74,30 @@ func New(opt Opt) (*Builder, error) {
 	b := &Builder{
 	b := &Builder{
 		controller: c,
 		controller: c,
 		results:    results,
 		results:    results,
+		jobs:       map[string]func(){},
 	}
 	}
 	return b, nil
 	return b, nil
 }
 }
 
 
+func (b *Builder) Cancel(ctx context.Context, id string) error {
+	b.mu.Lock()
+	if cancel, ok := b.jobs[id]; ok {
+		cancel()
+	}
+	b.mu.Unlock()
+	return nil
+}
+
 func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.Result, error) {
 func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.Result, error) {
+	if buildID := opt.Options.BuildID; buildID != "" {
+		b.mu.Lock()
+		ctx, b.jobs[buildID] = context.WithCancel(ctx)
+		b.mu.Unlock()
+		defer func() {
+			delete(b.jobs, buildID)
+		}()
+	}
+
 	id := identity.NewID()
 	id := identity.NewID()
 
 
 	attrs := map[string]string{
 	attrs := map[string]string{

+ 21 - 0
client/build_cancel.go

@@ -0,0 +1,21 @@
+package client // import "github.com/docker/docker/client"
+
+import (
+	"net/url"
+
+	"golang.org/x/net/context"
+)
+
+// BuildCancel requests the daemon to cancel ongoing build request
+func (cli *Client) BuildCancel(ctx context.Context, id string) error {
+	query := url.Values{}
+	query.Set("id", id)
+
+	serverResp, err := cli.post(ctx, "/build/cancel", query, nil, nil)
+	if err != nil {
+		return err
+	}
+	defer ensureReaderClosed(serverResp)
+
+	return nil
+}

+ 3 - 0
client/image_build.go

@@ -133,5 +133,8 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur
 	if options.Platform != "" {
 	if options.Platform != "" {
 		query.Set("platform", strings.ToLower(options.Platform))
 		query.Set("platform", strings.ToLower(options.Platform))
 	}
 	}
+	if options.BuildID != "" {
+		query.Set("buildid", options.BuildID)
+	}
 	return query, nil
 	return query, nil
 }
 }

+ 1 - 0
client/interface.go

@@ -86,6 +86,7 @@ type DistributionAPIClient interface {
 type ImageAPIClient interface {
 type ImageAPIClient interface {
 	ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error)
 	ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error)
 	BuildCachePrune(ctx context.Context) (*types.BuildCachePruneReport, error)
 	BuildCachePrune(ctx context.Context) (*types.BuildCachePruneReport, error)
+	BuildCancel(ctx context.Context, id string) error
 	ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error)
 	ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error)
 	ImageHistory(ctx context.Context, image string) ([]image.HistoryResponseItem, error)
 	ImageHistory(ctx context.Context, image string) ([]image.HistoryResponseItem, error)
 	ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
 	ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)