allow fleetctl preview to work with docker compose v1 and v2 (#5755)

This adds compatibility in `fleetctl preview` to work with docker compose (version 2). Since this version was released this April, we are still keeping backwards compatibility and using docker-compose as a fallback.

As v2 is now the recommended version and v1 is deprecated, this also rewords all prompts and help messages to say "docker compose".

Rel: #5746
This commit is contained in:
Roberto Dip 2022-05-16 18:06:29 -03:00 committed by GitHub
parent 816279eaf4
commit 32cc4c4641
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 20 deletions

View file

@ -0,0 +1 @@
Fixed `fleetctl preview` to support docker compose v2.

View file

@ -28,6 +28,8 @@ import (
"github.com/urfave/cli/v2"
)
type dockerComposeVersion int
const (
downloadUrl = "https://github.com/fleetdm/osquery-in-a-box/archive/%s.zip"
standardQueryLibraryUrl = "https://raw.githubusercontent.com/fleetdm/fleet/main/docs/01-Using-Fleet/standard-query-library/standard-query-library.yml"
@ -41,14 +43,51 @@ const (
updateRootKeys = "update-roots"
stdQueryLibFilePath = "std-query-lib-file-path"
disableOpenBrowser = "disable-open-browser"
dockerComposeV1 dockerComposeVersion = 1
dockerComposeV2 dockerComposeVersion = 2
)
type dockerCompose struct {
version dockerComposeVersion
}
func (d dockerCompose) String() string {
if d.version == dockerComposeV1 {
return "`docker-compose`"
}
return "`docker compose`"
}
func (d dockerCompose) Command(arg ...string) *exec.Cmd {
if d.version == dockerComposeV1 {
return exec.Command("docker-compose", arg...)
}
return exec.Command("docker", append([]string{"compose"}, arg...)...)
}
func newDockerCompose() (dockerCompose, error) {
// first, check if `docker compose` is available
if err := exec.Command("docker compose").Run(); err == nil {
return dockerCompose{dockerComposeV2}, nil
}
// if not, try to use `docker-compose`
if _, err := exec.LookPath("docker-compose"); err == nil {
return dockerCompose{dockerComposeV1}, nil
}
return dockerCompose{}, errors.New("`docker compose` is required for the fleetctl preview experience.\n\nPlease install `docker compose` (https://docs.docker.com/compose/install/).")
}
func previewCommand() *cli.Command {
return &cli.Command{
Name: "preview",
Aliases: []string{"sandbox"},
Usage: "Start a sandbox deployment of the Fleet server",
Description: `Start a sandbox deployment of the Fleet server using Docker and docker-compose. Docker tools must be available in the environment.
Description: `Start a sandbox deployment of the Fleet server using Docker and docker compose. Docker tools must be available in the environment.
Use the stop and reset subcommands to manage the server and dependencies once started.`,
Subcommands: []*cli.Command{
@ -113,6 +152,11 @@ Use the stop and reset subcommands to manage the server and dependencies once st
return err
}
compose, err := newDockerCompose()
if err != nil {
return err
}
// Download files every time to ensure the user gets the most up to date versions
previewDir := previewDirectory()
osqueryBranch := c.String(previewConfigFlagName)
@ -143,19 +187,19 @@ Use the stop and reset subcommands to manage the server and dependencies once st
}
fmt.Println("Pulling Docker dependencies...")
out, err := exec.Command("docker-compose", "pull").CombinedOutput()
out, err := compose.Command("pull").CombinedOutput()
if err != nil {
fmt.Println(string(out))
return errors.New("Failed to run docker-compose")
return fmt.Errorf("Failed to run %s", compose)
}
fmt.Println("Starting Docker containers...")
cmd := exec.Command("docker-compose", "up", "-d", "--remove-orphans", "mysql01", "redis01", "fleet01")
cmd := compose.Command("up", "-d", "--remove-orphans", "mysql01", "redis01", "fleet01")
cmd.Env = append(os.Environ(), "FLEET_LICENSE_KEY="+c.String(licenseKeyFlagName))
out, err = cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
return errors.New("Failed to run docker-compose")
return fmt.Errorf("Failed to run %s", compose)
}
fmt.Println("Waiting for server to start up...")
@ -166,12 +210,12 @@ Use the stop and reset subcommands to manage the server and dependencies once st
// Start fleet02 (UI server) after fleet01 (agent/fleetctl server)
// has finished starting up so that there is no conflict with
// running database migrations.
cmd = exec.Command("docker-compose", "up", "-d", "--remove-orphans", "fleet02")
cmd = compose.Command("up", "-d", "--remove-orphans", "fleet02")
cmd.Env = append(os.Environ(), "FLEET_LICENSE_KEY="+c.String(licenseKeyFlagName))
out, err = cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
return errors.New("Failed to run docker-compose")
return fmt.Errorf("Failed to run %s", compose)
}
fmt.Println("Initializing server...")
@ -309,7 +353,7 @@ Use the stop and reset subcommands to manage the server and dependencies once st
}
fmt.Println("Starting simulated Linux hosts...")
cmd = exec.Command("docker-compose", "up", "-d", "--remove-orphans")
cmd = compose.Command("up", "-d", "--remove-orphans")
cmd.Dir = filepath.Join(previewDir, "osquery")
cmd.Env = append(os.Environ(),
"ENROLL_SECRET="+secrets.Secrets[0].Secret,
@ -318,7 +362,7 @@ Use the stop and reset subcommands to manage the server and dependencies once st
out, err = cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
return errors.New("Failed to run docker-compose")
return fmt.Errorf("Failed to run %s", compose)
}
} else {
if !c.Bool(disableOpenBrowser) {
@ -500,9 +544,6 @@ func checkDocker() error {
if _, err := exec.LookPath("docker"); err != nil {
return errors.New("Docker is required for the fleetctl preview experience.\n\nPlease install Docker (https://docs.docker.com/get-docker/).")
}
if _, err := exec.LookPath("docker-compose"); err != nil {
return errors.New("Docker Compose is required for the fleetctl preview experience.\n\nPlease install Docker Compose (https://docs.docker.com/compose/install/).")
}
// Check running
if err := exec.Command("docker", "info").Run(); err != nil {
@ -526,6 +567,11 @@ func previewStopCommand() *cli.Command {
return err
}
compose, err := newDockerCompose()
if err != nil {
return err
}
previewDir := previewDirectory()
if err := os.Chdir(previewDir); err != nil {
return err
@ -534,13 +580,13 @@ func previewStopCommand() *cli.Command {
return fmt.Errorf("docker-compose file not found in preview directory: %w", err)
}
out, err := exec.Command("docker-compose", "stop").CombinedOutput()
out, err := compose.Command("stop").CombinedOutput()
if err != nil {
fmt.Println(string(out))
return errors.New("Failed to run docker-compose stop for Fleet server and dependencies")
return fmt.Errorf("Failed to run %s stop for Fleet server and dependencies", compose)
}
cmd := exec.Command("docker-compose", "stop")
cmd := compose.Command("stop")
cmd.Dir = filepath.Join(previewDir, "osquery")
cmd.Env = append(os.Environ(),
// Note that these must be set even though they are unused while
@ -551,7 +597,7 @@ func previewStopCommand() *cli.Command {
out, err = cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
return errors.New("Failed to run docker-compose stop for simulated hosts")
return fmt.Errorf("Failed to run %d stop for simulated hosts", compose)
}
if err := stopOrbit(previewDir); err != nil {
@ -579,6 +625,11 @@ func previewResetCommand() *cli.Command {
return err
}
compose, err := newDockerCompose()
if err != nil {
return err
}
previewDir := previewDirectory()
if err := os.Chdir(previewDir); err != nil {
return err
@ -587,13 +638,13 @@ func previewResetCommand() *cli.Command {
return fmt.Errorf("docker-compose file not found in preview directory: %w", err)
}
out, err := exec.Command("docker-compose", "rm", "-sf").CombinedOutput()
out, err := compose.Command("rm", "-sf").CombinedOutput()
if err != nil {
fmt.Println(string(out))
return errors.New("Failed to run docker-compose rm -sf for Fleet server and dependencies.")
return fmt.Errorf("Failed to run %s rm -sf for Fleet server and dependencies.", compose)
}
cmd := exec.Command("docker-compose", "rm", "-sf")
cmd := compose.Command("rm", "-sf")
cmd.Dir = filepath.Join(previewDir, "osquery")
cmd.Env = append(os.Environ(),
// Note that these must be set even though they are unused while
@ -604,7 +655,7 @@ func previewResetCommand() *cli.Command {
out, err = cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
return errors.New("Failed to run docker-compose rm -sf for simulated hosts.")
return fmt.Errorf("Failed to run %s rm -sf for simulated hosts.", compose)
}
if err := stopOrbit(previewDir); err != nil {

View file

@ -54,3 +54,23 @@ func TestPreview(t *testing.T) {
ok = strings.Contains(appConf, `databases_path: /vulndb`)
require.True(t, ok, appConf)
}
func TestDockerCompose(t *testing.T) {
t.Run("returns the right command according to the version", func(t *testing.T) {
v1 := dockerCompose{dockerComposeV1}
cmd1 := v1.Command("up")
require.Equal(t, []string{"docker-compose", "up"}, cmd1.Args)
v2 := dockerCompose{dockerComposeV2}
cmd2 := v2.Command("up")
require.Equal(t, []string{"docker", "compose", "up"}, cmd2.Args)
})
t.Run("strings according to the version", func(t *testing.T) {
v1 := dockerCompose{dockerComposeV1}
require.Equal(t, v1.String(), "`docker-compose`")
v2 := dockerCompose{dockerComposeV2}
require.Equal(t, v2.String(), "`docker compose`")
})
}