github.com/hazelops/ize@v1.1.12-0.20230915191306-97d7c0e48f11/internal/manager/ecs/docker.go (about)

     1  package ecs
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	ecssvc "github.com/aws/aws-sdk-go/service/ecs"
     7  	"io"
     8  	"os"
     9  	"strconv"
    10  
    11  	"github.com/docker/distribution/reference"
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/api/types/container"
    14  	"github.com/docker/docker/api/types/filters"
    15  	"github.com/docker/docker/api/types/mount"
    16  	"github.com/docker/docker/api/types/network"
    17  	"github.com/docker/docker/client"
    18  	"github.com/docker/docker/pkg/jsonmessage"
    19  	dockerutils "github.com/hazelops/ize/internal/docker/utils"
    20  )
    21  
    22  func (e *Manager) deployWithDocker(w io.Writer) error {
    23  	cli, err := client.NewClientWithOpts(client.FromEnv)
    24  	if err != nil {
    25  		return err
    26  	}
    27  
    28  	imageRef, err := reference.ParseNormalizedNamed(ecsDeployImage)
    29  	if err != nil {
    30  		return fmt.Errorf("error parsing Docker image: %s", err)
    31  	}
    32  
    33  	imageList, err := cli.ImageList(context.Background(), types.ImageListOptions{
    34  		Filters: filters.NewArgs(filters.KeyValuePair{
    35  			Key:   "reference",
    36  			Value: reference.FamiliarString(imageRef),
    37  		}),
    38  	})
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	if len(imageList) == 0 {
    44  		resp, err := cli.ImagePull(context.Background(), reference.FamiliarString(imageRef), types.ImagePullOptions{})
    45  		if err != nil {
    46  			return err
    47  		}
    48  		defer resp.Close()
    49  
    50  		if err != nil {
    51  			return err
    52  		}
    53  
    54  		err = jsonmessage.DisplayJSONMessagesStream(resp, os.Stderr, os.Stderr.Fd(), true, nil)
    55  		if err != nil {
    56  			return fmt.Errorf("unable to stream pull logs to the terminal: %s", err)
    57  		}
    58  	}
    59  
    60  	cmd := []string{"ecs", "deploy",
    61  		"--profile", e.App.AwsProfile,
    62  		"--region", e.App.AwsRegion,
    63  		e.App.Cluster,
    64  		fmt.Sprintf("%s-%s", e.Project.Env, e.App.Name),
    65  		"--image", e.App.Name,
    66  		e.App.Image,
    67  		"--diff",
    68  		"--timeout", strconv.Itoa(e.App.Timeout),
    69  		"--rollback",
    70  		"-e", e.App.Name,
    71  		"DD_VERSION", e.Project.Tag,
    72  	}
    73  
    74  	cfg := container.Config{
    75  		AttachStdout: true,
    76  		AttachStderr: true,
    77  		AttachStdin:  true,
    78  		OpenStdin:    true,
    79  		StdinOnce:    true,
    80  		Tty:          true,
    81  		Image:        ecsDeployImage,
    82  		User:         fmt.Sprintf("%v:%v", os.Getuid(), os.Getgid()),
    83  		WorkingDir:   fmt.Sprintf("%v", e.Project.EnvDir),
    84  		Cmd:          cmd,
    85  	}
    86  
    87  	hostconfig := container.HostConfig{
    88  		AutoRemove: true,
    89  		Mounts: []mount.Mount{
    90  			{
    91  				Type:   mount.TypeBind,
    92  				Source: fmt.Sprintf("%v/.aws", e.Project.Home),
    93  				Target: "/.aws",
    94  			},
    95  		},
    96  	}
    97  
    98  	cr, err := cli.ContainerCreate(context.Background(), &cfg, &hostconfig, &network.NetworkingConfig{}, nil, e.App.Name)
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	err = cli.ContainerStart(context.Background(), cr.ID, types.ContainerStartOptions{})
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	dockerutils.SetupSignalHandlers(cli, cr.ID)
   109  
   110  	out, err := cli.ContainerLogs(context.Background(), cr.ID, types.ContainerLogsOptions{
   111  		ShowStdout: true,
   112  		ShowStderr: true,
   113  		Follow:     true,
   114  		Timestamps: false,
   115  	})
   116  	if err != nil {
   117  		panic(err)
   118  	}
   119  
   120  	defer out.Close()
   121  
   122  	io.Copy(w, out)
   123  
   124  	wait, errC := cli.ContainerWait(context.Background(), cr.ID, container.WaitConditionRemoved)
   125  
   126  	select {
   127  	case status := <-wait:
   128  		if status.StatusCode == 0 {
   129  			return nil
   130  		}
   131  		return fmt.Errorf("container exit status code %d", status.StatusCode)
   132  	case err := <-errC:
   133  		return err
   134  	}
   135  }
   136  
   137  func (e *Manager) redeployWithDocker(w io.Writer) error {
   138  	cli, err := client.NewClientWithOpts(client.FromEnv)
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	imageRef, err := reference.ParseNormalizedNamed(ecsDeployImage)
   144  	if err != nil {
   145  		return fmt.Errorf("error parsing Docker image: %s", err)
   146  	}
   147  
   148  	imageList, err := cli.ImageList(context.Background(), types.ImageListOptions{
   149  		Filters: filters.NewArgs(filters.KeyValuePair{
   150  			Key:   "reference",
   151  			Value: reference.FamiliarString(imageRef),
   152  		}),
   153  	})
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	if len(imageList) == 0 {
   159  		resp, err := cli.ImagePull(context.Background(), reference.FamiliarString(imageRef), types.ImagePullOptions{})
   160  		if err != nil {
   161  			return err
   162  		}
   163  		defer resp.Close()
   164  
   165  		if err != nil {
   166  			return err
   167  		}
   168  
   169  		err = jsonmessage.DisplayJSONMessagesStream(resp, os.Stderr, os.Stderr.Fd(), true, nil)
   170  		if err != nil {
   171  			return fmt.Errorf("unable to stream pull logs to the terminal: %s", err)
   172  		}
   173  	}
   174  
   175  	name := fmt.Sprintf("%s-%s", e.Project.Env, e.App.Name)
   176  
   177  	var td string
   178  
   179  	switch e.App.TaskDefinitionRevision {
   180  	case "latest":
   181  		td = name
   182  	case "current":
   183  		dso, err := getService(name, e.App.Cluster, ecssvc.New(e.Project.Session))
   184  		if err != nil {
   185  			return err
   186  		}
   187  
   188  		td = *dso.Services[0].TaskDefinition
   189  	default:
   190  		r, err := strconv.Atoi(e.App.TaskDefinitionRevision)
   191  		if err == nil && r > 0 {
   192  			td = fmt.Sprintf("%s:%s", name, e.App.TaskDefinitionRevision)
   193  		} else {
   194  			return fmt.Errorf("invalid task definition revision: %s", e.App.TaskDefinitionRevision)
   195  		}
   196  	}
   197  
   198  	cmd := []string{"ecs", "deploy",
   199  		"--profile", e.App.AwsProfile,
   200  		"--region", e.App.AwsRegion,
   201  		e.App.Cluster,
   202  		name,
   203  		"--task", td,
   204  		"--diff",
   205  		"--timeout", strconv.Itoa(e.App.Timeout),
   206  		"--rollback",
   207  		"--no-deregister",
   208  		"-e", e.App.Name,
   209  		"DD_VERSION", e.Project.Tag,
   210  	}
   211  
   212  	cfg := container.Config{
   213  		AttachStdout: true,
   214  		AttachStderr: true,
   215  		AttachStdin:  true,
   216  		OpenStdin:    true,
   217  		StdinOnce:    true,
   218  		Tty:          true,
   219  		Image:        ecsDeployImage,
   220  		User:         fmt.Sprintf("%v:%v", os.Getuid(), os.Getgid()),
   221  		WorkingDir:   fmt.Sprintf("%v", e.Project.EnvDir),
   222  		Cmd:          cmd,
   223  	}
   224  
   225  	hostconfig := container.HostConfig{
   226  		AutoRemove: true,
   227  		Mounts: []mount.Mount{
   228  			{
   229  				Type:   mount.TypeBind,
   230  				Source: fmt.Sprintf("%v/.aws", e.Project.Home),
   231  				Target: "/.aws",
   232  			},
   233  		},
   234  	}
   235  
   236  	cr, err := cli.ContainerCreate(context.Background(), &cfg, &hostconfig, &network.NetworkingConfig{}, nil, e.App.Name)
   237  	if err != nil {
   238  		return err
   239  	}
   240  
   241  	err = cli.ContainerStart(context.Background(), cr.ID, types.ContainerStartOptions{})
   242  	if err != nil {
   243  		return err
   244  	}
   245  
   246  	dockerutils.SetupSignalHandlers(cli, cr.ID)
   247  
   248  	out, err := cli.ContainerLogs(context.Background(), cr.ID, types.ContainerLogsOptions{
   249  		ShowStdout: true,
   250  		ShowStderr: true,
   251  		Follow:     true,
   252  		Timestamps: false,
   253  	})
   254  	if err != nil {
   255  		panic(err)
   256  	}
   257  
   258  	defer out.Close()
   259  
   260  	io.Copy(w, out)
   261  
   262  	wait, errC := cli.ContainerWait(context.Background(), cr.ID, container.WaitConditionRemoved)
   263  
   264  	select {
   265  	case status := <-wait:
   266  		if status.StatusCode == 0 {
   267  			return nil
   268  		}
   269  		return fmt.Errorf("container exit status code %d", status.StatusCode)
   270  	case err := <-errC:
   271  		return err
   272  	}
   273  }