Skip to content

Commit ffb77ce

Browse files
committed
Implement Kaniko for Fargate builds.
It will work alongside Docker, which remains the default for EC2 builds, while Kaniko will be used for Fargate
1 parent ac5c006 commit ffb77ce

File tree

6 files changed

+575
-144
lines changed

6 files changed

+575
-144
lines changed

cmd/build/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func execute() error {
6363
fs.StringVar(&flagGeneration, "generation", "", "app generation")
6464
fs.StringVar(&flagID, "id", "latest", "build id")
6565
fs.StringVar(&flagManifest, "manifest", "", "path to app manifest")
66-
fs.StringVar(&flagMethod, "method", "", "source method")
66+
fs.StringVar(&flagMethod, "method", "tgz", "source method")
6767
fs.StringVar(&flagPush, "push", "", "push to registry")
6868
fs.StringVar(&flagRack, "rack", "convox", "rack name")
6969
fs.StringVar(&flagUrl, "url", "", "source url")
@@ -100,6 +100,10 @@ func execute() error {
100100
flagManifest = v
101101
}
102102

103+
if v := os.Getenv("BUILD_ENGINE"); v != "" {
104+
flagMethod = v
105+
}
106+
103107
if v := os.Getenv("BUILD_PUSH"); v != "" {
104108
flagPush = v
105109
}
@@ -122,6 +126,7 @@ func execute() error {
122126
Generation: flagGeneration,
123127
Id: flagID,
124128
Manifest: flagManifest,
129+
Method: flagMethod,
125130
Push: flagPush,
126131
Rack: flagRack,
127132
Source: flagUrl,

pkg/build/build.go

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package build
22

33
import (
4+
"bufio"
45
"bytes"
56
"compress/gzip"
67
"encoding/json"
@@ -32,6 +33,7 @@ type Options struct {
3233
Generation string
3334
Id string
3435
Manifest string
36+
Method string
3537
Output io.Writer
3638
Push string
3739
Rack string
@@ -60,6 +62,11 @@ func New(opts Options) (*Build, error) {
6062
}
6163
}
6264

65+
// Default to tgz method if not specified
66+
if b.Method == "" {
67+
b.Method = "tgz"
68+
}
69+
6370
r, err := sdk.NewFromEnv()
6471
if err != nil {
6572
return nil, err
@@ -164,6 +171,7 @@ func (bb *Build) execute() error {
164171
return nil
165172
}
166173

174+
// login handles authentication for both Docker and Kaniko builds
167175
func (bb *Build) login() error {
168176
var auth map[string]struct {
169177
Username string
@@ -174,19 +182,13 @@ func (bb *Build) login() error {
174182
return err
175183
}
176184

177-
for host, entry := range auth {
178-
buf := &bytes.Buffer{}
179-
180-
err := bb.Exec.Stream(buf, strings.NewReader(entry.Password), "docker", "login", "-u", entry.Username, "--password-stdin", host)
181-
182-
bb.Printf("Authenticating %s: %s\n", host, strings.TrimSpace(buf.String()))
183-
184-
if err != nil {
185-
return err
186-
}
185+
// For Kaniko, write Docker config.json instead of using docker login
186+
if bb.Method == "kaniko" {
187+
return loginForKaniko(auth)
187188
}
188189

189-
return nil
190+
// Standard Docker login for non-Kaniko builds
191+
return loginForDocker(bb, auth)
190192
}
191193

192194
func (bb *Build) buildGeneration1(dir string) error {
@@ -421,3 +423,57 @@ func (bb *Build) fail(buildError error) error {
421423

422424
return buildError
423425
}
426+
427+
// build calls the appropriate builder based on the method
428+
func (bb *Build) build(path, dockerfile string, tag string, env map[string]string) error {
429+
if path == "" {
430+
return fmt.Errorf("must have path to build")
431+
}
432+
433+
df := filepath.Join(path, dockerfile)
434+
435+
// Choose build implementation based on method
436+
switch bb.Method {
437+
case "kaniko":
438+
return bb.buildWithKaniko(path, df, tag, env)
439+
default:
440+
return bb.buildWithDocker(path, df, tag, env)
441+
}
442+
}
443+
444+
// Common function for extracting build args from Dockerfile
445+
func (bb *Build) buildArgs(dockerfile string, env map[string]string) ([]string, error) {
446+
fd, err := os.Open(dockerfile)
447+
if err != nil {
448+
return nil, err
449+
}
450+
defer fd.Close()
451+
452+
s := bufio.NewScanner(fd)
453+
454+
args := []string{}
455+
456+
for s.Scan() {
457+
fields := strings.Fields(strings.TrimSpace(s.Text()))
458+
459+
if len(fields) < 2 {
460+
continue
461+
}
462+
463+
parts := strings.Split(fields[1], "=")
464+
465+
switch fields[0] {
466+
case "FROM":
467+
if bb.Development && strings.Contains(strings.ToLower(s.Text()), "as development") {
468+
args = append(args, "--target", "development")
469+
}
470+
case "ARG":
471+
k := strings.TrimSpace(parts[0])
472+
if v, ok := env[k]; ok {
473+
args = append(args, "--build-arg", fmt.Sprintf("%s=%s", k, v))
474+
}
475+
}
476+
}
477+
478+
return args, nil
479+
}

pkg/build/docker.go

Lines changed: 64 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package build
22

33
import (
4-
"bufio"
54
"encoding/json"
65
"errors"
76
"fmt"
@@ -14,24 +13,39 @@ import (
1413
shellquote "github.com/kballard/go-shellquote"
1514
)
1615

17-
func (bb *Build) build(path, dockerfile string, tag string, env map[string]string) error {
18-
if path == "" {
19-
return fmt.Errorf("must have path to build")
16+
// loginForDocker performs Docker registry login using docker CLI
17+
func loginForDocker(bb *Build, auth map[string]struct {
18+
Username string
19+
Password string
20+
}) error {
21+
for host, entry := range auth {
22+
buf := &strings.Builder{}
23+
24+
err := bb.Exec.Stream(buf, strings.NewReader(entry.Password), "docker", "login", "-u", entry.Username, "--password-stdin", host)
25+
26+
bb.Printf("Authenticating %s: %s\n", host, strings.TrimSpace(buf.String()))
27+
28+
if err != nil {
29+
return err
30+
}
2031
}
2132

22-
df := filepath.Join(path, dockerfile)
33+
return nil
34+
}
2335

36+
// buildWithDocker builds a Docker image using the Docker CLI
37+
func (bb *Build) buildWithDocker(path, dockerfile string, tag string, env map[string]string) error {
2438
args := []string{"build"}
2539

2640
if !bb.Cache {
2741
args = append(args, "--no-cache")
2842
}
2943

3044
args = append(args, "-t", tag)
31-
args = append(args, "-f", df)
45+
args = append(args, "-f", dockerfile)
3246
args = append(args, "--network", "host")
3347

34-
ba, err := bb.buildArgs(df, env)
48+
ba, err := bb.buildArgs(dockerfile, env)
3549
if err != nil {
3650
return err
3751
}
@@ -68,43 +82,60 @@ func (bb *Build) build(path, dockerfile string, tag string, env map[string]strin
6882
return nil
6983
}
7084

71-
func (bb *Build) buildArgs(dockerfile string, env map[string]string) ([]string, error) {
72-
fd, err := os.Open(dockerfile)
85+
// pull retrieves an image from a Docker registry
86+
func (bb *Build) pull(image string) error {
87+
if bb.Method == "kaniko" {
88+
return bb.pullWithKaniko(image)
89+
}
90+
91+
fmt.Fprintf(bb.writer, "Running: docker pull %s\n", image)
92+
93+
data, err := bb.Exec.Execute("docker", "pull", image)
7394
if err != nil {
74-
return nil, err
95+
return errors.New(strings.TrimSpace(string(data)))
96+
}
97+
98+
return nil
99+
}
100+
101+
// push sends an image to a Docker registry
102+
func (bb *Build) push(tag string) error {
103+
if bb.Method == "kaniko" {
104+
return bb.pushWithKaniko(tag)
75105
}
76-
defer fd.Close()
77106

78-
s := bufio.NewScanner(fd)
107+
fmt.Fprintf(bb.writer, "Running: docker push %s\n", tag)
79108

80-
args := []string{}
109+
data, err := bb.Exec.Execute("docker", "push", tag)
110+
if err != nil {
111+
return errors.New(strings.TrimSpace(string(data)))
112+
}
81113

82-
for s.Scan() {
83-
fields := strings.Fields(strings.TrimSpace(s.Text()))
114+
return nil
115+
}
84116

85-
if len(fields) < 2 {
86-
continue
87-
}
117+
// tag adds a tag to an existing Docker image
118+
func (bb *Build) tag(from, to string) error {
119+
if bb.Method == "kaniko" {
120+
return bb.tagWithKaniko(from, to)
121+
}
88122

89-
parts := strings.Split(fields[1], "=")
90-
91-
switch fields[0] {
92-
case "FROM":
93-
if bb.Development && strings.Contains(strings.ToLower(s.Text()), "as development") {
94-
args = append(args, "--target", "development")
95-
}
96-
case "ARG":
97-
k := strings.TrimSpace(parts[0])
98-
if v, ok := env[k]; ok {
99-
args = append(args, "--build-arg", fmt.Sprintf("%s=%s", k, v))
100-
}
101-
}
123+
fmt.Fprintf(bb.writer, "Running: docker tag %s %s\n", from, to)
124+
125+
data, err := bb.Exec.Execute("docker", "tag", from, to)
126+
if err != nil {
127+
return errors.New(strings.TrimSpace(string(data)))
102128
}
103129

104-
return args, nil
130+
return nil
105131
}
106132

133+
// injectConvoxEnv adds the convox-env executable to a Docker image
107134
func (bb *Build) injectConvoxEnv(tag string) error {
135+
if bb.Method == "kaniko" {
136+
return bb.injectConvoxEnvWithKaniko(tag)
137+
}
138+
108139
fmt.Fprintf(bb.writer, "Injecting: convox-env\n")
109140

110141
var cmd []string
@@ -157,43 +188,10 @@ func (bb *Build) injectConvoxEnv(tag string) error {
157188
return err
158189
}
159190

160-
data, err = bb.Exec.Execute("docker", "build", "-t", tag, tmp)
191+
_, err = bb.Exec.Execute("docker", "build", "-t", tag, tmp)
161192
if err != nil {
162193
return err
163194
}
164195

165196
return nil
166197
}
167-
168-
func (bb *Build) pull(tag string) error {
169-
fmt.Fprintf(bb.writer, "Running: docker pull %s\n", tag)
170-
171-
data, err := bb.Exec.Execute("docker", "pull", tag)
172-
if err != nil {
173-
return errors.New(strings.TrimSpace(string(data)))
174-
}
175-
176-
return nil
177-
}
178-
179-
func (bb *Build) push(tag string) error {
180-
fmt.Fprintf(bb.writer, "Running: docker push %s\n", tag)
181-
182-
data, err := bb.Exec.Execute("docker", "push", tag)
183-
if err != nil {
184-
return errors.New(strings.TrimSpace(string(data)))
185-
}
186-
187-
return nil
188-
}
189-
190-
func (bb *Build) tag(from, to string) error {
191-
fmt.Fprintf(bb.writer, "Running: docker tag %s %s\n", from, to)
192-
193-
data, err := bb.Exec.Execute("docker", "tag", from, to)
194-
if err != nil {
195-
return errors.New(strings.TrimSpace(string(data)))
196-
}
197-
198-
return nil
199-
}

0 commit comments

Comments
 (0)