Skip to content

Commit

Permalink
Use compose v2, only create compose file and clone repos on up (#314)
Browse files Browse the repository at this point in the history
  • Loading branch information
cszatmary authored Aug 3, 2022
1 parent a28a844 commit 9b87268
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 1,023 deletions.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,30 @@ If you want to know more about why we built `tb`, check out our [blog post](http

### **Table of Contents**
- [Requirements](#requirements)
+ [Installed Software](#installed-software)
+ [SSH Key](#ssh-key)
- [Installed Software](#installed-software)
- [SSH Key](#ssh-key)
- [Installation](#installation)
+ [Updating tb](#updating-tb)
- [Homebrew (Recommended)](#homebrew-recommended)
- [From source](#from-source)
- [Updating tb](#updating-tb)
- [Quickstart](#quickstart)
- [Basic Usage](#basic-usage)
- [Running Apps](#running-apps)
- [Commands](#commands)
- [Configuration](#configuration)
+ [Changing log level](#changing-log-level)
+ [Adding custom playlists](#adding-custom-playlists)
+ [Overriding service properties](#overriding-service-properties)
- [Toggling experimental mode](#toggling-experimental-mode)
- [Adding custom playlists](#adding-custom-playlists)
- [Overriding service properties](#overriding-service-properties)
- [Contributing](#contributing)
- [License](#license)

## Requirements

### Installed Software

The main requirement for using `tb` is having `docker` and `docker-compose` installed.
The main requirement for using `tb` is having `docker` and `docker compose v2` installed.
See the [Docker installation instructions](https://docs.docker.com/get-docker/) and select your operating system for more details.
See the [Compose installation instructions](https://docs.docker.com/compose/install/) to insure you have Compose v2 installed.

If you are using macOS having the Xcode CLI tools is also required. These can be easily installed by running `xcode-select --install`.

Expand Down
9 changes: 4 additions & 5 deletions cli/commands/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,10 @@ func getDbConf(ctx context.Context, c *cli.Container, serviceName string) (dbCon

buf := &bytes.Buffer{}
exitCode, err := c.Engine.Exec(ctx, serviceName, engine.ExecOptions{
SkipGitPull: true,
Cmd: args,
Stdin: os.Stdin,
Stdout: buf,
Stderr: os.Stderr,
Cmd: args,
Stdin: os.Stdin,
Stdout: buf,
Stderr: os.Stderr,
})
if err != nil {
return dbConfig{}, errors.Wrap(err, errors.Meta{Reason: "failed execing command inside this service's container"})
Expand Down
22 changes: 11 additions & 11 deletions cli/commands/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@ import (
"github.com/spf13/cobra"
)

type execOptions struct {
skipGitPull bool
}

func newExecCommand(c *cli.Container) *cobra.Command {
var opts execOptions
execCmd := &cobra.Command{
Use: "exec <service> <command> [args...]",
Args: func(cmd *cobra.Command, args []string) error {
Expand All @@ -38,11 +33,10 @@ Start an interactive bash shell in the core-database container:
tb exec core-database bash`,
RunE: func(cmd *cobra.Command, args []string) error {
exitCode, err := c.Engine.Exec(c.Ctx, args[0], engine.ExecOptions{
SkipGitPull: opts.skipGitPull,
Cmd: args[1:],
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Cmd: args[1:],
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
})
if err != nil {
return err
Expand All @@ -56,6 +50,12 @@ Start an interactive bash shell in the core-database container:
}

flags := execCmd.Flags()
flags.BoolVar(&opts.skipGitPull, "no-git-pull", false, "Don't update git repositories")
flags.Bool("no-git-pull", false, "Don't update git repositories")
err := flags.MarkDeprecated("no-git-pull", "it is a no-op and will be removed")
if err != nil {
// MarkDeprecated only errors if the flag name is wrong or the message isn't set
// which is a programming error, so we wanna blow up
panic(err)
}
return execCmd
}
18 changes: 9 additions & 9 deletions cli/commands/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ import (
"github.com/spf13/cobra"
)

type logsOptions struct {
skipGitPull bool
}

func newLogsCommand(c *cli.Container) *cobra.Command {
var opts logsOptions
logsCmd := &cobra.Command{
Use: "logs [services...]",
Args: cobra.ArbitraryArgs,
Expand All @@ -35,14 +30,19 @@ Show logs only from the postgres and redis containers:
ServiceNames: args,
// TODO(@cszatmary): Make these configurable through flags.
// This would be a breaking change though.
Follow: true,
Tail: -1,
SkipGitPull: opts.skipGitPull,
Follow: true,
Tail: -1,
})
},
}

flags := logsCmd.Flags()
flags.BoolVar(&opts.skipGitPull, "no-git-pull", false, "Don't update git repositories")
flags.Bool("no-git-pull", false, "Don't update git repositories")
err := flags.MarkDeprecated("no-git-pull", "it is a no-op and will be removed")
if err != nil {
// MarkDeprecated only errors if the flag name is wrong or the message isn't set
// which is a programming error, so we wanna blow up
panic(err)
}
return logsCmd
}
34 changes: 0 additions & 34 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
_ "embed"
"fmt"
"io"
"os"
"path/filepath"
"strings"
Expand All @@ -15,7 +14,6 @@ import (
"github.com/TouchBistro/goutils/progress"
"github.com/TouchBistro/tb/engine"
"github.com/TouchBistro/tb/errkind"
"github.com/TouchBistro/tb/integrations/docker"
"github.com/TouchBistro/tb/integrations/git"
"github.com/TouchBistro/tb/integrations/simulator"
"github.com/TouchBistro/tb/internal/util"
Expand Down Expand Up @@ -270,38 +268,6 @@ func Init(ctx context.Context, config Config, opts InitOptions) (*engine.Engine,
p.Name = n
registryResult.Playlists.SetCustom(p)
}

// Create docker-compose.yml
tracker.Debug("Generating docker-compose.yml file")
composePath := filepath.Join(tbRoot, docker.ComposeFilename)
f, err := os.OpenFile(composePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
if err != nil {
return nil, errors.Wrap(err, errors.Meta{
Kind: errkind.IO,
Reason: fmt.Sprintf("failed to open file %s", composePath),
Op: op,
})
}
defer f.Close()

const header = "# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n\n"
if _, err := io.WriteString(f, header); err != nil {
return nil, errors.Wrap(err, errors.Meta{
Kind: errkind.IO,
Reason: "failed to write header comment to docker-compose yaml file",
Op: op,
})
}

composeConfig := service.ComposeConfig(registryResult.Services)
if err := yaml.NewEncoder(f).Encode(composeConfig); err != nil {
return nil, errors.Wrap(err, errors.Meta{
Kind: errkind.IO,
Reason: "failed to encode docker-compose struct to yaml",
Op: op,
})
}
tracker.Debug("Successfully generated docker-compose.yml")
}

// Only try loading devices if we are on macOS.
Expand Down
52 changes: 39 additions & 13 deletions engine/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/TouchBistro/tb/integrations/docker"
"github.com/TouchBistro/tb/integrations/login"
"github.com/TouchBistro/tb/resource/service"
"gopkg.in/yaml.v3"
)

// ResolveService resolves a single service from the given name.
Expand Down Expand Up @@ -69,6 +70,9 @@ func (e *Engine) Up(ctx context.Context, opts UpOptions) error {
if err := e.prepareGitRepos(ctx, op, opts.SkipGitPull); err != nil {
return err
}
if err := e.writeComposeFile(ctx, op); err != nil {
return err
}

tracker := progress.TrackerFromContext(ctx)
if len(e.loginStrategies) > 0 {
Expand Down Expand Up @@ -263,9 +267,6 @@ type LogsOptions struct {
// Tail is the number of lines to show from the end of the logs.
// A value of -1 means show all logs.
Tail int
// SkipGitPull skips pulling existing git repos to update them.
// Missing repos will still be cloned however.
SkipGitPull bool
}

// Logs retrieves the logs from one or more service containers and writes it to w.
Expand All @@ -275,9 +276,6 @@ func (e *Engine) Logs(ctx context.Context, w io.Writer, opts LogsOptions) error
if err != nil {
return err
}
if err := e.prepareGitRepos(ctx, op, opts.SkipGitPull); err != nil {
return err
}
err = e.dockerClient.LogsFromServices(ctx, docker.LogsFromServicesOptions{
ServiceNames: getServiceNames(services),
Out: w,
Expand All @@ -292,9 +290,6 @@ func (e *Engine) Logs(ctx context.Context, w io.Writer, opts LogsOptions) error

// ExecOptions customizes the behaviour of Exec.
type ExecOptions struct {
// SkipGitPull skips pulling existing git repos to update them.
// Missing repos will still be cloned however.
SkipGitPull bool
// Cmd is the command to execute. It must have at
// least one element which is the name of the command.
// Any additional elements are args for the command.
Expand All @@ -314,10 +309,6 @@ func (e *Engine) Exec(ctx context.Context, serviceName string, opts ExecOptions)
if len(opts.Cmd) == 0 {
panic("ExecOptions.Cmd must have at least one element")
}
if err := e.prepareGitRepos(ctx, op, opts.SkipGitPull); err != nil {
return -1, err
}

s, err := e.services.Get(serviceName)
if err != nil {
return -1, errors.Wrap(err, errors.Meta{Reason: "unable to resolve service", Op: op})
Expand Down Expand Up @@ -700,6 +691,41 @@ func (e *Engine) prepareGitRepos(ctx context.Context, op errors.Op, skipPull boo
return nil
}

func (e *Engine) writeComposeFile(ctx context.Context, op errors.Op) error {
tracker := progress.TrackerFromContext(ctx)
tracker.Debug("Generating docker-compose.yml file")
composePath := filepath.Join(e.workdir, docker.ComposeFilename)
f, err := os.OpenFile(composePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
if err != nil {
return errors.Wrap(err, errors.Meta{
Kind: errkind.IO,
Reason: fmt.Sprintf("failed to open file %s", composePath),
Op: op,
})
}
defer f.Close()

const header = "# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n\n"
if _, err := io.WriteString(f, header); err != nil {
return errors.Wrap(err, errors.Meta{
Kind: errkind.IO,
Reason: "failed to write header comment to docker-compose yaml file",
Op: op,
})
}

composeConfig := service.ComposeConfig(e.services)
if err := yaml.NewEncoder(f).Encode(composeConfig); err != nil {
return errors.Wrap(err, errors.Meta{
Kind: errkind.IO,
Reason: "failed to encode docker-compose struct to yaml",
Op: op,
})
}
tracker.Debug("Successfully generated docker-compose.yml")
return nil
}

// stopServices stops and removes any containers for the given services.
func (e *Engine) stopServices(ctx context.Context, op errors.Op, services []service.Service) error {
serviceNames := getServiceNames(services)
Expand Down
Loading

0 comments on commit 9b87268

Please sign in to comment.