зеркало из https://github.com/docker/compose-cli.git
Merge pull request #165 from docker/azure_private_images
Allow pulling private images from hub or other registries
This commit is contained in:
Коммит
e77513c1c8
|
@ -42,6 +42,12 @@ func ToContainerGroup(aciContext store.AciContext, p compose.Project) (container
|
|||
} else {
|
||||
volumes = &allVolumes
|
||||
}
|
||||
|
||||
registryCreds, err := getRegistryCredentials(p, newCliRegistryConfLoader())
|
||||
if err != nil {
|
||||
return containerinstance.ContainerGroup{}, err
|
||||
}
|
||||
|
||||
var containers []containerinstance.Container
|
||||
groupDefinition := containerinstance.ContainerGroup{
|
||||
Name: &containerGroupName,
|
||||
|
@ -50,6 +56,7 @@ func ToContainerGroup(aciContext store.AciContext, p compose.Project) (container
|
|||
OsType: containerinstance.Linux,
|
||||
Containers: &containers,
|
||||
Volumes: volumes,
|
||||
ImageRegistryCredentials: ®istryCreds,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package convert
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/containerinstance/mgmt/containerinstance"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/config/types"
|
||||
|
||||
"github.com/docker/api/compose"
|
||||
)
|
||||
|
||||
// Specific username from ACR docs : https://github.com/Azure/acr/blob/master/docs/AAD-OAuth.md#getting-credentials-programatically
|
||||
const (
|
||||
tokenUsername = "00000000-0000-0000-0000-000000000000"
|
||||
dockerHub = "index.docker.io"
|
||||
)
|
||||
|
||||
type registryConfLoader interface {
|
||||
getAllRegistryCredentials() (map[string]types.AuthConfig, error)
|
||||
}
|
||||
|
||||
type cliRegistryConfLoader struct {
|
||||
cfg *configfile.ConfigFile
|
||||
}
|
||||
|
||||
func (c cliRegistryConfLoader) getAllRegistryCredentials() (map[string]types.AuthConfig, error) {
|
||||
return c.cfg.GetAllCredentials()
|
||||
}
|
||||
|
||||
func newCliRegistryConfLoader() cliRegistryConfLoader {
|
||||
return cliRegistryConfLoader{
|
||||
cfg: config.LoadDefaultConfigFile(os.Stderr),
|
||||
}
|
||||
}
|
||||
|
||||
func getRegistryCredentials(project compose.Project, registryLoader registryConfLoader) ([]containerinstance.ImageRegistryCredential, error) {
|
||||
allCreds, err := registryLoader.getAllRegistryCredentials()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
usedRegistries := map[string]bool{}
|
||||
for _, service := range project.Services {
|
||||
imageName := service.Image
|
||||
tokens := strings.Split(imageName, "/")
|
||||
registry := tokens[0]
|
||||
if len(tokens) == 1 { // ! image names can include "." ...
|
||||
registry = dockerHub
|
||||
} else if !strings.Contains(registry, ".") {
|
||||
registry = dockerHub
|
||||
}
|
||||
usedRegistries[registry] = true
|
||||
}
|
||||
var registryCreds []containerinstance.ImageRegistryCredential
|
||||
for name, oneCred := range allCreds {
|
||||
parsedURL, err := url.Parse(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostname := parsedURL.Host
|
||||
if hostname == "" {
|
||||
hostname = parsedURL.Path
|
||||
}
|
||||
if _, ok := usedRegistries[hostname]; ok {
|
||||
if oneCred.Username != "" {
|
||||
aciCredential := containerinstance.ImageRegistryCredential{
|
||||
Server: to.StringPtr(hostname),
|
||||
Password: to.StringPtr(oneCred.Password),
|
||||
Username: to.StringPtr(oneCred.Username),
|
||||
}
|
||||
registryCreds = append(registryCreds, aciCredential)
|
||||
} else if oneCred.IdentityToken != "" {
|
||||
aciCredential := containerinstance.ImageRegistryCredential{
|
||||
Server: to.StringPtr(hostname),
|
||||
Password: to.StringPtr(oneCred.IdentityToken),
|
||||
Username: to.StringPtr(tokenUsername),
|
||||
}
|
||||
registryCreds = append(registryCreds, aciCredential)
|
||||
}
|
||||
}
|
||||
}
|
||||
return registryCreds, nil
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
package convert
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
cliconfigtypes "github.com/docker/cli/cli/config/types"
|
||||
|
||||
"github.com/docker/api/compose"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/containerinstance/mgmt/containerinstance"
|
||||
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
const getAllCredentials = "getAllRegistryCredentials"
|
||||
|
||||
type RegistryConvertTestSuite struct {
|
||||
suite.Suite
|
||||
loader *MockRegistryLoader
|
||||
}
|
||||
|
||||
func (suite *RegistryConvertTestSuite) BeforeTest(suiteName, testName string) {
|
||||
suite.loader = &MockRegistryLoader{}
|
||||
}
|
||||
|
||||
func (suite *RegistryConvertTestSuite) TestHubPrivateImage() {
|
||||
suite.loader.On(getAllCredentials).Return(registry("https://index.docker.io", userPwdCreds("toto", "pwd")), nil)
|
||||
|
||||
creds, err := getRegistryCredentials(composeServices("gtardif/privateimg"), suite.loader)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(creds).To(Equal([]containerinstance.ImageRegistryCredential{
|
||||
{
|
||||
Server: to.StringPtr(dockerHub),
|
||||
Username: to.StringPtr("toto"),
|
||||
Password: to.StringPtr("pwd"),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
func (suite *RegistryConvertTestSuite) TestRegistryNameWithoutProtocol() {
|
||||
suite.loader.On(getAllCredentials).Return(registry("index.docker.io", userPwdCreds("toto", "pwd")), nil)
|
||||
|
||||
creds, err := getRegistryCredentials(composeServices("gtardif/privateimg"), suite.loader)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(creds).To(Equal([]containerinstance.ImageRegistryCredential{
|
||||
{
|
||||
Server: to.StringPtr(dockerHub),
|
||||
Username: to.StringPtr("toto"),
|
||||
Password: to.StringPtr("pwd"),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
func (suite *RegistryConvertTestSuite) TestImageWithDotInName() {
|
||||
suite.loader.On(getAllCredentials).Return(registry("index.docker.io", userPwdCreds("toto", "pwd")), nil)
|
||||
|
||||
creds, err := getRegistryCredentials(composeServices("my.image"), suite.loader)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(creds).To(Equal([]containerinstance.ImageRegistryCredential{
|
||||
{
|
||||
Server: to.StringPtr(dockerHub),
|
||||
Username: to.StringPtr("toto"),
|
||||
Password: to.StringPtr("pwd"),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
func (suite *RegistryConvertTestSuite) TestAcrPrivateImage() {
|
||||
suite.loader.On(getAllCredentials).Return(registry("https://mycontainerregistrygta.azurecr.io", tokenCreds("123456")), nil)
|
||||
|
||||
creds, err := getRegistryCredentials(composeServices("mycontainerregistrygta.azurecr.io/privateimg"), suite.loader)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(creds).To(Equal([]containerinstance.ImageRegistryCredential{
|
||||
{
|
||||
Server: to.StringPtr("mycontainerregistrygta.azurecr.io"),
|
||||
Username: to.StringPtr(tokenUsername),
|
||||
Password: to.StringPtr("123456"),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
func (suite *RegistryConvertTestSuite) TestNoMoreRegistriesThanImages() {
|
||||
configs := map[string]cliconfigtypes.AuthConfig{
|
||||
"https://mycontainerregistrygta.azurecr.io": tokenCreds("123456"),
|
||||
"https://index.docker.io": userPwdCreds("toto", "pwd"),
|
||||
}
|
||||
suite.loader.On(getAllCredentials).Return(configs, nil)
|
||||
|
||||
creds, err := getRegistryCredentials(composeServices("mycontainerregistrygta.azurecr.io/privateimg"), suite.loader)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(creds).To(Equal([]containerinstance.ImageRegistryCredential{
|
||||
{
|
||||
Server: to.StringPtr("mycontainerregistrygta.azurecr.io"),
|
||||
Username: to.StringPtr(tokenUsername),
|
||||
Password: to.StringPtr("123456"),
|
||||
},
|
||||
}))
|
||||
|
||||
creds, err = getRegistryCredentials(composeServices("someuser/privateimg"), suite.loader)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(creds).To(Equal([]containerinstance.ImageRegistryCredential{
|
||||
{
|
||||
Server: to.StringPtr(dockerHub),
|
||||
Username: to.StringPtr("toto"),
|
||||
Password: to.StringPtr("pwd"),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
func (suite *RegistryConvertTestSuite) TestHubAndSeveralACRRegistries() {
|
||||
configs := map[string]cliconfigtypes.AuthConfig{
|
||||
"https://mycontainerregistry1.azurecr.io": tokenCreds("123456"),
|
||||
"https://mycontainerregistry2.azurecr.io": tokenCreds("456789"),
|
||||
"https://mycontainerregistry3.azurecr.io": tokenCreds("123456789"),
|
||||
"https://index.docker.io": userPwdCreds("toto", "pwd"),
|
||||
"https://other.registry.io": userPwdCreds("user", "password"),
|
||||
}
|
||||
suite.loader.On(getAllCredentials).Return(configs, nil)
|
||||
|
||||
creds, err := getRegistryCredentials(composeServices("mycontainerregistry1.azurecr.io/privateimg", "someuser/privateImg2", "mycontainerregistry2.azurecr.io/privateimg"), suite.loader)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(creds).To(ContainElement(containerinstance.ImageRegistryCredential{
|
||||
Server: to.StringPtr("mycontainerregistry1.azurecr.io"),
|
||||
Username: to.StringPtr(tokenUsername),
|
||||
Password: to.StringPtr("123456"),
|
||||
}))
|
||||
Expect(creds).To(ContainElement(containerinstance.ImageRegistryCredential{
|
||||
Server: to.StringPtr("mycontainerregistry2.azurecr.io"),
|
||||
Username: to.StringPtr(tokenUsername),
|
||||
Password: to.StringPtr("456789"),
|
||||
}))
|
||||
Expect(creds).To(ContainElement(containerinstance.ImageRegistryCredential{
|
||||
Server: to.StringPtr(dockerHub),
|
||||
Username: to.StringPtr("toto"),
|
||||
Password: to.StringPtr("pwd"),
|
||||
}))
|
||||
}
|
||||
|
||||
func composeServices(images ...string) compose.Project {
|
||||
var services []types.ServiceConfig
|
||||
for index, name := range images {
|
||||
service := types.ServiceConfig{
|
||||
Name: "service" + strconv.Itoa(index),
|
||||
Image: name,
|
||||
}
|
||||
services = append(services, service)
|
||||
}
|
||||
return compose.Project{
|
||||
Config: types.Config{
|
||||
Services: services,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func registry(host string, configregistryData cliconfigtypes.AuthConfig) map[string]cliconfigtypes.AuthConfig {
|
||||
return map[string]cliconfigtypes.AuthConfig{
|
||||
host: configregistryData,
|
||||
}
|
||||
}
|
||||
|
||||
func userPwdCreds(user string, password string) cliconfigtypes.AuthConfig {
|
||||
return cliconfigtypes.AuthConfig{
|
||||
Username: user,
|
||||
Password: password,
|
||||
}
|
||||
}
|
||||
|
||||
func tokenCreds(token string) cliconfigtypes.AuthConfig {
|
||||
return cliconfigtypes.AuthConfig{
|
||||
IdentityToken: token,
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistryConvertTestSuite(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
suite.Run(t, new(RegistryConvertTestSuite))
|
||||
}
|
||||
|
||||
type MockRegistryLoader struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (s *MockRegistryLoader) getAllRegistryCredentials() (map[string]cliconfigtypes.AuthConfig, error) {
|
||||
args := s.Called()
|
||||
return args.Get(0).(map[string]cliconfigtypes.AuthConfig), args.Error(1)
|
||||
}
|
3
go.mod
3
go.mod
|
@ -18,8 +18,10 @@ require (
|
|||
github.com/compose-spec/compose-go v0.0.0-20200423124427-63dcf8c22cae
|
||||
github.com/containerd/console v1.0.0
|
||||
github.com/containerd/containerd v1.3.4 // indirect
|
||||
github.com/docker/cli v0.0.0-20200528204125-dd360c7c0de8
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible
|
||||
github.com/docker/docker-credential-helpers v0.6.3 // indirect
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect
|
||||
github.com/gobwas/pool v0.2.0 // indirect
|
||||
|
@ -33,6 +35,7 @@ require (
|
|||
github.com/onsi/gomega v1.9.0
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/opencontainers/runc v0.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.5.1 // indirect
|
||||
github.com/robpike/filter v0.0.0-20150108201509-2984852a2183
|
||||
|
|
9
go.sum
9
go.sum
|
@ -80,10 +80,17 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
|||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/docker/cli v0.0.0-20200303162255-7d407207c304 h1:A7SYzidcyuQ/yS4wezWGYeUioUFJQk8HYWY9aMYTF4I=
|
||||
github.com/docker/cli v0.0.0-20200303162255-7d407207c304/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v0.0.0-20200528204125-dd360c7c0de8 h1:JRquW4uqIU+eSilDhuo9X9QFX4NEmGj5B1x97ZA8djM=
|
||||
github.com/docker/cli v0.0.0-20200528204125-dd360c7c0de8/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v17.12.1-ce-rc2+incompatible h1:ESUycEAqvFuLglAHkUW66rCc2djYtd3i1x231svLq9o=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible h1:G2hY8RD7jB9QaSmcb8mYEIg8QbEvVAB7se8+lXHZHfg=
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
|
||||
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
|
@ -207,6 +214,8 @@ github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2i
|
|||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
Загрузка…
Ссылка в новой задаче