2015-07-30 17:01:53 -04:00
// Package daemon exposes the functions that occur on the host server
// that the Docker daemon is running.
//
// In implementing the various functions of the daemon, there is often
// a method-specific struct for configuring the runtime behavior.
2014-04-17 17:43:01 -04:00
package daemon
2013-01-18 19:13:39 -05:00
import (
"fmt"
2014-04-28 17:36:04 -04:00
"io"
"io/ioutil"
2015-12-17 12:35:24 -05:00
"net"
2014-04-28 17:36:04 -04:00
"os"
2015-09-03 20:51:04 -04:00
"path"
2015-01-16 14:48:25 -05:00
"path/filepath"
2014-07-30 02:51:43 -04:00
"runtime"
Remove static errors from errors package.
Moving all strings to the errors package wasn't a good idea after all.
Our custom implementation of Go errors predates everything that's nice
and good about working with errors in Go. Take as an example what we
have to do to get an error message:
```go
func GetErrorMessage(err error) string {
switch err.(type) {
case errcode.Error:
e, _ := err.(errcode.Error)
return e.Message
case errcode.ErrorCode:
ec, _ := err.(errcode.ErrorCode)
return ec.Message()
default:
return err.Error()
}
}
```
This goes against every good practice for Go development. The language already provides a simple, intuitive and standard way to get error messages, that is calling the `Error()` method from an error. Reinventing the error interface is a mistake.
Our custom implementation also makes very hard to reason about errors, another nice thing about Go. I found several (>10) error declarations that we don't use anywhere. This is a clear sign about how little we know about the errors we return. I also found several error usages where the number of arguments was different than the parameters declared in the error, another clear example of how difficult is to reason about errors.
Moreover, our custom implementation didn't really make easier for people to return custom HTTP status code depending on the errors. Again, it's hard to reason about when to set custom codes and how. Take an example what we have to do to extract the message and status code from an error before returning a response from the API:
```go
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
```
You can notice two things in that code:
1. We have to explain how errors work, because our implementation goes against how easy to use Go errors are.
2. At no moment we arrived to remove that `switch` statement that was the original reason to use our custom implementation.
This change removes all our status errors from the errors package and puts them back in their specific contexts.
IT puts the messages back with their contexts. That way, we know right away when errors used and how to generate their messages.
It uses custom interfaces to reason about errors. Errors that need to response with a custom status code MUST implementent this simple interface:
```go
type errorWithStatus interface {
HTTPErrorStatusCode() int
}
```
This interface is very straightforward to implement. It also preserves Go errors real behavior, getting the message is as simple as using the `Error()` method.
I included helper functions to generate errors that use custom status code in `errors/errors.go`.
By doing this, we remove the hard dependency we have eeverywhere to our custom errors package. Yes, you can use it as a helper to generate error, but it's still very easy to generate errors without it.
Please, read this fantastic blog post about errors in Go: http://dave.cheney.net/2014/12/24/inspecting-errors
Signed-off-by: David Calavera <david.calavera@gmail.com>
2016-02-25 10:53:35 -05:00
"strings"
2014-04-28 17:36:04 -04:00
"sync"
2015-12-17 12:35:24 -05:00
"syscall"
2014-04-28 17:36:04 -04:00
"time"
2015-03-26 18:22:04 -04:00
"github.com/Sirupsen/logrus"
2016-03-18 14:50:19 -04:00
containerd "github.com/docker/containerd/api/grpc/types"
2014-11-17 14:23:41 -05:00
"github.com/docker/docker/api"
2016-01-20 18:32:02 -05:00
"github.com/docker/docker/builder"
2015-11-12 14:55:17 -05:00
"github.com/docker/docker/container"
2015-04-03 18:17:49 -04:00
"github.com/docker/docker/daemon/events"
2015-11-20 17:35:16 -05:00
"github.com/docker/docker/daemon/exec"
Remove static errors from errors package.
Moving all strings to the errors package wasn't a good idea after all.
Our custom implementation of Go errors predates everything that's nice
and good about working with errors in Go. Take as an example what we
have to do to get an error message:
```go
func GetErrorMessage(err error) string {
switch err.(type) {
case errcode.Error:
e, _ := err.(errcode.Error)
return e.Message
case errcode.ErrorCode:
ec, _ := err.(errcode.ErrorCode)
return ec.Message()
default:
return err.Error()
}
}
```
This goes against every good practice for Go development. The language already provides a simple, intuitive and standard way to get error messages, that is calling the `Error()` method from an error. Reinventing the error interface is a mistake.
Our custom implementation also makes very hard to reason about errors, another nice thing about Go. I found several (>10) error declarations that we don't use anywhere. This is a clear sign about how little we know about the errors we return. I also found several error usages where the number of arguments was different than the parameters declared in the error, another clear example of how difficult is to reason about errors.
Moreover, our custom implementation didn't really make easier for people to return custom HTTP status code depending on the errors. Again, it's hard to reason about when to set custom codes and how. Take an example what we have to do to extract the message and status code from an error before returning a response from the API:
```go
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
```
You can notice two things in that code:
1. We have to explain how errors work, because our implementation goes against how easy to use Go errors are.
2. At no moment we arrived to remove that `switch` statement that was the original reason to use our custom implementation.
This change removes all our status errors from the errors package and puts them back in their specific contexts.
IT puts the messages back with their contexts. That way, we know right away when errors used and how to generate their messages.
It uses custom interfaces to reason about errors. Errors that need to response with a custom status code MUST implementent this simple interface:
```go
type errorWithStatus interface {
HTTPErrorStatusCode() int
}
```
This interface is very straightforward to implement. It also preserves Go errors real behavior, getting the message is as simple as using the `Error()` method.
I included helper functions to generate errors that use custom status code in `errors/errors.go`.
By doing this, we remove the hard dependency we have eeverywhere to our custom errors package. Yes, you can use it as a helper to generate error, but it's still very easy to generate errors without it.
Please, read this fantastic blog post about errors in Go: http://dave.cheney.net/2014/12/24/inspecting-errors
Signed-off-by: David Calavera <david.calavera@gmail.com>
2016-02-25 10:53:35 -05:00
"github.com/docker/docker/errors"
2016-01-04 19:05:26 -05:00
"github.com/docker/engine-api/types"
containertypes "github.com/docker/engine-api/types/container"
2016-01-21 17:24:35 -05:00
networktypes "github.com/docker/engine-api/types/network"
2016-01-04 19:05:26 -05:00
registrytypes "github.com/docker/engine-api/types/registry"
"github.com/docker/engine-api/types/strslice"
2015-12-23 13:43:34 -05:00
// register graph drivers
_ "github.com/docker/docker/daemon/graphdriver/register"
2015-04-09 00:23:30 -04:00
"github.com/docker/docker/daemon/logger"
2015-04-04 00:06:48 -04:00
"github.com/docker/docker/daemon/network"
2015-11-18 17:20:54 -05:00
dmetadata "github.com/docker/docker/distribution/metadata"
2015-11-13 19:59:01 -05:00
"github.com/docker/docker/distribution/xfer"
2016-01-04 13:36:01 -05:00
"github.com/docker/docker/dockerversion"
2015-07-20 13:57:15 -04:00
"github.com/docker/docker/image"
2015-11-18 17:20:54 -05:00
"github.com/docker/docker/image/tarexport"
"github.com/docker/docker/layer"
2016-03-18 14:50:19 -04:00
"github.com/docker/docker/libcontainerd"
2015-11-18 17:20:54 -05:00
"github.com/docker/docker/migrate/v1"
2015-07-24 20:49:43 -04:00
"github.com/docker/docker/pkg/archive"
2015-03-29 17:17:23 -04:00
"github.com/docker/docker/pkg/fileutils"
2015-11-21 13:45:34 -05:00
"github.com/docker/docker/pkg/graphdb"
2015-10-08 11:51:41 -04:00
"github.com/docker/docker/pkg/idtools"
2014-07-24 18:19:50 -04:00
"github.com/docker/docker/pkg/namesgenerator"
2015-11-13 19:59:01 -05:00
"github.com/docker/docker/pkg/progress"
2015-09-03 20:51:04 -04:00
"github.com/docker/docker/pkg/registrar"
2015-08-24 21:42:58 -04:00
"github.com/docker/docker/pkg/signal"
2015-11-13 19:59:01 -05:00
"github.com/docker/docker/pkg/streamformatter"
2015-03-24 07:25:26 -04:00
"github.com/docker/docker/pkg/stringid"
2014-07-24 18:19:50 -04:00
"github.com/docker/docker/pkg/sysinfo"
2015-05-15 19:34:26 -04:00
"github.com/docker/docker/pkg/system"
2014-07-24 18:19:50 -04:00
"github.com/docker/docker/pkg/truncindex"
2015-12-04 16:55:15 -05:00
"github.com/docker/docker/reference"
2015-03-31 19:21:37 -04:00
"github.com/docker/docker/registry"
2014-07-24 18:19:50 -04:00
"github.com/docker/docker/runconfig"
2015-10-16 11:54:05 -04:00
"github.com/docker/docker/utils"
2015-09-16 17:18:24 -04:00
volumedrivers "github.com/docker/docker/volume/drivers"
"github.com/docker/docker/volume/local"
2015-09-18 19:58:05 -04:00
"github.com/docker/docker/volume/store"
2015-12-18 12:58:48 -05:00
"github.com/docker/go-connections/nat"
2015-05-15 19:34:26 -04:00
"github.com/docker/libnetwork"
2016-03-09 23:33:21 -05:00
nwconfig "github.com/docker/libnetwork/config"
2015-11-18 17:20:54 -05:00
"github.com/docker/libtrust"
2015-11-13 19:59:01 -05:00
"golang.org/x/net/context"
)
const (
// maxDownloadConcurrency is the maximum number of downloads that
// may take place at a time for each pull.
maxDownloadConcurrency = 3
// maxUploadConcurrency is the maximum number of uploads that
// may take place at a time for each push.
maxUploadConcurrency = 5
2013-01-18 19:13:39 -05:00
)
2013-12-12 16:34:26 -05:00
var (
2015-10-20 13:09:48 -04:00
validContainerNameChars = utils . RestrictedNameChars
validContainerNamePattern = utils . RestrictedNamePattern
2015-05-15 19:34:26 -04:00
Remove static errors from errors package.
Moving all strings to the errors package wasn't a good idea after all.
Our custom implementation of Go errors predates everything that's nice
and good about working with errors in Go. Take as an example what we
have to do to get an error message:
```go
func GetErrorMessage(err error) string {
switch err.(type) {
case errcode.Error:
e, _ := err.(errcode.Error)
return e.Message
case errcode.ErrorCode:
ec, _ := err.(errcode.ErrorCode)
return ec.Message()
default:
return err.Error()
}
}
```
This goes against every good practice for Go development. The language already provides a simple, intuitive and standard way to get error messages, that is calling the `Error()` method from an error. Reinventing the error interface is a mistake.
Our custom implementation also makes very hard to reason about errors, another nice thing about Go. I found several (>10) error declarations that we don't use anywhere. This is a clear sign about how little we know about the errors we return. I also found several error usages where the number of arguments was different than the parameters declared in the error, another clear example of how difficult is to reason about errors.
Moreover, our custom implementation didn't really make easier for people to return custom HTTP status code depending on the errors. Again, it's hard to reason about when to set custom codes and how. Take an example what we have to do to extract the message and status code from an error before returning a response from the API:
```go
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
```
You can notice two things in that code:
1. We have to explain how errors work, because our implementation goes against how easy to use Go errors are.
2. At no moment we arrived to remove that `switch` statement that was the original reason to use our custom implementation.
This change removes all our status errors from the errors package and puts them back in their specific contexts.
IT puts the messages back with their contexts. That way, we know right away when errors used and how to generate their messages.
It uses custom interfaces to reason about errors. Errors that need to response with a custom status code MUST implementent this simple interface:
```go
type errorWithStatus interface {
HTTPErrorStatusCode() int
}
```
This interface is very straightforward to implement. It also preserves Go errors real behavior, getting the message is as simple as using the `Error()` method.
I included helper functions to generate errors that use custom status code in `errors/errors.go`.
By doing this, we remove the hard dependency we have eeverywhere to our custom errors package. Yes, you can use it as a helper to generate error, but it's still very easy to generate errors without it.
Please, read this fantastic blog post about errors in Go: http://dave.cheney.net/2014/12/24/inspecting-errors
Signed-off-by: David Calavera <david.calavera@gmail.com>
2016-02-25 10:53:35 -05:00
errSystemNotSupported = fmt . Errorf ( "The Docker daemon is not supported on this platform." )
2013-12-12 16:34:26 -05:00
)
2013-09-06 20:33:05 -04:00
2015-11-18 17:20:54 -05:00
// 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 )
}
2015-07-30 17:01:53 -04:00
// Daemon holds information about the Docker daemon.
2014-04-17 17:43:01 -04:00
type Daemon struct {
2015-11-18 17:20:54 -05:00
ID string
repository string
2016-01-15 18:55:46 -05:00
containers container . Store
2015-11-18 17:20:54 -05:00
execCommands * exec . Store
2015-12-04 16:55:15 -05:00
referenceStore reference . Store
2015-11-13 19:59:01 -05:00
downloadManager * xfer . LayerDownloadManager
uploadManager * xfer . LayerUploadManager
2015-11-18 17:20:54 -05:00
distributionMetadataStore dmetadata . Store
trustKey libtrust . PrivateKey
idIndex * truncindex . TruncIndex
configStore * Config
statsCollector * statsCollector
2015-12-18 13:36:17 -05:00
defaultLogConfig containertypes . LogConfig
2015-11-18 17:20:54 -05:00
RegistryService * registry . Service
EventsService * events . Events
netController libnetwork . NetworkController
volumes * store . VolumeStore
2015-12-10 18:35:10 -05:00
discoveryWatcher discoveryReloader
2015-11-18 17:20:54 -05:00
root string
2016-01-11 14:44:34 -05:00
seccompEnabled bool
2015-11-18 17:20:54 -05:00
shutdown bool
uidMaps [ ] idtools . IDMap
gidMaps [ ] idtools . IDMap
layerStore layer . Store
imageStore image . Store
2015-09-03 20:51:04 -04:00
nameIndex * registrar . Registrar
linkIndex * linkIndex
2016-03-18 14:50:19 -04:00
containerd libcontainerd . Client
defaultIsolation containertypes . Isolation // Default isolation mode on Windows
2013-03-21 03:25:00 -04:00
}
2015-12-11 12:39:28 -05:00
// GetContainer looks for a container using the provided information, which could be
2015-02-13 15:14:38 -05:00
// one of the following inputs from the caller:
// - A full container ID, which will exact match a container in daemon's list
// - A container name, which will only exact match via the GetByName() function
// - A partial container ID prefix (e.g. short ID) of any length that is
// unique enough to only return a single container object
// If none of these searches succeed, an error is returned
2015-12-11 12:39:28 -05:00
func ( daemon * Daemon ) GetContainer ( prefixOrName string ) ( * container . Container , error ) {
2015-02-13 15:14:38 -05:00
if containerByID := daemon . containers . Get ( prefixOrName ) ; containerByID != nil {
2014-12-16 18:06:35 -05:00
// prefix is an exact match to a full container ID
return containerByID , nil
2014-08-06 11:54:13 -04:00
}
2014-11-02 19:25:44 -05:00
2015-02-13 15:14:38 -05:00
// GetByName will match only an exact name provided; we ignore errors
2015-09-29 13:51:40 -04:00
if containerByName , _ := daemon . GetByName ( prefixOrName ) ; containerByName != nil {
2014-12-16 18:06:35 -05:00
// prefix is an exact match to a full container Name
return containerByName , nil
2013-10-04 22:25:15 -04:00
}
2014-11-02 19:25:44 -05:00
2015-07-30 17:01:53 -04:00
containerID , indexError := daemon . idIndex . Get ( prefixOrName )
2015-06-09 01:47:31 -04:00
if indexError != nil {
2015-07-21 16:30:32 -04:00
// When truncindex defines an error type, use that instead
2015-11-03 20:19:18 -05:00
if indexError == truncindex . ErrNotExist {
Remove static errors from errors package.
Moving all strings to the errors package wasn't a good idea after all.
Our custom implementation of Go errors predates everything that's nice
and good about working with errors in Go. Take as an example what we
have to do to get an error message:
```go
func GetErrorMessage(err error) string {
switch err.(type) {
case errcode.Error:
e, _ := err.(errcode.Error)
return e.Message
case errcode.ErrorCode:
ec, _ := err.(errcode.ErrorCode)
return ec.Message()
default:
return err.Error()
}
}
```
This goes against every good practice for Go development. The language already provides a simple, intuitive and standard way to get error messages, that is calling the `Error()` method from an error. Reinventing the error interface is a mistake.
Our custom implementation also makes very hard to reason about errors, another nice thing about Go. I found several (>10) error declarations that we don't use anywhere. This is a clear sign about how little we know about the errors we return. I also found several error usages where the number of arguments was different than the parameters declared in the error, another clear example of how difficult is to reason about errors.
Moreover, our custom implementation didn't really make easier for people to return custom HTTP status code depending on the errors. Again, it's hard to reason about when to set custom codes and how. Take an example what we have to do to extract the message and status code from an error before returning a response from the API:
```go
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
```
You can notice two things in that code:
1. We have to explain how errors work, because our implementation goes against how easy to use Go errors are.
2. At no moment we arrived to remove that `switch` statement that was the original reason to use our custom implementation.
This change removes all our status errors from the errors package and puts them back in their specific contexts.
IT puts the messages back with their contexts. That way, we know right away when errors used and how to generate their messages.
It uses custom interfaces to reason about errors. Errors that need to response with a custom status code MUST implementent this simple interface:
```go
type errorWithStatus interface {
HTTPErrorStatusCode() int
}
```
This interface is very straightforward to implement. It also preserves Go errors real behavior, getting the message is as simple as using the `Error()` method.
I included helper functions to generate errors that use custom status code in `errors/errors.go`.
By doing this, we remove the hard dependency we have eeverywhere to our custom errors package. Yes, you can use it as a helper to generate error, but it's still very easy to generate errors without it.
Please, read this fantastic blog post about errors in Go: http://dave.cheney.net/2014/12/24/inspecting-errors
Signed-off-by: David Calavera <david.calavera@gmail.com>
2016-02-25 10:53:35 -05:00
err := fmt . Errorf ( "No such container: %s" , prefixOrName )
return nil , errors . NewRequestNotFoundError ( err )
2015-07-21 16:30:32 -04:00
}
2015-06-09 01:47:31 -04:00
return nil , indexError
2014-11-02 19:25:44 -05:00
}
2015-07-30 17:01:53 -04:00
return daemon . containers . Get ( containerID ) , nil
2013-01-18 19:13:39 -05:00
}
2013-09-06 20:43:34 -04:00
// Exists returns a true if a container of the specified ID or name exists,
// false otherwise.
2015-09-29 13:51:40 -04:00
func ( daemon * Daemon ) Exists ( id string ) bool {
2015-12-11 12:39:28 -05:00
c , _ := daemon . GetContainer ( id )
2014-12-16 18:06:35 -05:00
return c != nil
2013-01-18 19:13:39 -05:00
}
2015-10-28 21:00:09 -04:00
// IsPaused returns a bool indicating if the specified container is paused.
func ( daemon * Daemon ) IsPaused ( id string ) bool {
2015-12-11 12:39:28 -05:00
c , _ := daemon . GetContainer ( id )
2015-11-12 14:55:17 -05:00
return c . State . IsPaused ( )
2015-10-28 21:00:09 -04:00
}
2014-04-17 17:43:01 -04:00
func ( daemon * Daemon ) containerRoot ( id string ) string {
2015-05-15 19:34:26 -04:00
return filepath . Join ( daemon . repository , id )
2013-03-21 03:25:00 -04:00
}
2013-10-04 22:25:15 -04:00
// Load reads the contents of a container from disk
2013-09-06 20:43:34 -04:00
// This is typically done at startup.
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) load ( id string ) ( * container . Container , error ) {
2015-07-16 17:14:58 -04:00
container := daemon . newBaseContainer ( id )
2015-04-29 18:53:35 -04:00
2015-11-12 14:55:17 -05:00
if err := container . FromDisk ( ) ; err != nil {
2013-03-21 03:25:00 -04:00
return nil , err
}
2014-08-06 13:40:43 -04:00
2013-06-04 14:00:22 -04:00
if container . ID != id {
2015-10-27 12:05:25 -04:00
return container , fmt . Errorf ( "Container %s is stored at %s" , container . ID , id )
2013-03-21 03:25:00 -04:00
}
2014-08-06 13:40:43 -04:00
2015-10-27 12:05:25 -04:00
return container , nil
2013-01-18 19:13:39 -05:00
}
2015-12-21 14:23:20 -05:00
func ( daemon * Daemon ) registerName ( container * container . Container ) error {
2015-11-03 14:25:22 -05:00
if daemon . Exists ( container . ID ) {
2013-03-21 03:25:00 -04:00
return fmt . Errorf ( "Container is already loaded" )
}
2013-06-04 14:00:22 -04:00
if err := validateID ( container . ID ) ; err != nil {
2013-03-21 03:25:00 -04:00
return err
}
2015-12-21 14:23:20 -05:00
if container . Name == "" {
name , err := daemon . generateNewName ( container . ID )
if err != nil {
return err
}
container . Name = name
if err := container . ToDiskLocking ( ) ; err != nil {
logrus . Errorf ( "Error saving container name to disk: %v" , err )
}
2013-11-04 12:28:40 -05:00
}
2015-09-03 20:51:04 -04:00
return daemon . nameIndex . Reserve ( container . Name , container . ID )
2015-12-21 14:23:20 -05:00
}
// Register makes a container object usable by the daemon as <container.ID>
2016-03-18 14:50:19 -04:00
func ( daemon * Daemon ) Register ( c * container . Container ) error {
2013-03-21 03:25:00 -04:00
// Attach to stdout and stderr
2016-03-18 14:50:19 -04:00
if c . Config . OpenStdin {
c . NewInputPipes ( )
2013-03-21 03:25:00 -04:00
} else {
2016-03-18 14:50:19 -04:00
c . NewNopInputPipe ( )
2013-03-21 03:25:00 -04:00
}
2014-05-14 10:58:37 -04:00
2016-03-18 14:50:19 -04:00
daemon . containers . Add ( c . ID , c )
daemon . idIndex . Add ( c . ID )
2015-04-15 20:39:34 -04:00
2013-03-21 03:25:00 -04:00
return nil
}
2015-09-29 13:51:40 -04:00
func ( daemon * Daemon ) restore ( ) error {
2014-06-05 20:31:58 -04:00
var (
2015-12-10 18:35:10 -05:00
debug = utils . IsDebugEnabled ( )
2015-12-16 15:32:16 -05:00
currentDriver = daemon . GraphDriverName ( )
2015-09-03 20:51:04 -04:00
containers = make ( map [ string ] * container . Container )
2014-06-05 20:31:58 -04:00
)
2014-05-30 14:03:56 -04:00
if ! debug {
2015-03-26 18:22:04 -04:00
logrus . Info ( "Loading containers: start." )
2013-08-16 09:31:50 -04:00
}
2014-04-17 17:43:01 -04:00
dir , err := ioutil . ReadDir ( daemon . repository )
2013-01-18 19:13:39 -05:00
if err != nil {
return err
}
2013-10-24 19:49:28 -04:00
2013-12-18 13:43:42 -05:00
for _ , v := range dir {
2013-03-21 03:25:00 -04:00
id := v . Name ( )
2014-04-17 17:43:01 -04:00
container , err := daemon . load ( id )
2015-03-26 18:22:04 -04:00
if ! debug && logrus . GetLevel ( ) == logrus . InfoLevel {
2013-12-18 13:43:42 -05:00
fmt . Print ( "." )
2013-08-16 09:31:50 -04:00
}
2013-01-18 19:13:39 -05:00
if err != nil {
2015-03-26 18:22:04 -04:00
logrus . Errorf ( "Failed to load container %v: %v" , id , err )
2013-01-18 19:13:39 -05:00
continue
}
2013-11-15 01:52:08 -05:00
// Ignore the container if it does not support the current driver being used by the graph
2014-08-06 13:40:43 -04:00
if ( container . Driver == "" && currentDriver == "aufs" ) || container . Driver == currentDriver {
2016-01-26 20:20:30 -05:00
rwlayer , err := daemon . layerStore . GetRWLayer ( container . ID )
if err != nil {
logrus . Errorf ( "Failed to load container mount %v: %v" , id , err )
continue
}
container . RWLayer = rwlayer
2015-03-26 18:22:04 -04:00
logrus . Debugf ( "Loaded container %v" , container . ID )
2014-08-06 13:40:43 -04:00
2015-09-03 20:51:04 -04:00
containers [ container . ID ] = container
2013-11-15 01:52:08 -05:00
} else {
2015-03-26 18:22:04 -04:00
logrus . Debugf ( "Cannot load container %s because it was created with another graph driver." , container . ID )
2013-11-15 01:52:08 -05:00
}
2013-10-04 22:25:15 -04:00
}
2015-11-21 13:45:34 -05:00
var migrateLegacyLinks bool
2015-11-24 15:25:12 -05:00
restartContainers := make ( map [ * container . Container ] chan struct { } )
2015-05-19 16:05:25 -04:00
for _ , c := range containers {
2015-09-03 20:51:04 -04:00
if err := daemon . registerName ( c ) ; err != nil {
logrus . Errorf ( "Failed to register container %s: %s" , c . ID , err )
continue
2015-11-24 15:25:12 -05:00
}
2015-09-03 20:51:04 -04:00
if err := daemon . Register ( c ) ; err != nil {
logrus . Errorf ( "Failed to register container %s: %s" , c . ID , err )
2015-11-24 15:25:12 -05:00
continue
}
2016-03-18 14:50:19 -04:00
}
var wg sync . WaitGroup
var mapLock sync . Mutex
for _ , c := range containers {
wg . Add ( 1 )
go func ( c * container . Container ) {
defer wg . Done ( )
2016-03-22 11:46:40 -04:00
rm := c . RestartManager ( false )
2016-03-18 14:50:19 -04:00
if c . IsRunning ( ) || c . IsPaused ( ) {
2016-03-04 17:41:53 -05:00
// 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
}
2016-03-22 11:46:40 -04:00
if err := daemon . containerd . Restore ( c . ID , libcontainerd . WithRestartManager ( rm ) ) ; err != nil {
2016-03-18 14:50:19 -04:00
logrus . Errorf ( "Failed to restore with containerd: %q" , err )
return
}
}
// fixme: only if not running
// get list of containers we need to restart
if daemon . configStore . AutoRestart && ! c . IsRunning ( ) && ! c . IsPaused ( ) && c . ShouldRestart ( ) {
mapLock . Lock ( )
restartContainers [ c ] = make ( chan struct { } )
mapLock . Unlock ( )
}
2015-09-03 20:51:04 -04:00
2016-03-18 14:50:19 -04:00
// if c.hostConfig.Links is nil (not just empty), then it is using the old sqlite links and needs to be migrated
if c . HostConfig != nil && c . HostConfig . Links == nil {
migrateLegacyLinks = true
}
} ( c )
2015-11-21 13:45:34 -05:00
}
2016-03-18 14:50:19 -04:00
wg . Wait ( )
2015-11-21 13:45:34 -05:00
// migrate any legacy links from sqlite
linkdbFile := filepath . Join ( daemon . root , "linkgraph.db" )
var legacyLinkDB * graphdb . Database
if migrateLegacyLinks {
legacyLinkDB , err = graphdb . NewSqliteConn ( linkdbFile )
if err != nil {
return fmt . Errorf ( "error connecting to legacy link graph DB %s, container links may be lost: %v" , linkdbFile , err )
}
defer legacyLinkDB . Close ( )
2015-09-03 20:51:04 -04:00
}
// Now that all the containers are registered, register the links
for _ , c := range containers {
2015-11-21 13:45:34 -05:00
if migrateLegacyLinks {
if err := daemon . migrateLegacySqliteLinks ( legacyLinkDB , c ) ; err != nil {
return err
}
}
2015-09-03 20:51:04 -04:00
if err := daemon . registerLinks ( c , c . HostConfig ) ; err != nil {
logrus . Errorf ( "failed to register link for container %s: %v" , c . ID , err )
2015-11-24 15:25:12 -05:00
}
}
2014-08-06 13:40:43 -04:00
2015-11-24 15:25:12 -05:00
group := sync . WaitGroup { }
for c , notifier := range restartContainers {
group . Add ( 1 )
2015-09-03 20:51:04 -04:00
go func ( c * container . Container , chNotify chan struct { } ) {
2015-11-24 15:25:12 -05:00
defer group . Done ( )
2015-09-03 20:51:04 -04:00
logrus . Debugf ( "Starting container %s" , c . ID )
2014-08-06 13:40:43 -04:00
2015-11-24 15:25:12 -05:00
// ignore errors here as this is a best effort to wait for children to be
// running before we try to start the container
2015-09-03 20:51:04 -04:00
children := daemon . children ( c )
2015-11-24 15:25:12 -05:00
timeout := time . After ( 5 * time . Second )
for _ , child := range children {
if notifier , exists := restartContainers [ child ] ; exists {
select {
case <- notifier :
case <- timeout :
}
2014-08-06 13:40:43 -04:00
}
}
2015-09-03 20:51:04 -04:00
if err := daemon . containerStart ( c ) ; err != nil {
logrus . Errorf ( "Failed to start container %s: %s" , c . ID , err )
2015-11-24 15:25:12 -05:00
}
close ( chNotify )
} ( c , notifier )
2015-09-03 20:51:04 -04:00
2014-06-05 20:31:58 -04:00
}
2015-05-19 16:05:25 -04:00
group . Wait ( )
2014-06-05 20:31:58 -04:00
2016-01-20 12:06:03 -05:00
// any containers that were started above would already have had this done,
// however we need to now prepare the mountpoints for the rest of the containers as well.
// This shouldn't cause any issue running on the containers that already had this run.
// This must be run after any containers with a restart policy so that containerized plugins
// can have a chance to be running before we try to initialize them.
for _ , c := range containers {
2016-01-27 02:43:40 -05:00
// if the container has restart policy, do not
// prepare the mountpoints since it has been done on restarting.
// This is to speed up the daemon start when a restart container
// has a volume and the volume dirver is not available.
if _ , ok := restartContainers [ c ] ; ok {
continue
}
2016-01-20 12:06:03 -05:00
group . Add ( 1 )
go func ( c * container . Container ) {
defer group . Done ( )
if err := daemon . prepareMountPoints ( c ) ; err != nil {
logrus . Error ( err )
}
} ( c )
}
group . Wait ( )
2014-05-30 14:03:56 -04:00
if ! debug {
2015-03-26 18:22:04 -04:00
if logrus . GetLevel ( ) == logrus . InfoLevel {
2015-03-17 20:27:53 -04:00
fmt . Println ( )
}
2015-03-26 18:22:04 -04:00
logrus . Info ( "Loading containers: done." )
2013-08-16 09:31:50 -04:00
}
2013-10-04 22:25:15 -04:00
2013-01-18 19:13:39 -05:00
return nil
}
2015-12-18 13:36:17 -05:00
func ( daemon * Daemon ) mergeAndVerifyConfig ( config * containertypes . Config , img * image . Image ) error {
2014-10-28 17:06:23 -04:00
if img != nil && img . Config != nil {
2016-01-05 11:48:09 -05:00
if err := merge ( config , img . Config ) ; err != nil {
2015-05-24 09:17:29 -04:00
return err
2013-10-30 14:13:10 -04:00
}
}
2016-02-29 06:28:37 -05:00
if len ( config . Entrypoint ) == 0 && len ( config . Cmd ) == 0 {
2015-05-24 09:17:29 -04:00
return fmt . Errorf ( "No command specified" )
2013-09-06 20:33:05 -04:00
}
2015-05-24 09:17:29 -04:00
return nil
2014-04-07 15:20:23 -04:00
}
2013-09-06 20:33:05 -04:00
2015-09-29 13:51:40 -04:00
func ( daemon * Daemon ) generateIDAndName ( name string ) ( string , string , error ) {
2014-04-07 15:20:23 -04:00
var (
err error
2015-07-28 20:19:17 -04:00
id = stringid . GenerateNonCryptoID ( )
2014-04-07 15:20:23 -04:00
)
2013-10-04 22:25:15 -04:00
2013-10-28 19:58:59 -04:00
if name == "" {
2014-05-23 20:51:16 -04:00
if name , err = daemon . generateNewName ( id ) ; err != nil {
return "" , "" , err
2013-12-12 16:34:26 -05:00
}
2014-05-23 20:51:16 -04:00
return id , name , nil
}
2015-09-29 13:51:40 -04:00
if name , err = daemon . reserveName ( id , name ) ; err != nil {
2014-05-23 20:51:16 -04:00
return "" , "" , err
}
return id , name , nil
}
2015-09-29 13:51:40 -04:00
func ( daemon * Daemon ) reserveName ( id , name string ) ( string , error ) {
2014-05-23 20:51:16 -04:00
if ! validContainerNamePattern . MatchString ( name ) {
return "" , fmt . Errorf ( "Invalid container name (%s), only %s are allowed" , name , validContainerNameChars )
2013-10-28 19:58:59 -04:00
}
if name [ 0 ] != '/' {
name = "/" + name
}
2014-05-23 20:51:16 -04:00
2015-09-03 20:51:04 -04:00
if err := daemon . nameIndex . Reserve ( name , id ) ; err != nil {
if err == registrar . ErrNameReserved {
id , err := daemon . nameIndex . Get ( name )
if err != nil {
logrus . Errorf ( "got unexpected error while looking up reserved name: %v" , err )
return "" , err
}
return "" , fmt . Errorf ( "Conflict. The name %q is already in use by container %s. You have to remove (or rename) that container to be able to reuse that name." , name , id )
2013-10-30 14:24:50 -04:00
}
2015-09-03 20:51:04 -04:00
return "" , fmt . Errorf ( "error reserving name: %s, error: %v" , name , err )
2013-10-04 22:25:15 -04:00
}
2014-05-23 20:51:16 -04:00
return name , nil
}
2015-09-03 20:51:04 -04:00
func ( daemon * Daemon ) releaseName ( name string ) {
daemon . nameIndex . Release ( name )
}
2014-05-23 20:51:16 -04:00
func ( daemon * Daemon ) generateNewName ( id string ) ( string , error ) {
var name string
2014-05-30 15:08:21 -04:00
for i := 0 ; i < 6 ; i ++ {
2014-05-23 20:51:16 -04:00
name = namesgenerator . GetRandomName ( i )
if name [ 0 ] != '/' {
name = "/" + name
}
2015-09-03 20:51:04 -04:00
if err := daemon . nameIndex . Reserve ( name , id ) ; err != nil {
if err == registrar . ErrNameReserved {
continue
2014-05-23 20:51:16 -04:00
}
2015-09-03 20:51:04 -04:00
return "" , err
2014-05-23 20:51:16 -04:00
}
return name , nil
}
2015-03-24 07:25:26 -04:00
name = "/" + stringid . TruncateID ( id )
2015-09-03 20:51:04 -04:00
if err := daemon . nameIndex . Reserve ( name , id ) ; err != nil {
2014-05-23 20:51:16 -04:00
return "" , err
}
return name , nil
2014-04-07 15:20:23 -04:00
}
2013-10-04 22:25:15 -04:00
2015-12-18 13:36:17 -05:00
func ( daemon * Daemon ) generateHostname ( id string , config * containertypes . Config ) {
2013-09-06 20:33:05 -04:00
// Generate default hostname
if config . Hostname == "" {
config . Hostname = id [ : 12 ]
}
2014-04-07 15:20:23 -04:00
}
2013-09-06 20:33:05 -04:00
2016-02-29 06:28:37 -05:00
func ( daemon * Daemon ) getEntrypointAndArgs ( configEntrypoint strslice . StrSlice , configCmd strslice . StrSlice ) ( string , [ ] string ) {
if len ( configEntrypoint ) != 0 {
return configEntrypoint [ 0 ] , append ( configEntrypoint [ 1 : ] , configCmd ... )
2013-09-06 20:33:05 -04:00
}
2016-02-29 06:28:37 -05:00
return configCmd [ 0 ] , configCmd [ 1 : ]
2014-04-07 15:20:23 -04:00
}
2015-12-18 13:36:17 -05:00
func ( daemon * Daemon ) newContainer ( name string , config * containertypes . Config , imgID image . ID ) ( * container . Container , error ) {
2014-09-30 15:10:03 -04:00
var (
2015-10-21 12:08:19 -04:00
id string
err error
noExplicitName = name == ""
2014-04-07 15:20:23 -04:00
)
2015-09-29 13:51:40 -04:00
id , name , err = daemon . generateIDAndName ( name )
2014-04-07 15:20:23 -04:00
if err != nil {
return nil , err
}
2014-04-17 17:43:01 -04:00
daemon . generateHostname ( id , config )
2014-09-09 00:19:32 -04:00
entrypoint , args := daemon . getEntrypointAndArgs ( config . Entrypoint , config . Cmd )
2013-09-06 20:33:05 -04:00
2015-06-03 12:26:41 -04:00
base := daemon . newBaseContainer ( id )
base . Created = time . Now ( ) . UTC ( )
base . Path = entrypoint
base . Args = args //FIXME: de-duplicate from config
base . Config = config
2015-12-18 13:36:17 -05:00
base . HostConfig = & containertypes . HostConfig { }
2015-06-03 12:26:41 -04:00
base . ImageID = imgID
2015-10-21 12:08:19 -04:00
base . NetworkSettings = & network . Settings { IsAnonymousEndpoint : noExplicitName }
2015-06-03 12:26:41 -04:00
base . Name = name
2015-12-16 15:32:16 -05:00
base . Driver = daemon . GraphDriverName ( )
2015-06-03 12:26:41 -04:00
2015-10-27 12:05:25 -04:00
return base , err
2014-04-07 15:20:23 -04:00
}
2015-07-30 17:01:53 -04:00
// GetByName returns a container given a name.
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) GetByName ( name string ) ( * container . Container , error ) {
2015-09-03 20:51:04 -04:00
fullName := name
if name [ 0 ] != '/' {
fullName = "/" + name
2013-11-04 12:28:40 -05:00
}
2015-09-03 20:51:04 -04:00
id , err := daemon . nameIndex . Get ( fullName )
if err != nil {
2013-10-04 22:25:15 -04:00
return nil , fmt . Errorf ( "Could not find entity for %s" , name )
}
2015-09-03 20:51:04 -04:00
e := daemon . containers . Get ( id )
2013-10-04 22:25:15 -04:00
if e == nil {
2015-09-03 20:51:04 -04:00
return nil , fmt . Errorf ( "Could not find container for entity id %s" , id )
2013-10-04 22:25:15 -04:00
}
2014-05-30 04:55:25 -04:00
return e , nil
2013-10-04 22:25:15 -04:00
}
2015-09-03 20:51:04 -04:00
// GetLabels for a container or image id
func ( daemon * Daemon ) GetLabels ( id string ) map [ string ] string {
// TODO: TestCase
container := daemon . containers . Get ( id )
if container != nil {
return container . Config . Labels
2013-11-04 12:28:40 -05:00
}
2013-10-04 22:25:15 -04:00
2015-09-03 20:51:04 -04:00
img , err := daemon . GetImage ( id )
if err == nil {
return img . ContainerConfig . Labels
2013-10-04 22:25:15 -04:00
}
2015-09-03 20:51:04 -04:00
return nil
}
func ( daemon * Daemon ) children ( c * container . Container ) map [ string ] * container . Container {
return daemon . linkIndex . children ( c )
2013-10-04 22:25:15 -04:00
}
2015-07-30 17:01:53 -04:00
// parents returns the names of the parent containers of the container
// with the given name.
2015-09-03 20:51:04 -04:00
func ( daemon * Daemon ) parents ( c * container . Container ) map [ string ] * container . Container {
return daemon . linkIndex . parents ( c )
2014-07-14 19:19:37 -04:00
}
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) registerLink ( parent , child * container . Container , alias string ) error {
2015-09-03 20:51:04 -04:00
fullName := path . Join ( parent . Name , alias )
if err := daemon . nameIndex . Reserve ( fullName , child . ID ) ; err != nil {
2016-01-20 16:24:16 -05:00
if err == registrar . ErrNameReserved {
logrus . Warnf ( "error registering link for %s, to %s, as alias %s, ignoring: %v" , parent . ID , child . ID , alias , err )
return nil
}
2013-10-04 22:25:15 -04:00
return err
}
2015-09-03 20:51:04 -04:00
daemon . linkIndex . link ( parent , child , fullName )
2013-10-28 19:58:59 -04:00
return nil
2013-10-04 22:25:15 -04:00
}
2015-07-30 17:01:53 -04:00
// NewDaemon sets up everything for the daemon to be able to service
// requests from the webserver.
2016-03-18 14:50:19 -04:00
func NewDaemon ( config * Config , registryService * registry . Service , containerdRemote libcontainerd . Remote ) ( daemon * Daemon , err error ) {
2015-06-15 19:33:02 -04:00
setDefaultMtu ( config )
2016-01-22 21:15:09 -05:00
// Ensure we have compatible and valid configuration options
if err := verifyDaemonSettings ( config ) ; err != nil {
2015-05-15 19:34:26 -04:00
return nil , err
2014-09-16 23:00:15 -04:00
}
2015-05-15 19:34:26 -04:00
// Do we have a disabled network?
2015-06-30 13:34:15 -04:00
config . DisableBridge = isBridgeNetworkDisabled ( config )
2014-08-09 21:18:32 -04:00
2015-07-11 15:32:08 -04:00
// Verify the platform is supported as a daemon
2015-08-07 12:33:29 -04:00
if ! platformSupported {
2015-07-30 17:01:53 -04:00
return nil , errSystemNotSupported
2015-07-11 15:32:08 -04:00
}
// Validate platform-specific requirements
2015-05-15 19:34:26 -04:00
if err := checkSystem ( ) ; err != nil {
2014-09-16 13:42:59 -04:00
return nil , err
2014-07-30 02:51:43 -04:00
}
2015-07-06 21:58:53 -04:00
// set up SIGUSR1 handler on Unix-like systems, or a Win32 global event
// on Windows to dump Go routine stacks
setupDumpStackTrap ( )
2015-04-21 00:24:24 -04:00
2015-10-08 11:51:41 -04:00
uidMaps , gidMaps , err := setupRemappedRoot ( config )
if err != nil {
return nil , err
}
rootUID , rootGID , err := idtools . GetRootUIDGID ( uidMaps , gidMaps )
if err != nil {
return nil , err
}
2014-07-30 02:51:03 -04:00
// get the canonical path to the Docker root directory
var realRoot string
if _ , err := os . Stat ( config . Root ) ; err != nil && os . IsNotExist ( err ) {
realRoot = config . Root
} else {
2015-03-29 17:17:23 -04:00
realRoot , err = fileutils . ReadSymlinkedDirectory ( config . Root )
2014-07-30 02:51:03 -04:00
if err != nil {
2014-09-16 13:42:59 -04:00
return nil , fmt . Errorf ( "Unable to get the full path to root (%s): %s" , config . Root , err )
2014-07-30 02:51:03 -04:00
}
}
2015-10-08 11:51:41 -04:00
if err = setupDaemonRoot ( config , realRoot , rootUID , rootGID ) ; err != nil {
2014-05-09 21:05:54 -04:00
return nil , err
}
2015-06-23 08:53:18 -04:00
// set up the tmpDir to use a canonical path
2015-10-08 11:51:41 -04:00
tmp , err := tempDir ( config . Root , rootUID , rootGID )
2015-06-23 08:53:18 -04:00
if err != nil {
return nil , fmt . Errorf ( "Unable to get the TempDir under %s: %s" , config . Root , err )
}
realTmp , err := fileutils . ReadSymlinkedDirectory ( tmp )
if err != nil {
return nil , fmt . Errorf ( "Unable to get the full path to the TempDir (%s): %s" , tmp , err )
}
os . Setenv ( "TMPDIR" , realTmp )
2016-03-18 14:50:19 -04:00
d := & Daemon { configStore : config }
2015-12-16 15:32:16 -05:00
// Ensure the daemon is properly shutdown if there is a failure during
// initialization
2015-04-27 17:11:29 -04:00
defer func ( ) {
if err != nil {
2015-09-29 13:51:40 -04:00
if err := d . Shutdown ( ) ; err != nil {
2015-04-27 17:11:29 -04:00
logrus . Error ( err )
}
2015-03-11 10:33:06 -04:00
}
2015-04-27 17:11:29 -04:00
} ( )
2013-11-07 15:34:01 -05:00
2016-03-18 14:50:19 -04:00
// Set the default isolation mode (only applicable on Windows)
if err := d . setDefaultIsolation ( ) ; err != nil {
return nil , fmt . Errorf ( "error setting default isolation mode: %v" , err )
}
2015-04-09 00:23:30 -04:00
// Verify logging driver type
if config . LogConfig . Type != "none" {
if _ , err := logger . GetLogDriver ( config . LogConfig . Type ) ; err != nil {
return nil , fmt . Errorf ( "error finding the logging driver: %v" , err )
}
}
logrus . Debugf ( "Using default logging driver %s" , config . LogConfig . Type )
2015-12-02 05:26:30 -05:00
if err := configureMaxThreads ( config ) ; err != nil {
logrus . Warnf ( "Failed to configure golang's threads limit: %v" , err )
}
2016-03-18 14:50:19 -04:00
installDefaultAppArmorProfile ( )
2015-05-15 19:34:26 -04:00
daemonRepo := filepath . Join ( config . Root , "containers" )
2015-10-08 11:51:41 -04:00
if err := idtools . MkdirAllAs ( daemonRepo , 0700 , rootUID , rootGID ) ; err != nil && ! os . IsExist ( err ) {
2013-03-13 21:48:50 -04:00
return nil , err
}
2015-12-16 15:32:16 -05:00
driverName := os . Getenv ( "DOCKER_DRIVER" )
if driverName == "" {
driverName = config . GraphDriver
}
d . layerStore , err = layer . NewStoreFromOptions ( layer . StoreOptions {
StorePath : config . Root ,
2015-12-30 01:18:12 -05:00
MetadataStorePathTemplate : filepath . Join ( config . Root , "image" , "%s" , "layerdb" ) ,
2015-12-16 15:32:16 -05:00
GraphDriver : driverName ,
GraphDriverOptions : config . GraphOptions ,
UIDMaps : uidMaps ,
GIDMaps : gidMaps ,
} )
2015-11-18 17:20:54 -05:00
if err != nil {
return nil , err
}
2015-12-16 15:32:16 -05:00
graphDriver := d . layerStore . DriverName ( )
imageRoot := filepath . Join ( config . Root , "image" , graphDriver )
// Configure and validate the kernels security support
if err := configureKernelSecuritySupport ( config , graphDriver ) ; err != nil {
2015-11-18 17:20:54 -05:00
return nil , err
}
2015-11-13 19:59:01 -05:00
d . downloadManager = xfer . NewLayerDownloadManager ( d . layerStore , maxDownloadConcurrency )
d . uploadManager = xfer . NewLayerUploadManager ( maxUploadConcurrency )
2015-11-18 17:20:54 -05:00
ifs , err := image . NewFSStoreBackend ( filepath . Join ( imageRoot , "imagedb" ) )
if err != nil {
return nil , err
}
d . imageStore , err = image . NewImageStore ( ifs , d . layerStore )
2013-02-26 20:45:46 -05:00
if err != nil {
return nil , err
}
2013-11-15 05:30:28 -05:00
2015-05-15 19:34:26 -04:00
// Configure the volumes driver
2015-10-08 11:51:41 -04:00
volStore , err := configureVolumes ( config , rootUID , rootGID )
2015-06-12 09:25:32 -04:00
if err != nil {
2013-04-05 21:00:10 -04:00
return nil , err
}
2014-08-28 10:18:08 -04:00
2015-01-07 17:59:12 -05:00
trustKey , err := api . LoadOrCreateTrustKey ( config . TrustKeyPath )
if err != nil {
return nil , err
}
2015-05-15 19:34:26 -04:00
trustDir := filepath . Join ( config . Root , "trust" )
Simplify and fix os.MkdirAll() usage
TL;DR: check for IsExist(err) after a failed MkdirAll() is both
redundant and wrong -- so two reasons to remove it.
Quoting MkdirAll documentation:
> MkdirAll creates a directory named path, along with any necessary
> parents, and returns nil, or else returns an error. If path
> is already a directory, MkdirAll does nothing and returns nil.
This means two things:
1. If a directory to be created already exists, no error is returned.
2. If the error returned is IsExist (EEXIST), it means there exists
a non-directory with the same name as MkdirAll need to use for
directory. Example: we want to MkdirAll("a/b"), but file "a"
(or "a/b") already exists, so MkdirAll fails.
The above is a theory, based on quoted documentation and my UNIX
knowledge.
3. In practice, though, current MkdirAll implementation [1] returns
ENOTDIR in most of cases described in #2, with the exception when
there is a race between MkdirAll and someone else creating the
last component of MkdirAll argument as a file. In this very case
MkdirAll() will indeed return EEXIST.
Because of #1, IsExist check after MkdirAll is not needed.
Because of #2 and #3, ignoring IsExist error is just plain wrong,
as directory we require is not created. It's cleaner to report
the error now.
Note this error is all over the tree, I guess due to copy-paste,
or trying to follow the same usage pattern as for Mkdir(),
or some not quite correct examples on the Internet.
[v2: a separate aufs commit is merged into this one]
[1] https://github.com/golang/go/blob/f9ed2f75/src/os/path.go
Signed-off-by: Kir Kolyshkin <kir@openvz.org>
2015-07-29 19:49:05 -04:00
if err := system . MkdirAll ( trustDir , 0700 ) ; err != nil {
2014-10-01 21:26:06 -04:00
return nil , err
}
2015-11-18 17:20:54 -05:00
distributionMetadataStore , err := dmetadata . NewFSMetadataStore ( filepath . Join ( imageRoot , "distribution" ) )
if err != nil {
return nil , err
}
2015-04-20 15:48:33 -04:00
eventsService := events . New ( )
2015-11-18 17:20:54 -05:00
2015-12-04 16:55:15 -05:00
referenceStore , err := reference . NewReferenceStore ( filepath . Join ( imageRoot , "repositories.json" ) )
2015-04-20 15:48:33 -04:00
if err != nil {
2015-11-18 17:20:54 -05:00
return nil , fmt . Errorf ( "Couldn't create Tag store repositories: %s" , err )
2015-04-20 15:48:33 -04:00
}
2015-12-16 15:32:16 -05:00
if err := restoreCustomImage ( d . imageStore , d . layerStore , referenceStore ) ; err != nil {
2015-11-18 17:20:54 -05:00
return nil , fmt . Errorf ( "Couldn't restore custom images: %s" , err )
}
2015-11-29 22:55:22 -05:00
migrationStart := time . Now ( )
2015-12-16 15:32:16 -05:00
if err := v1 . Migrate ( config . Root , graphDriver , d . layerStore , d . imageStore , referenceStore , distributionMetadataStore ) ; err != nil {
2016-02-09 12:44:33 -05:00
logrus . Errorf ( "Graph migration failed: %q. Your old graph data was found to be too inconsistent for upgrading to content-addressable storage. Some of the old data was probably not upgraded. We recommend starting over with a clean storage directory if possible." , err )
2015-07-24 20:49:43 -04:00
}
2015-11-29 22:55:22 -05:00
logrus . Infof ( "Graph migration to content-addressability took %.2f seconds" , time . Since ( migrationStart ) . Seconds ( ) )
2015-07-24 20:49:43 -04:00
2015-09-21 08:04:36 -04:00
// Discovery is only enabled when the daemon is launched with an address to advertise. When
// initialized, the daemon is registered and we can store the discovery backend as its read-only
2015-12-10 18:35:10 -05:00
if err := d . initDiscovery ( config ) ; err != nil {
return nil , err
2015-09-21 08:04:36 -04:00
}
d . netController , err = d . initNetworkController ( config )
2015-06-30 13:34:15 -04:00
if err != nil {
return nil , fmt . Errorf ( "Error initializing network controller: %v" , err )
2013-04-04 08:33:28 -04:00
}
2013-10-04 22:25:15 -04:00
2014-03-05 04:40:55 -05:00
sysInfo := sysinfo . New ( false )
2015-06-19 18:29:47 -04:00
// Check if Devices cgroup is mounted, it is hard requirement for container security,
// on Linux/FreeBSD.
if runtime . GOOS != "windows" && ! sysInfo . CgroupDevicesEnabled {
2015-06-16 22:36:20 -04:00
return nil , fmt . Errorf ( "Devices cgroup isn't mounted" )
}
2015-04-27 17:11:29 -04:00
d . ID = trustKey . PublicKey ( ) . KeyID ( )
d . repository = daemonRepo
2016-01-15 18:55:46 -05:00
d . containers = container . NewMemoryStore ( )
2015-11-20 17:35:16 -05:00
d . execCommands = exec . NewStore ( )
2015-12-04 16:55:15 -05:00
d . referenceStore = referenceStore
2015-11-18 17:20:54 -05:00
d . distributionMetadataStore = distributionMetadataStore
d . trustKey = trustKey
2015-04-27 17:11:29 -04:00
d . idIndex = truncindex . NewTruncIndex ( [ ] string { } )
2015-11-03 14:06:16 -05:00
d . statsCollector = d . newStatsCollector ( 1 * time . Second )
2015-12-10 18:35:10 -05:00
d . defaultLogConfig = containertypes . LogConfig {
Type : config . LogConfig . Type ,
Config : config . LogConfig . Config ,
}
2015-04-27 17:11:29 -04:00
d . RegistryService = registryService
d . EventsService = eventsService
2015-06-12 09:25:32 -04:00
d . volumes = volStore
2015-05-19 16:05:25 -04:00
d . root = config . Root
2015-10-08 11:51:41 -04:00
d . uidMaps = uidMaps
d . gidMaps = gidMaps
2016-01-11 14:44:34 -05:00
d . seccompEnabled = sysInfo . Seccomp
2015-04-27 17:11:29 -04:00
2015-09-03 20:51:04 -04:00
d . nameIndex = registrar . NewRegistrar ( )
d . linkIndex = newLinkIndex ( )
2016-03-18 14:50:19 -04:00
go d . execCommandGC ( )
d . containerd , err = containerdRemote . Client ( d )
if err != nil {
2015-03-06 15:44:31 -05:00
return nil , err
}
2015-04-27 17:11:29 -04:00
2015-09-29 13:51:40 -04:00
if err := d . restore ( ) ; err != nil {
2015-03-06 15:44:31 -05:00
return nil , err
}
2015-05-06 18:39:29 -04:00
return d , nil
}
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) shutdownContainer ( c * container . Container ) error {
2015-10-29 18:11:35 -04:00
// TODO(windows): Handle docker restart with paused containers
2015-11-12 14:55:17 -05:00
if c . IsPaused ( ) {
2015-10-29 18:11:35 -04:00
// To terminate a process in freezer cgroup, we should send
// SIGTERM to this process then unfreeze it, and the process will
// force to terminate immediately.
logrus . Debugf ( "Found container %s is paused, sending SIGTERM before unpause it" , c . ID )
sig , ok := signal . SignalMap [ "TERM" ]
if ! ok {
return fmt . Errorf ( "System doesn not support SIGTERM" )
}
2015-11-02 18:39:39 -05:00
if err := daemon . kill ( c , int ( sig ) ) ; err != nil {
2015-10-29 18:11:35 -04:00
return fmt . Errorf ( "sending SIGTERM to container %s with error: %v" , c . ID , err )
}
2015-11-02 18:39:39 -05:00
if err := daemon . containerUnpause ( c ) ; err != nil {
2015-10-29 18:11:35 -04:00
return fmt . Errorf ( "Failed to unpause container %s with error: %v" , c . ID , err )
}
if _ , err := c . WaitStop ( 10 * time . Second ) ; err != nil {
logrus . Debugf ( "container %s failed to exit in 10 second of SIGTERM, sending SIGKILL to force" , c . ID )
sig , ok := signal . SignalMap [ "KILL" ]
if ! ok {
return fmt . Errorf ( "System does not support SIGKILL" )
}
2015-11-02 18:39:39 -05:00
if err := daemon . kill ( c , int ( sig ) ) ; err != nil {
2015-10-29 18:11:35 -04:00
logrus . Errorf ( "Failed to SIGKILL container %s" , c . ID )
}
c . WaitStop ( - 1 * time . Second )
return err
}
}
// If container failed to exit in 10 seconds of SIGTERM, then using the force
2015-11-02 18:25:26 -05:00
if err := daemon . containerStop ( c , 10 ) ; err != nil {
2015-10-29 18:11:35 -04:00
return fmt . Errorf ( "Stop container %s with error: %v" , c . ID , err )
}
c . WaitStop ( - 1 * time . Second )
return nil
}
2015-07-30 17:01:53 -04:00
// Shutdown stops the daemon.
2015-09-29 13:51:40 -04:00
func ( daemon * Daemon ) Shutdown ( ) error {
2015-08-05 17:09:08 -04:00
daemon . shutdown = true
2015-04-27 17:11:29 -04:00
if daemon . containers != nil {
logrus . Debug ( "starting clean shutdown of all containers..." )
2016-01-15 18:55:46 -05:00
daemon . containers . ApplyAll ( func ( c * container . Container ) {
if ! c . IsRunning ( ) {
return
2015-04-27 17:11:29 -04:00
}
2016-01-15 18:55:46 -05:00
logrus . Debugf ( "stopping %s" , c . ID )
if err := daemon . shutdownContainer ( c ) ; err != nil {
logrus . Errorf ( "Stop container error: %v" , err )
return
}
2016-03-18 14:50:19 -04:00
if mountid , err := daemon . layerStore . GetMountID ( c . ID ) ; err == nil {
daemon . cleanupMountsByID ( mountid )
}
2016-01-15 18:55:46 -05:00
logrus . Debugf ( "container stopped %s" , c . ID )
} )
2015-10-29 18:11:35 -04:00
}
2015-06-05 18:02:56 -04:00
2015-10-29 18:11:35 -04:00
// trigger libnetwork Stop only if it's initialized
if daemon . netController != nil {
daemon . netController . Stop ( )
2015-04-27 17:11:29 -04:00
}
2014-03-25 19:21:07 -04:00
2015-12-16 15:32:16 -05:00
if daemon . layerStore != nil {
if err := daemon . layerStore . Cleanup ( ) ; err != nil {
logrus . Errorf ( "Error during layer Store.Cleanup(): %v" , err )
2015-06-10 19:07:53 -04:00
}
}
2015-08-03 18:05:34 -04:00
if err := daemon . cleanupMounts ( ) ; err != nil {
return err
}
2014-03-25 19:21:07 -04:00
return nil
}
2015-11-12 14:55:17 -05:00
// Mount sets container.BaseFS
2015-07-30 17:01:53 -04:00
// (is it not set coming in? why is it unset?)
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) Mount ( container * container . Container ) error {
2015-12-16 17:13:50 -05:00
dir , err := container . RWLayer . Mount ( container . GetMountLabel ( ) )
2015-11-18 17:20:54 -05:00
if err != nil {
return err
}
logrus . Debugf ( "container mounted via layerStore: %v" , dir )
2015-05-15 19:34:26 -04:00
2015-11-12 14:55:17 -05:00
if container . BaseFS != dir {
2015-05-15 19:34:26 -04:00
// The mount path reported by the graph driver should always be trusted on Windows, since the
// volume path for a given mounted layer may change over time. This should only be an error
// on non-Windows operating systems.
2015-11-12 14:55:17 -05:00
if container . BaseFS != "" && runtime . GOOS != "windows" {
2015-11-18 17:20:54 -05:00
daemon . Unmount ( container )
2015-05-15 19:34:26 -04:00
return fmt . Errorf ( "Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')" ,
2015-12-16 15:32:16 -05:00
daemon . GraphDriverName ( ) , container . ID , container . BaseFS , dir )
2015-05-15 19:34:26 -04:00
}
2013-10-31 21:07:54 -04:00
}
2015-11-12 14:55:17 -05:00
container . BaseFS = dir // TODO: combine these fields
2013-11-07 15:34:01 -05:00
return nil
2013-10-31 21:07:54 -04:00
}
2015-11-02 20:06:09 -05:00
// Unmount unsets the container base filesystem
2016-03-18 14:50:19 -04:00
func ( daemon * Daemon ) Unmount ( container * container . Container ) error {
2015-12-16 17:13:50 -05:00
if err := container . RWLayer . Unmount ( ) ; err != nil {
2015-11-18 17:20:54 -05:00
logrus . Errorf ( "Error unmounting container %s: %s" , container . ID , err )
2016-03-18 14:50:19 -04:00
return err
2015-11-18 17:20:54 -05:00
}
2016-03-18 14:50:19 -04:00
return nil
2014-01-10 17:26:29 -05:00
}
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) kill ( c * container . Container , sig int ) error {
2016-03-18 14:50:19 -04:00
return daemon . containerd . Signal ( c . ID , sig )
2015-01-07 17:43:04 -05:00
}
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) subscribeToContainerStats ( c * container . Container ) chan interface { } {
2015-10-28 18:15:22 -04:00
return daemon . statsCollector . collect ( c )
2015-01-07 17:43:04 -05:00
}
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) unsubscribeToContainerStats ( c * container . Container , ch chan interface { } ) {
2015-01-07 21:02:08 -05:00
daemon . statsCollector . unsubscribe ( c , ch )
}
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) changes ( container * container . Container ) ( [ ] archive . Change , error ) {
2015-12-16 17:13:50 -05:00
return container . RWLayer . Changes ( )
2015-07-24 20:49:43 -04:00
}
2015-11-13 19:59:01 -05:00
func writeDistributionProgress ( cancelFunc func ( ) , outStream io . Writer , progressChan <- chan progress . Progress ) {
progressOutput := streamformatter . NewJSONStreamFormatter ( ) . NewProgressOutput ( outStream , false )
operationCancelled := false
for prog := range progressChan {
if err := progressOutput . WriteProgress ( prog ) ; err != nil && ! operationCancelled {
2015-12-17 12:35:24 -05:00
// don't log broken pipe errors as this is the normal case when a client aborts
if isBrokenPipe ( err ) {
logrus . Info ( "Pull session cancelled" )
} else {
logrus . Errorf ( "error writing progress to client: %v" , err )
}
2015-11-13 19:59:01 -05:00
cancelFunc ( )
operationCancelled = true
// Don't return, because we need to continue draining
// progressChan until it's closed to avoid a deadlock.
}
}
}
2015-12-17 12:35:24 -05:00
func isBrokenPipe ( e error ) bool {
if netErr , ok := e . ( * net . OpError ) ; ok {
e = netErr . Err
if sysErr , ok := netErr . Err . ( * os . SyscallError ) ; ok {
e = sysErr . Err
}
}
return e == syscall . EPIPE
}
2015-09-28 18:00:45 -04:00
// 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 {
2015-12-04 16:55:15 -05:00
imageExporter := tarexport . NewTarExporter ( daemon . imageStore , daemon . layerStore , daemon . referenceStore )
2015-11-18 17:20:54 -05:00
return imageExporter . Save ( names , outStream )
2015-09-28 18:00:45 -04:00
}
// LookupImage looks up an image by name and returns it as an ImageInspect
// structure.
func ( daemon * Daemon ) LookupImage ( name string ) ( * types . ImageInspect , error ) {
2015-11-18 17:20:54 -05:00
img , err := daemon . GetImage ( name )
if err != nil {
return nil , fmt . Errorf ( "No such image: %s" , name )
}
2015-12-04 16:55:15 -05:00
refs := daemon . referenceStore . References ( img . ID ( ) )
2015-11-18 17:20:54 -05:00
repoTags := [ ] string { }
repoDigests := [ ] string { }
for _ , ref := range refs {
switch ref . ( type ) {
2015-12-04 16:55:15 -05:00
case reference . NamedTagged :
2015-11-18 17:20:54 -05:00
repoTags = append ( repoTags , ref . String ( ) )
2015-12-04 16:55:15 -05:00
case reference . Canonical :
2015-11-18 17:20:54 -05:00
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
}
}
2015-12-30 14:52:53 -05:00
comment := img . Comment
if len ( comment ) == 0 && len ( img . History ) > 0 {
comment = img . History [ len ( img . History ) - 1 ] . Comment
}
2015-11-18 17:20:54 -05:00
imageInspect := & types . ImageInspect {
ID : img . ID ( ) . String ( ) ,
RepoTags : repoTags ,
RepoDigests : repoDigests ,
Parent : img . Parent . String ( ) ,
2015-12-30 14:52:53 -05:00
Comment : comment ,
2015-11-18 17:20:54 -05:00
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
2016-03-21 12:56:51 -04:00
RootFS : rootFSToAPIType ( img . RootFS ) ,
2015-11-18 17:20:54 -05:00
}
2015-12-16 15:32:16 -05:00
imageInspect . GraphDriver . Name = daemon . GraphDriverName ( )
2015-11-18 17:20:54 -05:00
imageInspect . GraphDriver . Data = layerMetadata
return imageInspect , nil
2015-09-28 18:00:45 -04:00
}
// 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.
2016-02-03 21:31:47 -05:00
func ( daemon * Daemon ) LoadImage ( inTar io . ReadCloser , outStream io . Writer , quiet bool ) error {
2015-12-04 16:55:15 -05:00
imageExporter := tarexport . NewTarExporter ( daemon . imageStore , daemon . layerStore , daemon . referenceStore )
2016-02-03 21:31:47 -05:00
return imageExporter . Load ( inTar , outStream , quiet )
2015-09-28 18:00:45 -04:00
}
// 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 ) {
2015-11-18 17:20:54 -05:00
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 {
Remove static errors from errors package.
Moving all strings to the errors package wasn't a good idea after all.
Our custom implementation of Go errors predates everything that's nice
and good about working with errors in Go. Take as an example what we
have to do to get an error message:
```go
func GetErrorMessage(err error) string {
switch err.(type) {
case errcode.Error:
e, _ := err.(errcode.Error)
return e.Message
case errcode.ErrorCode:
ec, _ := err.(errcode.ErrorCode)
return ec.Message()
default:
return err.Error()
}
}
```
This goes against every good practice for Go development. The language already provides a simple, intuitive and standard way to get error messages, that is calling the `Error()` method from an error. Reinventing the error interface is a mistake.
Our custom implementation also makes very hard to reason about errors, another nice thing about Go. I found several (>10) error declarations that we don't use anywhere. This is a clear sign about how little we know about the errors we return. I also found several error usages where the number of arguments was different than the parameters declared in the error, another clear example of how difficult is to reason about errors.
Moreover, our custom implementation didn't really make easier for people to return custom HTTP status code depending on the errors. Again, it's hard to reason about when to set custom codes and how. Take an example what we have to do to extract the message and status code from an error before returning a response from the API:
```go
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
```
You can notice two things in that code:
1. We have to explain how errors work, because our implementation goes against how easy to use Go errors are.
2. At no moment we arrived to remove that `switch` statement that was the original reason to use our custom implementation.
This change removes all our status errors from the errors package and puts them back in their specific contexts.
IT puts the messages back with their contexts. That way, we know right away when errors used and how to generate their messages.
It uses custom interfaces to reason about errors. Errors that need to response with a custom status code MUST implementent this simple interface:
```go
type errorWithStatus interface {
HTTPErrorStatusCode() int
}
```
This interface is very straightforward to implement. It also preserves Go errors real behavior, getting the message is as simple as using the `Error()` method.
I included helper functions to generate errors that use custom status code in `errors/errors.go`.
By doing this, we remove the hard dependency we have eeverywhere to our custom errors package. Yes, you can use it as a helper to generate error, but it's still very easy to generate errors without it.
Please, read this fantastic blog post about errors in Go: http://dave.cheney.net/2014/12/24/inspecting-errors
Signed-off-by: David Calavera <david.calavera@gmail.com>
2016-02-25 10:53:35 -05:00
return nil , fmt . Errorf ( "too many non-empty layers in History section" )
2015-11-18 17:20:54 -05:00
}
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
2015-12-04 16:55:15 -05:00
for _ , r := range daemon . referenceStore . References ( id ) {
2015-11-18 17:20:54 -05:00
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
2015-09-28 18:00:45 -04:00
}
2015-11-18 17:20:54 -05:00
// GetImageID returns an image ID corresponding to the image referred to by
// refOrID.
func ( daemon * Daemon ) GetImageID ( refOrID string ) ( image . ID , error ) {
2016-03-07 13:56:24 -05:00
id , ref , err := reference . ParseIDOrReference ( refOrID )
if err != nil {
return "" , err
}
if id != "" {
2016-01-25 14:45:20 -05:00
if _ , err := daemon . imageStore . Get ( image . ID ( id ) ) ; err != nil {
return "" , ErrImageDoesNotExist { refOrID }
}
2015-11-18 17:20:54 -05:00
return image . ID ( id ) , nil
}
2016-03-07 13:56:24 -05:00
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
2015-11-18 17:20:54 -05:00
}
}
}
}
// 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 )
2015-10-01 17:51:36 -04:00
}
2016-01-20 18:32:02 -05:00
// 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
}
2015-12-16 15:32:16 -05:00
// GraphDriverName returns the name of the graph driver used by the layer.Store
func ( daemon * Daemon ) GraphDriverName ( ) string {
return daemon . layerStore . DriverName ( )
2014-03-07 21:42:29 -05:00
}
2015-10-08 11:51:41 -04:00
// GetUIDGIDMaps returns the current daemon's user namespace settings
// for the full uid and gid maps which will be applied to containers
// started in this instance.
func ( daemon * Daemon ) GetUIDGIDMaps ( ) ( [ ] idtools . IDMap , [ ] idtools . IDMap ) {
return daemon . uidMaps , daemon . gidMaps
}
// GetRemappedUIDGID returns the current daemon's uid and gid values
// if user namespaces are in use for this daemon instance. If not
// this function will return "real" root values of 0, 0.
func ( daemon * Daemon ) GetRemappedUIDGID ( ) ( int , int ) {
uid , gid , _ := idtools . GetRootUIDGID ( daemon . uidMaps , daemon . gidMaps )
return uid , gid
}
2016-01-20 18:32:02 -05:00
// GetCachedImage returns the most recent created image that is a child
2015-07-30 17:01:53 -04:00
// 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.
2016-01-20 18:32:02 -05:00
func ( daemon * Daemon ) GetCachedImage ( imgID image . ID , config * containertypes . Config ) ( * image . Image , error ) {
2016-01-25 23:37:18 -05:00
// 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 )
}
2014-07-29 01:22:58 -04:00
2016-01-25 23:37:18 -05:00
if runconfig . Compare ( & img . ContainerConfig , config ) {
// check for the most up to date match
if match == nil || match . Created . Before ( img . Created ) {
match = img
}
}
2014-07-29 01:22:58 -04:00
}
2016-01-25 23:37:18 -05:00
return match , nil
2014-07-29 01:22:58 -04:00
}
2016-01-25 23:37:18 -05:00
// 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 )
2014-07-29 01:22:58 -04:00
}
}
2016-01-25 23:37:18 -05:00
return getMatch ( siblings )
2014-07-29 01:22:58 -04:00
}
2016-01-25 23:37:18 -05:00
// find match from child images
siblings := daemon . imageStore . Children ( imgID )
return getMatch ( siblings )
2014-07-29 01:22:58 -04:00
}
2014-07-30 02:51:43 -04:00
2016-01-20 18:32:02 -05:00
// 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
}
2015-03-29 14:51:17 -04:00
// tempDir returns the default directory to use for temporary files.
2015-10-08 11:51:41 -04:00
func tempDir ( rootDir string , rootUID , rootGID int ) ( string , error ) {
2015-03-29 14:51:17 -04:00
var tmpDir string
if tmpDir = os . Getenv ( "DOCKER_TMPDIR" ) ; tmpDir == "" {
tmpDir = filepath . Join ( rootDir , "tmp" )
}
2015-10-08 11:51:41 -04:00
return tmpDir , idtools . MkdirAllAs ( tmpDir , 0700 , rootUID , rootGID )
2015-04-16 02:31:52 -04:00
}
2015-04-22 22:23:02 -04:00
2015-12-21 14:23:20 -05:00
func ( daemon * Daemon ) setSecurityOptions ( container * container . Container , hostConfig * containertypes . HostConfig ) error {
2015-11-18 17:20:54 -05:00
container . Lock ( )
2015-12-21 14:23:20 -05:00
defer container . Unlock ( )
return parseSecurityOpt ( container , hostConfig )
}
2015-05-19 16:05:25 -04:00
2015-12-21 14:23:20 -05:00
func ( daemon * Daemon ) setHostConfig ( container * container . Container , hostConfig * containertypes . HostConfig ) error {
2015-05-27 15:29:49 -04:00
// Do not lock while creating volumes since this could be calling out to external plugins
// Don't want to block other actions, like `docker ps` because we're waiting on an external plugin
2015-09-29 13:51:40 -04:00
if err := daemon . registerMountPoints ( container , hostConfig ) ; err != nil {
2015-04-22 22:23:02 -04:00
return err
}
2015-05-27 15:29:49 -04:00
container . Lock ( )
defer container . Unlock ( )
2015-06-11 20:34:20 -04:00
2015-04-22 22:23:02 -04:00
// Register any links from the host config before starting the container
2015-09-29 13:51:40 -04:00
if err := daemon . registerLinks ( container , hostConfig ) ; err != nil {
2015-04-22 22:23:02 -04:00
return err
}
2015-11-21 13:45:34 -05:00
// make sure links is not nil
// this ensures that on the next daemon restart we don't try to migrate from legacy sqlite links
if hostConfig . Links == nil {
hostConfig . Links = [ ] string { }
}
2015-11-12 14:55:17 -05:00
container . HostConfig = hostConfig
2015-09-03 20:51:04 -04:00
return container . ToDisk ( )
2015-04-22 22:23:02 -04:00
}
2015-06-03 12:26:41 -04:00
2015-11-18 17:20:54 -05:00
func ( daemon * Daemon ) setupInitLayer ( initPath string ) error {
rootUID , rootGID := daemon . GetRemappedUIDGID ( )
return setupInitLayer ( initPath , rootUID , rootGID )
}
2015-06-15 19:33:02 -04:00
func setDefaultMtu ( config * Config ) {
// do nothing if the config does not have the default 0 value.
if config . Mtu != 0 {
return
}
config . Mtu = defaultNetworkMtu
}
2015-07-30 18:28:11 -04:00
// verifyContainerSettings performs validation of the hostconfig and config
// structures.
2016-02-24 00:36:47 -05:00
func ( daemon * Daemon ) verifyContainerSettings ( hostConfig * containertypes . HostConfig , config * containertypes . Config , update bool ) ( [ ] string , error ) {
2015-07-30 18:28:11 -04:00
// First perform verification of settings common across all platforms.
if config != nil {
2015-09-01 21:50:41 -04:00
if config . WorkingDir != "" {
config . WorkingDir = filepath . FromSlash ( config . WorkingDir ) // Ensure in platform semantics
if ! system . IsAbs ( config . WorkingDir ) {
2016-03-21 20:53:57 -04:00
return nil , fmt . Errorf ( "The working directory '%s' is invalid. It needs to be an absolute path" , config . WorkingDir )
2015-09-01 21:50:41 -04:00
}
2015-07-30 18:28:11 -04:00
}
2015-08-18 13:30:44 -04:00
if len ( config . StopSignal ) > 0 {
_ , err := signal . ParseSignal ( config . StopSignal )
if err != nil {
return nil , err
}
}
2015-07-30 18:28:11 -04:00
}
if hostConfig == nil {
return nil , nil
}
2016-03-02 07:22:18 -05:00
logCfg := daemon . getLogConfig ( hostConfig . LogConfig )
if err := logger . ValidateLogOpts ( logCfg . Type , logCfg . Config ) ; err != nil {
return nil , err
}
2015-07-30 18:28:11 -04:00
for port := range hostConfig . PortBindings {
_ , portStr := nat . SplitProtoPort ( string ( port ) )
if _ , err := nat . ParsePort ( portStr ) ; err != nil {
return nil , fmt . Errorf ( "Invalid port specification: %q" , portStr )
}
for _ , pb := range hostConfig . PortBindings [ port ] {
_ , err := nat . NewPort ( nat . SplitProtoPort ( pb . HostPort ) )
if err != nil {
return nil , fmt . Errorf ( "Invalid port specification: %q" , pb . HostPort )
}
}
}
// Now do platform-specific verification
2016-02-24 00:36:47 -05:00
return verifyPlatformContainerSettings ( daemon , hostConfig , config , update )
2015-07-30 18:28:11 -04:00
}
2015-09-16 17:18:24 -04:00
2016-01-21 17:24:35 -05:00
// Checks if the client set configurations for more than one network while creating a container
func ( daemon * Daemon ) verifyNetworkingConfig ( nwConfig * networktypes . NetworkingConfig ) error {
if nwConfig == nil || len ( nwConfig . EndpointsConfig ) <= 1 {
return nil
}
l := make ( [ ] string , 0 , len ( nwConfig . EndpointsConfig ) )
for k := range nwConfig . EndpointsConfig {
l = append ( l , k )
}
Remove static errors from errors package.
Moving all strings to the errors package wasn't a good idea after all.
Our custom implementation of Go errors predates everything that's nice
and good about working with errors in Go. Take as an example what we
have to do to get an error message:
```go
func GetErrorMessage(err error) string {
switch err.(type) {
case errcode.Error:
e, _ := err.(errcode.Error)
return e.Message
case errcode.ErrorCode:
ec, _ := err.(errcode.ErrorCode)
return ec.Message()
default:
return err.Error()
}
}
```
This goes against every good practice for Go development. The language already provides a simple, intuitive and standard way to get error messages, that is calling the `Error()` method from an error. Reinventing the error interface is a mistake.
Our custom implementation also makes very hard to reason about errors, another nice thing about Go. I found several (>10) error declarations that we don't use anywhere. This is a clear sign about how little we know about the errors we return. I also found several error usages where the number of arguments was different than the parameters declared in the error, another clear example of how difficult is to reason about errors.
Moreover, our custom implementation didn't really make easier for people to return custom HTTP status code depending on the errors. Again, it's hard to reason about when to set custom codes and how. Take an example what we have to do to extract the message and status code from an error before returning a response from the API:
```go
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
```
You can notice two things in that code:
1. We have to explain how errors work, because our implementation goes against how easy to use Go errors are.
2. At no moment we arrived to remove that `switch` statement that was the original reason to use our custom implementation.
This change removes all our status errors from the errors package and puts them back in their specific contexts.
IT puts the messages back with their contexts. That way, we know right away when errors used and how to generate their messages.
It uses custom interfaces to reason about errors. Errors that need to response with a custom status code MUST implementent this simple interface:
```go
type errorWithStatus interface {
HTTPErrorStatusCode() int
}
```
This interface is very straightforward to implement. It also preserves Go errors real behavior, getting the message is as simple as using the `Error()` method.
I included helper functions to generate errors that use custom status code in `errors/errors.go`.
By doing this, we remove the hard dependency we have eeverywhere to our custom errors package. Yes, you can use it as a helper to generate error, but it's still very easy to generate errors without it.
Please, read this fantastic blog post about errors in Go: http://dave.cheney.net/2014/12/24/inspecting-errors
Signed-off-by: David Calavera <david.calavera@gmail.com>
2016-02-25 10:53:35 -05:00
err := fmt . Errorf ( "Container cannot be connected to network endpoints: %s" , strings . Join ( l , ", " ) )
return errors . NewBadRequestError ( err )
2016-01-21 17:24:35 -05:00
}
2015-10-08 11:51:41 -04:00
func configureVolumes ( config * Config , rootUID , rootGID int ) ( * store . VolumeStore , error ) {
volumesDriver , err := local . New ( config . Root , rootUID , rootGID )
2015-09-16 17:18:24 -04:00
if err != nil {
return nil , err
}
2015-09-18 19:58:05 -04:00
2015-09-16 17:18:24 -04:00
volumedrivers . Register ( volumesDriver , volumesDriver . Name ( ) )
2016-03-16 17:52:34 -04:00
return store . New ( config . Root )
2015-09-16 17:18:24 -04:00
}
2015-10-08 19:16:36 -04:00
// AuthenticateToRegistry checks the validity of credentials in authConfig
2016-03-18 17:42:40 -04:00
func ( daemon * Daemon ) AuthenticateToRegistry ( ctx context . Context , authConfig * types . AuthConfig ) ( string , string , error ) {
return daemon . RegistryService . Auth ( authConfig , dockerversion . DockerUserAgent ( ctx ) )
2015-10-08 19:16:36 -04:00
}
// SearchRegistryForImages queries the registry for images matching
// term. authConfig is used to login.
2016-03-18 17:42:40 -04:00
func ( daemon * Daemon ) SearchRegistryForImages ( ctx context . Context , term string ,
2015-12-11 23:11:42 -05:00
authConfig * types . AuthConfig ,
2015-12-15 11:44:20 -05:00
headers map [ string ] [ ] string ) ( * registrytypes . SearchResults , error ) {
2016-03-18 17:42:40 -04:00
return daemon . RegistryService . Search ( term , authConfig , dockerversion . DockerUserAgent ( ctx ) , headers )
2015-10-08 19:16:36 -04:00
}
2015-11-03 14:06:16 -05:00
2015-11-03 14:25:22 -05:00
// IsShuttingDown tells whether the daemon is shutting down or not
func ( daemon * Daemon ) IsShuttingDown ( ) bool {
return daemon . shutdown
}
// GetContainerStats collects all the stats published by a container
2016-03-18 14:50:19 -04:00
func ( daemon * Daemon ) GetContainerStats ( container * container . Container ) ( * types . StatsJSON , error ) {
2015-11-03 14:06:16 -05:00
stats , err := daemon . stats ( container )
if err != nil {
return nil , err
}
2016-03-19 15:56:45 -04:00
if stats . Networks , err = daemon . getNetworkStats ( container ) ; err != nil {
2015-11-03 14:06:16 -05:00
return nil , err
}
2016-03-18 14:50:19 -04:00
2015-11-03 14:06:16 -05:00
return stats , nil
}
2016-04-09 02:57:04 -04:00
// Resolve Network SandboxID in case the container reuse another container's network stack
func ( daemon * Daemon ) getNetworkSandboxID ( c * container . Container ) ( string , error ) {
curr := c
for curr . HostConfig . NetworkMode . IsContainer ( ) {
containerID := curr . HostConfig . NetworkMode . ConnectedContainer ( )
connected , err := daemon . GetContainer ( containerID )
if err != nil {
return "" , fmt . Errorf ( "Could not get container for %s" , containerID )
}
curr = connected
}
return curr . NetworkSettings . SandboxID , nil
}
2016-03-19 15:56:45 -04:00
func ( daemon * Daemon ) getNetworkStats ( c * container . Container ) ( map [ string ] types . NetworkStats , error ) {
2016-04-09 02:57:04 -04:00
sandboxID , err := daemon . getNetworkSandboxID ( c )
if err != nil {
return nil , err
}
sb , err := daemon . netController . SandboxByID ( sandboxID )
2015-11-03 14:06:16 -05:00
if err != nil {
2016-03-19 15:56:45 -04:00
return nil , err
2015-11-03 14:06:16 -05:00
}
2016-03-19 15:56:45 -04:00
lnstats , err := sb . Statistics ( )
2015-11-03 14:06:16 -05:00
if err != nil {
2016-03-19 15:56:45 -04:00
return nil , err
2015-11-03 14:06:16 -05:00
}
2016-03-19 15:56:45 -04:00
stats := make ( map [ string ] types . NetworkStats )
// Convert libnetwork nw stats into engine-api stats
for ifName , ifStats := range lnstats {
stats [ ifName ] = types . NetworkStats {
RxBytes : ifStats . RxBytes ,
RxPackets : ifStats . RxPackets ,
RxErrors : ifStats . RxErrors ,
RxDropped : ifStats . RxDropped ,
TxBytes : ifStats . TxBytes ,
TxPackets : ifStats . TxPackets ,
TxErrors : ifStats . TxErrors ,
TxDropped : ifStats . TxDropped ,
}
2015-11-03 14:06:16 -05:00
}
2016-03-19 15:56:45 -04:00
return stats , nil
2015-11-03 14:06:16 -05:00
}
2015-11-18 05:04:23 -05:00
// newBaseContainer creates a new container with its initial
// configuration based on the root storage from the daemon.
2015-11-12 14:55:17 -05:00
func ( daemon * Daemon ) newBaseContainer ( id string ) * container . Container {
return container . NewBaseContainer ( id , daemon . containerRoot ( id ) )
2015-11-18 05:04:23 -05:00
}
2015-12-10 18:35:10 -05:00
// initDiscovery initializes the discovery watcher for this daemon.
func ( daemon * Daemon ) initDiscovery ( config * Config ) error {
advertise , err := parseClusterAdvertiseSettings ( config . ClusterStore , config . ClusterAdvertise )
if err != nil {
if err == errDiscoveryDisabled {
return nil
}
return err
}
config . ClusterAdvertise = advertise
discoveryWatcher , err := initDiscovery ( config . ClusterStore , config . ClusterAdvertise , config . ClusterOpts )
if err != nil {
return fmt . Errorf ( "discovery initialization failed (%v)" , err )
}
daemon . discoveryWatcher = discoveryWatcher
return nil
}
// Reload reads configuration changes and modifies the
// daemon according to those changes.
2016-03-28 06:57:55 -04:00
// These are the settings that Reload changes:
2015-12-10 18:35:10 -05:00
// - Daemon labels.
2016-03-28 06:57:55 -04:00
// - Daemon debug log level.
2016-02-17 20:08:11 -05:00
// - Cluster discovery (reconfigure and restart).
2015-12-10 18:35:10 -05:00
func ( daemon * Daemon ) Reload ( config * Config ) error {
daemon . configStore . reloadLock . Lock ( )
2016-02-17 20:08:11 -05:00
defer daemon . configStore . reloadLock . Unlock ( )
2016-02-24 03:13:44 -05:00
if config . IsValueSet ( "label" ) {
daemon . configStore . Labels = config . Labels
}
if config . IsValueSet ( "debug" ) {
daemon . configStore . Debug = config . Debug
}
2016-02-17 20:08:11 -05:00
return daemon . reloadClusterDiscovery ( config )
2015-12-10 18:35:10 -05:00
}
func ( daemon * Daemon ) reloadClusterDiscovery ( config * Config ) error {
2016-02-24 03:13:44 -05:00
var err error
newAdvertise := daemon . configStore . ClusterAdvertise
newClusterStore := daemon . configStore . ClusterStore
if config . IsValueSet ( "cluster-advertise" ) {
if config . IsValueSet ( "cluster-store" ) {
newClusterStore = config . ClusterStore
}
newAdvertise , err = parseClusterAdvertiseSettings ( newClusterStore , config . ClusterAdvertise )
if err != nil && err != errDiscoveryDisabled {
return err
}
2015-12-10 18:35:10 -05:00
}
// check discovery modifications
2016-02-24 03:13:44 -05:00
if ! modifiedDiscoverySettings ( daemon . configStore , newAdvertise , newClusterStore , config . ClusterOpts ) {
2015-12-10 18:35:10 -05:00
return nil
}
// enable discovery for the first time if it was not previously enabled
if daemon . discoveryWatcher == nil {
2016-02-24 03:13:44 -05:00
discoveryWatcher , err := initDiscovery ( newClusterStore , newAdvertise , config . ClusterOpts )
2015-12-10 18:35:10 -05:00
if err != nil {
return fmt . Errorf ( "discovery initialization failed (%v)" , err )
}
daemon . discoveryWatcher = discoveryWatcher
} else {
if err == errDiscoveryDisabled {
// disable discovery if it was previously enabled and it's disabled now
daemon . discoveryWatcher . Stop ( )
} else {
// reload discovery
if err = daemon . discoveryWatcher . Reload ( config . ClusterStore , newAdvertise , config . ClusterOpts ) ; err != nil {
return err
}
}
}
2016-02-24 03:13:44 -05:00
daemon . configStore . ClusterStore = newClusterStore
2015-12-10 18:35:10 -05:00
daemon . configStore . ClusterOpts = config . ClusterOpts
daemon . configStore . ClusterAdvertise = newAdvertise
2016-02-17 20:08:11 -05:00
if daemon . netController == nil {
return nil
}
netOptions , err := daemon . networkOptions ( daemon . configStore )
if err != nil {
logrus . Warnf ( "Failed to reload configuration with network controller: %v" , err )
return nil
}
err = daemon . netController . ReloadConfiguration ( netOptions ... )
if err != nil {
logrus . Warnf ( "Failed to reload configuration with network controller: %v" , err )
}
2015-12-10 18:35:10 -05:00
return nil
}
2015-11-12 14:55:17 -05:00
func validateID ( id string ) error {
if id == "" {
Remove static errors from errors package.
Moving all strings to the errors package wasn't a good idea after all.
Our custom implementation of Go errors predates everything that's nice
and good about working with errors in Go. Take as an example what we
have to do to get an error message:
```go
func GetErrorMessage(err error) string {
switch err.(type) {
case errcode.Error:
e, _ := err.(errcode.Error)
return e.Message
case errcode.ErrorCode:
ec, _ := err.(errcode.ErrorCode)
return ec.Message()
default:
return err.Error()
}
}
```
This goes against every good practice for Go development. The language already provides a simple, intuitive and standard way to get error messages, that is calling the `Error()` method from an error. Reinventing the error interface is a mistake.
Our custom implementation also makes very hard to reason about errors, another nice thing about Go. I found several (>10) error declarations that we don't use anywhere. This is a clear sign about how little we know about the errors we return. I also found several error usages where the number of arguments was different than the parameters declared in the error, another clear example of how difficult is to reason about errors.
Moreover, our custom implementation didn't really make easier for people to return custom HTTP status code depending on the errors. Again, it's hard to reason about when to set custom codes and how. Take an example what we have to do to extract the message and status code from an error before returning a response from the API:
```go
switch err.(type) {
case errcode.ErrorCode:
daError, _ := err.(errcode.ErrorCode)
statusCode = daError.Descriptor().HTTPStatusCode
errMsg = daError.Message()
case errcode.Error:
// For reference, if you're looking for a particular error
// then you can do something like :
// import ( derr "github.com/docker/docker/errors" )
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
daError, _ := err.(errcode.Error)
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
errMsg = daError.Message
default:
// This part of will be removed once we've
// converted everything over to use the errcode package
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types,
// we should create appropriate error types with clearly defined meaning
errStr := strings.ToLower(err.Error())
for keyword, status := range map[string]int{
"not found": http.StatusNotFound,
"no such": http.StatusNotFound,
"bad parameter": http.StatusBadRequest,
"conflict": http.StatusConflict,
"impossible": http.StatusNotAcceptable,
"wrong login/password": http.StatusUnauthorized,
"hasn't been activated": http.StatusForbidden,
} {
if strings.Contains(errStr, keyword) {
statusCode = status
break
}
}
}
```
You can notice two things in that code:
1. We have to explain how errors work, because our implementation goes against how easy to use Go errors are.
2. At no moment we arrived to remove that `switch` statement that was the original reason to use our custom implementation.
This change removes all our status errors from the errors package and puts them back in their specific contexts.
IT puts the messages back with their contexts. That way, we know right away when errors used and how to generate their messages.
It uses custom interfaces to reason about errors. Errors that need to response with a custom status code MUST implementent this simple interface:
```go
type errorWithStatus interface {
HTTPErrorStatusCode() int
}
```
This interface is very straightforward to implement. It also preserves Go errors real behavior, getting the message is as simple as using the `Error()` method.
I included helper functions to generate errors that use custom status code in `errors/errors.go`.
By doing this, we remove the hard dependency we have eeverywhere to our custom errors package. Yes, you can use it as a helper to generate error, but it's still very easy to generate errors without it.
Please, read this fantastic blog post about errors in Go: http://dave.cheney.net/2014/12/24/inspecting-errors
Signed-off-by: David Calavera <david.calavera@gmail.com>
2016-02-25 10:53:35 -05:00
return fmt . Errorf ( "Invalid empty id" )
2015-11-12 14:55:17 -05:00
}
return nil
}
2016-03-09 23:33:21 -05:00
func isBridgeNetworkDisabled ( config * Config ) bool {
return config . bridgeConfig . Iface == disableNetworkBridge
}
func ( daemon * Daemon ) networkOptions ( dconfig * Config ) ( [ ] nwconfig . Option , error ) {
options := [ ] nwconfig . Option { }
if dconfig == nil {
return options , nil
}
options = append ( options , nwconfig . OptionDataDir ( dconfig . Root ) )
dd := runconfig . DefaultDaemonNetworkMode ( )
dn := runconfig . DefaultDaemonNetworkMode ( ) . NetworkName ( )
options = append ( options , nwconfig . OptionDefaultDriver ( string ( dd ) ) )
options = append ( options , nwconfig . OptionDefaultNetwork ( dn ) )
if strings . TrimSpace ( dconfig . ClusterStore ) != "" {
kv := strings . Split ( dconfig . ClusterStore , "://" )
if len ( kv ) != 2 {
return nil , fmt . Errorf ( "kv store daemon config must be of the form KV-PROVIDER://KV-URL" )
}
options = append ( options , nwconfig . OptionKVProvider ( kv [ 0 ] ) )
options = append ( options , nwconfig . OptionKVProviderURL ( kv [ 1 ] ) )
}
if len ( dconfig . ClusterOpts ) > 0 {
options = append ( options , nwconfig . OptionKVOpts ( dconfig . ClusterOpts ) )
}
if daemon . discoveryWatcher != nil {
options = append ( options , nwconfig . OptionDiscoveryWatcher ( daemon . discoveryWatcher ) )
}
if dconfig . ClusterAdvertise != "" {
options = append ( options , nwconfig . OptionDiscoveryAddress ( dconfig . ClusterAdvertise ) )
}
options = append ( options , nwconfig . OptionLabels ( dconfig . Labels ) )
options = append ( options , driverOptions ( dconfig ) ... )
return options , nil
}
2016-03-18 14:50:19 -04:00
func copyBlkioEntry ( entries [ ] * containerd . BlkioStatsEntry ) [ ] types . BlkioStatEntry {
out := make ( [ ] types . BlkioStatEntry , len ( entries ) )
for i , re := range entries {
out [ i ] = types . BlkioStatEntry {
Major : re . Major ,
Minor : re . Minor ,
Op : re . Op ,
Value : re . Value ,
}
}
return out
}