-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmake.go
127 lines (103 loc) · 3.45 KB
/
make.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"archive/tar"
"context"
"crypto/sha256"
_ "embed"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"time"
dockertypes "github.com/docker/docker/api/types"
dockerclient "github.com/docker/docker/client"
"github.com/spf13/cobra"
)
//go:generate ./build-proxy.sh
//go:embed proxy-linux-amd64
var proxyBinary []byte
var makeCmd = &cobra.Command{
Use: "make image-name",
Short: "Modify a docker image by adding lambdafy proxy to it",
Args: cobra.ExactArgs(1),
RunE: func(c *cobra.Command, args []string) error {
return lambdafyImage(args[0])
},
}
// lambdafyImage modifies the image by adding lambda proxy to it.
func lambdafyImage(imgName string) error {
ctx := context.Background()
// Setup client
dc, err := dockerclient.NewClientWithOpts(
dockerclient.WithAPIVersionNegotiation(),
dockerclient.FromEnv,
)
if err != nil {
return fmt.Errorf("failed to get docker client: %s", err)
}
// Extract entrypoint from the given imgName as it needs to be prefixed with
// the proxy command
img, _, err := dc.ImageInspectWithRaw(ctx, imgName)
if err != nil {
return fmt.Errorf("failed to inspect docker image '%s': %s", imgName, err)
}
// Check if the image is already lambdafied with the same proxy version.
// If so, we can skip the rest of the process.
proxyChksum := sha256.Sum256(proxyBinary)
proxyChksumHex := hex.EncodeToString(proxyChksum[:])
if proxyChksumHex == img.Config.Labels["lambdafy.proxy.checksum"] {
log.Print("image is already lambdafied with the same proxy version - skipping")
return nil
}
if img.Architecture != "amd64" || img.Os != "linux" {
return fmt.Errorf("platform of docker image '%s' must be linux/amd64", imgName)
}
// In case the image is already lambdafied, we need to remove the old proxy
// entry from command line.
if len(img.Config.Entrypoint) > 0 && img.Config.Entrypoint[0] == "/lambdafy-proxy" {
img.Config.Entrypoint = img.Config.Entrypoint[1:]
}
ep, err := json.Marshal(append([]string{"/lambdafy-proxy"}, img.Config.Entrypoint...))
if err != nil {
return fmt.Errorf("failed to marshal docker image '%s' entrypoint to json: %s", imgName, err)
}
cmd, err := json.Marshal(img.Config.Cmd)
if err != nil {
return fmt.Errorf("failed to marshal docker image '%s' command to json: %s", imgName, err)
}
// Build a new docker image with the proxy embedded
dockerFile := fmt.Sprintf(`
FROM --platform=linux/amd64 %s
RUN rm -f /lambdafy-proxy
COPY --chmod=775 lambdafy-proxy /
ENTRYPOINT %s
CMD %s
LABEL "lambdafy.proxy.checksum"="%s"
`, imgName, string(ep), string(cmd), proxyChksumHex)
r, w := io.Pipe()
t := time.UnixMicro(0)
go func() {
tr := tar.NewWriter(w)
_ = tr.WriteHeader(&tar.Header{Name: "Dockerfile", Size: int64(len(dockerFile)), ModTime: t, AccessTime: t, ChangeTime: t})
_, _ = tr.Write([]byte(dockerFile))
_ = tr.WriteHeader(&tar.Header{Name: "lambdafy-proxy", Size: int64(len(proxyBinary)), ModTime: t, AccessTime: t, ChangeTime: t})
_, _ = tr.Write(proxyBinary)
_ = tr.Close()
_ = w.Close()
}()
resp, err := dc.ImageBuild(ctx, r, dockertypes.ImageBuildOptions{
Tags: []string{imgName},
Version: dockertypes.BuilderBuildKit,
Platform: "linux/amd64",
SuppressOutput: true,
})
if err != nil {
return fmt.Errorf("failed to build lambdafied image: %s", err)
}
defer resp.Body.Close()
if err := processDockerResponse(resp.Body); err != nil {
return fmt.Errorf("failed to build lambdafied image: %s", err)
}
return nil
}