浏览代码

introduce experimental watch command (skeletton)

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 2 年之前
父节点
当前提交
bb9cf32245

+ 31 - 0
cmd/compose/alpha.go

@@ -0,0 +1,31 @@
+/*
+
+   Copyright 2020 Docker Compose CLI authors
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+       http://www.apache.org/licenses/LICENSE-2.0
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"github.com/docker/compose/v2/pkg/api"
+	"github.com/spf13/cobra"
+)
+
+// alphaCommand groups all experimental subcommands
+func alphaCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
+	cmd := &cobra.Command{
+		Short:  "Experimental commands",
+		Use:    "alpha [COMMAND]",
+		Hidden: true,
+	}
+	cmd.AddCommand(watchCommand(p, backend))
+	return cmd
+}

+ 2 - 0
cmd/compose/compose.go

@@ -365,7 +365,9 @@ func RootCommand(streams api.Streams, backend api.Service) *cobra.Command { //no
 		pullCommand(&opts, backend),
 		createCommand(&opts, backend),
 		copyCommand(&opts, backend),
+		alphaCommand(&opts, backend),
 	)
+
 	c.Flags().SetInterspersed(false)
 	opts.addProjectFlags(c.Flags())
 	c.RegisterFlagCompletionFunc( //nolint:errcheck

+ 61 - 0
cmd/compose/watch.go

@@ -0,0 +1,61 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"context"
+	"fmt"
+	"os"
+
+	"github.com/docker/compose/v2/pkg/api"
+	"github.com/spf13/cobra"
+)
+
+type watchOptions struct {
+	*ProjectOptions
+	quiet bool
+}
+
+func watchCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
+	opts := watchOptions{
+		ProjectOptions: p,
+	}
+	cmd := &cobra.Command{
+		Use:   "watch [SERVICE...]",
+		Short: "EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated",
+		PreRunE: Adapt(func(ctx context.Context, args []string) error {
+			return nil
+		}),
+		RunE: Adapt(func(ctx context.Context, args []string) error {
+			return runWatch(ctx, backend, opts, args)
+		}),
+		ValidArgsFunction: completeServiceNames(p),
+	}
+
+	cmd.Flags().BoolVar(&opts.quiet, "quiet", false, "hide build output")
+	return cmd
+}
+
+func runWatch(ctx context.Context, backend api.Service, opts watchOptions, services []string) error {
+	fmt.Fprintln(os.Stderr, "watch command is EXPERIMENTAL")
+	project, err := opts.ToProject(nil)
+	if err != nil {
+		return err
+	}
+
+	return backend.Watch(ctx, project, services, api.WatchOptions{})
+}

+ 1 - 0
docs/reference/compose.md

@@ -7,6 +7,7 @@ Docker Compose
 
 | Name                            | Description                                                             |
 |:--------------------------------|:------------------------------------------------------------------------|
+| [`alpha`](compose_alpha.md)     | Experimental commands                                                   |
 | [`build`](compose_build.md)     | Build or rebuild services                                               |
 | [`convert`](compose_convert.md) | Converts the compose file to platform's canonical format                |
 | [`cp`](compose_cp.md)           | Copy files/folders between a service container and the local filesystem |

+ 15 - 0
docs/reference/compose_alpha.md

@@ -0,0 +1,15 @@
+# docker compose alpha
+
+<!---MARKER_GEN_START-->
+Experimental commands
+
+### Subcommands
+
+| Name                              | Description                                                                                          |
+|:----------------------------------|:-----------------------------------------------------------------------------------------------------|
+| [`watch`](compose_alpha_watch.md) | EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated |
+
+
+
+<!---MARKER_GEN_END-->
+

+ 14 - 0
docs/reference/compose_alpha_watch.md

@@ -0,0 +1,14 @@
+# docker compose alpha watch
+
+<!---MARKER_GEN_START-->
+EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated
+
+### Options
+
+| Name      | Type | Default | Description       |
+|:----------|:-----|:--------|:------------------|
+| `--quiet` |      |         | hide build output |
+
+
+<!---MARKER_GEN_END-->
+

+ 15 - 0
docs/reference/docker_compose_alpha.yaml

@@ -0,0 +1,15 @@
+command: docker compose alpha
+short: Experimental commands
+long: Experimental commands
+pname: docker compose
+plink: docker_compose.yaml
+cname:
+    - docker compose alpha watch
+clink:
+    - docker_compose_alpha_watch.yaml
+deprecated: false
+experimental: false
+experimentalcli: false
+kubernetes: false
+swarm: false
+

+ 25 - 0
docs/reference/docker_compose_alpha_watch.yaml

@@ -0,0 +1,25 @@
+command: docker compose alpha watch
+short: |
+    EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated
+long: |
+    EXPERIMENTAL - Watch build context for service and rebuild/refresh containers when files are updated
+usage: docker compose alpha watch [SERVICE...]
+pname: docker compose alpha
+plink: docker_compose_alpha.yaml
+options:
+    - option: quiet
+      value_type: bool
+      default_value: "false"
+      description: hide build output
+      deprecated: false
+      hidden: false
+      experimental: false
+      experimentalcli: false
+      kubernetes: false
+      swarm: false
+deprecated: false
+experimental: false
+experimentalcli: false
+kubernetes: false
+swarm: false
+

+ 15 - 18
go.mod

@@ -8,6 +8,7 @@ require (
 	github.com/compose-spec/compose-go v1.8.2
 	github.com/containerd/console v1.0.3
 	github.com/containerd/containerd v1.6.15
+	github.com/cucumber/godog v0.0.0-00010101000000-000000000000
 	github.com/distribution/distribution/v3 v3.0.0-20221201083218-92d136e113cf
 	github.com/docker/buildx v0.9.1 // when updating, also update the replace rules accordingly
 	github.com/docker/cli v20.10.20+incompatible // replaced; see replace rule for actual version
@@ -15,11 +16,12 @@ require (
 	github.com/docker/docker v20.10.20+incompatible // replaced; see replace rule for actual version
 	github.com/docker/go-connections v0.4.0
 	github.com/docker/go-units v0.5.0
+	github.com/fsnotify/fsnotify v1.6.0
 	github.com/golang/mock v1.6.0
 	github.com/hashicorp/go-multierror v1.1.1
 	github.com/hashicorp/go-version v1.6.0
-	github.com/mattn/go-isatty v0.0.16 // indirect
 	github.com/mattn/go-shellwords v1.0.12
+	github.com/mitchellh/mapstructure v1.5.0
 	github.com/moby/buildkit v0.10.4 // replaced; see replace rule for actual version
 	github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f
 	github.com/morikuni/aec v1.0.0
@@ -42,11 +44,15 @@ require (
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/Microsoft/go-winio v0.5.2 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/bugsnag/bugsnag-go v1.5.0 // indirect
 	github.com/cenkalti/backoff/v4 v4.1.2 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
+	github.com/cloudflare/cfssl v1.4.1 // indirect
 	github.com/containerd/continuity v0.3.0 // indirect
 	github.com/containerd/ttrpc v1.1.0 // indirect
 	github.com/containerd/typeurl v1.0.2 // indirect
+	github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
+	github.com/cucumber/messages-go/v16 v16.0.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/docker/distribution v2.8.1+incompatible // indirect
 	github.com/docker/docker-credential-helpers v0.7.0 // indirect
@@ -57,6 +63,7 @@ require (
 	github.com/go-logr/logr v1.2.3 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/gofrs/flock v0.8.0 // indirect
+	github.com/gofrs/uuid v4.2.0+incompatible // indirect
 	github.com/gogo/googleapis v1.4.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
@@ -68,17 +75,21 @@ require (
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
+	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
+	github.com/hashicorp/go-memdb v1.3.2 // indirect
+	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/imdario/mergo v0.3.13 // indirect
 	github.com/inconshreveable/mousetrap v1.0.1 // indirect
+	github.com/jinzhu/gorm v1.9.11 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 	github.com/klauspost/compress v1.15.9 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
+	github.com/mattn/go-isatty v0.0.16 // indirect
 	github.com/mattn/go-runewidth v0.0.14 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 	github.com/miekg/pkcs11 v1.1.1 // indirect
-	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/moby/locker v1.0.1 // indirect
 	github.com/moby/patternmatcher v0.5.0 // indirect
 	github.com/moby/spdystream v0.2.0 // indirect
@@ -96,6 +107,7 @@ require (
 	github.com/prometheus/procfs v0.7.3 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
+	github.com/spf13/viper v1.4.0 // indirect
 	github.com/tonistiigi/fsutil v0.0.0-20220930225714-4638ad635be5 // indirect
 	github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
 	github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f // indirect
@@ -119,7 +131,7 @@ require (
 	golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 // indirect
 	golang.org/x/net v0.4.0 // indirect
 	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
-	golang.org/x/sys v0.3.0 // indirect
+	golang.org/x/sys v0.4.0 // indirect
 	golang.org/x/term v0.3.0 // indirect
 	golang.org/x/text v0.6.0 // indirect
 	golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
@@ -137,21 +149,6 @@ require (
 	sigs.k8s.io/yaml v1.2.0 // indirect
 )
 
-require github.com/cucumber/godog v0.0.0-00010101000000-000000000000
-
-require (
-	github.com/bugsnag/bugsnag-go v1.5.0 // indirect
-	github.com/cloudflare/cfssl v1.4.1 // indirect
-	github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
-	github.com/cucumber/messages-go/v16 v16.0.1 // indirect
-	github.com/gofrs/uuid v4.2.0+incompatible // indirect
-	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
-	github.com/hashicorp/go-memdb v1.3.2 // indirect
-	github.com/hashicorp/golang-lru v0.5.4 // indirect
-	github.com/jinzhu/gorm v1.9.11 // indirect
-	github.com/spf13/viper v1.4.0 // indirect
-)
-
 replace (
 	// Override for e2e tests
 	github.com/cucumber/godog => github.com/laurazard/godog v0.0.0-20220922095256-4c4b17abdae7

+ 5 - 3
go.sum

@@ -206,8 +206,9 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
 github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
 github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
 github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
 github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
@@ -903,8 +904,9 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

+ 6 - 0
pkg/api/api.go

@@ -77,6 +77,12 @@ type Service interface {
 	Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
 	// MaxConcurrency defines upper limit for concurrent operations against engine API
 	MaxConcurrency(parallel int)
+	// Watch services' development context and sync/notify/rebuild/restart on changes
+	Watch(ctx context.Context, project *types.Project, services []string, options WatchOptions) error
+}
+
+// WatchOptions group options of the Watch API
+type WatchOptions struct {
 }
 
 // BuildOptions group options of the Build API

+ 10 - 0
pkg/api/proxy.go

@@ -50,6 +50,7 @@ type ServiceProxy struct {
 	EventsFn             func(ctx context.Context, project string, options EventsOptions) error
 	PortFn               func(ctx context.Context, project string, service string, port uint16, options PortOptions) (string, int, error)
 	ImagesFn             func(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
+	WatchFn              func(ctx context.Context, project *types.Project, services []string, options WatchOptions) error
 	MaxConcurrencyFn     func(parallel int)
 	interceptors         []Interceptor
 }
@@ -88,6 +89,7 @@ func (s *ServiceProxy) WithService(service Service) *ServiceProxy {
 	s.EventsFn = service.Events
 	s.PortFn = service.Port
 	s.ImagesFn = service.Images
+	s.WatchFn = service.Watch
 	s.MaxConcurrencyFn = service.MaxConcurrency
 	return s
 }
@@ -311,6 +313,14 @@ func (s *ServiceProxy) Images(ctx context.Context, project string, options Image
 	return s.ImagesFn(ctx, project, options)
 }
 
+// Watch implements Service interface
+func (s *ServiceProxy) Watch(ctx context.Context, project *types.Project, services []string, options WatchOptions) error {
+	if s.WatchFn == nil {
+		return ErrNotImplemented
+	}
+	return s.WatchFn(ctx, project, services, options)
+}
+
 func (s *ServiceProxy) MaxConcurrency(i int) {
 	s.MaxConcurrencyFn(i)
 }

+ 79 - 0
pkg/compose/watch.go

@@ -0,0 +1,79 @@
+/*
+
+   Copyright 2020 Docker Compose CLI authors
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+       http://www.apache.org/licenses/LICENSE-2.0
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"context"
+	"fmt"
+	"log"
+
+	"github.com/compose-spec/compose-go/types"
+	"github.com/docker/compose/v2/pkg/api"
+	"github.com/fsnotify/fsnotify"
+	"github.com/mitchellh/mapstructure"
+	"github.com/pkg/errors"
+	"golang.org/x/sync/errgroup"
+)
+
+type DevelopmentConfig struct {
+}
+
+func (s *composeService) Watch(ctx context.Context, project *types.Project, services []string, options api.WatchOptions) error {
+	fmt.Fprintln(s.stderr(), "not implemented yet")
+
+	eg, ctx := errgroup.WithContext(ctx)
+	err := project.WithServices(services, func(service types.ServiceConfig) error {
+		var config DevelopmentConfig
+		if y, ok := service.Extensions["x-develop"]; ok {
+			err := mapstructure.Decode(y, &config)
+			if err != nil {
+				return err
+			}
+		}
+		if service.Build == nil {
+			return errors.New("can't watch a service without a build section")
+		}
+		context := service.Build.Context
+
+		watcher, err := fsnotify.NewWatcher()
+		if err != nil {
+			return err
+		}
+		fmt.Println("watching " + context)
+		err = watcher.Add(context)
+		if err != nil {
+			return err
+		}
+		eg.Go(func() error {
+			defer watcher.Close() //nolint:errcheck
+			for {
+				select {
+				case <-ctx.Done():
+					return nil
+				case event := <-watcher.Events:
+					log.Println("fs event :", event.String())
+				case err := <-watcher.Errors:
+					return err
+				}
+			}
+		})
+		return nil
+	})
+	if err != nil {
+		return err
+	}
+
+	return eg.Wait()
+}

+ 14 - 0
pkg/mocks/mock_docker_compose_api.go

@@ -393,6 +393,20 @@ func (mr *MockServiceMockRecorder) Up(ctx, project, options interface{}) *gomock
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Up", reflect.TypeOf((*MockService)(nil).Up), ctx, project, options)
 }
 
+// Watch mocks base method.
+func (m *MockService) Watch(ctx context.Context, project *types.Project, services []string, options api.WatchOptions) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Watch", ctx, project, services, options)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Watch indicates an expected call of Watch.
+func (mr *MockServiceMockRecorder) Watch(ctx, project, services, options interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockService)(nil).Watch), ctx, project, services, options)
+}
+
 // MockLogConsumer is a mock of LogConsumer interface.
 type MockLogConsumer struct {
 	ctrl     *gomock.Controller