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 }