mirror of
https://github.com/docker/cli.git
synced 2026-09-26 09:20:58 -04:00
Merge branch 'master' into b
Upstream-commit: 59bb86a964c38d0f2cc724c686803472bb48968b Component: engine
This commit is contained in:
@@ -88,7 +88,7 @@ type CommonConfig struct {
|
||||
Root string `json:"graph,omitempty"`
|
||||
SocketGroup string `json:"group,omitempty"`
|
||||
TrustKeyPath string `json:"-"`
|
||||
CorsHeaders string `json:"api-cors-headers,omitempty"`
|
||||
CorsHeaders string `json:"api-cors-header,omitempty"`
|
||||
EnableCors bool `json:"api-enable-cors,omitempty"`
|
||||
|
||||
// ClusterStore is the storage backend used for the cluster information. It is used by both
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultPidFile = "/var/run/docker.pid"
|
||||
defaultGraph = "/var/lib/docker"
|
||||
defaultExec = "zones"
|
||||
)
|
||||
|
||||
// Config defines the configuration of a docker daemon.
|
||||
// These are the configuration settings that you pass
|
||||
// to the docker daemon when you launch it with say: `docker -d -e lxc`
|
||||
type Config struct {
|
||||
CommonConfig
|
||||
|
||||
// Fields below here are platform specific.
|
||||
ExecRoot string `json:"exec-root,omitempty"`
|
||||
}
|
||||
|
||||
// bridgeConfig stores all the bridge driver specific
|
||||
// configuration.
|
||||
type bridgeConfig struct {
|
||||
commonBridgeConfig
|
||||
}
|
||||
|
||||
// InstallFlags adds command-line options to the top-level flag parser for
|
||||
// the current process.
|
||||
// Subsequent calls to `flag.Parse` will populate config with values parsed
|
||||
// from the command-line.
|
||||
func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
|
||||
// First handle install flags which are consistent cross-platform
|
||||
config.InstallCommonFlags(cmd, usageFn)
|
||||
|
||||
// Then platform-specific install flags
|
||||
config.attachExperimentalFlags(cmd, usageFn)
|
||||
}
|
||||
@@ -41,7 +41,7 @@ type bridgeConfig struct {
|
||||
EnableIPv6 bool `json:"ipv6,omitempty"`
|
||||
EnableIPTables bool `json:"iptables,omitempty"`
|
||||
EnableIPForward bool `json:"ip-forward,omitempty"`
|
||||
EnableIPMasq bool `json:"ip-mask,omitempty"`
|
||||
EnableIPMasq bool `json:"ip-masq,omitempty"`
|
||||
EnableUserlandProxy bool `json:"userland-proxy,omitempty"`
|
||||
DefaultIP net.IP `json:"ip,omitempty"`
|
||||
IP string `json:"bip,omitempty"`
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// +build solaris
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"github.com/docker/libnetwork"
|
||||
)
|
||||
|
||||
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ConnectToNetwork connects a container to a network
|
||||
func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
|
||||
return fmt.Errorf("Solaris does not support connecting a running container to a network")
|
||||
}
|
||||
|
||||
// getSize returns real size & virtual size
|
||||
func (daemon *Daemon) getSize(container *container.Container) (int64, int64) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// DisconnectFromNetwork disconnects a container from the network
|
||||
func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error {
|
||||
return fmt.Errorf("Solaris does not support disconnecting a running container from a network")
|
||||
}
|
||||
|
||||
func (daemon *Daemon) setupIpcDirs(container *container.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) mountVolumes(container *container.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func killProcessDirectly(container *container.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func detachMounted(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLinkable(child *container.Container) bool {
|
||||
return false
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
volumestore "github.com/docker/docker/volume/store"
|
||||
"github.com/docker/engine-api/types"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
@@ -122,6 +123,9 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig) (retC *containe
|
||||
if params.NetworkingConfig != nil {
|
||||
endpointsConfigs = params.NetworkingConfig.EndpointsConfig
|
||||
}
|
||||
// Make sure NetworkMode has an acceptable value. We do this to ensure
|
||||
// backwards API compatibility.
|
||||
container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig)
|
||||
|
||||
if err := daemon.updateContainerNetworkSettings(container, endpointsConfigs); err != nil {
|
||||
return nil, err
|
||||
|
||||
+106
-286
@@ -6,6 +6,7 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -23,7 +25,6 @@ import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
containerd "github.com/docker/containerd/api/grpc/types"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/events"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
@@ -40,7 +41,6 @@ import (
|
||||
"github.com/docker/docker/distribution/xfer"
|
||||
"github.com/docker/docker/dockerversion"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/image/tarexport"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
"github.com/docker/docker/migrate/v1"
|
||||
@@ -64,6 +64,7 @@ import (
|
||||
volumedrivers "github.com/docker/docker/volume/drivers"
|
||||
"github.com/docker/docker/volume/local"
|
||||
"github.com/docker/docker/volume/store"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/libnetwork"
|
||||
nwconfig "github.com/docker/libnetwork/config"
|
||||
@@ -78,15 +79,6 @@ var (
|
||||
errSystemNotSupported = fmt.Errorf("The Docker daemon is not supported on this platform.")
|
||||
)
|
||||
|
||||
// ErrImageDoesNotExist is error returned when no image can be found for a reference.
|
||||
type ErrImageDoesNotExist struct {
|
||||
RefOrID string
|
||||
}
|
||||
|
||||
func (e ErrImageDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("no such id: %s", e.RefOrID)
|
||||
}
|
||||
|
||||
// Daemon holds information about the Docker daemon.
|
||||
type Daemon struct {
|
||||
ID string
|
||||
@@ -286,11 +278,6 @@ func (daemon *Daemon) restore() error {
|
||||
defer wg.Done()
|
||||
rm := c.RestartManager(false)
|
||||
if c.IsRunning() || c.IsPaused() {
|
||||
// Fix activityCount such that graph mounts can be unmounted later
|
||||
if err := daemon.layerStore.ReinitRWLayer(c.RWLayer); err != nil {
|
||||
logrus.Errorf("Failed to ReinitRWLayer for %s due to %s", c.ID, err)
|
||||
return
|
||||
}
|
||||
if err := daemon.containerd.Restore(c.ID, libcontainerd.WithRestartManager(rm)); err != nil {
|
||||
logrus.Errorf("Failed to restore with containerd: %q", err)
|
||||
return
|
||||
@@ -808,7 +795,7 @@ func NewDaemon(config *Config, registryService *registry.Service, containerdRemo
|
||||
sysInfo := sysinfo.New(false)
|
||||
// Check if Devices cgroup is mounted, it is hard requirement for container security,
|
||||
// on Linux/FreeBSD.
|
||||
if runtime.GOOS != "windows" && !sysInfo.CgroupDevicesEnabled {
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "solaris" && !sysInfo.CgroupDevicesEnabled {
|
||||
return nil, fmt.Errorf("Devices cgroup isn't mounted")
|
||||
}
|
||||
|
||||
@@ -1006,221 +993,6 @@ func isBrokenPipe(e error) bool {
|
||||
return e == syscall.EPIPE
|
||||
}
|
||||
|
||||
// ExportImage exports a list of images to the given output stream. The
|
||||
// exported images are archived into a tar when written to the output
|
||||
// stream. All images with the given tag and all versions containing
|
||||
// the same tag are exported. names is the set of tags to export, and
|
||||
// outStream is the writer which the images are written to.
|
||||
func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
|
||||
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
|
||||
return imageExporter.Save(names, outStream)
|
||||
}
|
||||
|
||||
// LookupImage looks up an image by name and returns it as an ImageInspect
|
||||
// structure.
|
||||
func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
|
||||
img, err := daemon.GetImage(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("No such image: %s", name)
|
||||
}
|
||||
|
||||
refs := daemon.referenceStore.References(img.ID())
|
||||
repoTags := []string{}
|
||||
repoDigests := []string{}
|
||||
for _, ref := range refs {
|
||||
switch ref.(type) {
|
||||
case reference.NamedTagged:
|
||||
repoTags = append(repoTags, ref.String())
|
||||
case reference.Canonical:
|
||||
repoDigests = append(repoDigests, ref.String())
|
||||
}
|
||||
}
|
||||
|
||||
var size int64
|
||||
var layerMetadata map[string]string
|
||||
layerID := img.RootFS.ChainID()
|
||||
if layerID != "" {
|
||||
l, err := daemon.layerStore.Get(layerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer layer.ReleaseAndLog(daemon.layerStore, l)
|
||||
size, err = l.Size()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layerMetadata, err = l.Metadata()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
comment := img.Comment
|
||||
if len(comment) == 0 && len(img.History) > 0 {
|
||||
comment = img.History[len(img.History)-1].Comment
|
||||
}
|
||||
|
||||
imageInspect := &types.ImageInspect{
|
||||
ID: img.ID().String(),
|
||||
RepoTags: repoTags,
|
||||
RepoDigests: repoDigests,
|
||||
Parent: img.Parent.String(),
|
||||
Comment: comment,
|
||||
Created: img.Created.Format(time.RFC3339Nano),
|
||||
Container: img.Container,
|
||||
ContainerConfig: &img.ContainerConfig,
|
||||
DockerVersion: img.DockerVersion,
|
||||
Author: img.Author,
|
||||
Config: img.Config,
|
||||
Architecture: img.Architecture,
|
||||
Os: img.OS,
|
||||
Size: size,
|
||||
VirtualSize: size, // TODO: field unused, deprecate
|
||||
RootFS: rootFSToAPIType(img.RootFS),
|
||||
}
|
||||
|
||||
imageInspect.GraphDriver.Name = daemon.GraphDriverName()
|
||||
|
||||
imageInspect.GraphDriver.Data = layerMetadata
|
||||
|
||||
return imageInspect, nil
|
||||
}
|
||||
|
||||
// LoadImage uploads a set of images into the repository. This is the
|
||||
// complement of ImageExport. The input stream is an uncompressed tar
|
||||
// ball containing images and metadata.
|
||||
func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
|
||||
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
|
||||
return imageExporter.Load(inTar, outStream, quiet)
|
||||
}
|
||||
|
||||
// ImageHistory returns a slice of ImageHistory structures for the specified image
|
||||
// name by walking the image lineage.
|
||||
func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
|
||||
img, err := daemon.GetImage(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
history := []*types.ImageHistory{}
|
||||
|
||||
layerCounter := 0
|
||||
rootFS := *img.RootFS
|
||||
rootFS.DiffIDs = nil
|
||||
|
||||
for _, h := range img.History {
|
||||
var layerSize int64
|
||||
|
||||
if !h.EmptyLayer {
|
||||
if len(img.RootFS.DiffIDs) <= layerCounter {
|
||||
return nil, fmt.Errorf("too many non-empty layers in History section")
|
||||
}
|
||||
|
||||
rootFS.Append(img.RootFS.DiffIDs[layerCounter])
|
||||
l, err := daemon.layerStore.Get(rootFS.ChainID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
layerSize, err = l.DiffSize()
|
||||
layer.ReleaseAndLog(daemon.layerStore, l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layerCounter++
|
||||
}
|
||||
|
||||
history = append([]*types.ImageHistory{{
|
||||
ID: "<missing>",
|
||||
Created: h.Created.Unix(),
|
||||
CreatedBy: h.CreatedBy,
|
||||
Comment: h.Comment,
|
||||
Size: layerSize,
|
||||
}}, history...)
|
||||
}
|
||||
|
||||
// Fill in image IDs and tags
|
||||
histImg := img
|
||||
id := img.ID()
|
||||
for _, h := range history {
|
||||
h.ID = id.String()
|
||||
|
||||
var tags []string
|
||||
for _, r := range daemon.referenceStore.References(id) {
|
||||
if _, ok := r.(reference.NamedTagged); ok {
|
||||
tags = append(tags, r.String())
|
||||
}
|
||||
}
|
||||
|
||||
h.Tags = tags
|
||||
|
||||
id = histImg.Parent
|
||||
if id == "" {
|
||||
break
|
||||
}
|
||||
histImg, err = daemon.GetImage(id.String())
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return history, nil
|
||||
}
|
||||
|
||||
// GetImageID returns an image ID corresponding to the image referred to by
|
||||
// refOrID.
|
||||
func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
|
||||
id, ref, err := reference.ParseIDOrReference(refOrID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if id != "" {
|
||||
if _, err := daemon.imageStore.Get(image.ID(id)); err != nil {
|
||||
return "", ErrImageDoesNotExist{refOrID}
|
||||
}
|
||||
return image.ID(id), nil
|
||||
}
|
||||
|
||||
if id, err := daemon.referenceStore.Get(ref); err == nil {
|
||||
return id, nil
|
||||
}
|
||||
if tagged, ok := ref.(reference.NamedTagged); ok {
|
||||
if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil {
|
||||
for _, namedRef := range daemon.referenceStore.References(id) {
|
||||
if namedRef.Name() == ref.Name() {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search based on ID
|
||||
if id, err := daemon.imageStore.Search(refOrID); err == nil {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
return "", ErrImageDoesNotExist{refOrID}
|
||||
}
|
||||
|
||||
// GetImage returns an image corresponding to the image referred to by refOrID.
|
||||
func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
|
||||
imgID, err := daemon.GetImageID(refOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return daemon.imageStore.Get(imgID)
|
||||
}
|
||||
|
||||
// GetImageOnBuild looks up a Docker image referenced by `name`.
|
||||
func (daemon *Daemon) GetImageOnBuild(name string) (builder.Image, error) {
|
||||
img, err := daemon.GetImage(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// GraphDriverName returns the name of the graph driver used by the layer.Store
|
||||
func (daemon *Daemon) GraphDriverName() string {
|
||||
return daemon.layerStore.DriverName()
|
||||
@@ -1241,57 +1013,6 @@ func (daemon *Daemon) GetRemappedUIDGID() (int, int) {
|
||||
return uid, gid
|
||||
}
|
||||
|
||||
// GetCachedImage returns the most recent created image that is a child
|
||||
// of the image with imgID, that had the same config when it was
|
||||
// created. nil is returned if a child cannot be found. An error is
|
||||
// returned if the parent image cannot be found.
|
||||
func (daemon *Daemon) GetCachedImage(imgID image.ID, config *containertypes.Config) (*image.Image, error) {
|
||||
// Loop on the children of the given image and check the config
|
||||
getMatch := func(siblings []image.ID) (*image.Image, error) {
|
||||
var match *image.Image
|
||||
for _, id := range siblings {
|
||||
img, err := daemon.imageStore.Get(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to find image %q", id)
|
||||
}
|
||||
|
||||
if runconfig.Compare(&img.ContainerConfig, config) {
|
||||
// check for the most up to date match
|
||||
if match == nil || match.Created.Before(img.Created) {
|
||||
match = img
|
||||
}
|
||||
}
|
||||
}
|
||||
return match, nil
|
||||
}
|
||||
|
||||
// In this case, this is `FROM scratch`, which isn't an actual image.
|
||||
if imgID == "" {
|
||||
images := daemon.imageStore.Map()
|
||||
var siblings []image.ID
|
||||
for id, img := range images {
|
||||
if img.Parent == imgID {
|
||||
siblings = append(siblings, id)
|
||||
}
|
||||
}
|
||||
return getMatch(siblings)
|
||||
}
|
||||
|
||||
// find match from child images
|
||||
siblings := daemon.imageStore.Children(imgID)
|
||||
return getMatch(siblings)
|
||||
}
|
||||
|
||||
// GetCachedImageOnBuild returns a reference to a cached image whose parent equals `parent`
|
||||
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
|
||||
func (daemon *Daemon) GetCachedImageOnBuild(imgID string, cfg *containertypes.Config) (string, error) {
|
||||
cache, err := daemon.GetCachedImage(image.ID(imgID), cfg)
|
||||
if cache == nil || err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cache.ID().String(), nil
|
||||
}
|
||||
|
||||
// tempDir returns the default directory to use for temporary files.
|
||||
func tempDir(rootDir string, rootUID, rootGID int) (string, error) {
|
||||
var tmpDir string
|
||||
@@ -1427,12 +1148,85 @@ func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *ty
|
||||
return daemon.RegistryService.Auth(authConfig, dockerversion.DockerUserAgent(ctx))
|
||||
}
|
||||
|
||||
var acceptedSearchFilterTags = map[string]bool{
|
||||
"is-automated": true,
|
||||
"is-official": true,
|
||||
"stars": true,
|
||||
}
|
||||
|
||||
// SearchRegistryForImages queries the registry for images matching
|
||||
// term. authConfig is used to login.
|
||||
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, term string,
|
||||
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs string, term string,
|
||||
authConfig *types.AuthConfig,
|
||||
headers map[string][]string) (*registrytypes.SearchResults, error) {
|
||||
return daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
|
||||
|
||||
searchFilters, err := filters.FromParam(filtersArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := searchFilters.Validate(acceptedSearchFilterTags); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unfilteredResult, err := daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var isAutomated, isOfficial bool
|
||||
var hasStarFilter = 0
|
||||
if searchFilters.Include("is-automated") {
|
||||
if searchFilters.ExactMatch("is-automated", "true") {
|
||||
isAutomated = true
|
||||
} else if !searchFilters.ExactMatch("is-automated", "false") {
|
||||
return nil, fmt.Errorf("Invalid filter 'is-automated=%s'", searchFilters.Get("is-automated"))
|
||||
}
|
||||
}
|
||||
if searchFilters.Include("is-official") {
|
||||
if searchFilters.ExactMatch("is-official", "true") {
|
||||
isOfficial = true
|
||||
} else if !searchFilters.ExactMatch("is-official", "false") {
|
||||
return nil, fmt.Errorf("Invalid filter 'is-official=%s'", searchFilters.Get("is-official"))
|
||||
}
|
||||
}
|
||||
if searchFilters.Include("stars") {
|
||||
hasStars := searchFilters.Get("stars")
|
||||
for _, hasStar := range hasStars {
|
||||
iHasStar, err := strconv.Atoi(hasStar)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid filter 'stars=%s'", hasStar)
|
||||
}
|
||||
if iHasStar > hasStarFilter {
|
||||
hasStarFilter = iHasStar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filteredResults := []registrytypes.SearchResult{}
|
||||
for _, result := range unfilteredResult.Results {
|
||||
if searchFilters.Include("is-automated") {
|
||||
if isAutomated != result.IsAutomated {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if searchFilters.Include("is-official") {
|
||||
if isOfficial != result.IsOfficial {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if searchFilters.Include("stars") {
|
||||
if result.StarCount < hasStarFilter {
|
||||
continue
|
||||
}
|
||||
}
|
||||
filteredResults = append(filteredResults, result)
|
||||
}
|
||||
|
||||
return ®istrytypes.SearchResults{
|
||||
Query: unfilteredResult.Query,
|
||||
NumResults: len(filteredResults),
|
||||
Results: filteredResults,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsShuttingDown tells whether the daemon is shutting down or not
|
||||
@@ -1539,6 +1333,11 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
|
||||
func (daemon *Daemon) Reload(config *Config) error {
|
||||
daemon.configStore.reloadLock.Lock()
|
||||
defer daemon.configStore.reloadLock.Unlock()
|
||||
|
||||
if err := daemon.reloadClusterDiscovery(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.IsValueSet("labels") {
|
||||
daemon.configStore.Labels = config.Labels
|
||||
}
|
||||
@@ -1572,7 +1371,28 @@ func (daemon *Daemon) Reload(config *Config) error {
|
||||
daemon.uploadManager.SetConcurrency(*daemon.configStore.MaxConcurrentUploads)
|
||||
}
|
||||
|
||||
return daemon.reloadClusterDiscovery(config)
|
||||
// We emit daemon reload event here with updatable configurations
|
||||
attributes := map[string]string{}
|
||||
attributes["debug"] = fmt.Sprintf("%t", daemon.configStore.Debug)
|
||||
attributes["cluster-store"] = daemon.configStore.ClusterStore
|
||||
if daemon.configStore.ClusterOpts != nil {
|
||||
opts, _ := json.Marshal(daemon.configStore.ClusterOpts)
|
||||
attributes["cluster-store-opts"] = string(opts)
|
||||
} else {
|
||||
attributes["cluster-store-opts"] = "{}"
|
||||
}
|
||||
attributes["cluster-advertise"] = daemon.configStore.ClusterAdvertise
|
||||
if daemon.configStore.Labels != nil {
|
||||
labels, _ := json.Marshal(daemon.configStore.Labels)
|
||||
attributes["labels"] = string(labels)
|
||||
} else {
|
||||
attributes["labels"] = "[]"
|
||||
}
|
||||
attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads)
|
||||
attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads)
|
||||
daemon.LogDaemonEventWithAttributes("reload", attributes)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) reloadClusterDiscovery(config *Config) error {
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
// +build solaris,cgo
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/libnetwork"
|
||||
nwconfig "github.com/docker/libnetwork/config"
|
||||
)
|
||||
|
||||
//#include <zone.h>
|
||||
import "C"
|
||||
|
||||
const (
|
||||
defaultVirtualSwitch = "Virtual Switch"
|
||||
platformSupported = true
|
||||
solarisMinCPUShares = 1
|
||||
solarisMaxCPUShares = 65535
|
||||
)
|
||||
|
||||
func (daemon *Daemon) cleanupMountsByID(id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupInitLayer populates a directory with mountpoints suitable
|
||||
// for bind-mounting dockerinit into the container. The mountpoint is simply an
|
||||
// empty file at /.dockerinit
|
||||
//
|
||||
// This extra layer is used by all containers as the top-most ro layer. It protects
|
||||
// the container from unwanted side-effects on the rw layer.
|
||||
func setupInitLayer(initLayer string, rootUID, rootGID int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkKernel() error {
|
||||
// solaris can rely upon checkSystem() below, we don't skew kernel versions
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) getCgroupDriver() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyPlatformContainerSettings performs platform-specific validation of the
|
||||
// hostconfig and config structures.
|
||||
func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
|
||||
warnings := []string{}
|
||||
return warnings, nil
|
||||
}
|
||||
|
||||
// verifyDaemonSettings performs validation of daemon config struct
|
||||
func verifyDaemonSettings(config *Config) error {
|
||||
// checkSystem validates platform-specific requirements
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSystem() error {
|
||||
// check OS version for compatibility, ensure running in global zone
|
||||
var err error
|
||||
var id C.zoneid_t
|
||||
|
||||
if id, err = C.getzoneid(); err != nil {
|
||||
return fmt.Errorf("Exiting. Error getting zone id: %+v", err)
|
||||
}
|
||||
if int(id) != 0 {
|
||||
return fmt.Errorf("Exiting because the Docker daemon is not running in the global zone")
|
||||
}
|
||||
|
||||
v, err := kernel.GetKernelVersion()
|
||||
if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 5, Major: 12, Minor: 0}) < 0 {
|
||||
return fmt.Errorf("Your Solaris kernel version: %s doesn't support Docker. Please upgrade to 5.12.0", v.String())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// configureMaxThreads sets the Go runtime max threads threshold
|
||||
// which is 90% of the kernel setting from /proc/sys/kernel/threads-max
|
||||
func configureMaxThreads(config *Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureKernelSecuritySupport configures and validate security support for the kernel
|
||||
func configureKernelSecuritySupport(config *Config, driverName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkController, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// registerLinks sets up links between containers and writes the
|
||||
// configuration out for persistence.
|
||||
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) cleanupMounts() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// conditionalMountOnStart is a platform specific helper function during the
|
||||
// container start to call mount.
|
||||
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// conditionalUnmountOnCleanup is a platform specific helper function called
|
||||
// during the cleanup of a container to unmount.
|
||||
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
|
||||
return daemon.Unmount(container)
|
||||
}
|
||||
|
||||
func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
|
||||
// Solaris has no custom images to register
|
||||
return nil
|
||||
}
|
||||
|
||||
func driverOptions(config *Config) []nwconfig.Option {
|
||||
return []nwconfig.Option{}
|
||||
}
|
||||
|
||||
func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// setDefaultIsolation determine the default isolation mode for the
|
||||
// daemon to run in. This is only applicable on Windows
|
||||
func (daemon *Daemon) setDefaultIsolation() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
|
||||
return types.RootFS{}
|
||||
}
|
||||
@@ -488,7 +488,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
|
||||
if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 {
|
||||
return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000]", hostConfig.OomScoreAdj)
|
||||
}
|
||||
if sysInfo.IPv4ForwardingDisabled {
|
||||
|
||||
// ip-forwarding does not affect container with '--net=host' (or '--net=none')
|
||||
if sysInfo.IPv4ForwardingDisabled && !(hostConfig.NetworkMode.IsHost() || hostConfig.NetworkMode.IsNone()) {
|
||||
warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.")
|
||||
logrus.Warnf("IPv4 forwarding is disabled. Networking will not work")
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux,!freebsd,!windows
|
||||
// +build !linux,!freebsd,!windows,!solaris
|
||||
|
||||
package daemon
|
||||
|
||||
|
||||
@@ -470,6 +470,10 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
|
||||
// daemon to run in. This is only applicable on Windows
|
||||
func (daemon *Daemon) setDefaultIsolation() error {
|
||||
daemon.defaultIsolation = containertypes.Isolation("process")
|
||||
// On client SKUs, default to Hyper-V
|
||||
if system.IsWindowsClient() {
|
||||
daemon.defaultIsolation = containertypes.Isolation("hyperv")
|
||||
}
|
||||
for _, option := range daemon.configStore.ExecOptions {
|
||||
key, val, err := parsers.ParseKeyValueOpt(option)
|
||||
if err != nil {
|
||||
@@ -485,6 +489,12 @@ func (daemon *Daemon) setDefaultIsolation() error {
|
||||
if containertypes.Isolation(val).IsHyperV() {
|
||||
daemon.defaultIsolation = containertypes.Isolation("hyperv")
|
||||
}
|
||||
if containertypes.Isolation(val).IsProcess() {
|
||||
if system.IsWindowsClient() {
|
||||
return fmt.Errorf("Windows client operating systems only support Hyper-V containers")
|
||||
}
|
||||
daemon.defaultIsolation = containertypes.Isolation("process")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Unrecognised exec-opt '%s'\n", key)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux,!darwin,!freebsd,!windows
|
||||
// +build !linux,!darwin,!freebsd,!windows,!solaris
|
||||
|
||||
package daemon
|
||||
|
||||
|
||||
@@ -80,6 +80,20 @@ func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, actio
|
||||
daemon.EventsService.Log(action, events.NetworkEventType, actor)
|
||||
}
|
||||
|
||||
// LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes.
|
||||
func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) {
|
||||
if daemon.EventsService != nil {
|
||||
if info, err := daemon.SystemInfo(); err == nil && info.Name != "" {
|
||||
attributes["name"] = info.Name
|
||||
}
|
||||
actor := events.Actor{
|
||||
ID: daemon.ID,
|
||||
Attributes: attributes,
|
||||
}
|
||||
daemon.EventsService.Log(action, events.DaemonEventType, actor)
|
||||
}
|
||||
}
|
||||
|
||||
// SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
|
||||
func (daemon *Daemon) SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{}) {
|
||||
ef := daemonevents.NewFilter(filter)
|
||||
|
||||
@@ -20,6 +20,7 @@ func NewFilter(filter filters.Args) *Filter {
|
||||
func (ef *Filter) Include(ev events.Message) bool {
|
||||
return ef.filter.ExactMatch("event", ev.Action) &&
|
||||
ef.filter.ExactMatch("type", ev.Type) &&
|
||||
ef.matchDaemon(ev) &&
|
||||
ef.matchContainer(ev) &&
|
||||
ef.matchVolume(ev) &&
|
||||
ef.matchNetwork(ev) &&
|
||||
@@ -34,6 +35,10 @@ func (ef *Filter) matchLabels(attributes map[string]string) bool {
|
||||
return ef.filter.MatchKVList("label", attributes)
|
||||
}
|
||||
|
||||
func (ef *Filter) matchDaemon(ev events.Message) bool {
|
||||
return ef.fuzzyMatchName(ev, events.DaemonEventType)
|
||||
}
|
||||
|
||||
func (ef *Filter) matchContainer(ev events.Message) bool {
|
||||
return ef.fuzzyMatchName(ev, events.ContainerEventType)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
)
|
||||
|
||||
func execSetPlatformOpt(c *container.Container, ec *exec.Config, p *libcontainerd.Process) error {
|
||||
return nil
|
||||
}
|
||||
@@ -70,6 +70,7 @@ type Driver struct {
|
||||
root string
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
ctr *graphdriver.RefCounter
|
||||
pathCacheLock sync.Mutex
|
||||
pathCache map[string]string
|
||||
}
|
||||
@@ -108,6 +109,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
pathCache: make(map[string]string),
|
||||
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicAufs)),
|
||||
}
|
||||
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
|
||||
@@ -320,6 +322,9 @@ func (a *Driver) Get(id, mountLabel string) (string, error) {
|
||||
m = a.getMountpoint(id)
|
||||
}
|
||||
}
|
||||
if count := a.ctr.Increment(m); count > 1 {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// If a dir does not have a parent ( no layers )do not try to mount
|
||||
// just return the diff path to the data
|
||||
@@ -344,6 +349,9 @@ func (a *Driver) Put(id string) error {
|
||||
a.pathCache[id] = m
|
||||
}
|
||||
a.pathCacheLock.Unlock()
|
||||
if count := a.ctr.Decrement(m); count > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := a.unmount(m)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,31 +2,66 @@ package graphdriver
|
||||
|
||||
import "sync"
|
||||
|
||||
type minfo struct {
|
||||
check bool
|
||||
count int
|
||||
}
|
||||
|
||||
// RefCounter is a generic counter for use by graphdriver Get/Put calls
|
||||
type RefCounter struct {
|
||||
counts map[string]int
|
||||
mu sync.Mutex
|
||||
counts map[string]*minfo
|
||||
mu sync.Mutex
|
||||
checker Checker
|
||||
}
|
||||
|
||||
// NewRefCounter returns a new RefCounter
|
||||
func NewRefCounter() *RefCounter {
|
||||
return &RefCounter{counts: make(map[string]int)}
|
||||
func NewRefCounter(c Checker) *RefCounter {
|
||||
return &RefCounter{
|
||||
checker: c,
|
||||
counts: make(map[string]*minfo),
|
||||
}
|
||||
}
|
||||
|
||||
// Increment increaes the ref count for the given id and returns the current count
|
||||
func (c *RefCounter) Increment(id string) int {
|
||||
func (c *RefCounter) Increment(path string) int {
|
||||
c.mu.Lock()
|
||||
c.counts[id]++
|
||||
count := c.counts[id]
|
||||
m := c.counts[path]
|
||||
if m == nil {
|
||||
m = &minfo{}
|
||||
c.counts[path] = m
|
||||
}
|
||||
// if we are checking this path for the first time check to make sure
|
||||
// if it was already mounted on the system and make sure we have a correct ref
|
||||
// count if it is mounted as it is in use.
|
||||
if !m.check {
|
||||
m.check = true
|
||||
if c.checker.IsMounted(path) {
|
||||
m.count++
|
||||
}
|
||||
}
|
||||
m.count++
|
||||
c.mu.Unlock()
|
||||
return count
|
||||
return m.count
|
||||
}
|
||||
|
||||
// Decrement decreases the ref count for the given id and returns the current count
|
||||
func (c *RefCounter) Decrement(id string) int {
|
||||
func (c *RefCounter) Decrement(path string) int {
|
||||
c.mu.Lock()
|
||||
c.counts[id]--
|
||||
count := c.counts[id]
|
||||
m := c.counts[path]
|
||||
if m == nil {
|
||||
m = &minfo{}
|
||||
c.counts[path] = m
|
||||
}
|
||||
// if we are checking this path for the first time check to make sure
|
||||
// if it was already mounted on the system and make sure we have a correct ref
|
||||
// count if it is mounted as it is in use.
|
||||
if !m.check {
|
||||
m.check = true
|
||||
if c.checker.IsMounted(path) {
|
||||
m.count++
|
||||
}
|
||||
}
|
||||
m.count--
|
||||
c.mu.Unlock()
|
||||
return count
|
||||
return m.count
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||
home: home,
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
ctr: graphdriver.NewRefCounter(),
|
||||
ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
|
||||
}
|
||||
|
||||
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
||||
@@ -160,35 +160,35 @@ func (d *Driver) Remove(id string) error {
|
||||
// Get mounts a device with given id into the root filesystem
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
if count := d.ctr.Increment(id); count > 1 {
|
||||
if count := d.ctr.Increment(mp); count > 1 {
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
uid, gid, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
||||
if err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mp)
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Create the target directories if they don't exist
|
||||
if err := idtools.MkdirAllAs(path.Join(d.home, "mnt"), 0755, uid, gid); err != nil && !os.IsExist(err) {
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mp)
|
||||
return "", err
|
||||
}
|
||||
if err := idtools.MkdirAs(mp, 0755, uid, gid); err != nil && !os.IsExist(err) {
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mp)
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Mount the device
|
||||
if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mp)
|
||||
return "", err
|
||||
}
|
||||
|
||||
rootFs := path.Join(mp, "rootfs")
|
||||
if err := idtools.MkdirAllAs(rootFs, 0755, uid, gid); err != nil && !os.IsExist(err) {
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mp)
|
||||
d.DeviceSet.UnmountDevice(id, mp)
|
||||
return "", err
|
||||
}
|
||||
@@ -198,7 +198,7 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
// Create an "id" file with the container/image id in it to help reconstruct this in case
|
||||
// of later problems
|
||||
if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mp)
|
||||
d.DeviceSet.UnmountDevice(id, mp)
|
||||
return "", err
|
||||
}
|
||||
@@ -209,10 +209,10 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
|
||||
// Put unmounts a device and removes it.
|
||||
func (d *Driver) Put(id string) error {
|
||||
if count := d.ctr.Decrement(id); count > 0 {
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
if count := d.ctr.Decrement(mp); count > 0 {
|
||||
return nil
|
||||
}
|
||||
mp := path.Join(d.home, "mnt", id)
|
||||
err := d.DeviceSet.UnmountDevice(id, mp)
|
||||
if err != nil {
|
||||
logrus.Errorf("devmapper: Error unmounting device %s: %s", id, err)
|
||||
|
||||
@@ -113,6 +113,12 @@ type FileGetCloser interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Checker makes checks on specified filesystems.
|
||||
type Checker interface {
|
||||
// IsMounted returns true if the provided path is mounted for the specific checker
|
||||
IsMounted(path string) bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
drivers = make(map[string]InitFunc)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ package graphdriver
|
||||
import (
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -89,6 +91,36 @@ func GetFSMagic(rootpath string) (FsMagic, error) {
|
||||
return FsMagic(buf.Type), nil
|
||||
}
|
||||
|
||||
// NewFsChecker returns a checker configured for the provied FsMagic
|
||||
func NewFsChecker(t FsMagic) Checker {
|
||||
return &fsChecker{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
type fsChecker struct {
|
||||
t FsMagic
|
||||
}
|
||||
|
||||
func (c *fsChecker) IsMounted(path string) bool {
|
||||
m, _ := Mounted(c.t, path)
|
||||
return m
|
||||
}
|
||||
|
||||
// NewDefaultChecker returns a check that parses /proc/mountinfo to check
|
||||
// if the specified path is mounted.
|
||||
func NewDefaultChecker() Checker {
|
||||
return &defaultChecker{}
|
||||
}
|
||||
|
||||
type defaultChecker struct {
|
||||
}
|
||||
|
||||
func (c *defaultChecker) IsMounted(path string) bool {
|
||||
m, _ := mount.Mounted(path)
|
||||
return m
|
||||
}
|
||||
|
||||
// Mounted checks if the given path is mounted as the fs type
|
||||
func Mounted(fsType FsMagic, mountPath string) (bool, error) {
|
||||
var buf syscall.Statfs_t
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// +build solaris,cgo
|
||||
|
||||
package graphdriver
|
||||
|
||||
/*
|
||||
#include <sys/statvfs.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline struct statvfs *getstatfs(char *s) {
|
||||
struct statvfs *buf;
|
||||
int err;
|
||||
buf = (struct statvfs *)malloc(sizeof(struct statvfs));
|
||||
err = statvfs(s, buf);
|
||||
return buf;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"path/filepath"
|
||||
"unsafe"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
// FsMagicZfs filesystem id for Zfs
|
||||
FsMagicZfs = FsMagic(0x2fc12fc1)
|
||||
)
|
||||
|
||||
var (
|
||||
// Slice of drivers that should be used in an order
|
||||
priority = []string{
|
||||
"zfs",
|
||||
}
|
||||
|
||||
// FsNames maps filesystem id to name of the filesystem.
|
||||
FsNames = map[FsMagic]string{
|
||||
FsMagicZfs: "zfs",
|
||||
}
|
||||
)
|
||||
|
||||
// GetFSMagic returns the filesystem id given the path.
|
||||
func GetFSMagic(rootpath string) (FsMagic, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Mounted checks if the given path is mounted as the fs type
|
||||
//Solaris supports only ZFS for now
|
||||
func Mounted(fsType FsMagic, mountPath string) (bool, error) {
|
||||
|
||||
cs := C.CString(filepath.Dir(mountPath))
|
||||
buf := C.getstatfs(cs)
|
||||
|
||||
// on Solaris buf.f_basetype contains ['z', 'f', 's', 0 ... ]
|
||||
if (buf.f_basetype[0] != 122) || (buf.f_basetype[1] != 102) || (buf.f_basetype[2] != 115) ||
|
||||
(buf.f_basetype[3] != 0) {
|
||||
log.Debugf("[zfs] no zfs dataset found for rootdir '%s'", mountPath)
|
||||
C.free(unsafe.Pointer(buf))
|
||||
return false, ErrPrerequisites
|
||||
}
|
||||
|
||||
C.free(unsafe.Pointer(buf))
|
||||
C.free(unsafe.Pointer(cs))
|
||||
return true, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux,!windows,!freebsd
|
||||
// +build !linux,!windows,!freebsd,!solaris
|
||||
|
||||
package graphdriver
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ var (
|
||||
// Slice of drivers that should be used in order
|
||||
priority = []string{
|
||||
"windowsfilter",
|
||||
"windowsdiff",
|
||||
"vfs",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
@@ -92,12 +91,10 @@ func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff archive.Rea
|
||||
|
||||
// Driver contains information about the home directory and the list of active mounts that are created using this driver.
|
||||
type Driver struct {
|
||||
home string
|
||||
pathCacheLock sync.Mutex
|
||||
pathCache map[string]string
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
ctr *graphdriver.RefCounter
|
||||
home string
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
ctr *graphdriver.RefCounter
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -141,11 +138,10 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||
}
|
||||
|
||||
d := &Driver{
|
||||
home: home,
|
||||
pathCache: make(map[string]string),
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
ctr: graphdriver.NewRefCounter(),
|
||||
home: home,
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
|
||||
}
|
||||
|
||||
return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
|
||||
@@ -328,110 +324,64 @@ func (d *Driver) Remove(id string) error {
|
||||
if err := os.RemoveAll(d.dir(id)); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
d.pathCacheLock.Lock()
|
||||
delete(d.pathCache, id)
|
||||
d.pathCacheLock.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get creates and mounts the required file system for the given id and returns the mount path.
|
||||
func (d *Driver) Get(id string, mountLabel string) (string, error) {
|
||||
func (d *Driver) Get(id string, mountLabel string) (s string, err error) {
|
||||
dir := d.dir(id)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If id has a root, just return it
|
||||
rootDir := path.Join(dir, "root")
|
||||
if _, err := os.Stat(rootDir); err == nil {
|
||||
d.pathCacheLock.Lock()
|
||||
d.pathCache[id] = rootDir
|
||||
d.pathCacheLock.Unlock()
|
||||
return rootDir, nil
|
||||
}
|
||||
|
||||
mergedDir := path.Join(dir, "merged")
|
||||
if count := d.ctr.Increment(mergedDir); count > 1 {
|
||||
return mergedDir, nil
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if c := d.ctr.Decrement(mergedDir); c <= 0 {
|
||||
syscall.Unmount(mergedDir, 0)
|
||||
}
|
||||
}
|
||||
}()
|
||||
lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
lowerDir := path.Join(d.dir(string(lowerID)), "root")
|
||||
upperDir := path.Join(dir, "upper")
|
||||
workDir := path.Join(dir, "work")
|
||||
mergedDir := path.Join(dir, "merged")
|
||||
|
||||
if count := d.ctr.Increment(id); count > 1 {
|
||||
return mergedDir, nil
|
||||
}
|
||||
|
||||
opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
|
||||
|
||||
// if it's mounted already, just return
|
||||
mounted, err := d.mounted(mergedDir)
|
||||
if err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
return "", err
|
||||
}
|
||||
if mounted {
|
||||
d.ctr.Decrement(id)
|
||||
return mergedDir, nil
|
||||
}
|
||||
|
||||
var (
|
||||
lowerDir = path.Join(d.dir(string(lowerID)), "root")
|
||||
upperDir = path.Join(dir, "upper")
|
||||
workDir = path.Join(dir, "work")
|
||||
opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
|
||||
)
|
||||
if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
|
||||
}
|
||||
// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
|
||||
// user namespace requires this to move a directory from lower to upper.
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
||||
if err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
syscall.Unmount(mergedDir, 0)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
syscall.Unmount(mergedDir, 0)
|
||||
return "", err
|
||||
}
|
||||
|
||||
d.pathCacheLock.Lock()
|
||||
d.pathCache[id] = mergedDir
|
||||
d.pathCacheLock.Unlock()
|
||||
|
||||
return mergedDir, nil
|
||||
}
|
||||
|
||||
func (d *Driver) mounted(dir string) (bool, error) {
|
||||
return graphdriver.Mounted(graphdriver.FsMagicOverlay, dir)
|
||||
}
|
||||
|
||||
// Put unmounts the mount path created for the give id.
|
||||
func (d *Driver) Put(id string) error {
|
||||
if count := d.ctr.Decrement(id); count > 0 {
|
||||
mountpoint := path.Join(d.dir(id), "merged")
|
||||
if count := d.ctr.Decrement(mountpoint); count > 0 {
|
||||
return nil
|
||||
}
|
||||
d.pathCacheLock.Lock()
|
||||
mountpoint, exists := d.pathCache[id]
|
||||
d.pathCacheLock.Unlock()
|
||||
|
||||
if !exists {
|
||||
logrus.Debugf("Put on a non-mounted device %s", id)
|
||||
// but it might be still here
|
||||
if d.Exists(id) {
|
||||
mountpoint = path.Join(d.dir(id), "merged")
|
||||
}
|
||||
|
||||
d.pathCacheLock.Lock()
|
||||
d.pathCache[id] = mountpoint
|
||||
d.pathCacheLock.Unlock()
|
||||
}
|
||||
|
||||
if mounted, err := d.mounted(mountpoint); mounted || err != nil {
|
||||
if err = syscall.Unmount(mountpoint, 0); err != nil {
|
||||
logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
|
||||
}
|
||||
return err
|
||||
if err := syscall.Unmount(mountpoint, 0); err != nil {
|
||||
logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd
|
||||
// +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd, solaris
|
||||
|
||||
package register
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
@@ -26,7 +27,6 @@ import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/longpath"
|
||||
@@ -35,28 +35,33 @@ import (
|
||||
"github.com/vbatts/tar-split/tar/storage"
|
||||
)
|
||||
|
||||
// filterDriver is an HCSShim driver type for the Windows Filter driver.
|
||||
const filterDriver = 1
|
||||
|
||||
// init registers the windows graph drivers to the register.
|
||||
func init() {
|
||||
graphdriver.Register("windowsfilter", InitFilter)
|
||||
graphdriver.Register("windowsdiff", InitDiff)
|
||||
reexec.Register("docker-windows-write-layer", writeLayer)
|
||||
}
|
||||
|
||||
const (
|
||||
// diffDriver is an hcsshim driver type
|
||||
diffDriver = iota
|
||||
// filterDriver is an hcsshim driver type
|
||||
filterDriver
|
||||
)
|
||||
type checker struct {
|
||||
}
|
||||
|
||||
func (c *checker) IsMounted(path string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Driver represents a windows graph driver.
|
||||
type Driver struct {
|
||||
// info stores the shim driver information
|
||||
info hcsshim.DriverInfo
|
||||
ctr *graphdriver.RefCounter
|
||||
// it is safe for windows to use a cache here because it does not support
|
||||
// restoring containers when the daemon dies.
|
||||
cacheMu sync.Mutex
|
||||
cache map[string]string
|
||||
}
|
||||
|
||||
var _ graphdriver.DiffGetterDriver = &Driver{}
|
||||
|
||||
func isTP5OrOlder() bool {
|
||||
return system.GetOSVersion().Build <= 14300
|
||||
}
|
||||
@@ -69,32 +74,15 @@ func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap)
|
||||
HomeDir: home,
|
||||
Flavour: filterDriver,
|
||||
},
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// InitDiff returns a new Windows differencing disk driver.
|
||||
func InitDiff(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
logrus.Debugf("WindowsGraphDriver InitDiff at %s", home)
|
||||
d := &Driver{
|
||||
info: hcsshim.DriverInfo{
|
||||
HomeDir: home,
|
||||
Flavour: diffDriver,
|
||||
},
|
||||
cache: make(map[string]string),
|
||||
ctr: graphdriver.NewRefCounter(&checker{}),
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// String returns the string representation of a driver.
|
||||
func (d *Driver) String() string {
|
||||
switch d.info.Flavour {
|
||||
case diffDriver:
|
||||
return "windowsdiff"
|
||||
case filterDriver:
|
||||
return "windowsfilter"
|
||||
default:
|
||||
return "Unknown driver flavour"
|
||||
}
|
||||
return "Windows filter storage driver"
|
||||
}
|
||||
|
||||
// Status returns the status of the driver.
|
||||
@@ -238,17 +226,23 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if count := d.ctr.Increment(rID); count > 1 {
|
||||
return d.cache[rID], nil
|
||||
}
|
||||
|
||||
// Getting the layer paths must be done outside of the lock.
|
||||
layerChain, err := d.getLayerChain(rID)
|
||||
if err != nil {
|
||||
d.ctr.Decrement(rID)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
|
||||
d.ctr.Decrement(rID)
|
||||
return "", err
|
||||
}
|
||||
if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
|
||||
d.ctr.Decrement(rID)
|
||||
if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
|
||||
logrus.Warnf("Failed to Deactivate %s: %s", id, err)
|
||||
}
|
||||
@@ -257,11 +251,15 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
|
||||
mountPath, err := hcsshim.GetLayerMountPath(d.info, rID)
|
||||
if err != nil {
|
||||
d.ctr.Decrement(rID)
|
||||
if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
|
||||
logrus.Warnf("Failed to Deactivate %s: %s", id, err)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
d.cacheMu.Lock()
|
||||
d.cache[rID] = mountPath
|
||||
d.cacheMu.Unlock()
|
||||
|
||||
// If the layer has a mount path, use that. Otherwise, use the
|
||||
// folder path.
|
||||
@@ -282,6 +280,12 @@ func (d *Driver) Put(id string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count := d.ctr.Decrement(rID); count > 0 {
|
||||
return nil
|
||||
}
|
||||
d.cacheMu.Lock()
|
||||
delete(d.cache, rID)
|
||||
d.cacheMu.Unlock()
|
||||
|
||||
if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
|
||||
return err
|
||||
@@ -390,20 +394,6 @@ func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
|
||||
// new layer in bytes.
|
||||
// The layer should not be mounted when calling this function
|
||||
func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) {
|
||||
if d.info.Flavour == diffDriver {
|
||||
start := time.Now().UTC()
|
||||
logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer")
|
||||
destination := d.dir(id)
|
||||
destination = filepath.Dir(destination)
|
||||
size, err := chrootarchive.ApplyUncompressedLayer(destination, diff, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
var layerChain []string
|
||||
if parent != "" {
|
||||
rPId, err := d.resolveID(parent)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build linux freebsd
|
||||
// +build linux freebsd solaris
|
||||
|
||||
package zfs
|
||||
|
||||
@@ -105,7 +105,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
|
||||
filesystemsCache: filesystemsCache,
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
ctr: graphdriver.NewRefCounter(),
|
||||
ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
|
||||
}
|
||||
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
|
||||
}
|
||||
@@ -307,7 +307,7 @@ func (d *Driver) Remove(id string) error {
|
||||
// Get returns the mountpoint for the given id after creating the target directories if necessary.
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
mountpoint := d.mountPath(id)
|
||||
if count := d.ctr.Increment(id); count > 1 {
|
||||
if count := d.ctr.Increment(mountpoint); count > 1 {
|
||||
return mountpoint, nil
|
||||
}
|
||||
|
||||
@@ -317,17 +317,17 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
|
||||
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
|
||||
if err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mountpoint)
|
||||
return "", err
|
||||
}
|
||||
// Create the target directories if they don't exist
|
||||
if err := idtools.MkdirAllAs(mountpoint, 0755, rootUID, rootGID); err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mountpoint)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil {
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mountpoint)
|
||||
return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err)
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
// permissions instead of the remapped root uid:gid (if user namespaces are enabled):
|
||||
if err := os.Chown(mountpoint, rootUID, rootGID); err != nil {
|
||||
mount.Unmount(mountpoint)
|
||||
d.ctr.Decrement(id)
|
||||
d.ctr.Decrement(mountpoint)
|
||||
return "", fmt.Errorf("error modifying zfs mountpoint (%s) directory ownership: %v", mountpoint, err)
|
||||
}
|
||||
|
||||
@@ -344,10 +344,10 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
|
||||
// Put removes the existing mountpoint for the given id if it exists.
|
||||
func (d *Driver) Put(id string) error {
|
||||
if count := d.ctr.Decrement(id); count > 0 {
|
||||
mountpoint := d.mountPath(id)
|
||||
if count := d.ctr.Decrement(mountpoint); count > 0 {
|
||||
return nil
|
||||
}
|
||||
mountpoint := d.mountPath(id)
|
||||
mounted, err := graphdriver.Mounted(graphdriver.FsMagicZfs, mountpoint)
|
||||
if err != nil || !mounted {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// +build solaris,cgo
|
||||
|
||||
package zfs
|
||||
|
||||
/*
|
||||
#include <sys/statvfs.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline struct statvfs *getstatfs(char *s) {
|
||||
struct statvfs *buf;
|
||||
int err;
|
||||
buf = (struct statvfs *)malloc(sizeof(struct statvfs));
|
||||
err = statvfs(s, buf);
|
||||
return buf;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
)
|
||||
|
||||
func checkRootdirFs(rootdir string) error {
|
||||
|
||||
cs := C.CString(filepath.Dir(rootdir))
|
||||
buf := C.getstatfs(cs)
|
||||
|
||||
// on Solaris buf.f_basetype contains ['z', 'f', 's', 0 ... ]
|
||||
if (buf.f_basetype[0] != 122) || (buf.f_basetype[1] != 102) || (buf.f_basetype[2] != 115) ||
|
||||
(buf.f_basetype[3] != 0) {
|
||||
log.Debugf("[zfs] no zfs dataset found for rootdir '%s'", rootdir)
|
||||
C.free(unsafe.Pointer(buf))
|
||||
return graphdriver.ErrPrerequisites
|
||||
}
|
||||
|
||||
C.free(unsafe.Pointer(buf))
|
||||
C.free(unsafe.Pointer(cs))
|
||||
return nil
|
||||
}
|
||||
|
||||
/* rootfs is introduced to comply with the OCI spec
|
||||
which states that root filesystem must be mounted at <CID>/rootfs/ instead of <CID>/
|
||||
*/
|
||||
func getMountpoint(id string) string {
|
||||
maxlen := 12
|
||||
|
||||
// we need to preserve filesystem suffix
|
||||
suffix := strings.SplitN(id, "-", 2)
|
||||
|
||||
if len(suffix) > 1 {
|
||||
return filepath.Join(id[:maxlen]+"-"+suffix[1], "rootfs", "root")
|
||||
}
|
||||
|
||||
return filepath.Join(id[:maxlen], "rootfs", "root")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux,!freebsd
|
||||
// +build !linux,!freebsd,!solaris
|
||||
|
||||
package zfs
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/runconfig"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
||||
// ErrImageDoesNotExist is error returned when no image can be found for a reference.
|
||||
type ErrImageDoesNotExist struct {
|
||||
RefOrID string
|
||||
}
|
||||
|
||||
func (e ErrImageDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("no such id: %s", e.RefOrID)
|
||||
}
|
||||
|
||||
// GetImageID returns an image ID corresponding to the image referred to by
|
||||
// refOrID.
|
||||
func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
|
||||
id, ref, err := reference.ParseIDOrReference(refOrID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if id != "" {
|
||||
if _, err := daemon.imageStore.Get(image.ID(id)); err != nil {
|
||||
return "", ErrImageDoesNotExist{refOrID}
|
||||
}
|
||||
return image.ID(id), nil
|
||||
}
|
||||
|
||||
if id, err := daemon.referenceStore.Get(ref); err == nil {
|
||||
return id, nil
|
||||
}
|
||||
if tagged, ok := ref.(reference.NamedTagged); ok {
|
||||
if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil {
|
||||
for _, namedRef := range daemon.referenceStore.References(id) {
|
||||
if namedRef.Name() == ref.Name() {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search based on ID
|
||||
if id, err := daemon.imageStore.Search(refOrID); err == nil {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
return "", ErrImageDoesNotExist{refOrID}
|
||||
}
|
||||
|
||||
// GetImage returns an image corresponding to the image referred to by refOrID.
|
||||
func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
|
||||
imgID, err := daemon.GetImageID(refOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return daemon.imageStore.Get(imgID)
|
||||
}
|
||||
|
||||
// GetImageOnBuild looks up a Docker image referenced by `name`.
|
||||
func (daemon *Daemon) GetImageOnBuild(name string) (builder.Image, error) {
|
||||
img, err := daemon.GetImage(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// GetCachedImage returns the most recent created image that is a child
|
||||
// of the image with imgID, that had the same config when it was
|
||||
// created. nil is returned if a child cannot be found. An error is
|
||||
// returned if the parent image cannot be found.
|
||||
func (daemon *Daemon) GetCachedImage(imgID image.ID, config *containertypes.Config) (*image.Image, error) {
|
||||
// Loop on the children of the given image and check the config
|
||||
getMatch := func(siblings []image.ID) (*image.Image, error) {
|
||||
var match *image.Image
|
||||
for _, id := range siblings {
|
||||
img, err := daemon.imageStore.Get(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to find image %q", id)
|
||||
}
|
||||
|
||||
if runconfig.Compare(&img.ContainerConfig, config) {
|
||||
// check for the most up to date match
|
||||
if match == nil || match.Created.Before(img.Created) {
|
||||
match = img
|
||||
}
|
||||
}
|
||||
}
|
||||
return match, nil
|
||||
}
|
||||
|
||||
// In this case, this is `FROM scratch`, which isn't an actual image.
|
||||
if imgID == "" {
|
||||
images := daemon.imageStore.Map()
|
||||
var siblings []image.ID
|
||||
for id, img := range images {
|
||||
if img.Parent == imgID {
|
||||
siblings = append(siblings, id)
|
||||
}
|
||||
}
|
||||
return getMatch(siblings)
|
||||
}
|
||||
|
||||
// find match from child images
|
||||
siblings := daemon.imageStore.Children(imgID)
|
||||
return getMatch(siblings)
|
||||
}
|
||||
|
||||
// GetCachedImageOnBuild returns a reference to a cached image whose parent equals `parent`
|
||||
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
|
||||
func (daemon *Daemon) GetCachedImageOnBuild(imgID string, cfg *containertypes.Config) (string, error) {
|
||||
cache, err := daemon.GetCachedImage(image.ID(imgID), cfg)
|
||||
if cache == nil || err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cache.ID().String(), nil
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/image/tarexport"
|
||||
)
|
||||
|
||||
// ExportImage exports a list of images to the given output stream. The
|
||||
// exported images are archived into a tar when written to the output
|
||||
// stream. All images with the given tag and all versions containing
|
||||
// the same tag are exported. names is the set of tags to export, and
|
||||
// outStream is the writer which the images are written to.
|
||||
func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
|
||||
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
|
||||
return imageExporter.Save(names, outStream)
|
||||
}
|
||||
|
||||
// LoadImage uploads a set of images into the repository. This is the
|
||||
// complement of ImageExport. The input stream is an uncompressed tar
|
||||
// ball containing images and metadata.
|
||||
func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
|
||||
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
|
||||
return imageExporter.Load(inTar, outStream, quiet)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// ImageHistory returns a slice of ImageHistory structures for the specified image
|
||||
// name by walking the image lineage.
|
||||
func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
|
||||
img, err := daemon.GetImage(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
history := []*types.ImageHistory{}
|
||||
|
||||
layerCounter := 0
|
||||
rootFS := *img.RootFS
|
||||
rootFS.DiffIDs = nil
|
||||
|
||||
for _, h := range img.History {
|
||||
var layerSize int64
|
||||
|
||||
if !h.EmptyLayer {
|
||||
if len(img.RootFS.DiffIDs) <= layerCounter {
|
||||
return nil, fmt.Errorf("too many non-empty layers in History section")
|
||||
}
|
||||
|
||||
rootFS.Append(img.RootFS.DiffIDs[layerCounter])
|
||||
l, err := daemon.layerStore.Get(rootFS.ChainID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
layerSize, err = l.DiffSize()
|
||||
layer.ReleaseAndLog(daemon.layerStore, l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layerCounter++
|
||||
}
|
||||
|
||||
history = append([]*types.ImageHistory{{
|
||||
ID: "<missing>",
|
||||
Created: h.Created.Unix(),
|
||||
CreatedBy: h.CreatedBy,
|
||||
Comment: h.Comment,
|
||||
Size: layerSize,
|
||||
}}, history...)
|
||||
}
|
||||
|
||||
// Fill in image IDs and tags
|
||||
histImg := img
|
||||
id := img.ID()
|
||||
for _, h := range history {
|
||||
h.ID = id.String()
|
||||
|
||||
var tags []string
|
||||
for _, r := range daemon.referenceStore.References(id) {
|
||||
if _, ok := r.(reference.NamedTagged); ok {
|
||||
tags = append(tags, r.String())
|
||||
}
|
||||
}
|
||||
|
||||
h.Tags = tags
|
||||
|
||||
id = histImg.Parent
|
||||
if id == "" {
|
||||
break
|
||||
}
|
||||
histImg, err = daemon.GetImage(id.String())
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return history, nil
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// LookupImage looks up an image by name and returns it as an ImageInspect
|
||||
// structure.
|
||||
func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
|
||||
img, err := daemon.GetImage(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("No such image: %s", name)
|
||||
}
|
||||
|
||||
refs := daemon.referenceStore.References(img.ID())
|
||||
repoTags := []string{}
|
||||
repoDigests := []string{}
|
||||
for _, ref := range refs {
|
||||
switch ref.(type) {
|
||||
case reference.NamedTagged:
|
||||
repoTags = append(repoTags, ref.String())
|
||||
case reference.Canonical:
|
||||
repoDigests = append(repoDigests, ref.String())
|
||||
}
|
||||
}
|
||||
|
||||
var size int64
|
||||
var layerMetadata map[string]string
|
||||
layerID := img.RootFS.ChainID()
|
||||
if layerID != "" {
|
||||
l, err := daemon.layerStore.Get(layerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer layer.ReleaseAndLog(daemon.layerStore, l)
|
||||
size, err = l.Size()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layerMetadata, err = l.Metadata()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
comment := img.Comment
|
||||
if len(comment) == 0 && len(img.History) > 0 {
|
||||
comment = img.History[len(img.History)-1].Comment
|
||||
}
|
||||
|
||||
imageInspect := &types.ImageInspect{
|
||||
ID: img.ID().String(),
|
||||
RepoTags: repoTags,
|
||||
RepoDigests: repoDigests,
|
||||
Parent: img.Parent.String(),
|
||||
Comment: comment,
|
||||
Created: img.Created.Format(time.RFC3339Nano),
|
||||
Container: img.Container,
|
||||
ContainerConfig: &img.ContainerConfig,
|
||||
DockerVersion: img.DockerVersion,
|
||||
Author: img.Author,
|
||||
Config: img.Config,
|
||||
Architecture: img.Architecture,
|
||||
Os: img.OS,
|
||||
Size: size,
|
||||
VirtualSize: size, // TODO: field unused, deprecate
|
||||
RootFS: rootFSToAPIType(img.RootFS),
|
||||
}
|
||||
|
||||
imageInspect.GraphDriver.Name = daemon.GraphDriverName()
|
||||
|
||||
imageInspect.GraphDriver.Data = layerMetadata
|
||||
|
||||
return imageInspect, nil
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// This sets platform-specific fields
|
||||
func setPlatformSpecificContainerFields(container *container.Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
|
||||
return contJSONBase
|
||||
}
|
||||
|
||||
// containerInspectPre120 get containers for pre 1.20 APIs.
|
||||
func (daemon *Daemon) containerInspectPre120(name string) (*types.ContainerJSON, error) {
|
||||
return daemon.containerInspectCurrent(name, false)
|
||||
}
|
||||
|
||||
func addMountPoints(container *container.Container) []types.MountPoint {
|
||||
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
|
||||
for _, m := range container.MountPoints {
|
||||
mountPoints = append(mountPoints, types.MountPoint{
|
||||
Name: m.Name,
|
||||
Source: m.Path(),
|
||||
Destination: m.Destination,
|
||||
Driver: m.Driver,
|
||||
RW: m.RW,
|
||||
})
|
||||
}
|
||||
return mountPoints
|
||||
}
|
||||
|
||||
func inspectExecProcessConfig(e *exec.Config) *backend.ExecProcessConfig {
|
||||
return &backend.ExecProcessConfig{
|
||||
Tty: e.Tty,
|
||||
Entrypoint: e.Entrypoint,
|
||||
Arguments: e.Args,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !windows
|
||||
// +build !windows,!solaris
|
||||
|
||||
package daemon
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build linux freebsd
|
||||
// +build linux freebsd solaris
|
||||
|
||||
package daemon
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
)
|
||||
|
||||
// platformConstructExitStatus returns a platform specific exit status structure
|
||||
func platformConstructExitStatus(e libcontainerd.StateInfo) *container.ExitStatus {
|
||||
return &container.ExitStatus{
|
||||
ExitCode: int(e.ExitCode),
|
||||
}
|
||||
}
|
||||
|
||||
// postRunProcessing perfoms any processing needed on the container after it has stopped.
|
||||
func (daemon *Daemon) postRunProcessing(container *container.Container, e libcontainerd.StateInfo) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
"github.com/docker/docker/oci"
|
||||
)
|
||||
|
||||
func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, error) {
|
||||
s := oci.DefaultSpec()
|
||||
return (*libcontainerd.Spec)(&s), nil
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
profile, err = seccomp.GetDefaultProfile()
|
||||
profile, err = seccomp.GetDefaultProfile(rs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/container"
|
||||
"time"
|
||||
)
|
||||
|
||||
// newStatsCollector returns a new statsCollector for collection stats
|
||||
// for a registered container at the specified interval. The collector allows
|
||||
// non-running containers to be added and will start processing stats when
|
||||
// they are started.
|
||||
func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector {
|
||||
return &statsCollector{}
|
||||
}
|
||||
|
||||
// statsCollector manages and provides container resource stats
|
||||
type statsCollector struct {
|
||||
}
|
||||
|
||||
// collect registers the container with the collector and adds it to
|
||||
// the event loop for collection on the specified interval returning
|
||||
// a channel for the subscriber to receive on.
|
||||
func (s *statsCollector) collect(c *container.Container) chan interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// stopCollection closes the channels for all subscribers and removes
|
||||
// the container from metrics collection.
|
||||
func (s *statsCollector) stopCollection(c *container.Container) {
|
||||
}
|
||||
|
||||
// unsubscribe removes a specific subscriber from receiving updates for a container's stats.
|
||||
func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) {
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !windows
|
||||
// +build !windows,!solaris
|
||||
|
||||
package daemon
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
||||
func toContainerdResources(resources container.Resources) libcontainerd.Resources {
|
||||
var r libcontainerd.Resources
|
||||
return r
|
||||
}
|
||||
Reference in New Issue
Block a user