Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support dockerfiles #16

Merged
merged 3 commits into from
May 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/buildexample/.yeyrc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build:
dockerfile: ./Dockerfile
context: .
3 changes: 3 additions & 0 deletions examples/buildexample/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM ubuntu:latest

CMD ["bash"]
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/silphid/yey

go 1.14
go 1.16

require (
github.com/AlecAivazis/survey/v2 v2.2.12
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
Expand Down
26 changes: 26 additions & 0 deletions src/cmd/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package run
import (
"context"
"fmt"
"os"

yey "github.com/silphid/yey/src/internal"
"github.com/silphid/yey/src/internal/docker"
"github.com/silphid/yey/src/internal/logging"

"github.com/silphid/yey/src/cmd"

Expand Down Expand Up @@ -65,6 +67,15 @@ func run(ctx context.Context, name string, options Options) error {
yeyContext.Remove = options.Remove
}

if yeyContext.Image == "" {
var err error
yeyContext.Image, err = readAndBuildDockerfile(ctx, yeyContext.Build)
if err != nil {
return fmt.Errorf("failed to build yey context image: %w", err)
}
logging.Log("using image: %s", yeyContext.Image)
}

containerName := yey.ContainerName(contexts.Path, yeyContext)

if options.Reset {
Expand All @@ -75,3 +86,18 @@ func run(ctx context.Context, name string, options Options) error {

return docker.Start(ctx, yeyContext, containerName)
}

func readAndBuildDockerfile(ctx context.Context, build yey.DockerBuild) (string, error) {
dockerBytes, err := os.ReadFile(build.Dockerfile)
if err != nil {
return "", fmt.Errorf("failed to read dockerfile: %w", err)
}

imageName := yey.ImageName(dockerBytes)

if err := docker.Build(ctx, build.Dockerfile, imageName, build.Args, build.Context); err != nil {
return "", fmt.Errorf("failed to build image: %w", err)
}

return imageName, nil
}
20 changes: 20 additions & 0 deletions src/internal/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@ import (
"gopkg.in/yaml.v2"
)

type DockerBuild struct {
Dockerfile string
Args map[string]string
Context string
}

// Context represents execution configuration for some docker container
type Context struct {
Name string `yaml:",omitempty"`
Remove *bool
Image string
Build DockerBuild
Env map[string]string
Mounts map[string]string
Cmd []string
Expand All @@ -30,6 +37,10 @@ func (c Context) Clone() Context {
for key, value := range c.Mounts {
clone.Mounts[key] = value
}
clone.Build.Args = make(map[string]string)
for key, value := range c.Build.Args {
clone.Build.Args[key] = value
}
return clone
}

Expand All @@ -49,6 +60,15 @@ func (c Context) Merge(source Context) Context {
for key, value := range source.Mounts {
merged.Mounts[key] = value
}
if source.Build.Dockerfile != "" {
merged.Build.Dockerfile = source.Build.Dockerfile
}
if source.Build.Context != "" {
merged.Build.Context = source.Build.Context
}
for key, value := range source.Build.Args {
merged.Build.Args[key] = value
}
return merged
}

Expand Down
37 changes: 34 additions & 3 deletions src/internal/contextFile.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func readContextFileFromNetwork(url string) ([]byte, error) {
}

// parseContextFile unmarshals the contextFile data and resolves any parent contextfiles
func parseContextFile(data []byte) (Contexts, error) {
func parseContextFile(dir string, data []byte) (Contexts, error) {
var ctxFile ContextFile
if err := yaml.Unmarshal(data, &ctxFile); err != nil {
return Contexts{}, fmt.Errorf("failed to decode context file: %w", err)
Expand All @@ -89,6 +89,13 @@ func parseContextFile(data []byte) (Contexts, error) {
Named: ctxFile.Named,
}

if dir != "" {
contexts.Context = resolveContextPaths(dir, contexts.Context)
for name, context := range contexts.Named {
contexts.Named[name] = resolveContextPaths(dir, context)
}
}

if ctxFile.Parent != "" {
parent, err := readAndParseContextFileFromURI(ctxFile.Parent)
if err != nil {
Expand All @@ -105,18 +112,20 @@ func parseContextFile(data []byte) (Contexts, error) {
func readAndParseContextFileFromURI(path string) (Contexts, error) {
var bytes []byte
var err error
var dir string

if strings.HasPrefix(path, "https:") || strings.HasPrefix(path, "http:") {
bytes, err = readContextFileFromNetwork(path)
} else {
dir = filepath.Dir(path)
bytes, err = readContextFileFromFilePath(path)
}

if err != nil {
return Contexts{}, fmt.Errorf("failed to read context file: %w", err)
}

return parseContextFile(bytes)
return parseContextFile(dir, bytes)
}

// LoadContexts reads the context file and returns the contexts. It starts by reading from current
Expand All @@ -127,11 +136,33 @@ func LoadContexts() (Contexts, error) {
return Contexts{}, fmt.Errorf("failed to read context file: %w", err)
}

contexts, err := parseContextFile(bytes)
contexts, err := parseContextFile(filepath.Dir(path), bytes)
if err != nil {
return Contexts{}, err
}
contexts.Path = path

return contexts, nil
}

func resolveContextPaths(dir string, context Context) Context {
clone := context.Clone()
clone.Build.Dockerfile = resolvePath(dir, context.Build.Dockerfile)
clone.Build.Context = resolvePath(dir, clone.Build.Context)
for key, value := range clone.Mounts {
clone.Mounts[resolvePath(dir, key)] = value
}
return clone
}

func resolvePath(dir, path string) string {
if path == "" {
return ""
}

if filepath.IsAbs(path) {
return path
}

return filepath.Join(dir, path)
}
35 changes: 35 additions & 0 deletions src/internal/docker/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"

"github.com/mitchellh/go-homedir"
yey "github.com/silphid/yey/src/internal"
"github.com/silphid/yey/src/internal/logging"
)

func Start(ctx context.Context, yeyCtx yey.Context, containerName string) error {
Expand Down Expand Up @@ -45,6 +47,28 @@ func Remove(ctx context.Context, containerName string) error {
return attachStdPipes(exec.CommandContext(ctx, "docker", "rm", "-v", containerName)).Run()
}

func Build(ctx context.Context, dockerPath string, imageTag string, buildArgs map[string]string, context string) error {
exists, err := imageExists(ctx, imageTag)
if err != nil {
return fmt.Errorf("failed to look up image tag %q: %w", imageTag, err)
}
if exists {
logging.Log("found prebuilt image: %q: skipping build step", imageTag)
return nil
}

args := []string{"build", "-f", dockerPath, "-t", imageTag}
for key, value := range buildArgs {
args = append(args, "--build-arg", fmt.Sprintf("%s=%q", key, value))
}
if context == "" {
context = filepath.Dir(dockerPath)
}
args = append(args, context)

return attachStdPipes(exec.CommandContext(ctx, "docker", args...)).Run()
}

var newlines = regexp.MustCompile(`\r?\n`)

func ListContainers(ctx context.Context) ([]string, error) {
Expand All @@ -57,6 +81,17 @@ func ListContainers(ctx context.Context) ([]string, error) {
return newlines.Split(string(output), -1), nil
}

func imageExists(ctx context.Context, tag string) (bool, error) {
output, err := exec.CommandContext(ctx, "docker", "image", "inspect", tag).Output()
if string(bytes.TrimSpace(output)) == "[]" {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
}

func getContainerStatus(ctx context.Context, name string) (string, error) {
cmd := exec.CommandContext(ctx, "docker", "inspect", name, "--format", "{{.State.Status}}")

Expand Down
4 changes: 4 additions & 0 deletions src/internal/yey.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ func ContainerPathPrefix(path string) string {
}
return fmt.Sprintf("yey-%s-%s", pathBase, hash(path))
}

func ImageName(dockerfile []byte) string {
return fmt.Sprintf("yey-%s", hash(string(dockerfile)))
}