mirror of https://github.com/docker/compose.git
watch: only allow a single instance per-project
This is a good place to start introducing (local) exclusivity to Compose. Now, when `alpha watch` launches, it will check for the existence of a PID file in the user XDG runtime directory, and create one if the existing one is stale or does not exist. If the PID file exists and is valid, an error is returned and Compose exits. A slight tweak to the experimental remote Git loader has been made to use the XDG package for consistency. Signed-off-by: Milas Bowman <milas.bowman@docker.com>
This commit is contained in:
parent
186744e034
commit
19f66918cc
|
@ -21,6 +21,8 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/compose/v2/internal/locker"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -57,5 +59,13 @@ func runWatch(ctx context.Context, backend api.Service, opts watchOptions, servi
|
|||
return err
|
||||
}
|
||||
|
||||
l, err := locker.NewPidfile(project.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot take exclusive lock for project %q: %v", project.Name, err)
|
||||
}
|
||||
if err := l.Lock(); err != nil {
|
||||
return fmt.Errorf("cannot take exclusive lock for project %q: %v", project.Name, err)
|
||||
}
|
||||
|
||||
return backend.Watch(ctx, project, services, api.WatchOptions{})
|
||||
}
|
||||
|
|
1
go.mod
1
go.mod
|
@ -5,6 +5,7 @@ go 1.21
|
|||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/Microsoft/go-winio v0.6.1
|
||||
github.com/adrg/xdg v0.4.0
|
||||
github.com/buger/goterm v1.0.4
|
||||
github.com/compose-spec/compose-go v1.18.2
|
||||
github.com/containerd/console v1.0.3
|
||||
|
|
2
go.sum
2
go.sum
|
@ -66,6 +66,8 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/O
|
|||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright 2023 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 locker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/docker/docker/pkg/pidfile"
|
||||
)
|
||||
|
||||
type Pidfile struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func NewPidfile(projectName string) (*Pidfile, error) {
|
||||
path, err := xdg.RuntimeFile(fmt.Sprintf("docker-compose.%s.pid", projectName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Pidfile{path: path}, nil
|
||||
}
|
||||
|
||||
func (f *Pidfile) Lock() error {
|
||||
return pidfile.Write(f.path, os.Getpid())
|
||||
}
|
|
@ -71,6 +71,9 @@ func doTest(t *testing.T, svcName string, tarSync bool) {
|
|||
CopyFile(t, filepath.Join("fixtures", "watch", "compose.yaml"), composeFilePath)
|
||||
|
||||
projName := "e2e-watch-" + svcName
|
||||
if tarSync {
|
||||
projName += "-tar"
|
||||
}
|
||||
env := []string{
|
||||
"COMPOSE_FILE=" + composeFilePath,
|
||||
"COMPOSE_PROJECT_NAME=" + projName,
|
||||
|
@ -96,6 +99,7 @@ func doTest(t *testing.T, svcName string, tarSync bool) {
|
|||
t.Cleanup(func() {
|
||||
// IMPORTANT: watch doesn't exit on its own, don't leak processes!
|
||||
if r.Cmd.Process != nil {
|
||||
t.Logf("Killing watch process: pid[%d]", r.Cmd.Process.Pid)
|
||||
_ = r.Cmd.Process.Kill()
|
||||
}
|
||||
})
|
||||
|
|
|
@ -25,6 +25,8 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
|
@ -45,19 +47,15 @@ func GitRemoteLoaderEnabled() (bool, error) {
|
|||
}
|
||||
|
||||
func NewGitRemoteLoader() (loader.ResourceLoader, error) {
|
||||
var base string
|
||||
if cacheHome := os.Getenv("XDG_CACHE_HOME"); cacheHome != "" {
|
||||
base = cacheHome
|
||||
} else {
|
||||
home, err := os.UserHomeDir()
|
||||
// xdg.CacheFile creates the parent directories for the target file path
|
||||
// and returns the fully qualified path, so use "git" as a filename and
|
||||
// then chop it off after, i.e. no ~/.cache/docker-compose/git file will
|
||||
// ever be created
|
||||
cache, err := xdg.CacheFile(filepath.Join("docker-compose", "git"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("initializing git cache: %w", err)
|
||||
}
|
||||
base = filepath.Join(home, ".cache")
|
||||
}
|
||||
cache := filepath.Join(base, "docker-compose")
|
||||
|
||||
err := os.MkdirAll(cache, 0o700)
|
||||
cache = filepath.Dir(cache)
|
||||
return gitRemoteLoader{
|
||||
cache: cache,
|
||||
}, err
|
||||
|
|
Loading…
Reference in New Issue