diff --git a/compose/project.go b/compose/project.go index f2bddbcd..e2edb60d 100644 --- a/compose/project.go +++ b/compose/project.go @@ -36,7 +36,7 @@ func CreateService(cfg *config.CloudConfig, name string, serviceConfig *composeC } func CreateServiceSet(name string, cfg *config.CloudConfig, configs map[string]*composeConfig.ServiceConfigV1) (*project.Project, error) { - p, err := newProject(name, cfg, nil) + p, err := newProject(name, cfg, nil, nil) if err != nil { return nil, err } @@ -58,7 +58,7 @@ func GetProject(cfg *config.CloudConfig, networkingAvailable bool) (*project.Pro return newCoreServiceProject(cfg, networkingAvailable) } -func newProject(name string, cfg *config.CloudConfig, environmentLookup composeConfig.EnvironmentLookup) (*project.Project, error) { +func newProject(name string, cfg *config.CloudConfig, environmentLookup composeConfig.EnvironmentLookup, authLookup *rosDocker.ConfigAuthLookup) (*project.Project, error) { clientFactory, err := rosDocker.NewClientFactory(composeClient.Options{}) if err != nil { return nil, err @@ -67,12 +67,16 @@ func newProject(name string, cfg *config.CloudConfig, environmentLookup composeC if environmentLookup == nil { environmentLookup = rosDocker.NewConfigEnvironment(cfg) } + if authLookup == nil { + authLookup = rosDocker.NewConfigAuthLookup(cfg) + } serviceFactory := &rosDocker.ServiceFactory{ Deps: map[string][]string{}, } context := &docker.Context{ ClientFactory: clientFactory, + AuthLookup: authLookup, Context: project.Context{ ProjectName: name, EnvironmentLookup: environmentLookup, @@ -82,6 +86,8 @@ func newProject(name string, cfg *config.CloudConfig, environmentLookup composeC } serviceFactory.Context = context + authLookup.SetContext(context) + return docker.NewProject(context, &composeConfig.ParseOptions{ Interpolate: true, Validate: false, @@ -181,8 +187,9 @@ func newCoreServiceProject(cfg *config.CloudConfig, useNetwork bool) (*project.P enabled := map[interface{}]interface{}{} environmentLookup := rosDocker.NewConfigEnvironment(cfg) + authLookup := rosDocker.NewConfigAuthLookup(cfg) - p, err := newProject("os", cfg, environmentLookup) + p, err := newProject("os", cfg, environmentLookup, authLookup) if err != nil { return nil, err } @@ -194,6 +201,7 @@ func newCoreServiceProject(cfg *config.CloudConfig, useNetwork bool) (*project.P cfg = config.LoadConfig() environmentLookup.SetConfig(cfg) + authLookup.SetConfig(cfg) enabled = addServices(p, enabled, cfg.Rancher.Services) @@ -252,7 +260,7 @@ func newCoreServiceProject(cfg *config.CloudConfig, useNetwork bool) (*project.P } func StageServices(cfg *config.CloudConfig, services ...string) error { - p, err := newProject("stage-services", cfg, nil) + p, err := newProject("stage-services", cfg, nil, nil) if err != nil { return err } diff --git a/config/disk.go b/config/disk.go index 6f9cbb7a..0d990fa5 100644 --- a/config/disk.go +++ b/config/disk.go @@ -11,6 +11,7 @@ import ( yaml "github.com/cloudfoundry-incubator/candiedyaml" "github.com/coreos/coreos-cloudinit/datasource" "github.com/coreos/coreos-cloudinit/initialize" + "github.com/docker/engine-api/types" composeConfig "github.com/docker/libcompose/config" "github.com/rancher/os/util" ) @@ -194,6 +195,9 @@ func amendNils(c *CloudConfig) *CloudConfig { if t.Rancher.ServicesInclude == nil { t.Rancher.ServicesInclude = map[string]bool{} } + if t.Rancher.RegistryAuths == nil { + t.Rancher.RegistryAuths = map[string]types.AuthConfig{} + } return &t } diff --git a/config/types.go b/config/types.go index afb29752..beb6e033 100644 --- a/config/types.go +++ b/config/types.go @@ -4,6 +4,7 @@ import ( "runtime" "github.com/coreos/coreos-cloudinit/config" + "github.com/docker/engine-api/types" composeConfig "github.com/docker/libcompose/config" "github.com/rancher/netconf" ) @@ -104,6 +105,7 @@ type RancherConfig struct { SystemDocker DockerConfig `yaml:"system_docker,omitempty"` Upgrade UpgradeConfig `yaml:"upgrade,omitempty"` Docker DockerConfig `yaml:"docker,omitempty"` + RegistryAuths map[string]types.AuthConfig `yaml:"registry_auths,omitempty"` Defaults Defaults `yaml:"defaults,omitempty"` } diff --git a/docker/auth.go b/docker/auth.go new file mode 100644 index 00000000..c4a086ad --- /dev/null +++ b/docker/auth.go @@ -0,0 +1,82 @@ +package docker + +import ( + "encoding/base64" + "fmt" + "strings" + + log "github.com/Sirupsen/logrus" + "github.com/docker/docker/registry" + "github.com/docker/engine-api/types" + "github.com/docker/libcompose/docker" + "github.com/rancher/os/config" +) + +// ConfigAuthLookup will lookup registry auth info from cloud config +// if a context is set, it will also lookup auth info from the Docker config file +type ConfigAuthLookup struct { + cfg *config.CloudConfig + context *docker.Context + dockerConfigAuthLookup *docker.ConfigAuthLookup +} + +func NewConfigAuthLookup(cfg *config.CloudConfig) *ConfigAuthLookup { + return &ConfigAuthLookup{ + cfg: cfg, + } +} + +func populateRemaining(authConfig *types.AuthConfig) error { + if authConfig.Auth == "" { + return nil + } + + decoded, err := base64.URLEncoding.DecodeString(authConfig.Auth) + if err != nil { + return err + } + + decodedSplit := strings.Split(string(decoded), ":") + if len(decodedSplit) != 2 { + return fmt.Errorf("Invalid auth: %s", authConfig.Auth) + } + + authConfig.Username = decodedSplit[0] + authConfig.Password = decodedSplit[1] + + return nil +} + +func (c *ConfigAuthLookup) SetConfig(cfg *config.CloudConfig) { + c.cfg = cfg +} + +func (c *ConfigAuthLookup) SetContext(context *docker.Context) { + c.context = context + c.dockerConfigAuthLookup = docker.NewConfigAuthLookup(context) +} + +func (c *ConfigAuthLookup) Lookup(repoInfo *registry.RepositoryInfo) types.AuthConfig { + if repoInfo == nil || repoInfo.Index == nil { + return types.AuthConfig{} + } + authConfig := registry.ResolveAuthConfig(c.All(), repoInfo.Index) + + err := populateRemaining(&authConfig) + if err != nil { + log.Error(err) + return types.AuthConfig{} + } + + return authConfig +} + +func (c *ConfigAuthLookup) All() map[string]types.AuthConfig { + registryAuths := c.cfg.Rancher.RegistryAuths + if c.dockerConfigAuthLookup != nil { + for registry, authConfig := range c.dockerConfigAuthLookup.All() { + registryAuths[registry] = authConfig + } + } + return registryAuths +}