mirror of
https://github.com/docker/docs.git
synced 2026-03-31 00:08:55 +07:00
Add token pass-thru for Authconfig
This augments the CreateContainer call to detect the AuthConfig header
and use any supplied auth for pull operations. This will allow pulling
of protected image on to specific node during the create operation.
CLI usage example using username/password:
# Calculate the header
REPO_USER=yourusername
read -s PASSWORD
HEADER=$(echo "{\"username\":\"${REPO_USER}\",\"password\":\"${PASSWORD}\"}"|base64 -w 0 )
unset PASSWORD
echo HEADER=$HEADER
# Then add the following to your ~/.docker/config.json
"HttpHeaders": {
"X-Registry-Auth": "<HEADER string from above>"
}
# Now run a private image against swarm:
docker run --rm -it yourprivateimage:latest
CLI usage example using registry tokens: (Required engine 1.10 with new auth token support)
REPO=yourrepo/yourimage
REPO_USER=yourusername
read -s PASSWORD
AUTH_URL=https://auth.docker.io/token
TOKEN=$(curl -s -u "${REPO_USER}:${PASSWORD}" "${AUTH_URL}?scope=repository:${REPO}:pull&service=registry.docker.io" |
jq -r ".token")
HEADER=$(echo "{\"registrytoken\":\"${TOKEN}\"}"|base64 -w 0 )
echo HEADER=$HEADER
# Update the docker config as above, but the token will expire quickly...
Signed-off-by: Daniel Hiltgen <daniel.hiltgen@docker.com>
This commit is contained in:
@@ -427,7 +427,15 @@ func postContainersCreate(c *context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
container, err := c.cluster.CreateContainer(cluster.BuildContainerConfig(config), name)
|
||||
// Pass auth information along if present
|
||||
var authConfig *dockerclient.AuthConfig
|
||||
buf, err := base64.URLEncoding.DecodeString(r.Header.Get("X-Registry-Auth"))
|
||||
if err == nil {
|
||||
authConfig = &dockerclient.AuthConfig{}
|
||||
json.Unmarshal(buf, authConfig)
|
||||
}
|
||||
|
||||
container, err := c.cluster.CreateContainer(cluster.BuildContainerConfig(config), name, authConfig)
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), "Conflict") {
|
||||
httpError(w, err.Error(), http.StatusConflict)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
// Cluster is exported
|
||||
type Cluster interface {
|
||||
// Create a container
|
||||
CreateContainer(config *ContainerConfig, name string) (*Container, error)
|
||||
CreateContainer(config *ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (*Container, error)
|
||||
|
||||
// Remove a container
|
||||
RemoveContainer(container *Container, force, volumes bool) error
|
||||
|
||||
@@ -503,7 +503,7 @@ func (e *Engine) TotalCpus() int64 {
|
||||
}
|
||||
|
||||
// Create a new container
|
||||
func (e *Engine) Create(config *ContainerConfig, name string, pullImage bool) (*Container, error) {
|
||||
func (e *Engine) Create(config *ContainerConfig, name string, pullImage bool, authConfig *dockerclient.AuthConfig) (*Container, error) {
|
||||
var (
|
||||
err error
|
||||
id string
|
||||
@@ -521,17 +521,17 @@ func (e *Engine) Create(config *ContainerConfig, name string, pullImage bool) (*
|
||||
dockerConfig.CpuShares = int64(math.Ceil(float64(config.CpuShares*1024) / float64(e.Cpus)))
|
||||
dockerConfig.HostConfig.CpuShares = dockerConfig.CpuShares
|
||||
|
||||
if id, err = client.CreateContainer(&dockerConfig, name); err != nil {
|
||||
if id, err = client.CreateContainer(&dockerConfig, name, nil); err != nil {
|
||||
// If the error is other than not found, abort immediately.
|
||||
if err != dockerclient.ErrImageNotFound || !pullImage {
|
||||
return nil, err
|
||||
}
|
||||
// Otherwise, try to pull the image...
|
||||
if err = e.Pull(config.Image, nil); err != nil {
|
||||
if err = e.Pull(config.Image, authConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// ...And try again.
|
||||
if id, err = client.CreateContainer(&dockerConfig, name); err != nil {
|
||||
if id, err = client.CreateContainer(&dockerConfig, name, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,13 +193,14 @@ func TestCreateContainer(t *testing.T) {
|
||||
// Everything is ok
|
||||
name := "test1"
|
||||
id := "id1"
|
||||
client.On("CreateContainer", &mockConfig, name).Return(id, nil).Once()
|
||||
var auth *dockerclient.AuthConfig
|
||||
client.On("CreateContainer", &mockConfig, name, auth).Return(id, nil).Once()
|
||||
client.On("ListContainers", true, false, fmt.Sprintf(`{"id":[%q]}`, id)).Return([]dockerclient.Container{{Id: id}}, nil).Once()
|
||||
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil).Once()
|
||||
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
|
||||
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
|
||||
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: &config.ContainerConfig}, nil).Once()
|
||||
container, err := engine.Create(config, name, false)
|
||||
container, err := engine.Create(config, name, false, auth)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, container.Id, id)
|
||||
assert.Len(t, engine.Containers(), 1)
|
||||
@@ -207,8 +208,8 @@ func TestCreateContainer(t *testing.T) {
|
||||
// Image not found, pullImage == false
|
||||
name = "test2"
|
||||
mockConfig.CpuShares = int64(math.Ceil(float64(config.CpuShares*1024) / float64(mockInfo.NCPU)))
|
||||
client.On("CreateContainer", &mockConfig, name).Return("", dockerclient.ErrImageNotFound).Once()
|
||||
container, err = engine.Create(config, name, false)
|
||||
client.On("CreateContainer", &mockConfig, name, auth).Return("", dockerclient.ErrImageNotFound).Once()
|
||||
container, err = engine.Create(config, name, false, auth)
|
||||
assert.Equal(t, err, dockerclient.ErrImageNotFound)
|
||||
assert.Nil(t, container)
|
||||
|
||||
@@ -217,14 +218,14 @@ func TestCreateContainer(t *testing.T) {
|
||||
id = "id3"
|
||||
mockConfig.CpuShares = int64(math.Ceil(float64(config.CpuShares*1024) / float64(mockInfo.NCPU)))
|
||||
client.On("PullImage", config.Image+":latest", mock.Anything).Return(nil).Once()
|
||||
client.On("CreateContainer", &mockConfig, name).Return("", dockerclient.ErrImageNotFound).Once()
|
||||
client.On("CreateContainer", &mockConfig, name).Return(id, nil).Once()
|
||||
client.On("CreateContainer", &mockConfig, name, auth).Return("", dockerclient.ErrImageNotFound).Once()
|
||||
client.On("CreateContainer", &mockConfig, name, auth).Return(id, nil).Once()
|
||||
client.On("ListContainers", true, false, fmt.Sprintf(`{"id":[%q]}`, id)).Return([]dockerclient.Container{{Id: id}}, nil).Once()
|
||||
client.On("ListImages", mock.Anything).Return([]*dockerclient.Image{}, nil).Once()
|
||||
client.On("ListVolumes", mock.Anything).Return([]*dockerclient.Volume{}, nil)
|
||||
client.On("ListNetworks", mock.Anything).Return([]*dockerclient.NetworkResource{}, nil)
|
||||
client.On("InspectContainer", id).Return(&dockerclient.ContainerInfo{Config: &config.ContainerConfig}, nil).Once()
|
||||
container, err = engine.Create(config, name, true)
|
||||
container, err = engine.Create(config, name, true, auth)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, container.Id, id)
|
||||
assert.Len(t, engine.Containers(), 2)
|
||||
|
||||
@@ -164,7 +164,7 @@ func (c *Cluster) RegisterEventHandler(h cluster.EventHandler) error {
|
||||
}
|
||||
|
||||
// CreateContainer for container creation in Mesos task
|
||||
func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string) (*cluster.Container, error) {
|
||||
func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (*cluster.Container, error) {
|
||||
if config.Memory == 0 && config.CpuShares == 0 {
|
||||
return nil, errResourcesNeeded
|
||||
}
|
||||
|
||||
@@ -115,8 +115,8 @@ func (c *Cluster) generateUniqueID() string {
|
||||
}
|
||||
|
||||
// CreateContainer aka schedule a brand new container into the cluster.
|
||||
func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string) (*cluster.Container, error) {
|
||||
container, err := c.createContainer(config, name, false)
|
||||
func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string, authConfig *dockerclient.AuthConfig) (*cluster.Container, error) {
|
||||
container, err := c.createContainer(config, name, false, authConfig)
|
||||
|
||||
// fails with image not found, then try to reschedule with soft-image-affinity
|
||||
if err != nil {
|
||||
@@ -125,14 +125,14 @@ func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string)
|
||||
// Check if the image exists in the cluster
|
||||
// If exists, retry with a soft-image-affinity
|
||||
if image := c.Image(config.Image); image != nil {
|
||||
container, err = c.createContainer(config, name, true)
|
||||
container, err = c.createContainer(config, name, true, authConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
return container, err
|
||||
}
|
||||
|
||||
func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string, withSoftImageAffinity bool) (*cluster.Container, error) {
|
||||
func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string, withSoftImageAffinity bool, authConfig *dockerclient.AuthConfig) (*cluster.Container, error) {
|
||||
c.scheduler.Lock()
|
||||
|
||||
// Ensure the name is available
|
||||
@@ -170,7 +170,7 @@ func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string,
|
||||
|
||||
c.scheduler.Unlock()
|
||||
|
||||
container, err := engine.Create(config, name, true)
|
||||
container, err := engine.Create(config, name, true, authConfig)
|
||||
|
||||
c.scheduler.Lock()
|
||||
delete(c.pendingContainers, swarmID)
|
||||
|
||||
@@ -45,6 +45,49 @@ POST "/images/create" : "docker import" flow not implement
|
||||
|
||||
* `POST "/containers/create"`: `CpuShares` in `HostConfig` sets the number of CPU cores allocated to the container.
|
||||
|
||||
# Registry Authentication
|
||||
|
||||
During container create calls, the swarm API will optionally accept a X-Registry-Config header.
|
||||
If provided, this header will be passed down to the engine if the image must be pulled
|
||||
to complete the create operation.
|
||||
|
||||
The following two examples demonstrate how to utilize this using the existing docker CLI
|
||||
|
||||
* CLI usage example using username/password:
|
||||
|
||||
```bash
|
||||
# Calculate the header
|
||||
REPO_USER=yourusername
|
||||
read -s PASSWORD
|
||||
HEADER=$(echo "{\"username\":\"${REPO_USER}\",\"password\":\"${PASSWORD}\"}"|base64 -w 0 )
|
||||
unset PASSWORD
|
||||
echo HEADER=$HEADER
|
||||
|
||||
# Then add the following to your ~/.docker/config.json
|
||||
"HttpHeaders": {
|
||||
"X-Registry-Auth": "<HEADER string from above>"
|
||||
}
|
||||
|
||||
# Now run a private image against swarm:
|
||||
docker run --rm -it yourprivateimage:latest
|
||||
```
|
||||
|
||||
* CLI usage example using registry tokens: (Requires engine 1.10 with new auth token support)
|
||||
|
||||
```bash
|
||||
REPO=yourrepo/yourimage
|
||||
REPO_USER=yourusername
|
||||
read -s PASSWORD
|
||||
AUTH_URL=https://auth.docker.io/token
|
||||
TOKEN=$(curl -s -u "${REPO_USER}:${PASSWORD}" "${AUTH_URL}?scope=repository:${REPO}:pull&service=registry.docker.io" |
|
||||
jq -r ".token")
|
||||
HEADER=$(echo "{\"registrytoken\":\"${TOKEN}\"}"|base64 -w 0 )
|
||||
echo HEADER=$HEADER
|
||||
|
||||
# Update the docker config as above, but the token will expire quickly...
|
||||
```
|
||||
|
||||
|
||||
## Docker Swarm documentation index
|
||||
|
||||
- [User guide](https://docs.docker.com/swarm/)
|
||||
|
||||
Reference in New Issue
Block a user