github.com/Benchkram/bob@v0.0.0-20220321080157-7c8f3876e225/pkg/composectl/composectl.go (about)

     1  package composectl
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/Benchkram/errz"
     8  	"io"
     9  	"os"
    10  
    11  	"github.com/compose-spec/compose-go/types"
    12  	"github.com/docker/cli/cli/command"
    13  	"github.com/docker/cli/cli/flags"
    14  	"github.com/docker/compose/v2/pkg/api"
    15  	"github.com/docker/compose/v2/pkg/compose"
    16  )
    17  
    18  var (
    19  	ErrInvalidProject = fmt.Errorf("invalid project")
    20  )
    21  
    22  type ComposeController struct {
    23  	project *types.Project
    24  	service api.Service
    25  
    26  	stdout pipe
    27  	stderr pipe
    28  	stdin  pipe
    29  
    30  	logger  *logger
    31  
    32  	running bool
    33  }
    34  
    35  type pipe struct {
    36  	r *os.File
    37  	w *os.File
    38  }
    39  
    40  func New(project *types.Project, conflicts, mappings string) (*ComposeController, error) {
    41  	if project == nil || project.Name == "" {
    42  		return nil, ErrInvalidProject
    43  	}
    44  
    45  	c := &ComposeController{
    46  		project: project,
    47  	}
    48  
    49  	// create pipes for stdout, stderr and stdin
    50  	var err error
    51  	c.stdout.r, c.stdout.w, err = os.Pipe()
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	c.stderr.r, c.stderr.w, err = os.Pipe()
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	c.stdin.r, c.stdin.w, err = os.Pipe()
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	if conflicts != "" {
    67  		conflicts = fmt.Sprintf("%s\n%s\n", "Conflicting ports detected:", conflicts)
    68  		_, err = c.stdout.w.Write([]byte(conflicts))
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  	}
    73  
    74  	if mappings != "" {
    75  		mappings = fmt.Sprintf("%s\n%s\n", "Resolved port mapping:", mappings)
    76  		_, err = c.stdout.w.Write([]byte(mappings))
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  	}
    81  
    82  	logger, err := NewLogConsumer(c.stdout.w)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	c.logger = logger
    87  
    88  	dockerCli, err := command.NewDockerCli(
    89  		command.WithCombinedStreams(nil),
    90  		command.WithOutputStream(nil),
    91  		command.WithErrorStream(nil),
    92  		command.WithInputStream(nil),
    93  	)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	err = dockerCli.Initialize(flags.NewClientOptions())
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	c.service = compose.NewComposeService(dockerCli.Client(), dockerCli.ConfigFile())
   104  
   105  	return c, nil
   106  }
   107  
   108  func (ctl *ComposeController) Up(ctx context.Context) error {
   109  	err := ctl.service.Up(ctx, ctl.project, api.UpOptions{})
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	go func() {
   115  		err := ctl.service.Logs(ctx, ctl.project.Name, ctl.logger, api.LogOptions{
   116  			Services:   nil,
   117  			Tail:       "",
   118  			Since:      "",
   119  			Until:      "",
   120  			Follow:     true,
   121  			Timestamps: false,
   122  		})
   123  		if err != nil && !errors.Is(err, context.Canceled)  {
   124  			errz.Log(err)
   125  		}
   126  	}()
   127  
   128  	ctl.running = true
   129  
   130  	return nil
   131  }
   132  
   133  func (ctl *ComposeController) Down(ctx context.Context) error {
   134  	if ctl.project == nil {
   135  		return ErrInvalidProject
   136  	}
   137  
   138  	if !ctl.running {
   139  		return nil
   140  	}
   141  
   142  	ctl.running = false
   143  
   144  	err := ctl.service.Down(ctx, ctl.project.Name, api.DownOptions{
   145  		Project: ctl.project,
   146  	})
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	//if ctl.stderr.String() != "" {
   152  	//	return ErrComposeError
   153  	//}
   154  
   155  	return nil
   156  }
   157  
   158  func (ctl *ComposeController) Stdout() io.Reader {
   159  	return ctl.stdout.r
   160  }
   161  
   162  func (ctl *ComposeController) Stderr() io.Reader {
   163  	return ctl.stderr.r
   164  }
   165  
   166  func (ctl *ComposeController) Stdin() io.Writer {
   167  	return ctl.stdin.w
   168  }