mirror of https://github.com/docker/docs.git
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:
parent
f0b785a206
commit
dde577d154
|
@ -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/)
|
||||
|
|
Loading…
Reference in New Issue