Skip to content

Commit

Permalink
Merge branch 'master' into work-dir
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/cmd/run/run.go
  • Loading branch information
silphid committed May 29, 2021
2 parents 64b28a6 + f9b31c4 commit dea6c09
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 22 deletions.
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
53 changes: 53 additions & 0 deletions src/cmd/remove/remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package remove

import (
"context"
"fmt"

"github.com/spf13/cobra"

"github.com/silphid/yey/src/cmd"
yey "github.com/silphid/yey/src/internal"
"github.com/silphid/yey/src/internal/docker"
)

// New creates a cobra command
func New() *cobra.Command {
return &cobra.Command{
Use: "remove",
Aliases: []string{"rm"},
Short: "Removes context container",
Args: cobra.RangeArgs(0, 1),
RunE: func(cmd *cobra.Command, args []string) error {
name := ""
if len(args) == 1 {
name = args[0]
}
return run(cmd.Context(), name)
},
}
}

func run(ctx context.Context, name string) error {
contexts, err := yey.LoadContexts()
if err != nil {
return err
}

if name == "" {
var err error
name, err = cmd.PromptContext(contexts)
if err != nil {
return fmt.Errorf("failed to prompt for desired context: %w", err)
}
}

context, err := contexts.GetContext(name)
if err != nil {
return fmt.Errorf("failed to get context with name %q: %w", name, err)
}

container := yey.ContainerName(contexts.Path, context)

return docker.Remove(ctx, container)
}
25 changes: 25 additions & 0 deletions src/cmd/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

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 @@ -68,6 +69,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 Down Expand Up @@ -104,3 +114,18 @@ func getContainerWorkDir(yeyContext yey.Context) (string, error) {

return "", nil
}

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
}
8 changes: 1 addition & 7 deletions src/cmd/tidy/tidy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package tidy

import (
"context"
"fmt"
"path/filepath"
"strings"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -39,11 +37,7 @@ func run(ctx context.Context) error {
validNames[yey.ContainerName(contexts.Path, ctx)] = struct{}{}
}

prefix := fmt.Sprintf(
"yey-%s-%s",
filepath.Base(filepath.Dir(contexts.Path)),
yey.Hash(contexts.Path),
)
prefix := yey.ContainerPathPrefix(contexts.Path)

names, err := docker.ListContainers(ctx)
if err != nil {
Expand Down
20 changes: 20 additions & 0 deletions src/internal/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,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 @@ -33,6 +40,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 @@ -52,6 +63,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)
}
12 changes: 6 additions & 6 deletions src/internal/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,32 +84,32 @@ func TestMerge(t *testing.T) {

func TestSameHashesForSameContexts(t *testing.T) {
ctx1 := getCtx1()
hash1 := Hash(ctx1.String())
hash1 := hash(ctx1.String())

ctx2 := getCtx1()
hash2 := Hash(ctx2.String())
hash2 := hash(ctx2.String())

assert.Equal(t, hash1, hash2)
}

func TestDifferentHashesForDifferentEnvs(t *testing.T) {
ctx1 := getCtx1()
hash1 := Hash(ctx1.String())
hash1 := hash(ctx1.String())

ctx2 := getCtx1()
ctx2.Env["ENV1"] = "value1b"
hash2 := Hash(ctx2.String())
hash2 := hash(ctx2.String())

assert.NotEqual(t, hash1, hash2)
}

func TestDifferentHashesForDifferentMounts(t *testing.T) {
ctx1 := getCtx1()
hash1 := Hash(ctx1.String())
hash1 := hash(ctx1.String())

ctx2 := getCtx1()
ctx2.Mounts["/local/mount1"] = "/container/mount1b"
hash2 := Hash(ctx2.String())
hash2 := hash(ctx2.String())

assert.NotEqual(t, hash1, hash2)
}
Expand Down
35 changes: 35 additions & 0 deletions src/internal/docker/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"

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

func Start(ctx context.Context, yeyCtx yey.Context, containerName, workDir string) error {
Expand Down Expand Up @@ -44,6 +46,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 @@ -56,6 +80,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
Loading

0 comments on commit dea6c09

Please sign in to comment.