04cd20fa |
package docker
import ( |
2e797196 |
"bufio" |
599f85d4 |
"encoding/json" |
054451fd |
"errors" |
04cd20fa |
"fmt" |
f29e5dc8 |
"github.com/dotcloud/docker/auth" |
e4fbeddf |
"github.com/dotcloud/docker/gograph" |
9bb3dc98 |
"github.com/dotcloud/docker/registry" |
2e69e172 |
"github.com/dotcloud/docker/utils" |
04cd20fa |
"io" |
49b61af1 |
"io/ioutil" |
04cd20fa |
"log"
"net/http"
"net/url"
"os" |
2e797196 |
"os/exec" |
49b61af1 |
"path" |
04cd20fa |
"runtime"
"strings" |
fe204e6f |
"sync" |
b8d52ec2 |
"time" |
04cd20fa |
)
|
fd224ee5 |
func (srv *Server) DockerVersion() APIVersion {
return APIVersion{ |
1dae7a25 |
Version: VERSION, |
fd224ee5 |
GitCommit: GITCOMMIT, |
1dae7a25 |
GoVersion: runtime.Version(),
} |
04cd20fa |
}
|
ede1e6d4 |
// simpleVersionInfo is a simple implementation of
// the interface VersionInfo, which is used |
cd209f40 |
// to provide version information for some product,
// component, etc. It stores the product name and the version
// in string and returns them on calls to Name() and Version(). |
ede1e6d4 |
type simpleVersionInfo struct { |
5705a493 |
name string
version string
}
|
ede1e6d4 |
func (v *simpleVersionInfo) Name() string { |
5705a493 |
return v.name
}
|
ede1e6d4 |
func (v *simpleVersionInfo) Version() string { |
5705a493 |
return v.version
}
|
cd209f40 |
// versionCheckers() returns version informations of:
// docker, go, git-commit (of the docker) and the host's kernel.
//
// Such information will be used on call to NewRegistry(). |
6a56b7b3 |
func (srv *Server) versionInfos() []utils.VersionInfo { |
5705a493 |
v := srv.DockerVersion() |
6a56b7b3 |
ret := make([]utils.VersionInfo, 0, 4) |
ede1e6d4 |
ret = append(ret, &simpleVersionInfo{"docker", v.Version}) |
5705a493 |
if len(v.GoVersion) > 0 { |
ede1e6d4 |
ret = append(ret, &simpleVersionInfo{"go", v.GoVersion}) |
5705a493 |
}
if len(v.GitCommit) > 0 { |
ede1e6d4 |
ret = append(ret, &simpleVersionInfo{"git-commit", v.GitCommit}) |
5705a493 |
} |
d40efc46 |
kernelVersion, err := utils.GetKernelVersion()
if err == nil { |
ede1e6d4 |
ret = append(ret, &simpleVersionInfo{"kernel", kernelVersion.String()}) |
d40efc46 |
}
|
5705a493 |
return ret
}
|
04cd20fa |
func (srv *Server) ContainerKill(name string) error {
if container := srv.runtime.Get(name); container != nil {
if err := container.Kill(); err != nil { |
b5da8164 |
return fmt.Errorf("Error killing container %s: %s", name, err) |
04cd20fa |
} |
703905d7 |
srv.LogEvent("kill", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) |
04cd20fa |
} else {
return fmt.Errorf("No such container: %s", name)
}
return nil
}
|
57cfe72e |
func (srv *Server) ContainerExport(name string, out io.Writer) error { |
04cd20fa |
if container := srv.runtime.Get(name); container != nil {
data, err := container.Export()
if err != nil {
return err
}
// Stream the entire contents of the container (basically a volatile snapshot) |
57cfe72e |
if _, err := io.Copy(out, data); err != nil { |
04cd20fa |
return err
} |
703905d7 |
srv.LogEvent("export", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) |
04cd20fa |
return nil
}
return fmt.Errorf("No such container: %s", name)
}
|
fd224ee5 |
func (srv *Server) ImagesSearch(term string) ([]APISearch, error) { |
e1be3063 |
r, err := registry.NewRegistry(srv.runtime.config.GraphPath, nil, srv.HTTPRequestFactory(nil)) |
fde82f44 |
if err != nil {
return nil, err
}
results, err := r.SearchRepositories(term) |
59a6316f |
if err != nil { |
40794113 |
return nil, err
}
|
fd224ee5 |
var outs []APISearch |
59a6316f |
for _, repo := range results.Results { |
fd224ee5 |
var out APISearch |
59a6316f |
out.Description = repo["description"]
out.Name = repo["name"]
outs = append(outs, out)
}
return outs, nil
}
|
f339fc2e |
func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) (string, error) { |
ae9d7a51 |
out = utils.NewWriteFlusher(out) |
10c0e990 |
img, err := srv.runtime.repositories.LookupImage(name)
if err != nil { |
0f135ad7 |
return "", err |
10c0e990 |
}
|
2e69e172 |
file, err := utils.Download(url, out) |
10c0e990 |
if err != nil { |
0f135ad7 |
return "", err |
10c0e990 |
}
defer file.Body.Close()
|
4fdf11b2 |
config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities) |
10c0e990 |
if err != nil { |
0f135ad7 |
return "", err |
10c0e990 |
}
|
3e6d3ad1 |
c, _, err := srv.runtime.Create(config) |
10c0e990 |
if err != nil { |
0f135ad7 |
return "", err |
10c0e990 |
}
|
ba17f4a0 |
if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("", "Downloading", "%8v/%v (%v)"), sf, true), path); err != nil { |
a4879901 |
return "", err |
10c0e990 |
}
// FIXME: Handle custom repo, tag comment, author |
24e02043 |
img, err = srv.runtime.Commit(c, "", "", img.Comment, img.Author, nil) |
10c0e990 |
if err != nil { |
0f135ad7 |
return "", err |
10c0e990 |
} |
8742649a |
out.Write(sf.FormatStatus("", img.ID)) |
fd224ee5 |
return img.ShortID(), nil |
10c0e990 |
}
|
57cfe72e |
func (srv *Server) ImagesViz(out io.Writer) error { |
1fca99ad |
images, _ := srv.runtime.graph.Map() |
10c0e990 |
if images == nil {
return nil
} |
0ecf5e24 |
out.Write([]byte("digraph docker {\n")) |
10c0e990 |
|
57cfe72e |
var (
parentImage *Image
err error
) |
10c0e990 |
for _, image := range images {
parentImage, err = image.GetParent()
if err != nil {
return fmt.Errorf("Error while getting parent image: %v", err)
}
if parentImage != nil { |
fd224ee5 |
out.Write([]byte(" \"" + parentImage.ShortID() + "\" -> \"" + image.ShortID() + "\"\n")) |
10c0e990 |
} else { |
fd224ee5 |
out.Write([]byte(" base -> \"" + image.ShortID() + "\" [style=invis]\n")) |
10c0e990 |
}
}
reporefs := make(map[string][]string)
for name, repository := range srv.runtime.repositories.Repositories {
for tag, id := range repository { |
fd224ee5 |
reporefs[utils.TruncateID(id)] = append(reporefs[utils.TruncateID(id)], fmt.Sprintf("%s:%s", name, tag)) |
10c0e990 |
}
}
for id, repos := range reporefs { |
0ecf5e24 |
out.Write([]byte(" \"" + id + "\" [label=\"" + id + "\\n" + strings.Join(repos, "\\n") + "\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n")) |
10c0e990 |
} |
0ecf5e24 |
out.Write([]byte(" base [style=invisible]\n}\n")) |
10c0e990 |
return nil
}
|
fd224ee5 |
func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { |
0f312113 |
var (
allImages map[string]*Image
err error
) |
ab96da8e |
if all { |
04cd20fa |
allImages, err = srv.runtime.graph.Map()
} else {
allImages, err = srv.runtime.graph.Heads()
}
if err != nil {
return nil, err
} |
fd224ee5 |
outs := []APIImages{} //produce [] when empty instead of 'null' |
04cd20fa |
for name, repository := range srv.runtime.repositories.Repositories { |
35bcba80 |
if filter != "" { |
b44d1131 |
if match, _ := path.Match(filter, name); !match { |
35bcba80 |
continue
} |
04cd20fa |
}
for tag, id := range repository { |
fd224ee5 |
var out APIImages |
04cd20fa |
image, err := srv.runtime.graph.Get(id)
if err != nil {
log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
continue
}
delete(allImages, id) |
1990c49a |
out.Repository = name
out.Tag = tag |
fd224ee5 |
out.ID = image.ID |
1990c49a |
out.Created = image.Created.Unix() |
6fce89e6 |
out.Size = image.Size |
00cf2a1f |
out.VirtualSize = image.getParentsSize(0) + image.Size |
04cd20fa |
outs = append(outs, out)
}
}
// Display images which aren't part of a
if filter == "" { |
1990c49a |
for _, image := range allImages { |
fd224ee5 |
var out APIImages
out.ID = image.ID |
1990c49a |
out.Created = image.Created.Unix() |
6fce89e6 |
out.Size = image.Size |
00cf2a1f |
out.VirtualSize = image.getParentsSize(0) + image.Size |
04cd20fa |
outs = append(outs, out)
}
} |
cd6aeaf9 |
|
e6affb1b |
sortImagesByCreationAndTag(outs) |
04cd20fa |
return outs, nil
}
|
fd224ee5 |
func (srv *Server) DockerInfo() *APIInfo { |
1fca99ad |
images, _ := srv.runtime.graph.Map() |
04cd20fa |
var imgcount int
if images == nil {
imgcount = 0
} else {
imgcount = len(images)
} |
921c6994 |
lxcVersion := ""
if output, err := exec.Command("lxc-version").CombinedOutput(); err == nil {
outputStr := string(output)
if len(strings.SplitN(outputStr, ":", 2)) == 2 {
lxcVersion = strings.TrimSpace(strings.SplitN(string(output), ":", 2)[1])
}
} |
6057e6ad |
kernelVersion := "<unknown>"
if kv, err := utils.GetKernelVersion(); err == nil {
kernelVersion = kv.String()
} |
921c6994 |
|
fd224ee5 |
return &APIInfo{ |
30349016 |
Containers: len(srv.runtime.List()),
Images: imgcount,
MemoryLimit: srv.runtime.capabilities.MemoryLimit,
SwapLimit: srv.runtime.capabilities.SwapLimit, |
b21f8986 |
IPv4Forwarding: !srv.runtime.capabilities.IPv4ForwardingDisabled, |
30349016 |
Debug: os.Getenv("DEBUG") != "",
NFd: utils.GetTotalUsedFds(),
NGoroutines: runtime.NumGoroutine(),
LXCVersion: lxcVersion,
NEventsListener: len(srv.events),
KernelVersion: kernelVersion,
IndexServerAddress: auth.IndexServerAddress(), |
1dae7a25 |
} |
04cd20fa |
}
|
fd224ee5 |
func (srv *Server) ImageHistory(name string) ([]APIHistory, error) { |
04cd20fa |
image, err := srv.runtime.repositories.LookupImage(name)
if err != nil {
return nil, err
}
|
808faa63 |
lookupMap := make(map[string][]string) |
3bfc8225 |
for name, repository := range srv.runtime.repositories.Repositories {
for tag, id := range repository {
// If the ID already has a reverse lookup, do not update it unless for "latest" |
808faa63 |
if _, exists := lookupMap[id]; !exists {
lookupMap[id] = []string{} |
3bfc8225 |
} |
808faa63 |
lookupMap[id] = append(lookupMap[id], name+":"+tag) |
3bfc8225 |
}
}
|
fd224ee5 |
outs := []APIHistory{} //produce [] when empty instead of 'null' |
04cd20fa |
err = image.WalkHistory(func(img *Image) error { |
fd224ee5 |
var out APIHistory
out.ID = srv.runtime.repositories.ImageName(img.ShortID()) |
04cd20fa |
out.Created = img.Created.Unix()
out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ") |
808faa63 |
out.Tags = lookupMap[img.ID] |
04cd20fa |
outs = append(outs, out)
return nil
})
return outs, nil
}
|
cfec1c3e |
func (srv *Server) ContainerTop(name, ps_args string) (*APITop, error) { |
2e797196 |
if container := srv.runtime.Get(name); container != nil { |
cfec1c3e |
output, err := exec.Command("lxc-ps", "--name", container.ID, "--", ps_args).CombinedOutput() |
2e797196 |
if err != nil {
return nil, fmt.Errorf("Error trying to use lxc-ps: %s (%s)", err, output)
} |
cfec1c3e |
procs := APITop{} |
2e797196 |
for i, line := range strings.Split(string(output), "\n") { |
cfec1c3e |
if len(line) == 0 { |
2e797196 |
continue
} |
cfec1c3e |
words := []string{} |
2e797196 |
scanner := bufio.NewScanner(strings.NewReader(line))
scanner.Split(bufio.ScanWords)
if !scanner.Scan() {
return nil, fmt.Errorf("Error trying to use lxc-ps")
}
// no scanner.Text because we skip container id |
cfec1c3e |
for scanner.Scan() {
words = append(words, scanner.Text())
}
if i == 0 {
procs.Titles = words
} else {
procs.Processes = append(procs.Processes, words)
} |
2e797196 |
} |
cfec1c3e |
return &procs, nil |
2e797196 |
}
return nil, fmt.Errorf("No such container: %s", name)
}
|
60ddcaa1 |
func (srv *Server) ContainerChanges(name string) ([]Change, error) { |
04cd20fa |
if container := srv.runtime.Get(name); container != nil { |
60ddcaa1 |
return container.Changes() |
04cd20fa |
}
return nil, fmt.Errorf("No such container: %s", name)
}
|
bd04d7d4 |
func (srv *Server) Containers(all, size bool, n int, since, before string) []APIContainers { |
bc3fa506 |
var foundBefore bool
var displayed int |
e4fbeddf |
out := []APIContainers{} |
152ebeea |
|
bc3fa506 |
for _, container := range srv.runtime.List() {
if !container.State.Running && !all && n == -1 && since == "" && before == "" { |
04cd20fa |
continue
} |
bc3fa506 |
if before != "" { |
fd224ee5 |
if container.ShortID() == before { |
bc3fa506 |
foundBefore = true
continue
}
if !foundBefore {
continue
}
}
if displayed == n {
break
} |
fd224ee5 |
if container.ShortID() == since { |
04cd20fa |
break
} |
152ebeea |
displayed++ |
e4fbeddf |
c := createAPIContainer(container, size, srv.runtime) |
a5dbb9a9 |
out = append(out, c) |
04cd20fa |
} |
a5dbb9a9 |
return out |
04cd20fa |
}
|
e4fbeddf |
func createAPIContainer(container *Container, size bool, runtime *Runtime) APIContainers {
c := APIContainers{
ID: container.ID,
}
names := []string{} |
457a9260 |
runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error { |
e4fbeddf |
if e.ID() == container.ID {
names = append(names, p)
}
return nil |
457a9260 |
}, -1) |
e4fbeddf |
c.Names = names
c.Image = runtime.repositories.ImageName(container.Image)
c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
c.Created = container.Created.Unix()
c.Status = container.State.String()
c.Ports = container.NetworkSettings.PortMappingAPI()
if size {
c.SizeRw, c.SizeRootFs = container.GetSize()
}
return c
} |
04cd20fa |
func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, config *Config) (string, error) { |
10c0e990 |
container := srv.runtime.Get(name)
if container == nil {
return "", fmt.Errorf("No such container: %s", name)
} |
24e02043 |
img, err := srv.runtime.Commit(container, repo, tag, comment, author, config) |
04cd20fa |
if err != nil {
return "", err
} |
fd224ee5 |
return img.ShortID(), err |
04cd20fa |
}
func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
if err := srv.runtime.repositories.Set(repo, tag, name, force); err != nil {
return err
}
return nil
}
|
19121c16 |
func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoint string, token []string, sf *utils.StreamFormatter) error {
history, err := r.GetRemoteHistory(imgID, endpoint, token) |
9bb3dc98 |
if err != nil { |
04cd20fa |
return err
} |
3f802f4a |
out.Write(sf.FormatProgress(utils.TruncateID(imgID), "Pulling", "dependend layers")) |
9bb3dc98 |
// FIXME: Try to stream the images?
// FIXME: Launch the getRemoteImage() in goroutines |
3f802f4a |
|
9bb3dc98 |
for _, id := range history { |
3f802f4a |
// ensure no two downloads of the same layer happen at the same time
if err := srv.poolAdd("pull", "layer:"+id); err != nil {
utils.Debugf("Image (id: %s) pull is already running, skipping: %v", id, err)
return nil
}
defer srv.poolRemove("pull", "layer:"+id)
|
9bb3dc98 |
if !srv.runtime.graph.Exists(id) { |
213365c2 |
out.Write(sf.FormatProgress(utils.TruncateID(id), "Pulling", "metadata")) |
f355d33b |
imgJSON, imgSize, err := r.GetRemoteImageJSON(id, endpoint, token) |
9bb3dc98 |
if err != nil { |
3f802f4a |
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error", "pulling dependend layers")) |
9b2a5964 |
// FIXME: Keep going in case of error? |
9bb3dc98 |
return err
} |
fd224ee5 |
img, err := NewImgJSON(imgJSON) |
9bb3dc98 |
if err != nil { |
3f802f4a |
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error", "pulling dependend layers")) |
9bb3dc98 |
return fmt.Errorf("Failed to parse json: %s", err)
}
// Get the layer |
213365c2 |
out.Write(sf.FormatProgress(utils.TruncateID(id), "Pulling", "fs layer")) |
f355d33b |
layer, err := r.GetRemoteImageLayer(img.ID, endpoint, token) |
9bb3dc98 |
if err != nil { |
3f802f4a |
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error", "pulling dependend layers")) |
9bb3dc98 |
return err
} |
830c458f |
defer layer.Close() |
ba17f4a0 |
if err := srv.runtime.graph.Register(imgJSON, utils.ProgressReader(layer, imgSize, out, sf.FormatProgress(utils.TruncateID(id), "Downloading", "%8v/%v (%v)"), sf, false), img); err != nil { |
3f802f4a |
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error", "downloading dependend layers")) |
9bb3dc98 |
return err
}
} |
3f802f4a |
out.Write(sf.FormatProgress(utils.TruncateID(id), "Download", "complete"))
|
9bb3dc98 |
} |
04cd20fa |
return nil
}
|
46f59dd9 |
func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, localName, remoteName, askedTag, indexEp string, sf *utils.StreamFormatter, parallel bool) error { |
8742649a |
out.Write(sf.FormatStatus("", "Pulling repository %s", localName)) |
9bb3dc98 |
|
31c66d5a |
repoData, err := r.GetRepositoryData(indexEp, remoteName) |
f37399d2 |
if err != nil { |
9bb3dc98 |
return err
}
utils.Debugf("Retrieving the tag list") |
31c66d5a |
tagsList, err := r.GetRemoteTags(repoData.Endpoints, remoteName, repoData.Tokens) |
9bb3dc98 |
if err != nil { |
c3dd6e19 |
utils.Debugf("%v", err) |
9bb3dc98 |
return err
} |
de0a48bd |
|
66a9d06d |
for tag, id := range tagsList {
repoData.ImgList[id] = ®istry.ImgData{
ID: id,
Tag: tag,
Checksum: "", |
de0a48bd |
}
}
|
f498dd2a |
utils.Debugf("Registering tags") |
de0a48bd |
// If no tag has been specified, pull them all |
f498dd2a |
if askedTag == "" {
for tag, id := range tagsList {
repoData.ImgList[id].Tag = tag
}
} else {
// Otherwise, check that the tag exists and use only that one |
86ada2fa |
id, exists := tagsList[askedTag]
if !exists { |
31c66d5a |
return fmt.Errorf("Tag %s not found in repository %s", askedTag, localName) |
f498dd2a |
} |
86ada2fa |
repoData.ImgList[id].Tag = askedTag |
9bb3dc98 |
}
|
0e71e368 |
errors := make(chan error)
for _, image := range repoData.ImgList { |
46f59dd9 |
downloadImage := func(img *registry.ImgData) { |
0e71e368 |
if askedTag != "" && img.Tag != askedTag {
utils.Debugf("(%s) does not match %s (id: %s), skipping", img.Tag, askedTag, img.ID) |
3f802f4a |
if parallel {
errors <- nil
} |
0e71e368 |
return
} |
7cc294e7 |
|
0e71e368 |
if img.Tag == "" {
utils.Debugf("Image (id: %s) present in this repository but untagged, skipping", img.ID) |
3f802f4a |
if parallel {
errors <- nil
}
return
}
// ensure no two downloads of the same image happen at the same time
if err := srv.poolAdd("pull", "img:"+img.ID); err != nil {
utils.Debugf("Image (id: %s) pull is already running, skipping: %v", img.ID, err)
if parallel {
errors <- nil
} |
0e71e368 |
return |
9bb3dc98 |
} |
3f802f4a |
defer srv.poolRemove("pull", "img:"+img.ID)
|
213365c2 |
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Pulling", fmt.Sprintf("image (%s) from %s", img.Tag, localName))) |
0e71e368 |
success := false |
3f802f4a |
var lastErr error |
0e71e368 |
for _, ep := range repoData.Endpoints { |
3f802f4a |
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Pulling", fmt.Sprintf("image (%s) from %s, endpoint: %s", img.Tag, localName, ep))) |
0e71e368 |
if err := srv.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil { |
3f802f4a |
// Its not ideal that only the last error is returned, it would be better to concatenate the errors.
// As the error is also given to the output stream the user will see the error.
lastErr = err
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Error pulling", fmt.Sprintf("image (%s) from %s, endpoint: %s, %s", img.Tag, localName, ep, err))) |
0e71e368 |
continue
}
success = true
break
}
if !success { |
3f802f4a |
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Error pulling", fmt.Sprintf("image (%s) from %s, %s", img.Tag, localName, lastErr)))
if parallel {
errors <- fmt.Errorf("Could not find repository on any of the indexed registries.")
return
}
}
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Download", "complete"))
if parallel {
errors <- nil |
0e71e368 |
} |
9bb3dc98 |
} |
7cc294e7 |
|
46f59dd9 |
if parallel {
go downloadImage(image)
} else {
downloadImage(image) |
7cc294e7 |
} |
0e71e368 |
} |
46f59dd9 |
if parallel { |
3f802f4a |
var lastError error |
46f59dd9 |
for i := 0; i < len(repoData.ImgList); i++ {
if err := <-errors; err != nil { |
3f802f4a |
lastError = err |
9bb3dc98 |
}
} |
3f802f4a |
if lastError != nil {
return lastError
} |
0e71e368 |
|
3f802f4a |
} |
9bb3dc98 |
for tag, id := range tagsList { |
49505c59 |
if askedTag != "" && tag != askedTag {
continue
} |
31c66d5a |
if err := srv.runtime.repositories.Set(localName, tag, id, true); err != nil { |
9bb3dc98 |
return err
}
}
if err := srv.runtime.repositories.Save(); err != nil { |
f37399d2 |
return err
} |
9bb3dc98 |
return nil
}
|
fe204e6f |
func (srv *Server) poolAdd(kind, key string) error { |
1cf9c80e |
srv.Lock()
defer srv.Unlock() |
fe204e6f |
if _, exists := srv.pullingPool[key]; exists { |
fb005a3d |
return fmt.Errorf("pull %s is already in progress", key) |
2db99441 |
}
if _, exists := srv.pushingPool[key]; exists {
return fmt.Errorf("push %s is already in progress", key) |
fe204e6f |
}
switch kind {
case "pull":
srv.pullingPool[key] = struct{}{}
break
case "push":
srv.pushingPool[key] = struct{}{}
break
default: |
9b2a5964 |
return fmt.Errorf("Unknown pool type") |
fe204e6f |
}
return nil
}
func (srv *Server) poolRemove(kind, key string) error {
switch kind {
case "pull":
delete(srv.pullingPool, key)
break
case "push":
delete(srv.pushingPool, key)
break
default: |
9b2a5964 |
return fmt.Errorf("Unknown pool type") |
fe204e6f |
}
return nil
}
|
093b85b7 |
func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig, metaHeaders map[string][]string, parallel bool) error { |
e1be3063 |
r, err := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders)) |
fde82f44 |
if err != nil {
return err
} |
31c66d5a |
if err := srv.poolAdd("pull", localName+":"+tag); err != nil { |
fe204e6f |
return err
} |
31c66d5a |
defer srv.poolRemove("pull", localName+":"+tag) |
fe204e6f |
|
66a9d06d |
// Resolve the Repository name from fqn to endpoint + name |
31c66d5a |
endpoint, remoteName, err := registry.ResolveRepositoryName(localName) |
66a9d06d |
if err != nil { |
fe204e6f |
return err
} |
66a9d06d |
|
f44eac49 |
if endpoint == auth.IndexServerAddress() {
// If pull "index.docker.io/foo/bar", it's stored locally under "foo/bar"
localName = remoteName
} |
fe204e6f |
|
cf35e8ed |
out = utils.NewWriteFlusher(out) |
46f59dd9 |
err = srv.pullRepository(r, out, localName, remoteName, tag, endpoint, sf, parallel) |
fcee6056 |
if err == registry.ErrLoginRequired {
return err
} |
66a9d06d |
if err != nil { |
31c66d5a |
if err := srv.pullImage(r, out, remoteName, endpoint, nil, sf); err != nil { |
04cd20fa |
return err
} |
10c0e990 |
return nil |
04cd20fa |
} |
67ecd2cb |
|
9bb3dc98 |
return nil
}
|
828d1aa5 |
// Retrieve the all the images to be uploaded in the correct order
// Note: we can't use a map as it is not ordered |
b3a70d76 |
func (srv *Server) getImageList(localRepo map[string]string) ([][]*registry.ImgData, error) {
imgList := map[string]*registry.ImgData{}
depGraph := utils.NewDependencyGraph() |
828d1aa5 |
for tag, id := range localRepo { |
49b61af1 |
img, err := srv.runtime.graph.Get(id) |
828d1aa5 |
if err != nil { |
49b61af1 |
return nil, err |
828d1aa5 |
} |
b3a70d76 |
depGraph.NewNode(img.ID)
img.WalkHistory(func(current *Image) error {
imgList[current.ID] = ®istry.ImgData{ |
b44d1131 |
ID: current.ID, |
b3a70d76 |
Tag: tag,
}
parent, err := current.GetParent()
if err != nil {
return err
}
if parent == nil { |
828d1aa5 |
return nil
} |
b3a70d76 |
depGraph.NewNode(parent.ID)
depGraph.AddDependency(current.ID, parent.ID) |
828d1aa5 |
return nil
})
} |
b3a70d76 |
traversalMap, err := depGraph.GenerateTraversalMap()
if err != nil {
return nil, err
}
utils.Debugf("Traversal map: %v", traversalMap)
result := [][]*registry.ImgData{}
for _, round := range traversalMap {
dataRound := []*registry.ImgData{}
for _, imgID := range round {
dataRound = append(dataRound, imgList[imgID])
}
result = append(result, dataRound)
}
return result, nil
}
func flatten(slc [][]*registry.ImgData) []*registry.ImgData {
result := []*registry.ImgData{}
for _, x := range slc {
result = append(result, x...)
}
return result |
828d1aa5 |
}
|
31c66d5a |
func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName, remoteName string, localRepo map[string]string, indexEp string, sf *utils.StreamFormatter) error { |
ae9d7a51 |
out = utils.NewWriteFlusher(out) |
49b61af1 |
imgList, err := srv.getImageList(localRepo) |
828d1aa5 |
if err != nil {
return err
} |
b3a70d76 |
flattenedImgList := flatten(imgList) |
8742649a |
out.Write(sf.FormatStatus("", "Sending image list")) |
828d1aa5 |
|
de0a48bd |
var repoData *registry.RepositoryData |
b3a70d76 |
repoData, err = r.PushImageJSONIndex(indexEp, remoteName, flattenedImgList, false, nil) |
828d1aa5 |
if err != nil {
return err
}
for _, ep := range repoData.Endpoints { |
8742649a |
out.Write(sf.FormatStatus("", "Pushing repository %s (%d tags)", localName, len(localRepo))) |
b3a70d76 |
// This section can not be parallelized (each round depends on the previous one)
for _, round := range imgList {
// FIXME: This section can be parallelized
for _, elem := range round { |
64bc08f1 |
var pushTags func() error
pushTags = func() error {
out.Write(sf.FormatStatus("", "Pushing tags for rev [%s] on {%s}", elem.ID, ep+"repositories/"+remoteName+"/tags/"+elem.Tag))
if err := r.PushRegistryTag(remoteName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
return err
}
return nil
} |
b3a70d76 |
if _, exists := repoData.ImgList[elem.ID]; exists { |
64bc08f1 |
if err := pushTags(); err != nil {
return err
} |
b3a70d76 |
out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", elem.ID))
continue
} else if r.LookupRemoteImage(elem.ID, ep, repoData.Tokens) { |
64bc08f1 |
if err := pushTags(); err != nil {
return err
} |
b3a70d76 |
out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", elem.ID))
continue
}
if checksum, err := srv.pushImage(r, out, remoteName, elem.ID, ep, repoData.Tokens, sf); err != nil {
// FIXME: Continue on error?
return err
} else {
elem.Checksum = checksum
} |
c6dc90cc |
if err := pushTags(); err != nil {
return err
} |
49b61af1 |
} |
828d1aa5 |
}
}
|
b3a70d76 |
if _, err := r.PushImageJSONIndex(indexEp, remoteName, flattenedImgList, true, repoData.Endpoints); err != nil { |
828d1aa5 |
return err
} |
de0a48bd |
|
49b61af1 |
return nil |
828d1aa5 |
}
|
5b27652a |
func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID, ep string, token []string, sf *utils.StreamFormatter) (checksum string, err error) { |
ae9d7a51 |
out = utils.NewWriteFlusher(out) |
19121c16 |
jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgID, "json")) |
828d1aa5 |
if err != nil { |
9b2a5964 |
return "", fmt.Errorf("Error while retrieving the path for {%s}: %s", imgID, err) |
828d1aa5 |
} |
8742649a |
out.Write(sf.FormatStatus("", "Pushing %s", imgID)) |
828d1aa5 |
imgData := ®istry.ImgData{ |
8ca7b064 |
ID: imgID, |
828d1aa5 |
}
|
dc9d6c1c |
// Send the json |
fd224ee5 |
if err := r.PushImageJSONRegistry(imgData, jsonRaw, ep, token); err != nil { |
dc9d6c1c |
if err == registry.ErrAlreadyExists { |
8742649a |
out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", imgData.ID)) |
5b27652a |
return "", nil |
dc9d6c1c |
} |
5b27652a |
return "", err |
dc9d6c1c |
}
|
8ca7b064 |
layerData, err := srv.runtime.graph.TempLayerArchive(imgID, Uncompressed, sf, out) |
828d1aa5 |
if err != nil { |
5b27652a |
return "", fmt.Errorf("Failed to generate layer archive: %s", err) |
828d1aa5 |
} |
49b61af1 |
// Send the layer |
213365c2 |
if checksum, err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("", "Pushing", "%8v/%v (%v)"), sf, false), ep, token, jsonRaw); err != nil { |
5b27652a |
return "", err |
8ca7b064 |
} else {
imgData.Checksum = checksum |
04cd20fa |
} |
213365c2 |
out.Write(sf.FormatStatus("", "")) |
8ca7b064 |
// Send the checksum
if err := r.PushImageChecksumRegistry(imgData, ep, token); err != nil { |
5b27652a |
return "", err |
8ca7b064 |
}
|
5b27652a |
return imgData.Checksum, nil |
04cd20fa |
}
|
9b2a5964 |
// FIXME: Allow to interrupt current push when new push of same image is done. |
093b85b7 |
func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig, metaHeaders map[string][]string) error { |
31c66d5a |
if err := srv.poolAdd("push", localName); err != nil { |
fe204e6f |
return err
} |
31c66d5a |
defer srv.poolRemove("push", localName) |
fe204e6f |
|
66a9d06d |
// Resolve the Repository name from fqn to endpoint + name |
31c66d5a |
endpoint, remoteName, err := registry.ResolveRepositoryName(localName) |
d3125d85 |
if err != nil { |
fe204e6f |
return err
}
|
ae9d7a51 |
out = utils.NewWriteFlusher(out) |
31c66d5a |
img, err := srv.runtime.graph.Get(localName) |
e1be3063 |
r, err2 := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders)) |
fde82f44 |
if err2 != nil {
return err2
} |
57d751c3 |
|
f37399d2 |
if err != nil { |
31c66d5a |
reposLen := len(srv.runtime.repositories.Repositories[localName]) |
8742649a |
out.Write(sf.FormatStatus("", "The push refers to a repository [%s] (len: %d)", localName, reposLen)) |
b56b2da5 |
// If it fails, try to get the repository |
31c66d5a |
if localRepo, exists := srv.runtime.repositories.Repositories[localName]; exists {
if err := srv.pushRepository(r, out, localName, remoteName, localRepo, endpoint, sf); err != nil { |
f37399d2 |
return err
}
return nil
}
return err
} |
66a9d06d |
var token []string |
8742649a |
out.Write(sf.FormatStatus("", "The push refers to an image: [%s]", localName)) |
5b27652a |
if _, err := srv.pushImage(r, out, remoteName, img.ID, endpoint, token, sf); err != nil { |
f37399d2 |
return err
}
return nil
}
|
c8c7094b |
func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Writer, sf *utils.StreamFormatter) error { |
04cd20fa |
var archive io.Reader
var resp *http.Response
if src == "-" { |
57cfe72e |
archive = in |
04cd20fa |
} else {
u, err := url.Parse(src)
if err != nil { |
c8c7094b |
return err |
04cd20fa |
}
if u.Scheme == "" {
u.Scheme = "http"
u.Host = src
u.Path = ""
} |
8742649a |
out.Write(sf.FormatStatus("", "Downloading from %s", u)) |
b56b2da5 |
// Download with curl (pretty progress bar)
// If curl is not available, fallback to http.Get() |
2e69e172 |
resp, err = utils.Download(u.String(), out) |
04cd20fa |
if err != nil {
return err
} |
ba17f4a0 |
archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("", "Importing", "%8v/%v (%v)"), sf, true) |
04cd20fa |
}
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
if err != nil {
return err
} |
b56b2da5 |
// Optionally register the image at REPO/TAG |
04cd20fa |
if repo != "" { |
fd224ee5 |
if err := srv.runtime.repositories.Set(repo, tag, img.ID, true); err != nil { |
04cd20fa |
return err
}
} |
8742649a |
out.Write(sf.FormatStatus("", img.ShortID())) |
04cd20fa |
return nil
}
|
3e6d3ad1 |
func (srv *Server) ContainerCreate(config *Config) (string, []string, error) { |
9ee11161 |
if config.Memory != 0 && config.Memory < 524288 { |
3e6d3ad1 |
return "", nil, fmt.Errorf("Memory limit must be given in bytes (minimum 524288 bytes)") |
9ee11161 |
}
|
04cd20fa |
if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
config.Memory = 0
}
if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
config.MemorySwap = -1
} |
3e6d3ad1 |
container, buildWarnings, err := srv.runtime.Create(config) |
04cd20fa |
if err != nil {
if srv.runtime.graph.IsNotExist(err) { |
07a88703 |
_, tag := utils.ParseRepositoryTag(config.Image)
if tag == "" {
tag = DEFAULTTAG
}
|
3e6d3ad1 |
return "", nil, fmt.Errorf("No such image: %s (tag: %s)", config.Image, tag) |
04cd20fa |
} |
3e6d3ad1 |
return "", nil, err |
04cd20fa |
} |
703905d7 |
srv.LogEvent("create", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) |
3e6d3ad1 |
return container.ShortID(), buildWarnings, nil |
04cd20fa |
}
func (srv *Server) ContainerRestart(name string, t int) error {
if container := srv.runtime.Get(name); container != nil {
if err := container.Restart(t); err != nil { |
1277dca3 |
return fmt.Errorf("Error restarting container %s: %s", name, err) |
04cd20fa |
} |
703905d7 |
srv.LogEvent("restart", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) |
04cd20fa |
} else {
return fmt.Errorf("No such container: %s", name)
}
return nil
}
|
8c1fae65 |
func (srv *Server) ContainerDestroy(name string, removeVolume, removeLink bool) error {
if removeLink { |
51db855b |
p := name
if p[0] != '/' {
p = "/" + p
}
parent, n := path.Split(p)
l := len(parent)
if parent[l-1] == '/' {
parent = parent[:l-1]
}
pe := srv.runtime.containerGraph.Get(parent)
parentContainer := srv.runtime.Get(pe.ID())
|
8c1fae65 |
if parentContainer != nil && parentContainer.activeLinks != nil { |
51db855b |
if link, exists := parentContainer.activeLinks[n]; exists { |
8c1fae65 |
link.Disable() |
51db855b |
} else {
utils.Debugf("Could not find active link for %s", name) |
8c1fae65 |
}
}
if err := srv.runtime.containerGraph.Delete(name); err != nil {
return err
}
return nil
} |
04cd20fa |
if container := srv.runtime.Get(name); container != nil { |
1c841d4f |
if container.State.Running {
return fmt.Errorf("Impossible to remove a running container, please stop it first")
} |
6f9b574f |
volumes := make(map[string]struct{})
// Store all the deleted containers volumes
for _, volumeId := range container.Volumes {
volumes[volumeId] = struct{}{}
} |
04cd20fa |
if err := srv.runtime.Destroy(container); err != nil { |
1277dca3 |
return fmt.Errorf("Error destroying container %s: %s", name, err) |
04cd20fa |
} |
703905d7 |
srv.LogEvent("destroy", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) |
6f9b574f |
|
0c6380cc |
if removeVolume { |
6f9b574f |
// Retrieve all volumes from all remaining containers
usedVolumes := make(map[string]*Container)
for _, container := range srv.runtime.List() {
for _, containerVolumeId := range container.Volumes {
usedVolumes[containerVolumeId] = container
}
}
for volumeId := range volumes {
// If the requested volu
if c, exists := usedVolumes[volumeId]; exists { |
fd224ee5 |
log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.ID) |
6f9b574f |
continue
}
if err := srv.runtime.volumes.Delete(volumeId); err != nil {
return err
}
}
} |
04cd20fa |
} else {
return fmt.Errorf("No such container: %s", name)
}
return nil
}
|
054451fd |
var ErrImageReferenced = errors.New("Image referenced by a repository")
|
66d9a733 |
func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error { |
054451fd |
// If the image is referenced by a repo, do not delete |
66d9a733 |
if len(srv.runtime.repositories.ByID()[id]) != 0 { |
054451fd |
return ErrImageReferenced
}
// If the image is not referenced but has children, go recursive
referenced := false |
5aa95b66 |
byParents, err := srv.runtime.graph.ByParent() |
04cd20fa |
if err != nil { |
5aa95b66 |
return err |
86ada2fa |
} |
054451fd |
for _, img := range byParents[id] { |
66d9a733 |
if err := srv.deleteImageAndChildren(img.ID, imgs); err != nil { |
054451fd |
if err != ErrImageReferenced {
return err
} |
66d9a733 |
referenced = true |
054451fd |
} |
86ada2fa |
} |
054451fd |
if referenced {
return ErrImageReferenced
} |
5aa95b66 |
// If the image is not referenced and has no children, remove it
byParents, err = srv.runtime.graph.ByParent()
if err != nil {
return err
}
if len(byParents[id]) == 0 {
if err := srv.runtime.repositories.DeleteAll(id); err != nil {
return err
} |
9060b5c2 |
err := srv.runtime.graph.Delete(id)
if err != nil {
return err
} |
66d9a733 |
*imgs = append(*imgs, APIRmi{Deleted: utils.TruncateID(id)}) |
703905d7 |
srv.LogEvent("delete", utils.TruncateID(id), "") |
9060b5c2 |
return nil |
04cd20fa |
}
return nil
}
|
66d9a733 |
func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error { |
054451fd |
if img.Parent != "" {
parent, err := srv.runtime.graph.Get(img.Parent)
if err != nil {
return err
}
// Remove all children images |
9060b5c2 |
if err := srv.deleteImageAndChildren(img.Parent, imgs); err != nil { |
054451fd |
return err
} |
9060b5c2 |
return srv.deleteImageParents(parent, imgs) |
04cd20fa |
}
return nil
}
|
54da339b |
func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, error) { |
dea29e7c |
imgs := []APIRmi{} |
5a934fc9 |
//If delete by id, see if the id belong only to one repository
if strings.Contains(img.ID, repoName) && tag == "" {
for _, repoAndTag := range srv.runtime.repositories.ByID()[img.ID] { |
c84d74df |
parsedRepo, parsedTag := utils.ParseRepositoryTag(repoAndTag) |
5a934fc9 |
if strings.Contains(img.ID, repoName) {
repoName = parsedRepo |
c84d74df |
if len(srv.runtime.repositories.ByID()[img.ID]) == 1 && len(parsedTag) > 1 {
tag = parsedTag |
e608296b |
} |
5a934fc9 |
} else if repoName != parsedRepo {
// the id belongs to multiple repos, like base:latest and user:test,
// in that case return conflict
return imgs, nil
}
}
}
//Untag the current image |
9060b5c2 |
tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag)
if err != nil {
return nil, err
}
if tagDeleted { |
66d9a733 |
imgs = append(imgs, APIRmi{Untagged: img.ShortID()}) |
703905d7 |
srv.LogEvent("untag", img.ShortID(), "") |
054451fd |
} |
66d9a733 |
if len(srv.runtime.repositories.ByID()[img.ID]) == 0 {
if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil { |
5aa95b66 |
if err != ErrImageReferenced { |
54da339b |
return imgs, err |
5aa95b66 |
} |
9060b5c2 |
} else if err := srv.deleteImageParents(img, &imgs); err != nil { |
054451fd |
if err != ErrImageReferenced { |
54da339b |
return imgs, err |
054451fd |
}
}
} |
54da339b |
return imgs, nil |
054451fd |
}
|
54da339b |
func (srv *Server) ImageDelete(name string, autoPrune bool) ([]APIRmi, error) { |
5aa95b66 |
img, err := srv.runtime.repositories.LookupImage(name)
if err != nil { |
9060b5c2 |
return nil, fmt.Errorf("No such image: %s", name)
}
if !autoPrune { |
66d9a733 |
if err := srv.runtime.graph.Delete(img.ID); err != nil { |
1277dca3 |
return nil, fmt.Errorf("Error deleting image %s: %s", name, err) |
9060b5c2 |
}
return nil, nil |
5aa95b66 |
}
|
63876e7d |
name, tag := utils.ParseRepositoryTag(name) |
5aa95b66 |
return srv.deleteImage(img, name, tag) |
04cd20fa |
}
|
19121c16 |
func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error) { |
0f312113 |
// Retrieve all images |
1fca99ad |
images, err := srv.runtime.graph.Map() |
0f312113 |
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{})
} |
fd224ee5 |
imageMap[img.Parent][img.ID] = struct{}{} |
0f312113 |
}
// Loop on the children of the given image and check the config |
19121c16 |
for elem := range imageMap[imgID] { |
0f312113 |
img, err := srv.runtime.graph.Get(elem)
if err != nil {
return nil, err
}
if CompareConfig(&img.ContainerConfig, config) {
return img, nil
}
}
return nil, nil
}
|
4fdf11b2 |
func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error { |
457a9260 |
runtime := srv.runtime
container := runtime.Get(name)
if container == nil { |
04cd20fa |
return fmt.Errorf("No such container: %s", name)
} |
457a9260 |
// Register links
if hostConfig != nil && hostConfig.Links != nil {
for _, l := range hostConfig.Links {
parts, err := parseLink(l)
if err != nil {
return err
}
childName := parts["name"]
if err := runtime.Link(fmt.Sprintf("/%s", container.ID), childName, parts["alias"]); err != nil {
return err
}
}
}
if err := container.Start(hostConfig); err != nil {
return fmt.Errorf("Error starting container %s: %s", name, err)
}
srv.LogEvent("start", container.ShortID(), runtime.repositories.ImageName(container.Image))
|
04cd20fa |
return nil
}
func (srv *Server) ContainerStop(name string, t int) error {
if container := srv.runtime.Get(name); container != nil {
if err := container.Stop(t); err != nil { |
1277dca3 |
return fmt.Errorf("Error stopping container %s: %s", name, err) |
04cd20fa |
} |
703905d7 |
srv.LogEvent("stop", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) |
04cd20fa |
} else {
return fmt.Errorf("No such container: %s", name)
}
return nil
}
func (srv *Server) ContainerWait(name string) (int, error) {
if container := srv.runtime.Get(name); container != nil {
return container.Wait(), nil
}
return 0, fmt.Errorf("No such container: %s", name)
}
|
70d2123e |
func (srv *Server) ContainerResize(name string, h, w int) error {
if container := srv.runtime.Get(name); container != nil {
return container.Resize(h, w)
}
return fmt.Errorf("No such container: %s", name)
}
|
e854b7b2 |
func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, stderr bool, inStream io.ReadCloser, outStream, errStream io.Writer) error { |
cacc7e56 |
container := srv.runtime.Get(name)
if container == nil {
return fmt.Errorf("No such container: %s", name)
} |
e854b7b2 |
|
cacc7e56 |
//logs
if logs { |
599f85d4 |
cLog, err := container.ReadLog("json") |
a926cd4d |
if err != nil && os.IsNotExist(err) {
// Legacy logs |
1b0fd7ea |
utils.Debugf("Old logs format") |
a926cd4d |
if stdout {
cLog, err := container.ReadLog("stdout")
if err != nil {
utils.Debugf("Error reading logs (stdout): %s", err) |
e854b7b2 |
} else if _, err := io.Copy(outStream, cLog); err != nil { |
a926cd4d |
utils.Debugf("Error streaming logs (stdout): %s", err)
} |
04cd20fa |
} |
a926cd4d |
if stderr {
cLog, err := container.ReadLog("stderr")
if err != nil {
utils.Debugf("Error reading logs (stderr): %s", err) |
e854b7b2 |
} else if _, err := io.Copy(errStream, cLog); err != nil { |
a926cd4d |
utils.Debugf("Error streaming logs (stderr): %s", err)
} |
04cd20fa |
} |
a926cd4d |
} else if err != nil {
utils.Debugf("Error reading logs (json): %s", err)
} else {
dec := json.NewDecoder(cLog)
for { |
cb18a6e1 |
l := &utils.JSONLog{}
if err := dec.Decode(l); err == io.EOF { |
a926cd4d |
break
} else if err != nil {
utils.Debugf("Error streaming logs: %s", err)
break
} |
cb18a6e1 |
if l.Stream == "stdout" && stdout { |
e854b7b2 |
fmt.Fprintf(outStream, "%s", l.Log) |
a926cd4d |
} |
cb18a6e1 |
if l.Stream == "stderr" && stderr {
fmt.Fprintf(errStream, "%s", l.Log) |
a926cd4d |
} |
04cd20fa |
}
} |
cacc7e56 |
} |
04cd20fa |
|
cacc7e56 |
//stream
if stream {
if container.State.Ghost {
return fmt.Errorf("Impossible to attach to a ghost container")
} |
04cd20fa |
|
cacc7e56 |
var (
cStdin io.ReadCloser
cStdout, cStderr io.Writer
cStdinCloser io.Closer
) |
04cd20fa |
|
cacc7e56 |
if stdin {
r, w := io.Pipe()
go func() {
defer w.Close() |
2e69e172 |
defer utils.Debugf("Closing buffered stdin pipe") |
e854b7b2 |
io.Copy(w, inStream) |
cacc7e56 |
}()
cStdin = r |
e854b7b2 |
cStdinCloser = inStream |
cacc7e56 |
}
if stdout { |
e854b7b2 |
cStdout = outStream |
cacc7e56 |
}
if stderr { |
e854b7b2 |
cStderr = errStream |
cacc7e56 |
} |
4d30a32c |
|
cacc7e56 |
<-container.Attach(cStdin, cStdinCloser, cStdout, cStderr)
// If we are in stdinonce mode, wait for the process to end
// otherwise, simply return
if container.Config.StdinOnce && !container.Config.Tty {
container.Wait() |
04cd20fa |
}
}
return nil
}
func (srv *Server) ContainerInspect(name string) (*Container, error) {
if container := srv.runtime.Get(name); container != nil {
return container, nil
}
return nil, fmt.Errorf("No such container: %s", name)
}
func (srv *Server) ImageInspect(name string) (*Image, error) {
if image, err := srv.runtime.repositories.LookupImage(name); err == nil && image != nil {
return image, nil
}
return nil, fmt.Errorf("No such image: %s", name)
}
|
5b8cfbe1 |
func (srv *Server) ContainerCopy(name string, resource string, out io.Writer) error {
if container := srv.runtime.Get(name); container != nil {
data, err := container.Copy(resource)
if err != nil {
return err
}
if _, err := io.Copy(out, data); err != nil {
return err
}
return nil
}
return fmt.Errorf("No such container: %s", name)
}
|
e1be3063 |
func NewServer(config *DaemonConfig) (*Server, error) { |
04cd20fa |
if runtime.GOARCH != "amd64" {
log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
} |
e1be3063 |
runtime, err := NewRuntime(config) |
04cd20fa |
if err != nil {
return nil, err
}
srv := &Server{ |
fe204e6f |
runtime: runtime,
pullingPool: make(map[string]struct{}),
pushingPool: make(map[string]struct{}), |
2e4d4c9f |
events: make([]utils.JSONMessage, 0, 64), //only keeps the 64 last events
listeners: make(map[string]chan utils.JSONMessage), |
6a56b7b3 |
reqFactory: nil, |
04cd20fa |
} |
95dd6d31 |
runtime.srv = srv |
04cd20fa |
return srv, nil
}
|
093b85b7 |
func (srv *Server) HTTPRequestFactory(metaHeaders map[string][]string) *utils.HTTPRequestFactory { |
4bd287e1 |
if srv.reqFactory == nil {
ud := utils.NewHTTPUserAgentDecorator(srv.versionInfos()...) |
093b85b7 |
md := &utils.HTTPMetaHeadersDecorator{
Headers: metaHeaders,
}
factory := utils.NewHTTPRequestFactory(ud, md) |
4bd287e1 |
srv.reqFactory = factory
}
return srv.reqFactory
}
|
703905d7 |
func (srv *Server) LogEvent(action, id, from string) { |
2e4d4c9f |
now := time.Now().Unix() |
703905d7 |
jm := utils.JSONMessage{Status: action, ID: id, From: from, Time: now} |
2e4d4c9f |
srv.events = append(srv.events, jm)
for _, c := range srv.listeners { |
040c3b50 |
select { // non blocking channel
case c <- jm:
default:
} |
b5da8164 |
}
}
|
04cd20fa |
type Server struct { |
1cf9c80e |
sync.Mutex |
fe204e6f |
runtime *Runtime
pullingPool map[string]struct{}
pushingPool map[string]struct{} |
2e4d4c9f |
events []utils.JSONMessage
listeners map[string]chan utils.JSONMessage |
6a56b7b3 |
reqFactory *utils.HTTPRequestFactory |
04cd20fa |
} |