-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: integrate image builder(s) (#232)
* feat: integrate image builder(s) * fix: removed context from debug logs
- Loading branch information
1 parent
25bab2d
commit 194e665
Showing
14 changed files
with
414 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,66 @@ | ||
package builder | ||
|
||
import "context" | ||
import ( | ||
"context" | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"fmt" | ||
) | ||
|
||
type Builder interface { | ||
Build(ctx context.Context, b *BuilderOptions) (logs string, err error) | ||
} | ||
|
||
type CacheOptions struct { | ||
Enabled bool | ||
Dir string | ||
Repo string | ||
} | ||
|
||
type BuilderOptions struct { | ||
ImageName string | ||
BuildContext string | ||
Args []string | ||
Destination string | ||
Cache *CacheOptions | ||
} | ||
|
||
type CacheOptions struct { | ||
Enabled bool | ||
Dir string | ||
Repo string | ||
} | ||
|
||
func (c *CacheOptions) Default(buildContext string) (*CacheOptions, error) { | ||
if buildContext == "" { | ||
return nil, ErrBuildContextEmpty | ||
} | ||
|
||
ctxHash, err := hashString(buildContext) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &CacheOptions{ | ||
Enabled: true, | ||
Dir: "", | ||
// ttl.sh with the hash of build context is used as the cache repo | ||
// Kaniko adds a string tag to the image name, so we don't need to add it here | ||
Repo: fmt.Sprintf("ttl.sh/%s", ctxHash), | ||
}, nil | ||
} | ||
|
||
func DefaultImageName(buildContext string) (string, error) { | ||
if buildContext == "" { | ||
return "", ErrBuildContextEmpty | ||
} | ||
|
||
ctxHash, err := hashString(buildContext) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return fmt.Sprintf("ttl.sh/%s:24h", ctxHash), nil | ||
} | ||
|
||
func hashString(s string) (string, error) { | ||
hash := sha256.New() | ||
if _, err := hash.Write([]byte(s)); err != nil { | ||
return "", err | ||
} | ||
return hex.EncodeToString(hash.Sum(nil)), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package builder | ||
|
||
import ( | ||
"strings" | ||
) | ||
|
||
const ( | ||
dirProtocol = "dir:///" | ||
) | ||
|
||
type DirContext struct { | ||
Path string // This path must be an absolute path | ||
} | ||
|
||
func (d DirContext) BuildContext() string { | ||
return dirProtocol + strings.Trim(d.Path, "/") | ||
} | ||
|
||
func GetDirFromBuildContext(ctx string) string { | ||
// must be absolute path | ||
return "/" + strings.TrimPrefix(ctx, dirProtocol) | ||
} | ||
|
||
func IsDirContext(ctx string) bool { | ||
return strings.HasPrefix(ctx, dirProtocol) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package docker | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"strings" | ||
|
||
"github.com/celestiaorg/knuu/pkg/builder" | ||
"github.com/sirupsen/logrus" | ||
"k8s.io/client-go/kubernetes" | ||
) | ||
|
||
const () | ||
|
||
type Docker struct { | ||
K8sClientset kubernetes.Interface | ||
K8sNamespace string | ||
} | ||
|
||
var _ builder.Builder = &Docker{} | ||
|
||
func (d *Docker) Build(_ context.Context, b *builder.BuilderOptions) (logs string, err error) { | ||
if builder.IsGitContext(b.BuildContext) { | ||
return "", ErrGitContextNotSupported | ||
} | ||
|
||
// Check if there is an existing builder instance | ||
cmd := exec.Command("docker", "buildx", "ls") | ||
output, err := cmd.Output() | ||
logrus.Debugf("docker buildx ls: %s", output) | ||
if err != nil { | ||
return "", ErrFailedToListBuildxBuilders.Wrap(err) | ||
} | ||
|
||
// If no builder instance exists, create a new one | ||
if !strings.Contains(string(output), "default") { | ||
cmd = exec.Command("docker", "buildx", "create", "--use") | ||
if _, err := runCommand(cmd); err != nil { | ||
return "", ErrFailedToCreateBuilder.Wrap(err) | ||
} | ||
logrus.Debug("created new docker builder instance") | ||
} | ||
|
||
logrus.Debug("building docker image: ", b.Destination) | ||
|
||
buildContext := builder.GetDirFromBuildContext(b.BuildContext) | ||
|
||
// Since in docker the image name and destination must be the same, we just use the destination as the image name | ||
cmd = exec.Command("docker", "buildx", "build", "--load", "--platform", "linux/amd64", "-t", b.Destination, buildContext) | ||
cmdLogs, err := runCommand(cmd) | ||
if err != nil { | ||
return "", ErrFailedToBuildImage.Wrap(err) | ||
} | ||
logs += cmdLogs + "\n" | ||
logrus.Debug("built docker image: ", b.Destination) | ||
logrus.Debug("logs: ", cmdLogs) | ||
|
||
cmd = exec.Command("docker", "push", b.Destination) | ||
cmdLogs, err = runCommand(cmd) | ||
if err != nil { | ||
return "", ErrFailedToPushImage.Wrap(err) | ||
} | ||
logs += cmdLogs + "\n" | ||
logrus.Debug("pushed docker image: ", b.Destination) | ||
logrus.Debug("logs: ", cmdLogs) | ||
|
||
if err := os.RemoveAll(b.BuildContext); err != nil { | ||
return "", ErrFailedToRemoveContextDir.Wrap(err) | ||
} | ||
|
||
return logs, nil | ||
} | ||
|
||
func runCommand(cmd *exec.Cmd) (logs string, err error) { | ||
var stdout, stderr bytes.Buffer | ||
cmd.Stdout = &stdout | ||
cmd.Stderr = &stderr | ||
|
||
if err := cmd.Run(); err != nil { | ||
return "", ErrRunCommandFailed.Wrap(fmt.Errorf("%w\nstdout: %s\nstderr: %s", err, stdout.String(), stderr.String())) | ||
} | ||
return stdout.String() + stderr.String(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package docker | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
type Error struct { | ||
Code string | ||
Message string | ||
Err error | ||
} | ||
|
||
func (e *Error) Error() string { | ||
if e.Err != nil { | ||
return fmt.Sprintf("%s: %v", e.Message, e.Err) | ||
} | ||
return e.Message | ||
} | ||
|
||
func (e *Error) Wrap(err error) error { | ||
e.Err = err | ||
return e | ||
} | ||
|
||
var ( | ||
ErrFailedToListBuildxBuilders = &Error{Code: "FailedToListBuildxBuilders", Message: "failed to list buildx builders"} | ||
ErrRunCommandFailed = &Error{Code: "RunCommandFailed", Message: "failed to run command"} | ||
ErrFailedToCreateBuilder = &Error{Code: "FailedToCreateBuilder", Message: "failed to create buildx builder"} | ||
ErrFailedToBuildImage = &Error{Code: "FailedToBuildImage", Message: "failed to build image"} | ||
ErrFailedToPushImage = &Error{Code: "FailedToPushImage", Message: "failed to push image"} | ||
ErrFailedToRemoveContextDir = &Error{Code: "FailedToRemoveContextDir", Message: "failed to remove context directory"} | ||
ErrGitContextNotSupported = &Error{Code: "GitContextNotSupported", Message: "git context is not supported in the docker builder"} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package builder | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
type Error struct { | ||
Code string | ||
Message string | ||
Err error | ||
} | ||
|
||
func (e *Error) Error() string { | ||
if e.Err != nil { | ||
return fmt.Sprintf("%s: %v", e.Message, e.Err) | ||
} | ||
return e.Message | ||
} | ||
|
||
func (e *Error) Wrap(err error) error { | ||
e.Err = err | ||
return e | ||
} | ||
|
||
var ( | ||
ErrBuildContextEmpty = &Error{Code: "BuildContextEmpty", Message: "build context cannot be empty"} | ||
) |
Oops, something went wrong.