Move docker build to client

This commit is contained in:
Guillaume J. Charmes 2013-05-19 10:46:24 -07:00
parent ffb38ce0ad
commit 0f312113d3
7 changed files with 426 additions and 414 deletions

34
api.go
View file

@ -370,19 +370,6 @@ func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars ma
return nil
}
func postBuild(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
in, out, err := hijackServer(w)
if err != nil {
return err
}
defer in.Close()
fmt.Fprintf(out, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
if err := srv.ImageCreateFromFile(in, out); err != nil {
fmt.Fprintf(out, "Error: %s\n", err)
}
return nil
}
func postContainersCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
config := &Config{}
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
@ -593,6 +580,25 @@ func getImagesByName(srv *Server, w http.ResponseWriter, r *http.Request, vars m
return nil
}
func postImagesGetCache(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
apiConfig := &ApiImageConfig{}
if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil {
return err
}
image, err := srv.ImageGetCached(apiConfig.Id, apiConfig.Config)
if err != nil {
return err
}
apiId := &ApiId{Id: image.Id}
b, err := json.Marshal(apiId)
if err != nil {
return err
}
writeJson(w, b)
return nil
}
func ListenAndServe(addr string, srv *Server, logging bool) error {
r := mux.NewRouter()
log.Printf("Listening for HTTP on %s\n", addr)
@ -615,11 +621,11 @@ func ListenAndServe(addr string, srv *Server, logging bool) error {
"POST": {
"/auth": postAuth,
"/commit": postCommit,
"/build": postBuild,
"/images/create": postImagesCreate,
"/images/{name:.*}/insert": postImagesInsert,
"/images/{name:.*}/push": postImagesPush,
"/images/{name:.*}/tag": postImagesTag,
"/images/getCache": postImagesGetCache,
"/containers/create": postContainersCreate,
"/containers/{name:.*}/kill": postContainersKill,
"/containers/{name:.*}/restart": postContainersRestart,

View file

@ -64,3 +64,8 @@ type ApiWait struct {
type ApiAuth struct {
Status string
}
type ApiImageConfig struct {
Id string
*Config
}

View file

@ -1,14 +1,9 @@
package docker
import (
"bufio"
"encoding/json"
"fmt"
"github.com/dotcloud/docker/utils"
"io"
"os"
"path"
"strings"
"time"
)
@ -16,6 +11,9 @@ type Builder struct {
runtime *Runtime
repositories *TagStore
graph *Graph
config *Config
image *Image
}
func NewBuilder(runtime *Runtime) *Builder {
@ -26,45 +24,6 @@ func NewBuilder(runtime *Runtime) *Builder {
}
}
func (builder *Builder) mergeConfig(userConf, imageConf *Config) {
if userConf.Hostname != "" {
userConf.Hostname = imageConf.Hostname
}
if userConf.User != "" {
userConf.User = imageConf.User
}
if userConf.Memory == 0 {
userConf.Memory = imageConf.Memory
}
if userConf.MemorySwap == 0 {
userConf.MemorySwap = imageConf.MemorySwap
}
if userConf.CpuShares == 0 {
userConf.CpuShares = imageConf.CpuShares
}
if userConf.PortSpecs == nil || len(userConf.PortSpecs) == 0 {
userConf.PortSpecs = imageConf.PortSpecs
}
if !userConf.Tty {
userConf.Tty = imageConf.Tty
}
if !userConf.OpenStdin {
userConf.OpenStdin = imageConf.OpenStdin
}
if !userConf.StdinOnce {
userConf.StdinOnce = imageConf.StdinOnce
}
if userConf.Env == nil || len(userConf.Env) == 0 {
userConf.Env = imageConf.Env
}
if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
userConf.Cmd = imageConf.Cmd
}
if userConf.Dns == nil || len(userConf.Dns) == 0 {
userConf.Dns = imageConf.Dns
}
}
func (builder *Builder) Create(config *Config) (*Container, error) {
// Lookup image
img, err := builder.repositories.LookupImage(config.Image)
@ -73,7 +32,7 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
}
if img.Config != nil {
builder.mergeConfig(config, img.Config)
MergeConfig(config, img.Config)
}
if config.Cmd == nil || len(config.Cmd) == 0 {
@ -157,312 +116,3 @@ func (builder *Builder) Commit(container *Container, repository, tag, comment, a
}
return img, nil
}
func (builder *Builder) clearTmp(containers, images map[string]struct{}) {
for c := range containers {
tmp := builder.runtime.Get(c)
builder.runtime.Destroy(tmp)
utils.Debugf("Removing container %s", c)
}
for i := range images {
builder.runtime.graph.Delete(i)
utils.Debugf("Removing image %s", i)
}
}
func (builder *Builder) getCachedImage(image *Image, config *Config) (*Image, error) {
// Retrieve all images
images, err := builder.graph.All()
if err != nil {
return nil, err
}
// Store the tree in a map of map (map[parentId][childId])
imageMap := make(map[string]map[string]struct{})
for _, img := range images {
if _, exists := imageMap[img.Parent]; !exists {
imageMap[img.Parent] = make(map[string]struct{})
}
imageMap[img.Parent][img.Id] = struct{}{}
}
// Loop on the children of the given image and check the config
for elem := range imageMap[image.Id] {
img, err := builder.graph.Get(elem)
if err != nil {
return nil, err
}
if CompareConfig(&img.ContainerConfig, config) {
return img, nil
}
}
return nil, nil
}
func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, error) {
var (
image, base *Image
config *Config
maintainer string
env map[string]string = make(map[string]string)
tmpContainers map[string]struct{} = make(map[string]struct{})
tmpImages map[string]struct{} = make(map[string]struct{})
)
defer builder.clearTmp(tmpContainers, tmpImages)
file := bufio.NewReader(dockerfile)
for {
line, err := file.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
line = strings.Replace(strings.TrimSpace(line), " ", " ", 1)
// Skip comments and empty line
if len(line) == 0 || line[0] == '#' {
continue
}
tmp := strings.SplitN(line, " ", 2)
if len(tmp) != 2 {
return nil, fmt.Errorf("Invalid Dockerfile format")
}
instruction := strings.Trim(tmp[0], " ")
arguments := strings.Trim(tmp[1], " ")
switch strings.ToLower(instruction) {
case "from":
fmt.Fprintf(stdout, "FROM %s\n", arguments)
image, err = builder.runtime.repositories.LookupImage(arguments)
if err != nil {
// if builder.runtime.graph.IsNotExist(err) {
// var tag, remote string
// if strings.Contains(arguments, ":") {
// remoteParts := strings.Split(arguments, ":")
// tag = remoteParts[1]
// remote = remoteParts[0]
// } else {
// remote = arguments
// }
// panic("TODO: reimplement this")
// // if err := builder.runtime.graph.PullRepository(stdout, remote, tag, builder.runtime.repositories, builder.runtime.authConfig); err != nil {
// // return nil, err
// // }
// image, err = builder.runtime.repositories.LookupImage(arguments)
// if err != nil {
// return nil, err
// }
// } else {
return nil, err
// }
}
config = &Config{}
break
case "maintainer":
fmt.Fprintf(stdout, "MAINTAINER %s\n", arguments)
maintainer = arguments
break
case "run":
fmt.Fprintf(stdout, "RUN %s\n", arguments)
if image == nil {
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
}
config, _, err := ParseRun([]string{image.Id, "/bin/sh", "-c", arguments}, builder.runtime.capabilities)
if err != nil {
return nil, err
}
for key, value := range env {
config.Env = append(config.Env, fmt.Sprintf("%s=%s", key, value))
}
if cache, err := builder.getCachedImage(image, config); err != nil {
return nil, err
} else if cache != nil {
image = cache
fmt.Fprintf(stdout, "===> %s\n", image.ShortId())
break
}
utils.Debugf("Env -----> %v ------ %v\n", config.Env, env)
// Create the container and start it
c, err := builder.Create(config)
if err != nil {
return nil, err
}
if os.Getenv("DEBUG") != "" {
out, _ := c.StdoutPipe()
err2, _ := c.StderrPipe()
go io.Copy(os.Stdout, out)
go io.Copy(os.Stdout, err2)
}
if err := c.Start(); err != nil {
return nil, err
}
tmpContainers[c.Id] = struct{}{}
// Wait for it to finish
if result := c.Wait(); result != 0 {
return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result)
}
// Commit the container
base, err = builder.Commit(c, "", "", "", maintainer, nil)
if err != nil {
return nil, err
}
tmpImages[base.Id] = struct{}{}
fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
// use the base as the new image
image = base
break
case "env":
tmp := strings.SplitN(arguments, " ", 2)
if len(tmp) != 2 {
return nil, fmt.Errorf("Invalid ENV format")
}
key := strings.Trim(tmp[0], " ")
value := strings.Trim(tmp[1], " ")
fmt.Fprintf(stdout, "ENV %s %s\n", key, value)
env[key] = value
if image != nil {
fmt.Fprintf(stdout, "===> %s\n", image.ShortId())
} else {
fmt.Fprintf(stdout, "===> <nil>\n")
}
break
case "cmd":
fmt.Fprintf(stdout, "CMD %s\n", arguments)
// Create the container and start it
c, err := builder.Create(&Config{Image: image.Id, Cmd: []string{"", ""}})
if err != nil {
return nil, err
}
if err := c.Start(); err != nil {
return nil, err
}
tmpContainers[c.Id] = struct{}{}
cmd := []string{}
if err := json.Unmarshal([]byte(arguments), &cmd); err != nil {
return nil, err
}
config.Cmd = cmd
// Commit the container
base, err = builder.Commit(c, "", "", "", maintainer, config)
if err != nil {
return nil, err
}
tmpImages[base.Id] = struct{}{}
fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
image = base
break
case "expose":
ports := strings.Split(arguments, " ")
fmt.Fprintf(stdout, "EXPOSE %v\n", ports)
if image == nil {
return nil, fmt.Errorf("Please provide a source image with `from` prior to copy")
}
// Create the container and start it
c, err := builder.Create(&Config{Image: image.Id, Cmd: []string{"", ""}})
if err != nil {
return nil, err
}
if err := c.Start(); err != nil {
return nil, err
}
tmpContainers[c.Id] = struct{}{}
config.PortSpecs = append(ports, config.PortSpecs...)
// Commit the container
base, err = builder.Commit(c, "", "", "", maintainer, config)
if err != nil {
return nil, err
}
tmpImages[base.Id] = struct{}{}
fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
image = base
break
case "insert":
if image == nil {
return nil, fmt.Errorf("Please provide a source image with `from` prior to copy")
}
tmp = strings.SplitN(arguments, " ", 2)
if len(tmp) != 2 {
return nil, fmt.Errorf("Invalid INSERT format")
}
sourceUrl := strings.Trim(tmp[0], " ")
destPath := strings.Trim(tmp[1], " ")
fmt.Fprintf(stdout, "COPY %s to %s in %s\n", sourceUrl, destPath, base.ShortId())
file, err := utils.Download(sourceUrl, stdout)
if err != nil {
return nil, err
}
defer file.Body.Close()
config, _, err := ParseRun([]string{base.Id, "echo", "insert", sourceUrl, destPath}, builder.runtime.capabilities)
if err != nil {
return nil, err
}
c, err := builder.Create(config)
if err != nil {
return nil, err
}
if err := c.Start(); err != nil {
return nil, err
}
// Wait for echo to finish
if result := c.Wait(); result != 0 {
return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result)
}
if err := c.Inject(file.Body, destPath); err != nil {
return nil, err
}
base, err = builder.Commit(c, "", "", "", maintainer, nil)
if err != nil {
return nil, err
}
fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
image = base
break
default:
fmt.Fprintf(stdout, "Skipping unknown instruction %s\n", strings.ToUpper(instruction))
}
}
if image != nil {
// The build is successful, keep the temporary containers and images
for i := range tmpImages {
delete(tmpImages, i)
}
for i := range tmpContainers {
delete(tmpContainers, i)
}
fmt.Fprintf(stdout, "Build finished. image id: %s\n", image.ShortId())
return image, nil
}
return nil, fmt.Errorf("An error occured during the build\n")
}

275
builder_client.go Normal file
View file

@ -0,0 +1,275 @@
package docker
import (
"bufio"
"encoding/json"
"fmt"
"github.com/dotcloud/docker/utils"
"io"
"net/url"
"os"
"reflect"
"strings"
)
type BuilderClient struct {
builder *Builder
cli *DockerCli
image string
maintainer string
config *Config
tmpContainers map[string]struct{}
tmpImages map[string]struct{}
needCommit bool
}
func (b *BuilderClient) clearTmp(containers, images map[string]struct{}) {
for c := range containers {
tmp := b.builder.runtime.Get(c)
b.builder.runtime.Destroy(tmp)
utils.Debugf("Removing container %s", c)
}
for i := range images {
b.builder.runtime.graph.Delete(i)
utils.Debugf("Removing image %s", i)
}
}
func (b *BuilderClient) From(name string) error {
obj, statusCode, err := b.cli.call("GET", "/images/"+name+"/json", nil)
if statusCode == 404 {
if err := b.cli.hijack("POST", "/images/create?fromImage="+name, false); err != nil {
return err
}
obj, _, err = b.cli.call("GET", "/images/"+name+"/json", nil)
if err != nil {
return err
}
}
if err != nil {
return err
}
img := &ApiImages{}
if err := json.Unmarshal(obj, img); err != nil {
return err
}
b.image = img.Id
return nil
}
func (b *BuilderClient) Maintainer(name string) error {
b.needCommit = true
b.maintainer = name
return nil
}
func (b *BuilderClient) Run(args string) error {
if b.image == "" {
return fmt.Errorf("Please provide a source image with `from` prior to run")
}
config, _, err := ParseRun([]string{b.image, "/bin/sh", "-c", args}, b.builder.runtime.capabilities)
if err != nil {
return err
}
MergeConfig(b.config, config)
body, statusCode, err := b.cli.call("POST", "/images/getCache", &ApiImageConfig{Id: b.image, Config: b.config})
if err != nil {
if statusCode != 404 {
return err
}
}
if statusCode != 404 {
apiId := &ApiId{}
if err := json.Unmarshal(body, apiId); err != nil {
return err
}
b.image = apiId.Id
return nil
}
body, _, err = b.cli.call("POST", "/containers/create", b.config)
if err != nil {
return err
}
out := &ApiRun{}
err = json.Unmarshal(body, out)
if err != nil {
return err
}
for _, warning := range out.Warnings {
fmt.Fprintln(os.Stderr, "WARNING: ", warning)
}
//start the container
_, _, err = b.cli.call("POST", "/containers/"+out.Id+"/start", nil)
if err != nil {
return err
}
b.tmpContainers[out.Id] = struct{}{}
// Wait for it to finish
_, _, err = b.cli.call("POST", "/containers/"+out.Id+"/wait", nil)
if err != nil {
return err
}
// Commit the container
v := url.Values{}
v.Set("container", out.Id)
v.Set("author", b.maintainer)
body, _, err = b.cli.call("POST", "/commit?"+v.Encode(), b.config)
if err != nil {
return err
}
apiId := &ApiId{}
err = json.Unmarshal(body, apiId)
if err != nil {
return err
}
b.tmpImages[apiId.Id] = struct{}{}
b.image = apiId.Id
b.needCommit = false
return nil
}
func (b *BuilderClient) Env(args string) error {
b.needCommit = true
tmp := strings.SplitN(args, " ", 2)
if len(tmp) != 2 {
return fmt.Errorf("Invalid ENV format")
}
key := strings.Trim(tmp[0], " ")
value := strings.Trim(tmp[1], " ")
for i, elem := range b.config.Env {
if strings.HasPrefix(elem, key+"=") {
b.config.Env[i] = key + "=" + value
return nil
}
}
b.config.Env = append(b.config.Env, key+"="+value)
return nil
}
func (b *BuilderClient) Cmd(args string) error {
b.needCommit = true
b.config.Cmd = []string{"/bin/sh", "-c", args}
return nil
}
func (b *BuilderClient) Expose(args string) error {
ports := strings.Split(args, " ")
b.config.PortSpecs = append(ports, b.config.PortSpecs...)
return nil
}
func (b *BuilderClient) Insert(args string) error {
// FIXME: Reimplement this once the remove_hijack branch gets merged.
// We need to retrieve the resulting Id
return fmt.Errorf("INSERT not implemented")
}
func NewBuilderClient(dockerfile io.Reader) (string, error) {
// defer b.clearTmp(tmpContainers, tmpImages)
b := &BuilderClient{
cli: NewDockerCli("0.0.0.0", 4243),
}
file := bufio.NewReader(dockerfile)
for {
line, err := file.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
return "", err
}
line = strings.Replace(strings.TrimSpace(line), " ", " ", 1)
// Skip comments and empty line
if len(line) == 0 || line[0] == '#' {
continue
}
tmp := strings.SplitN(line, " ", 2)
if len(tmp) != 2 {
return "", fmt.Errorf("Invalid Dockerfile format")
}
instruction := strings.ToLower(strings.Trim(tmp[0], " "))
arguments := strings.Trim(tmp[1], " ")
fmt.Printf("%s %s\n", strings.ToUpper(instruction), arguments)
method, exists := reflect.TypeOf(b).MethodByName(strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
if !exists {
fmt.Printf("Skipping unknown instruction %s\n", strings.ToUpper(instruction))
}
ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
if ret != nil {
return "", ret.(error)
}
fmt.Printf("===> %v\n", b.image)
}
if b.needCommit {
body, _, err = b.cli.call("POST", "/containers/create", b.config)
if err != nil {
return err
}
out := &ApiRun{}
err = json.Unmarshal(body, out)
if err != nil {
return err
}
for _, warning := range out.Warnings {
fmt.Fprintln(os.Stderr, "WARNING: ", warning)
}
//start the container
_, _, err = b.cli.call("POST", "/containers/"+out.Id+"/start", nil)
if err != nil {
return err
}
b.tmpContainers[out.Id] = struct{}{}
// Wait for it to finish
_, _, err = b.cli.call("POST", "/containers/"+out.Id+"/wait", nil)
if err != nil {
return err
}
// Commit the container
v := url.Values{}
v.Set("container", out.Id)
v.Set("author", b.maintainer)
body, _, err = b.cli.call("POST", "/commit?"+v.Encode(), b.config)
if err != nil {
return err
}
apiId := &ApiId{}
err = json.Unmarshal(body, apiId)
if err != nil {
return err
}
b.tmpImages[apiId.Id] = struct{}{}
b.image = apiId.Id
}
if b.image != "" {
// The build is successful, keep the temporary containers and images
for i := range b.tmpImages {
delete(b.tmpImages, i)
}
for i := range b.tmpContainers {
delete(b.tmpContainers, i)
}
fmt.Printf("Build finished. image id: %s\n", b.image)
return b.image, nil
}
return "", fmt.Errorf("An error occured during the build\n")
}

View file

@ -54,37 +54,37 @@ func ParseCommands(args ...string) error {
func (cli *DockerCli) CmdHelp(args ...string) error {
help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
for _, cmd := range [][]string{
{"attach", "Attach to a running container"},
{"build", "Build a container from Dockerfile via stdin"},
{"commit", "Create a new image from a container's changes"},
{"diff", "Inspect changes on a container's filesystem"},
{"export", "Stream the contents of a container as a tar archive"},
{"history", "Show the history of an image"},
{"images", "List images"},
{"import", "Create a new filesystem image from the contents of a tarball"},
{"info", "Display system-wide information"},
{"insert", "Insert a file in an image"},
{"inspect", "Return low-level information on a container"},
{"kill", "Kill a running container"},
{"login", "Register or Login to the docker registry server"},
{"logs", "Fetch the logs of a container"},
{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
{"ps", "List containers"},
{"pull", "Pull an image or a repository from the docker registry server"},
{"push", "Push an image or a repository to the docker registry server"},
{"restart", "Restart a running container"},
{"rm", "Remove a container"},
{"rmi", "Remove an image"},
{"run", "Run a command in a new container"},
{"search", "Search for an image in the docker index"},
{"start", "Start a stopped container"},
{"stop", "Stop a running container"},
{"tag", "Tag an image into a repository"},
{"version", "Show the docker version information"},
{"wait", "Block until a container stops, then print its exit code"},
for cmd, description := range map[string]string{
"attach": "Attach to a running container",
"build": "Build a container from Dockerfile or via stdin",
"commit": "Create a new image from a container's changes",
"diff": "Inspect changes on a container's filesystem",
"export": "Stream the contents of a container as a tar archive",
"history": "Show the history of an image",
"images": "List images",
"import": "Create a new filesystem image from the contents of a tarball",
"info": "Display system-wide information",
"insert": "Insert a file in an image",
"inspect": "Return low-level information on a container",
"kill": "Kill a running container",
"login": "Register or Login to the docker registry server",
"logs": "Fetch the logs of a container",
"port": "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT",
"ps": "List containers",
"pull": "Pull an image or a repository from the docker registry server",
"push": "Push an image or a repository to the docker registry server",
"restart": "Restart a running container",
"rm": "Remove a container",
"rmi": "Remove an image",
"run": "Run a command in a new container",
"search": "Search for an image in the docker index",
"start": "Start a stopped container",
"stop": "Stop a running container",
"tag": "Tag an image into a repository",
"version": "Show the docker version information",
"wait": "Block until a container stops, then print its exit code",
} {
help += fmt.Sprintf(" %-10.10s%s\n", cmd[0], cmd[1])
help += fmt.Sprintf(" %-10.10s%s\n", cmd, description)
}
fmt.Println(help)
return nil
@ -112,15 +112,29 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
}
func (cli *DockerCli) CmdBuild(args ...string) error {
cmd := Subcmd("build", "-", "Build an image from Dockerfile via stdin")
cmd := Subcmd("build", "-|Dockerfile", "Build an image from Dockerfile or via stdin")
if err := cmd.Parse(args); err != nil {
return nil
}
var (
file io.ReadCloser
err error
)
err := cli.hijack("POST", "/build", false)
if err != nil {
return err
if cmd.NArg() == 0 {
file, err = os.Open("Dockerfile")
if err != nil {
return err
}
} else if cmd.Arg(0) == "-" {
file = os.Stdin
} else {
file, err = os.Open(cmd.Arg(0))
if err != nil {
return err
}
}
NewBuilderClient(file)
return nil
}

View file

@ -140,8 +140,10 @@ func (srv *Server) ImagesViz(out io.Writer) error {
}
func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
var allImages map[string]*Image
var err error
var (
allImages map[string]*Image
err error
)
if all {
allImages, err = srv.runtime.graph.Map()
} else {
@ -150,7 +152,7 @@ func (srv *Server) Images(all bool, filter string) ([]ApiImages, error) {
if err != nil {
return nil, err
}
var outs []ApiImages = []ApiImages{} //produce [] when empty instead of 'null'
outs := []ApiImages{} //produce [] when empty instead of 'null'
for name, repository := range srv.runtime.repositories.Repositories {
if filter != "" && name != filter {
continue
@ -653,15 +655,6 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
return container.ShortId(), nil
}
func (srv *Server) ImageCreateFromFile(dockerfile io.Reader, out io.Writer) error {
img, err := NewBuilder(srv.runtime).Build(dockerfile, out)
if err != nil {
return err
}
fmt.Fprintf(out, "%s\n", img.ShortId())
return nil
}
func (srv *Server) ContainerRestart(name string, t int) error {
if container := srv.runtime.Get(name); container != nil {
if err := container.Restart(t); err != nil {
@ -722,6 +715,36 @@ func (srv *Server) ImageDelete(name string) error {
return nil
}
func (srv *Server) ImageGetCached(imgId string, config *Config) (*Image, error) {
// Retrieve all images
images, err := srv.runtime.graph.All()
if err != nil {
return nil, err
}
// Store the tree in a map of map (map[parentId][childId])
imageMap := make(map[string]map[string]struct{})
for _, img := range images {
if _, exists := imageMap[img.Parent]; !exists {
imageMap[img.Parent] = make(map[string]struct{})
}
imageMap[img.Parent][img.Id] = struct{}{}
}
// Loop on the children of the given image and check the config
for elem := range imageMap[imgId] {
img, err := srv.runtime.graph.Get(elem)
if err != nil {
return nil, err
}
if CompareConfig(&img.ContainerConfig, config) {
return img, nil
}
}
return nil, nil
}
func (srv *Server) ContainerStart(name string) error {
if container := srv.runtime.Get(name); container != nil {
if err := container.Start(); err != nil {

View file

@ -47,3 +47,42 @@ func CompareConfig(a, b *Config) bool {
return true
}
func MergeConfig(userConf, imageConf *Config) {
if userConf.Hostname != "" {
userConf.Hostname = imageConf.Hostname
}
if userConf.User != "" {
userConf.User = imageConf.User
}
if userConf.Memory == 0 {
userConf.Memory = imageConf.Memory
}
if userConf.MemorySwap == 0 {
userConf.MemorySwap = imageConf.MemorySwap
}
if userConf.CpuShares == 0 {
userConf.CpuShares = imageConf.CpuShares
}
if userConf.PortSpecs == nil || len(userConf.PortSpecs) == 0 {
userConf.PortSpecs = imageConf.PortSpecs
}
if !userConf.Tty {
userConf.Tty = imageConf.Tty
}
if !userConf.OpenStdin {
userConf.OpenStdin = imageConf.OpenStdin
}
if !userConf.StdinOnce {
userConf.StdinOnce = imageConf.StdinOnce
}
if userConf.Env == nil || len(userConf.Env) == 0 {
userConf.Env = imageConf.Env
}
if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
userConf.Cmd = imageConf.Cmd
}
if userConf.Dns == nil || len(userConf.Dns) == 0 {
userConf.Dns = imageConf.Dns
}
}