github.com/pojntfx/hydrapp/hydrapp@v0.0.0-20240516002902-d08759d6ca9f/pkg/executors/docker.go (about) 1 package executors 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "os" 8 "os/user" 9 "runtime" 10 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/container" 13 "github.com/docker/docker/client" 14 cp "github.com/otiai10/copy" 15 ) 16 17 func DockerRunImage( 18 ctx context.Context, 19 cli *client.Client, 20 image string, 21 pull bool, 22 privileged bool, 23 src string, 24 dst string, 25 onID func(id string), 26 stdout io.Writer, 27 env map[string]string, 28 renderTemplates func(workdir string, ejecting bool) error, 29 cmds []string, 30 ) error { 31 currentUser, err := user.Current() 32 if err != nil { 33 return err 34 } 35 36 if runtime.GOOS != "windows" { 37 env["DST_UID"] = currentUser.Uid 38 env["DST_GID"] = currentUser.Gid 39 } 40 41 images, err := cli.ImageList(ctx, types.ImageListOptions{}) 42 if err != nil { 43 return err 44 } 45 46 imageExists := false 47 o: 48 for _, i := range images { 49 for _, t := range i.RepoTags { 50 if t == image { 51 imageExists = true 52 53 break o 54 } 55 } 56 } 57 58 if pull || !imageExists { 59 reader, err := cli.ImagePull(ctx, image, types.ImagePullOptions{}) 60 if err != nil { 61 return err 62 } 63 64 if _, err := io.Copy(stdout, reader); err != nil { 65 return err 66 } 67 } 68 69 workdir, err := os.MkdirTemp("", "hydrapp-build-dir-") 70 if err != nil { 71 return err 72 } 73 defer os.RemoveAll(workdir) 74 75 if err := cp.Copy(src, workdir); err != nil { 76 return err 77 } 78 79 if err := renderTemplates(workdir, false); err != nil { 80 return err 81 } 82 83 var cmd []string 84 binds := []string{ 85 workdir + ":/hydrapp/work:z", 86 } 87 88 if len(cmds) > 0 { 89 cmd = cmds 90 } else { 91 binds = append(binds, dst+":/hydrapp/dst:z") 92 } 93 94 resp, err := cli.ContainerCreate(ctx, &container.Config{ 95 Image: image, 96 AttachStdin: true, 97 AttachStdout: true, 98 AttachStderr: true, 99 OpenStdin: true, 100 Tty: true, 101 Env: func() []string { 102 out := []string{} 103 for key, value := range env { 104 out = append(out, key+"="+value) 105 } 106 107 return out 108 }(), 109 Cmd: cmd, 110 }, &container.HostConfig{ 111 Privileged: privileged, 112 Binds: binds, 113 }, nil, nil, "") 114 if err != nil { 115 return err 116 } 117 118 onID(resp.ID) 119 120 waiter, err := cli.ContainerAttach(ctx, resp.ID, container.AttachOptions{ 121 Stdin: false, 122 Stdout: true, 123 Stderr: true, 124 Stream: true, 125 }) 126 if err != nil { 127 return err 128 } 129 defer waiter.Close() 130 131 go io.Copy(stdout, waiter.Reader) // We intentionally ignore errors here since we can't handle them 132 133 if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil { 134 return err 135 } 136 137 statusChan, errChan := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning) 138 select { 139 case err := <-errChan: 140 return err 141 case status := <-statusChan: 142 if (status.StatusCode != 0 && status.StatusCode != 137) || status.Error != nil { 143 return fmt.Errorf("could not wait for container: %v", status) 144 } 145 } 146 147 return nil 148 }