Documentation ¶
Index ¶
- Constants
- Variables
- func BtrfsReflink(fd_out, fd_in uintptr) error
- func CompareConfig(a, b *Config) bool
- func CopyFile(dstFile, srcFile *os.File) error
- func EofReader(r io.Reader, callback func()) *eofReader
- func GenerateID() string
- func GetDefaultNetworkMtu() int
- func MergeConfig(userConf, imageConf *Config) error
- func MkBuildContext(dockerfile string, files [][2]string) (archive.Archive, error)
- func ParseCommands(proto, addr string, args ...string) error
- func ParseRun(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error)
- func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root, layer string) error
- func ValidateAttach(val string) (string, error)
- func ValidateEnv(val string) (string, error)
- func ValidateHost(val string) (string, error)
- func ValidateID(id string) error
- func ValidateIp4Address(val string) (string, error)
- func ValidateLink(val string) (string, error)
- func ValidatePath(val string) (string, error)
- type BindMap
- type BuildFile
- type Change
- type Config
- type Container
- func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error
- func (container *Container) BasefsPath() string
- func (container *Container) Changes() ([]archive.Change, error)
- func (container *Container) Copy(resource string) (archive.Archive, error)
- func (container *Container) EnvConfigPath() (string, error)
- func (container *Container) Export() (archive.Archive, error)
- func (container *Container) ExportRw() (archive.Archive, error)
- func (container *Container) Exposes(p Port) bool
- func (container *Container) FromDisk() error
- func (container *Container) GetImage() (*Image, error)
- func (container *Container) GetPtyMaster() (*os.File, error)
- func (container *Container) GetSize() (int64, int64)
- func (container *Container) Inject(file io.Reader, pth string) error
- func (container *Container) Kill() error
- func (container *Container) Mount() error
- func (container *Container) Output() (output []byte, err error)
- func (container *Container) ReadLog(name string) (io.Reader, error)
- func (container *Container) Resize(h, w int) error
- func (container *Container) Restart(seconds int) error
- func (container *Container) RootfsPath() string
- func (container *Container) Run() error
- func (container *Container) Start() (err error)
- func (container *Container) StderrPipe() (io.ReadCloser, error)
- func (container *Container) StdinPipe() (io.WriteCloser, error)
- func (container *Container) StdoutPipe() (io.ReadCloser, error)
- func (container *Container) Stop(seconds int) error
- func (container *Container) ToDisk() (err error)
- func (container *Container) Unmount() error
- func (container *Container) Wait() int
- func (container *Container) WaitTimeout(timeout time.Duration) error
- func (container *Container) When() time.Time
- type DaemonConfig
- type DockerCli
- func (cli *DockerCli) CmdAttach(args ...string) error
- func (cli *DockerCli) CmdBuild(args ...string) error
- func (cli *DockerCli) CmdCommit(args ...string) error
- func (cli *DockerCli) CmdCp(args ...string) error
- func (cli *DockerCli) CmdDiff(args ...string) error
- func (cli *DockerCli) CmdEvents(args ...string) error
- func (cli *DockerCli) CmdExport(args ...string) error
- func (cli *DockerCli) CmdHelp(args ...string) error
- func (cli *DockerCli) CmdHistory(args ...string) error
- func (cli *DockerCli) CmdImages(args ...string) error
- func (cli *DockerCli) CmdImport(args ...string) error
- func (cli *DockerCli) CmdInfo(args ...string) error
- func (cli *DockerCli) CmdInsert(args ...string) error
- func (cli *DockerCli) CmdInspect(args ...string) error
- func (cli *DockerCli) CmdKill(args ...string) error
- func (cli *DockerCli) CmdLoad(args ...string) error
- func (cli *DockerCli) CmdLogin(args ...string) error
- func (cli *DockerCli) CmdLogs(args ...string) error
- func (cli *DockerCli) CmdPort(args ...string) error
- func (cli *DockerCli) CmdPs(args ...string) error
- func (cli *DockerCli) CmdPull(args ...string) error
- func (cli *DockerCli) CmdPush(args ...string) error
- func (cli *DockerCli) CmdRestart(args ...string) error
- func (cli *DockerCli) CmdRm(args ...string) error
- func (cli *DockerCli) CmdRmi(args ...string) error
- func (cli *DockerCli) CmdRun(args ...string) error
- func (cli *DockerCli) CmdSave(args ...string) error
- func (cli *DockerCli) CmdSearch(args ...string) error
- func (cli *DockerCli) CmdStart(args ...string) error
- func (cli *DockerCli) CmdStop(args ...string) error
- func (cli *DockerCli) CmdTag(args ...string) error
- func (cli *DockerCli) CmdTop(args ...string) error
- func (cli *DockerCli) CmdVersion(args ...string) error
- func (cli *DockerCli) CmdWait(args ...string) error
- func (cli *DockerCli) LoadConfigFile() (err error)
- func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet
- func (cli *DockerCli) WalkTree(noTrunc bool, images *engine.Table, byParent map[string]*engine.Table, ...)
- type Graph
- func (graph *Graph) ByParent() (map[string][]*Image, error)
- func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, ...) (*Image, error)
- func (graph *Graph) Delete(name string) error
- func (graph *Graph) Driver() graphdriver.Driver
- func (graph *Graph) Exists(id string) bool
- func (graph *Graph) Get(name string) (*Image, error)
- func (graph *Graph) Heads() (map[string]*Image, error)
- func (graph *Graph) IsNotExist(err error) bool
- func (graph *Graph) Map() (map[string]*Image, error)
- func (graph *Graph) Mktemp(id string) (string, error)
- func (graph *Graph) Register(jsonData []byte, layerData archive.Archive, img *Image) (err error)
- func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, sf *utils.StreamFormatter, ...) (*archive.TempArchive, error)
- type History
- type HostConfig
- type Image
- func (img *Image) Depth() (int, error)
- func (img *Image) GetParent() (*Image, error)
- func (img *Image) History() ([]*Image, error)
- func (img *Image) SaveSize(root string) error
- func (img *Image) TarLayer() (arch archive.Archive, err error)
- func (img *Image) WalkHistory(handler func(*Image) error) (err error)
- type KeyValuePair
- type Link
- type ListOpts
- type NetworkSettings
- type Port
- type PortBinding
- type PortMapping
- type Repository
- type Runtime
- func (runtime *Runtime) Changes(container *Container) ([]archive.Change, error)
- func (runtime *Runtime) Children(name string) (map[string]*Container, error)
- func (runtime *Runtime) Close() error
- func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error)
- func (runtime *Runtime) Create(config *Config, name string) (*Container, []string, error)
- func (runtime *Runtime) Destroy(container *Container) error
- func (runtime *Runtime) Diff(container *Container) (archive.Archive, error)
- func (runtime *Runtime) Exists(id string) bool
- func (runtime *Runtime) Get(name string) *Container
- func (runtime *Runtime) GetByName(name string) (*Container, error)
- func (runtime *Runtime) Graph() *Graph
- func (runtime *Runtime) Kill(c *Container, sig int) error
- func (runtime *Runtime) List() []*Container
- func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error
- func (runtime *Runtime) Mount(container *Container) error
- func (runtime *Runtime) Nuke() error
- func (runtime *Runtime) Register(container *Container) error
- func (runtime *Runtime) RegisterLink(parent, child *Container, alias string) error
- func (runtime *Runtime) RestoreCommand(c *Container) error
- func (runtime *Runtime) Run(c *Container, startCallback execdriver.StartCallback) (int, error)
- func (runtime *Runtime) Unmount(container *Container) error
- type Server
- func (srv *Server) AddEvent(jm utils.JSONMessage)
- func (srv *Server) Auth(job *engine.Job) engine.Status
- func (srv *Server) Build(job *engine.Job) engine.Status
- func (srv *Server) Close() error
- func (srv *Server) ContainerAttach(job *engine.Job) engine.Status
- func (srv *Server) ContainerChanges(job *engine.Job) engine.Status
- func (srv *Server) ContainerCommit(job *engine.Job) engine.Status
- func (srv *Server) ContainerCopy(job *engine.Job) engine.Status
- func (srv *Server) ContainerCreate(job *engine.Job) engine.Status
- func (srv *Server) ContainerDestroy(job *engine.Job) engine.Status
- func (srv *Server) ContainerExport(job *engine.Job) engine.Status
- func (srv *Server) ContainerInspect(name string) (*Container, error)
- func (srv *Server) ContainerKill(job *engine.Job) engine.Status
- func (srv *Server) ContainerResize(job *engine.Job) engine.Status
- func (srv *Server) ContainerRestart(job *engine.Job) engine.Status
- func (srv *Server) ContainerStart(job *engine.Job) engine.Status
- func (srv *Server) ContainerStop(job *engine.Job) engine.Status
- func (srv *Server) ContainerTop(job *engine.Job) engine.Status
- func (srv *Server) ContainerWait(job *engine.Job) engine.Status
- func (srv *Server) Containers(job *engine.Job) engine.Status
- func (srv *Server) DeleteImage(name string, autoPrune bool) (*engine.Table, error)
- func (srv *Server) DockerInfo(job *engine.Job) engine.Status
- func (srv *Server) Events(job *engine.Job) engine.Status
- func (srv *Server) GetEvents() []utils.JSONMessage
- func (srv *Server) HTTPRequestFactory(metaHeaders map[string][]string) *utils.HTTPRequestFactory
- func (srv *Server) ImageDelete(job *engine.Job) engine.Status
- func (srv *Server) ImageExport(job *engine.Job) engine.Status
- func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
- func (srv *Server) ImageHistory(job *engine.Job) engine.Status
- func (srv *Server) ImageImport(job *engine.Job) engine.Status
- func (srv *Server) ImageInsert(job *engine.Job) engine.Status
- func (srv *Server) ImageInspect(name string) (*Image, error)
- func (srv *Server) ImageLoad(job *engine.Job) engine.Status
- func (srv *Server) ImagePull(job *engine.Job) engine.Status
- func (srv *Server) ImagePush(job *engine.Job) engine.Status
- func (srv *Server) ImageTag(job *engine.Job) engine.Status
- func (srv *Server) Images(job *engine.Job) engine.Status
- func (srv *Server) ImagesSearch(job *engine.Job) engine.Status
- func (srv *Server) ImagesViz(job *engine.Job) engine.Status
- func (srv *Server) JobInspect(job *engine.Job) engine.Status
- func (srv *Server) LogEvent(action, id, from string) *utils.JSONMessage
- func (srv *Server) RegisterLinks(container *Container, hostConfig *HostConfig) error
- type State
- type StderrFormater
- type StdoutFormater
- type TagStore
- func (store *TagStore) ByID() map[string][]string
- func (store *TagStore) Delete(repoName, tag string) (bool, error)
- func (store *TagStore) DeleteAll(id string) error
- func (store *TagStore) Get(repoName string) (Repository, error)
- func (store *TagStore) GetImage(repoName, tagOrID string) (*Image, error)
- func (store *TagStore) ImageName(id string) string
- func (store *TagStore) LookupImage(name string) (*Image, error)
- func (store *TagStore) Reload() error
- func (store *TagStore) Save() error
- func (store *TagStore) Set(repoName, tag, imageName string, force bool) error
- type ValidatorFctType
Constants ¶
const ( PortSpecTemplate = "ip:hostPort:containerPort" PortSpecTemplateFormat = "ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort" )
FIXME: network related stuff (including parsing) should be grouped in network file
const DEFAULTTAG = "latest"
const (
DisableNetworkBridge = "none"
)
const MaxImageDepth = 127
Set the max depth to the aufs default that most kernels are compiled with For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk
Variables ¶
var ( GITCOMMIT string VERSION string )
var ( ErrNotATTY = errors.New("The PTY is not a file") ErrNoTTY = errors.New("No PTY found") )
var ( ErrContainerStart = errors.New("The container failed to start. Unknown error") ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.") ErrInvalidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.") ErrConflictAttachDetach = errors.New("Conflicting options: -a and -d") ErrConflictDetachAutoRemove = errors.New("Conflicting options: -rm and -d") )
var (
ErrConnectionRefused = errors.New("Can't connect to docker daemon. Is 'docker -d' running on this host?")
)
var (
ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
)
var ErrImageReferenced = errors.New("Image referenced by a repository")
Functions ¶
func BtrfsReflink ¶
func CompareConfig ¶
Compare two Config struct. Do not compare the "Image" nor "Hostname" fields If OpenStdin is set, then it differs
func GenerateID ¶
func GenerateID() string
func GetDefaultNetworkMtu ¶
func GetDefaultNetworkMtu() int
func MergeConfig ¶
func MkBuildContext ¶
mkBuildContext returns an archive of an empty context with the contents of `dockerfile` at the path ./Dockerfile
func ParseCommands ¶
func StoreImage ¶
func ValidateAttach ¶
func ValidateEnv ¶
func ValidateHost ¶
func ValidateID ¶
func ValidateIp4Address ¶
func ValidateLink ¶
func ValidatePath ¶
Types ¶
type BuildFile ¶
type BuildFile interface { Build(io.Reader) (string, error) CmdFrom(string) error CmdRun(string) error }
func NewBuildFile ¶
func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *auth.AuthConfig, authConfigFile *auth.ConfigFile) BuildFile
type Config ¶
type Config struct { Hostname string Domainname string User string Memory int64 // Memory limit (in bytes) MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap AttachStdin bool AttachStdout bool AttachStderr bool PortSpecs []string // Deprecated - Can be in the format of 8080/tcp ExposedPorts map[Port]struct{} Tty bool // Attach standard streams to a tty, including stdin if it is not closed. OpenStdin bool // Open stdin StdinOnce bool // If true, close stdin after the 1 attached client disconnects. Env []string Cmd []string Dns []string Image string // Name of the image as it was passed by the operator (eg. could be symbolic) Volumes map[string]struct{} VolumesFrom string WorkingDir string Entrypoint []string NetworkDisabled bool OnBuild []string }
Note: the Config structure should hold only portable information about the container. Here, "portable" means "independent from the host we are running on". Non-portable information *should* appear in HostConfig.
func ContainerConfigFromJob ¶
type Container ¶
type Container struct { sync.Mutex ID string Created time.Time Path string Args []string Config *Config State State Image string NetworkSettings *NetworkSettings ResolvConfPath string HostnamePath string HostsPath string Name string Driver string Volumes map[string]string // Store rw/ro in a separate structure to preserve reverse-compatibility on-disk. // Easier than migrating older container configs :) VolumesRW map[string]bool // contains filtered or unexported fields }
func (*Container) BasefsPath ¶
This is the stand-alone version of the root fs, without any additional mounts. This directory is usable whenever the container is mounted (and not unmounted)
func (*Container) EnvConfigPath ¶
func (*Container) RootfsPath ¶
This method must be exported to be used from the lxc template This directory is only usable when the container is running
func (*Container) StderrPipe ¶
func (container *Container) StderrPipe() (io.ReadCloser, error)
func (*Container) StdoutPipe ¶
func (container *Container) StdoutPipe() (io.ReadCloser, error)
func (*Container) WaitTimeout ¶
type DaemonConfig ¶
type DaemonConfig struct { Pidfile string Root string AutoRestart bool Dns []string EnableIptables bool EnableIpForward bool DefaultIp net.IP BridgeIface string BridgeIP string InterContainerCommunication bool GraphDriver string Mtu int DisableNetwork bool }
FIXME: separate runtime configuration from http api configuration
func DaemonConfigFromJob ¶
func DaemonConfigFromJob(job *engine.Job) *DaemonConfig
ConfigFromJob creates and returns a new DaemonConfig object by parsing the contents of a job's environment.
type DockerCli ¶
type DockerCli struct {
// contains filtered or unexported fields
}
func NewDockerCli ¶
func (*DockerCli) CmdHistory ¶
func (*DockerCli) CmdInspect ¶
func (*DockerCli) CmdRestart ¶
func (*DockerCli) CmdVersion ¶
'docker version': show version information
func (*DockerCli) LoadConfigFile ¶
type Graph ¶
type Graph struct { Root string // contains filtered or unexported fields }
A Graph is a store for versioned filesystem images and the relationship between them.
func NewGraph ¶
func NewGraph(root string, driver graphdriver.Driver) (*Graph, error)
NewGraph instantiates a new graph at the given root path in the filesystem. `root` will be created if it doesn't exist.
func (*Graph) ByParent ¶
ByParent returns a lookup table of images by their parent. If an image of id ID has 3 children images, then the value for key ID will be a list of 3 images. If an image has no children, it will not have an entry in the table.
func (*Graph) Create ¶
func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, config *Config) (*Image, error)
Create creates a new image and registers it in the graph.
func (*Graph) Driver ¶
func (graph *Graph) Driver() graphdriver.Driver
func (*Graph) Exists ¶
Exists returns true if an image is registered at the given id. If the image doesn't exist or if an error is encountered, false is returned.
func (*Graph) Get ¶
Get returns the image with the given id, or an error if the image doesn't exist.
func (*Graph) Heads ¶
Heads returns all heads in the graph, keyed by id. A head is an image which is not the parent of another image in the graph.
func (*Graph) IsNotExist ¶
FIXME: Implement error subclass instead of looking at the error text Note: This is the way golang implements os.IsNotExists on Plan9
func (*Graph) Register ¶
Register imports a pre-existing image into the graph. FIXME: pass img as first argument
func (*Graph) TempLayerArchive ¶
func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, sf *utils.StreamFormatter, output io.Writer) (*archive.TempArchive, error)
TempLayerArchive creates a temporary archive of the given image's filesystem layer.
The archive is stored on disk and will be automatically deleted as soon as has been read. If output is not nil, a human-readable progress bar will be written to it. FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives?
type History ¶
type History []*Container
History is a convenience type for storing a list of containers, ordered by creation date.
type HostConfig ¶
type HostConfig struct { Binds []string ContainerIDFile string LxcConf []KeyValuePair Privileged bool PortBindings map[Port][]PortBinding Links []string PublishAllPorts bool }
func ContainerHostConfigFromJob ¶
func ContainerHostConfigFromJob(job *engine.Job) *HostConfig
type Image ¶
type Image struct { ID string `json:"id"` Parent string `json:"parent,omitempty"` Comment string `json:"comment,omitempty"` Created time.Time `json:"created"` Container string `json:"container,omitempty"` ContainerConfig Config `json:"container_config,omitempty"` DockerVersion string `json:"docker_version,omitempty"` Author string `json:"author,omitempty"` Config *Config `json:"config,omitempty"` Architecture string `json:"architecture,omitempty"` OS string `json:"os,omitempty"` Size int64 // contains filtered or unexported fields }
func NewImgJSON ¶
Build an Image object from raw json data
func (*Image) History ¶
Image includes convenience proxy functions to its graph These functions will return an error if the image is not registered (ie. if image.graph == nil)
type KeyValuePair ¶
type Link ¶
type Link struct { ParentIP string ChildIP string Name string ChildEnvironment []string Ports []Port IsEnabled bool // contains filtered or unexported fields }
type ListOpts ¶
type ListOpts struct {
// contains filtered or unexported fields
}
ListOpts type
func NewListOpts ¶
func NewListOpts(validator ValidatorFctType) ListOpts
func (*ListOpts) GetMap ¶
GetMap returns the content of values in a map in order to avoid duplicates. FIXME: can we remove this?
type NetworkSettings ¶
type NetworkSettings struct { IPAddress string IPPrefixLen int Gateway string Bridge string PortMapping map[string]PortMapping // Deprecated Ports map[Port][]PortBinding }
func (*NetworkSettings) PortMappingAPI ¶
func (settings *NetworkSettings) PortMappingAPI() *engine.Table
type PortBinding ¶
type PortMapping ¶
type Repository ¶
type Runtime ¶
type Runtime struct {
// contains filtered or unexported fields
}
func NewRuntime ¶
func NewRuntime(config *DaemonConfig, eng *engine.Engine) (*Runtime, error)
FIXME: harmonize with NewGraph()
func NewRuntimeFromDirectory ¶
func NewRuntimeFromDirectory(config *DaemonConfig, eng *engine.Engine) (*Runtime, error)
func (*Runtime) Commit ¶
func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error)
Commit creates a new filesystem image from the current state of a container. The image can optionally be tagged into a repository
func (*Runtime) Create ¶
Create creates a new container from the given configuration with a given name.
func (*Runtime) Destroy ¶
Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem.
func (*Runtime) Exists ¶
Exists returns a true if a container of the specified ID or name exists, false otherwise.
func (*Runtime) Get ¶
Get looks for a container by the specified ID or name, and returns it. If the container is not found, or if an error occurs, nil is returned.
func (*Runtime) Graph ¶
FIXME: this is a convenience function for integration tests which need direct access to runtime.graph. Once the tests switch to using engine and jobs, this method can go away.
func (*Runtime) LogToDisk ¶
func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error
func (*Runtime) Nuke ¶
Nuke kills all containers then removes all content from the content root, including images, volumes and container filesystems. Again: this will remove your entire docker runtime!
func (*Runtime) Register ¶
Register makes a container object usable by the runtime as <container.ID>
func (*Runtime) RegisterLink ¶
func (*Runtime) RestoreCommand ¶
func (*Runtime) Run ¶
func (runtime *Runtime) Run(c *Container, startCallback execdriver.StartCallback) (int, error)
type Server ¶
func (*Server) AddEvent ¶
func (srv *Server) AddEvent(jm utils.JSONMessage)
func (*Server) ContainerChanges ¶
func (*Server) ContainerDestroy ¶
func (*Server) ContainerInspect ¶
func (*Server) ContainerKill ¶
ContainerKill send signal to the container If no signal is given (sig 0), then Kill with SIGKILL and wait for the container to exit. If a signal is given, then just send it to the container and return.
func (*Server) ContainerRestart ¶
func (*Server) DeleteImage ¶
func (*Server) GetEvents ¶
func (srv *Server) GetEvents() []utils.JSONMessage
func (*Server) HTTPRequestFactory ¶
func (srv *Server) HTTPRequestFactory(metaHeaders map[string][]string) *utils.HTTPRequestFactory
func (*Server) ImageExport ¶
ImageExport exports all images with the given tag. All versions containing the same tag are exported. The resulting output is an uncompressed tar ball. name is the set of tags to export. out is the writer where the images are written to.
func (*Server) ImageGetCached ¶
func (*Server) ImageLoad ¶
Loads a set of images into the repository. This is the complementary of ImageExport. The input stream is an uncompressed tar ball containing images and metadata.
func (*Server) ImagePush ¶
FIXME: Allow to interrupt current push when new push of same image is done.
func (*Server) RegisterLinks ¶
func (srv *Server) RegisterLinks(container *Container, hostConfig *HostConfig) error
type State ¶
type State struct { sync.RWMutex Running bool Pid int ExitCode int StartedAt time.Time FinishedAt time.Time Ghost bool }
func (*State) GetExitCode ¶
func (*State) SetRunning ¶
func (*State) SetStopped ¶
type StderrFormater ¶
type StderrFormater struct { io.Writer *utils.StreamFormatter }
type StdoutFormater ¶
type StdoutFormater struct { io.Writer *utils.StreamFormatter }