Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
| ... | ... |
@@ -8,7 +8,6 @@ import ( |
| 8 | 8 |
"errors" |
| 9 | 9 |
"fmt" |
| 10 | 10 |
"github.com/dotcloud/docker/archive" |
| 11 |
- "github.com/dotcloud/docker/auth" |
|
| 12 | 11 |
"github.com/dotcloud/docker/dockerversion" |
| 13 | 12 |
"github.com/dotcloud/docker/engine" |
| 14 | 13 |
"github.com/dotcloud/docker/nat" |
| ... | ... |
@@ -229,7 +228,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
| 229 | 229 |
|
| 230 | 230 |
// 'docker login': login / register a user to registry service. |
| 231 | 231 |
func (cli *DockerCli) CmdLogin(args ...string) error {
|
| 232 |
- cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+auth.IndexServerAddress()+"\" is the default.")
|
|
| 232 |
+ cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.")
|
|
| 233 | 233 |
|
| 234 | 234 |
var username, password, email string |
| 235 | 235 |
|
| ... | ... |
@@ -240,7 +239,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
| 240 | 240 |
if err != nil {
|
| 241 | 241 |
return nil |
| 242 | 242 |
} |
| 243 |
- serverAddress := auth.IndexServerAddress() |
|
| 243 |
+ serverAddress := registry.IndexServerAddress() |
|
| 244 | 244 |
if len(cmd.Args()) > 0 {
|
| 245 | 245 |
serverAddress = cmd.Arg(0) |
| 246 | 246 |
} |
| ... | ... |
@@ -266,7 +265,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
| 266 | 266 |
cli.LoadConfigFile() |
| 267 | 267 |
authconfig, ok := cli.configFile.Configs[serverAddress] |
| 268 | 268 |
if !ok {
|
| 269 |
- authconfig = auth.AuthConfig{}
|
|
| 269 |
+ authconfig = registry.AuthConfig{}
|
|
| 270 | 270 |
} |
| 271 | 271 |
|
| 272 | 272 |
if username == "" {
|
| ... | ... |
@@ -311,7 +310,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
| 311 | 311 |
stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
|
| 312 | 312 |
if statusCode == 401 {
|
| 313 | 313 |
delete(cli.configFile.Configs, serverAddress) |
| 314 |
- auth.SaveConfig(cli.configFile) |
|
| 314 |
+ registry.SaveConfig(cli.configFile) |
|
| 315 | 315 |
return err |
| 316 | 316 |
} |
| 317 | 317 |
if err != nil {
|
| ... | ... |
@@ -320,10 +319,10 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
| 320 | 320 |
var out2 engine.Env |
| 321 | 321 |
err = out2.Decode(stream) |
| 322 | 322 |
if err != nil {
|
| 323 |
- cli.configFile, _ = auth.LoadConfig(os.Getenv("HOME"))
|
|
| 323 |
+ cli.configFile, _ = registry.LoadConfig(os.Getenv("HOME"))
|
|
| 324 | 324 |
return err |
| 325 | 325 |
} |
| 326 |
- auth.SaveConfig(cli.configFile) |
|
| 326 |
+ registry.SaveConfig(cli.configFile) |
|
| 327 | 327 |
if out2.Get("Status") != "" {
|
| 328 | 328 |
fmt.Fprintf(cli.out, "%s\n", out2.Get("Status"))
|
| 329 | 329 |
} |
| ... | ... |
@@ -1008,7 +1007,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
| 1008 | 1008 |
// Custom repositories can have different rules, and we must also |
| 1009 | 1009 |
// allow pushing by image ID. |
| 1010 | 1010 |
if len(strings.SplitN(name, "/", 2)) == 1 {
|
| 1011 |
- username := cli.configFile.Configs[auth.IndexServerAddress()].Username |
|
| 1011 |
+ username := cli.configFile.Configs[registry.IndexServerAddress()].Username |
|
| 1012 | 1012 |
if username == "" {
|
| 1013 | 1013 |
username = "<user>" |
| 1014 | 1014 |
} |
| ... | ... |
@@ -1016,7 +1015,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
| 1016 | 1016 |
} |
| 1017 | 1017 |
|
| 1018 | 1018 |
v := url.Values{}
|
| 1019 |
- push := func(authConfig auth.AuthConfig) error {
|
|
| 1019 |
+ push := func(authConfig registry.AuthConfig) error {
|
|
| 1020 | 1020 |
buf, err := json.Marshal(authConfig) |
| 1021 | 1021 |
if err != nil {
|
| 1022 | 1022 |
return err |
| ... | ... |
@@ -1075,7 +1074,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
| 1075 | 1075 |
v.Set("fromImage", remote)
|
| 1076 | 1076 |
v.Set("tag", *tag)
|
| 1077 | 1077 |
|
| 1078 |
- pull := func(authConfig auth.AuthConfig) error {
|
|
| 1078 |
+ pull := func(authConfig registry.AuthConfig) error {
|
|
| 1079 | 1079 |
buf, err := json.Marshal(authConfig) |
| 1080 | 1080 |
if err != nil {
|
| 1081 | 1081 |
return err |
| ... | ... |
@@ -2058,8 +2057,8 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
|
| 2058 | 2058 |
if passAuthInfo {
|
| 2059 | 2059 |
cli.LoadConfigFile() |
| 2060 | 2060 |
// Resolve the Auth config relevant for this server |
| 2061 |
- authConfig := cli.configFile.ResolveAuthConfig(auth.IndexServerAddress()) |
|
| 2062 |
- getHeaders := func(authConfig auth.AuthConfig) (map[string][]string, error) {
|
|
| 2061 |
+ authConfig := cli.configFile.ResolveAuthConfig(registry.IndexServerAddress()) |
|
| 2062 |
+ getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) {
|
|
| 2063 | 2063 |
buf, err := json.Marshal(authConfig) |
| 2064 | 2064 |
if err != nil {
|
| 2065 | 2065 |
return nil, err |
| ... | ... |
@@ -2340,7 +2339,7 @@ func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet |
| 2340 | 2340 |
} |
| 2341 | 2341 |
|
| 2342 | 2342 |
func (cli *DockerCli) LoadConfigFile() (err error) {
|
| 2343 |
- cli.configFile, err = auth.LoadConfig(os.Getenv("HOME"))
|
|
| 2343 |
+ cli.configFile, err = registry.LoadConfig(os.Getenv("HOME"))
|
|
| 2344 | 2344 |
if err != nil {
|
| 2345 | 2345 |
fmt.Fprintf(cli.err, "WARNING: %s\n", err) |
| 2346 | 2346 |
} |
| ... | ... |
@@ -2422,7 +2421,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *Doc |
| 2422 | 2422 |
type DockerCli struct {
|
| 2423 | 2423 |
proto string |
| 2424 | 2424 |
addr string |
| 2425 |
- configFile *auth.ConfigFile |
|
| 2425 |
+ configFile *registry.ConfigFile |
|
| 2426 | 2426 |
in io.ReadCloser |
| 2427 | 2427 |
out io.Writer |
| 2428 | 2428 |
err io.Writer |
| ... | ... |
@@ -8,12 +8,12 @@ import ( |
| 8 | 8 |
"encoding/json" |
| 9 | 9 |
"expvar" |
| 10 | 10 |
"fmt" |
| 11 |
- "github.com/dotcloud/docker/auth" |
|
| 12 | 11 |
"github.com/dotcloud/docker/engine" |
| 13 | 12 |
"github.com/dotcloud/docker/pkg/listenbuffer" |
| 14 | 13 |
"github.com/dotcloud/docker/pkg/systemd" |
| 15 | 14 |
"github.com/dotcloud/docker/pkg/user" |
| 16 | 15 |
"github.com/dotcloud/docker/pkg/version" |
| 16 |
+ "github.com/dotcloud/docker/registry" |
|
| 17 | 17 |
"github.com/dotcloud/docker/utils" |
| 18 | 18 |
"github.com/gorilla/mux" |
| 19 | 19 |
"io" |
| ... | ... |
@@ -381,13 +381,13 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon |
| 381 | 381 |
job *engine.Job |
| 382 | 382 |
) |
| 383 | 383 |
authEncoded := r.Header.Get("X-Registry-Auth")
|
| 384 |
- authConfig := &auth.AuthConfig{}
|
|
| 384 |
+ authConfig := ®istry.AuthConfig{}
|
|
| 385 | 385 |
if authEncoded != "" {
|
| 386 | 386 |
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
| 387 | 387 |
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
| 388 | 388 |
// for a pull it is not an error if no auth was given |
| 389 | 389 |
// to increase compatibility with the existing api it is defaulting to be empty |
| 390 |
- authConfig = &auth.AuthConfig{}
|
|
| 390 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 391 | 391 |
} |
| 392 | 392 |
} |
| 393 | 393 |
if image != "" { //pull
|
| ... | ... |
@@ -429,7 +429,7 @@ func getImagesSearch(eng *engine.Engine, version version.Version, w http.Respons |
| 429 | 429 |
} |
| 430 | 430 |
var ( |
| 431 | 431 |
authEncoded = r.Header.Get("X-Registry-Auth")
|
| 432 |
- authConfig = &auth.AuthConfig{}
|
|
| 432 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 433 | 433 |
metaHeaders = map[string][]string{}
|
| 434 | 434 |
) |
| 435 | 435 |
|
| ... | ... |
@@ -438,7 +438,7 @@ func getImagesSearch(eng *engine.Engine, version version.Version, w http.Respons |
| 438 | 438 |
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
| 439 | 439 |
// for a search it is not an error if no auth was given |
| 440 | 440 |
// to increase compatibility with the existing api it is defaulting to be empty |
| 441 |
- authConfig = &auth.AuthConfig{}
|
|
| 441 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 442 | 442 |
} |
| 443 | 443 |
} |
| 444 | 444 |
for k, v := range r.Header {
|
| ... | ... |
@@ -494,7 +494,7 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response |
| 494 | 494 |
if err := parseForm(r); err != nil {
|
| 495 | 495 |
return err |
| 496 | 496 |
} |
| 497 |
- authConfig := &auth.AuthConfig{}
|
|
| 497 |
+ authConfig := ®istry.AuthConfig{}
|
|
| 498 | 498 |
|
| 499 | 499 |
authEncoded := r.Header.Get("X-Registry-Auth")
|
| 500 | 500 |
if authEncoded != "" {
|
| ... | ... |
@@ -502,7 +502,7 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response |
| 502 | 502 |
authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) |
| 503 | 503 |
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
| 504 | 504 |
// to increase compatibility to existing api it is defaulting to be empty |
| 505 |
- authConfig = &auth.AuthConfig{}
|
|
| 505 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 506 | 506 |
} |
| 507 | 507 |
} else {
|
| 508 | 508 |
// the old format is supported for compatibility if there was no authConfig header |
| ... | ... |
@@ -823,9 +823,9 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite |
| 823 | 823 |
} |
| 824 | 824 |
var ( |
| 825 | 825 |
authEncoded = r.Header.Get("X-Registry-Auth")
|
| 826 |
- authConfig = &auth.AuthConfig{}
|
|
| 826 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 827 | 827 |
configFileEncoded = r.Header.Get("X-Registry-Config")
|
| 828 |
- configFile = &auth.ConfigFile{}
|
|
| 828 |
+ configFile = ®istry.ConfigFile{}
|
|
| 829 | 829 |
job = eng.Job("build")
|
| 830 | 830 |
) |
| 831 | 831 |
|
| ... | ... |
@@ -838,7 +838,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite |
| 838 | 838 |
if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
|
| 839 | 839 |
// for a pull it is not an error if no auth was given |
| 840 | 840 |
// to increase compatibility with the existing api it is defaulting to be empty |
| 841 |
- authConfig = &auth.AuthConfig{}
|
|
| 841 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 842 | 842 |
} |
| 843 | 843 |
} |
| 844 | 844 |
|
| ... | ... |
@@ -847,7 +847,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite |
| 847 | 847 |
if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
|
| 848 | 848 |
// for a pull it is not an error if no auth was given |
| 849 | 849 |
// to increase compatibility with the existing api it is defaulting to be empty |
| 850 |
- configFile = &auth.ConfigFile{}
|
|
| 850 |
+ configFile = ®istry.ConfigFile{}
|
|
| 851 | 851 |
} |
| 852 | 852 |
} |
| 853 | 853 |
|
| 4 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,290 +0,0 @@ |
| 1 |
-package auth |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/base64" |
|
| 5 |
- "encoding/json" |
|
| 6 |
- "errors" |
|
| 7 |
- "fmt" |
|
| 8 |
- "github.com/dotcloud/docker/utils" |
|
| 9 |
- "io/ioutil" |
|
| 10 |
- "net/http" |
|
| 11 |
- "os" |
|
| 12 |
- "path" |
|
| 13 |
- "strings" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-// Where we store the config file |
|
| 17 |
-const CONFIGFILE = ".dockercfg" |
|
| 18 |
- |
|
| 19 |
-// Only used for user auth + account creation |
|
| 20 |
-const INDEXSERVER = "https://index.docker.io/v1/" |
|
| 21 |
- |
|
| 22 |
-//const INDEXSERVER = "https://indexstaging-docker.dotcloud.com/v1/" |
|
| 23 |
- |
|
| 24 |
-var ( |
|
| 25 |
- ErrConfigFileMissing = errors.New("The Auth config file is missing")
|
|
| 26 |
-) |
|
| 27 |
- |
|
| 28 |
-type AuthConfig struct {
|
|
| 29 |
- Username string `json:"username,omitempty"` |
|
| 30 |
- Password string `json:"password,omitempty"` |
|
| 31 |
- Auth string `json:"auth"` |
|
| 32 |
- Email string `json:"email"` |
|
| 33 |
- ServerAddress string `json:"serveraddress,omitempty"` |
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 |
-type ConfigFile struct {
|
|
| 37 |
- Configs map[string]AuthConfig `json:"configs,omitempty"` |
|
| 38 |
- rootPath string |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 |
-func IndexServerAddress() string {
|
|
| 42 |
- return INDEXSERVER |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-// create a base64 encoded auth string to store in config |
|
| 46 |
-func encodeAuth(authConfig *AuthConfig) string {
|
|
| 47 |
- authStr := authConfig.Username + ":" + authConfig.Password |
|
| 48 |
- msg := []byte(authStr) |
|
| 49 |
- encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg))) |
|
| 50 |
- base64.StdEncoding.Encode(encoded, msg) |
|
| 51 |
- return string(encoded) |
|
| 52 |
-} |
|
| 53 |
- |
|
| 54 |
-// decode the auth string |
|
| 55 |
-func decodeAuth(authStr string) (string, string, error) {
|
|
| 56 |
- decLen := base64.StdEncoding.DecodedLen(len(authStr)) |
|
| 57 |
- decoded := make([]byte, decLen) |
|
| 58 |
- authByte := []byte(authStr) |
|
| 59 |
- n, err := base64.StdEncoding.Decode(decoded, authByte) |
|
| 60 |
- if err != nil {
|
|
| 61 |
- return "", "", err |
|
| 62 |
- } |
|
| 63 |
- if n > decLen {
|
|
| 64 |
- return "", "", fmt.Errorf("Something went wrong decoding auth config")
|
|
| 65 |
- } |
|
| 66 |
- arr := strings.SplitN(string(decoded), ":", 2) |
|
| 67 |
- if len(arr) != 2 {
|
|
| 68 |
- return "", "", fmt.Errorf("Invalid auth configuration file")
|
|
| 69 |
- } |
|
| 70 |
- password := strings.Trim(arr[1], "\x00") |
|
| 71 |
- return arr[0], password, nil |
|
| 72 |
-} |
|
| 73 |
- |
|
| 74 |
-// load up the auth config information and return values |
|
| 75 |
-// FIXME: use the internal golang config parser |
|
| 76 |
-func LoadConfig(rootPath string) (*ConfigFile, error) {
|
|
| 77 |
- configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath}
|
|
| 78 |
- confFile := path.Join(rootPath, CONFIGFILE) |
|
| 79 |
- if _, err := os.Stat(confFile); err != nil {
|
|
| 80 |
- return &configFile, nil //missing file is not an error |
|
| 81 |
- } |
|
| 82 |
- b, err := ioutil.ReadFile(confFile) |
|
| 83 |
- if err != nil {
|
|
| 84 |
- return &configFile, err |
|
| 85 |
- } |
|
| 86 |
- |
|
| 87 |
- if err := json.Unmarshal(b, &configFile.Configs); err != nil {
|
|
| 88 |
- arr := strings.Split(string(b), "\n") |
|
| 89 |
- if len(arr) < 2 {
|
|
| 90 |
- return &configFile, fmt.Errorf("The Auth config file is empty")
|
|
| 91 |
- } |
|
| 92 |
- authConfig := AuthConfig{}
|
|
| 93 |
- origAuth := strings.Split(arr[0], " = ") |
|
| 94 |
- if len(origAuth) != 2 {
|
|
| 95 |
- return &configFile, fmt.Errorf("Invalid Auth config file")
|
|
| 96 |
- } |
|
| 97 |
- authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1]) |
|
| 98 |
- if err != nil {
|
|
| 99 |
- return &configFile, err |
|
| 100 |
- } |
|
| 101 |
- origEmail := strings.Split(arr[1], " = ") |
|
| 102 |
- if len(origEmail) != 2 {
|
|
| 103 |
- return &configFile, fmt.Errorf("Invalid Auth config file")
|
|
| 104 |
- } |
|
| 105 |
- authConfig.Email = origEmail[1] |
|
| 106 |
- authConfig.ServerAddress = IndexServerAddress() |
|
| 107 |
- configFile.Configs[IndexServerAddress()] = authConfig |
|
| 108 |
- } else {
|
|
| 109 |
- for k, authConfig := range configFile.Configs {
|
|
| 110 |
- authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth) |
|
| 111 |
- if err != nil {
|
|
| 112 |
- return &configFile, err |
|
| 113 |
- } |
|
| 114 |
- authConfig.Auth = "" |
|
| 115 |
- configFile.Configs[k] = authConfig |
|
| 116 |
- authConfig.ServerAddress = k |
|
| 117 |
- } |
|
| 118 |
- } |
|
| 119 |
- return &configFile, nil |
|
| 120 |
-} |
|
| 121 |
- |
|
| 122 |
-// save the auth config |
|
| 123 |
-func SaveConfig(configFile *ConfigFile) error {
|
|
| 124 |
- confFile := path.Join(configFile.rootPath, CONFIGFILE) |
|
| 125 |
- if len(configFile.Configs) == 0 {
|
|
| 126 |
- os.Remove(confFile) |
|
| 127 |
- return nil |
|
| 128 |
- } |
|
| 129 |
- |
|
| 130 |
- configs := make(map[string]AuthConfig, len(configFile.Configs)) |
|
| 131 |
- for k, authConfig := range configFile.Configs {
|
|
| 132 |
- authCopy := authConfig |
|
| 133 |
- |
|
| 134 |
- authCopy.Auth = encodeAuth(&authCopy) |
|
| 135 |
- authCopy.Username = "" |
|
| 136 |
- authCopy.Password = "" |
|
| 137 |
- authCopy.ServerAddress = "" |
|
| 138 |
- configs[k] = authCopy |
|
| 139 |
- } |
|
| 140 |
- |
|
| 141 |
- b, err := json.Marshal(configs) |
|
| 142 |
- if err != nil {
|
|
| 143 |
- return err |
|
| 144 |
- } |
|
| 145 |
- err = ioutil.WriteFile(confFile, b, 0600) |
|
| 146 |
- if err != nil {
|
|
| 147 |
- return err |
|
| 148 |
- } |
|
| 149 |
- return nil |
|
| 150 |
-} |
|
| 151 |
- |
|
| 152 |
-// try to register/login to the registry server |
|
| 153 |
-func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, error) {
|
|
| 154 |
- var ( |
|
| 155 |
- status string |
|
| 156 |
- reqBody []byte |
|
| 157 |
- err error |
|
| 158 |
- client = &http.Client{}
|
|
| 159 |
- reqStatusCode = 0 |
|
| 160 |
- serverAddress = authConfig.ServerAddress |
|
| 161 |
- ) |
|
| 162 |
- |
|
| 163 |
- if serverAddress == "" {
|
|
| 164 |
- serverAddress = IndexServerAddress() |
|
| 165 |
- } |
|
| 166 |
- |
|
| 167 |
- loginAgainstOfficialIndex := serverAddress == IndexServerAddress() |
|
| 168 |
- |
|
| 169 |
- // to avoid sending the server address to the server it should be removed before being marshalled |
|
| 170 |
- authCopy := *authConfig |
|
| 171 |
- authCopy.ServerAddress = "" |
|
| 172 |
- |
|
| 173 |
- jsonBody, err := json.Marshal(authCopy) |
|
| 174 |
- if err != nil {
|
|
| 175 |
- return "", fmt.Errorf("Config Error: %s", err)
|
|
| 176 |
- } |
|
| 177 |
- |
|
| 178 |
- // using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status. |
|
| 179 |
- b := strings.NewReader(string(jsonBody)) |
|
| 180 |
- req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b) |
|
| 181 |
- if err != nil {
|
|
| 182 |
- return "", fmt.Errorf("Server Error: %s", err)
|
|
| 183 |
- } |
|
| 184 |
- reqStatusCode = req1.StatusCode |
|
| 185 |
- defer req1.Body.Close() |
|
| 186 |
- reqBody, err = ioutil.ReadAll(req1.Body) |
|
| 187 |
- if err != nil {
|
|
| 188 |
- return "", fmt.Errorf("Server Error: [%#v] %s", reqStatusCode, err)
|
|
| 189 |
- } |
|
| 190 |
- |
|
| 191 |
- if reqStatusCode == 201 {
|
|
| 192 |
- if loginAgainstOfficialIndex {
|
|
| 193 |
- status = "Account created. Please use the confirmation link we sent" + |
|
| 194 |
- " to your e-mail to activate it." |
|
| 195 |
- } else {
|
|
| 196 |
- status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it." |
|
| 197 |
- } |
|
| 198 |
- } else if reqStatusCode == 400 {
|
|
| 199 |
- if string(reqBody) == "\"Username or email already exists\"" {
|
|
| 200 |
- req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
|
|
| 201 |
- req.SetBasicAuth(authConfig.Username, authConfig.Password) |
|
| 202 |
- resp, err := client.Do(req) |
|
| 203 |
- if err != nil {
|
|
| 204 |
- return "", err |
|
| 205 |
- } |
|
| 206 |
- defer resp.Body.Close() |
|
| 207 |
- body, err := ioutil.ReadAll(resp.Body) |
|
| 208 |
- if err != nil {
|
|
| 209 |
- return "", err |
|
| 210 |
- } |
|
| 211 |
- if resp.StatusCode == 200 {
|
|
| 212 |
- status = "Login Succeeded" |
|
| 213 |
- } else if resp.StatusCode == 401 {
|
|
| 214 |
- return "", fmt.Errorf("Wrong login/password, please try again")
|
|
| 215 |
- } else if resp.StatusCode == 403 {
|
|
| 216 |
- if loginAgainstOfficialIndex {
|
|
| 217 |
- return "", fmt.Errorf("Login: Account is not Active. Please check your e-mail for a confirmation link.")
|
|
| 218 |
- } |
|
| 219 |
- return "", fmt.Errorf("Login: Account is not Active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
|
|
| 220 |
- } else {
|
|
| 221 |
- return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, resp.StatusCode, resp.Header)
|
|
| 222 |
- } |
|
| 223 |
- } else {
|
|
| 224 |
- return "", fmt.Errorf("Registration: %s", reqBody)
|
|
| 225 |
- } |
|
| 226 |
- } else if reqStatusCode == 401 {
|
|
| 227 |
- // This case would happen with private registries where /v1/users is |
|
| 228 |
- // protected, so people can use `docker login` as an auth check. |
|
| 229 |
- req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
|
|
| 230 |
- req.SetBasicAuth(authConfig.Username, authConfig.Password) |
|
| 231 |
- resp, err := client.Do(req) |
|
| 232 |
- if err != nil {
|
|
| 233 |
- return "", err |
|
| 234 |
- } |
|
| 235 |
- defer resp.Body.Close() |
|
| 236 |
- body, err := ioutil.ReadAll(resp.Body) |
|
| 237 |
- if err != nil {
|
|
| 238 |
- return "", err |
|
| 239 |
- } |
|
| 240 |
- if resp.StatusCode == 200 {
|
|
| 241 |
- status = "Login Succeeded" |
|
| 242 |
- } else if resp.StatusCode == 401 {
|
|
| 243 |
- return "", fmt.Errorf("Wrong login/password, please try again")
|
|
| 244 |
- } else {
|
|
| 245 |
- return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
|
|
| 246 |
- resp.StatusCode, resp.Header) |
|
| 247 |
- } |
|
| 248 |
- } else {
|
|
| 249 |
- return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
|
|
| 250 |
- } |
|
| 251 |
- return status, nil |
|
| 252 |
-} |
|
| 253 |
- |
|
| 254 |
-// this method matches a auth configuration to a server address or a url |
|
| 255 |
-func (config *ConfigFile) ResolveAuthConfig(hostname string) AuthConfig {
|
|
| 256 |
- if hostname == IndexServerAddress() || len(hostname) == 0 {
|
|
| 257 |
- // default to the index server |
|
| 258 |
- return config.Configs[IndexServerAddress()] |
|
| 259 |
- } |
|
| 260 |
- |
|
| 261 |
- // First try the happy case |
|
| 262 |
- if c, found := config.Configs[hostname]; found {
|
|
| 263 |
- return c |
|
| 264 |
- } |
|
| 265 |
- |
|
| 266 |
- convertToHostname := func(url string) string {
|
|
| 267 |
- stripped := url |
|
| 268 |
- if strings.HasPrefix(url, "http://") {
|
|
| 269 |
- stripped = strings.Replace(url, "http://", "", 1) |
|
| 270 |
- } else if strings.HasPrefix(url, "https://") {
|
|
| 271 |
- stripped = strings.Replace(url, "https://", "", 1) |
|
| 272 |
- } |
|
| 273 |
- |
|
| 274 |
- nameParts := strings.SplitN(stripped, "/", 2) |
|
| 275 |
- |
|
| 276 |
- return nameParts[0] |
|
| 277 |
- } |
|
| 278 |
- |
|
| 279 |
- // Maybe they have a legacy config file, we will iterate the keys converting |
|
| 280 |
- // them to the new format and testing |
|
| 281 |
- normalizedHostename := convertToHostname(hostname) |
|
| 282 |
- for registry, config := range config.Configs {
|
|
| 283 |
- if registryHostname := convertToHostname(registry); registryHostname == normalizedHostename {
|
|
| 284 |
- return config |
|
| 285 |
- } |
|
| 286 |
- } |
|
| 287 |
- |
|
| 288 |
- // When all else fails, return an empty auth config |
|
| 289 |
- return AuthConfig{}
|
|
| 290 |
-} |
| 291 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,149 +0,0 @@ |
| 1 |
-package auth |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "io/ioutil" |
|
| 5 |
- "os" |
|
| 6 |
- "testing" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func TestEncodeAuth(t *testing.T) {
|
|
| 10 |
- newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
|
|
| 11 |
- authStr := encodeAuth(newAuthConfig) |
|
| 12 |
- decAuthConfig := &AuthConfig{}
|
|
| 13 |
- var err error |
|
| 14 |
- decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr) |
|
| 15 |
- if err != nil {
|
|
| 16 |
- t.Fatal(err) |
|
| 17 |
- } |
|
| 18 |
- if newAuthConfig.Username != decAuthConfig.Username {
|
|
| 19 |
- t.Fatal("Encode Username doesn't match decoded Username")
|
|
| 20 |
- } |
|
| 21 |
- if newAuthConfig.Password != decAuthConfig.Password {
|
|
| 22 |
- t.Fatal("Encode Password doesn't match decoded Password")
|
|
| 23 |
- } |
|
| 24 |
- if authStr != "a2VuOnRlc3Q=" {
|
|
| 25 |
- t.Fatal("AuthString encoding isn't correct.")
|
|
| 26 |
- } |
|
| 27 |
-} |
|
| 28 |
- |
|
| 29 |
-func setupTempConfigFile() (*ConfigFile, error) {
|
|
| 30 |
- root, err := ioutil.TempDir("", "docker-test-auth")
|
|
| 31 |
- if err != nil {
|
|
| 32 |
- return nil, err |
|
| 33 |
- } |
|
| 34 |
- configFile := &ConfigFile{
|
|
| 35 |
- rootPath: root, |
|
| 36 |
- Configs: make(map[string]AuthConfig), |
|
| 37 |
- } |
|
| 38 |
- |
|
| 39 |
- for _, registry := range []string{"testIndex", IndexServerAddress()} {
|
|
| 40 |
- configFile.Configs[registry] = AuthConfig{
|
|
| 41 |
- Username: "docker-user", |
|
| 42 |
- Password: "docker-pass", |
|
| 43 |
- Email: "docker@docker.io", |
|
| 44 |
- } |
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- return configFile, nil |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-func TestSameAuthDataPostSave(t *testing.T) {
|
|
| 51 |
- configFile, err := setupTempConfigFile() |
|
| 52 |
- if err != nil {
|
|
| 53 |
- t.Fatal(err) |
|
| 54 |
- } |
|
| 55 |
- defer os.RemoveAll(configFile.rootPath) |
|
| 56 |
- |
|
| 57 |
- err = SaveConfig(configFile) |
|
| 58 |
- if err != nil {
|
|
| 59 |
- t.Fatal(err) |
|
| 60 |
- } |
|
| 61 |
- |
|
| 62 |
- authConfig := configFile.Configs["testIndex"] |
|
| 63 |
- if authConfig.Username != "docker-user" {
|
|
| 64 |
- t.Fail() |
|
| 65 |
- } |
|
| 66 |
- if authConfig.Password != "docker-pass" {
|
|
| 67 |
- t.Fail() |
|
| 68 |
- } |
|
| 69 |
- if authConfig.Email != "docker@docker.io" {
|
|
| 70 |
- t.Fail() |
|
| 71 |
- } |
|
| 72 |
- if authConfig.Auth != "" {
|
|
| 73 |
- t.Fail() |
|
| 74 |
- } |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-func TestResolveAuthConfigIndexServer(t *testing.T) {
|
|
| 78 |
- configFile, err := setupTempConfigFile() |
|
| 79 |
- if err != nil {
|
|
| 80 |
- t.Fatal(err) |
|
| 81 |
- } |
|
| 82 |
- defer os.RemoveAll(configFile.rootPath) |
|
| 83 |
- |
|
| 84 |
- for _, registry := range []string{"", IndexServerAddress()} {
|
|
| 85 |
- resolved := configFile.ResolveAuthConfig(registry) |
|
| 86 |
- if resolved != configFile.Configs[IndexServerAddress()] {
|
|
| 87 |
- t.Fail() |
|
| 88 |
- } |
|
| 89 |
- } |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-func TestResolveAuthConfigFullURL(t *testing.T) {
|
|
| 93 |
- configFile, err := setupTempConfigFile() |
|
| 94 |
- if err != nil {
|
|
| 95 |
- t.Fatal(err) |
|
| 96 |
- } |
|
| 97 |
- defer os.RemoveAll(configFile.rootPath) |
|
| 98 |
- |
|
| 99 |
- registryAuth := AuthConfig{
|
|
| 100 |
- Username: "foo-user", |
|
| 101 |
- Password: "foo-pass", |
|
| 102 |
- Email: "foo@example.com", |
|
| 103 |
- } |
|
| 104 |
- localAuth := AuthConfig{
|
|
| 105 |
- Username: "bar-user", |
|
| 106 |
- Password: "bar-pass", |
|
| 107 |
- Email: "bar@example.com", |
|
| 108 |
- } |
|
| 109 |
- configFile.Configs["https://registry.example.com/v1/"] = registryAuth |
|
| 110 |
- configFile.Configs["http://localhost:8000/v1/"] = localAuth |
|
| 111 |
- configFile.Configs["registry.com"] = registryAuth |
|
| 112 |
- |
|
| 113 |
- validRegistries := map[string][]string{
|
|
| 114 |
- "https://registry.example.com/v1/": {
|
|
| 115 |
- "https://registry.example.com/v1/", |
|
| 116 |
- "http://registry.example.com/v1/", |
|
| 117 |
- "registry.example.com", |
|
| 118 |
- "registry.example.com/v1/", |
|
| 119 |
- }, |
|
| 120 |
- "http://localhost:8000/v1/": {
|
|
| 121 |
- "https://localhost:8000/v1/", |
|
| 122 |
- "http://localhost:8000/v1/", |
|
| 123 |
- "localhost:8000", |
|
| 124 |
- "localhost:8000/v1/", |
|
| 125 |
- }, |
|
| 126 |
- "registry.com": {
|
|
| 127 |
- "https://registry.com/v1/", |
|
| 128 |
- "http://registry.com/v1/", |
|
| 129 |
- "registry.com", |
|
| 130 |
- "registry.com/v1/", |
|
| 131 |
- }, |
|
| 132 |
- } |
|
| 133 |
- |
|
| 134 |
- for configKey, registries := range validRegistries {
|
|
| 135 |
- for _, registry := range registries {
|
|
| 136 |
- var ( |
|
| 137 |
- configured AuthConfig |
|
| 138 |
- ok bool |
|
| 139 |
- ) |
|
| 140 |
- resolved := configFile.ResolveAuthConfig(registry) |
|
| 141 |
- if configured, ok = configFile.Configs[configKey]; !ok {
|
|
| 142 |
- t.Fail() |
|
| 143 |
- } |
|
| 144 |
- if resolved.Email != configured.Email {
|
|
| 145 |
- t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
|
|
| 146 |
- } |
|
| 147 |
- } |
|
| 148 |
- } |
|
| 149 |
-} |
| ... | ... |
@@ -7,7 +7,6 @@ import ( |
| 7 | 7 |
"errors" |
| 8 | 8 |
"fmt" |
| 9 | 9 |
"github.com/dotcloud/docker/archive" |
| 10 |
- "github.com/dotcloud/docker/auth" |
|
| 11 | 10 |
"github.com/dotcloud/docker/registry" |
| 12 | 11 |
"github.com/dotcloud/docker/runconfig" |
| 13 | 12 |
"github.com/dotcloud/docker/runtime" |
| ... | ... |
@@ -49,8 +48,8 @@ type buildFile struct {
|
| 49 | 49 |
utilizeCache bool |
| 50 | 50 |
rm bool |
| 51 | 51 |
|
| 52 |
- authConfig *auth.AuthConfig |
|
| 53 |
- configFile *auth.ConfigFile |
|
| 52 |
+ authConfig *registry.AuthConfig |
|
| 53 |
+ configFile *registry.ConfigFile |
|
| 54 | 54 |
|
| 55 | 55 |
tmpContainers map[string]struct{}
|
| 56 | 56 |
tmpImages map[string]struct{}
|
| ... | ... |
@@ -793,7 +792,7 @@ func (b *buildFile) BuildStep(name, expression string) error {
|
| 793 | 793 |
return nil |
| 794 | 794 |
} |
| 795 | 795 |
|
| 796 |
-func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *auth.AuthConfig, authConfigFile *auth.ConfigFile) BuildFile {
|
|
| 796 |
+func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *registry.AuthConfig, authConfigFile *registry.ConfigFile) BuildFile {
|
|
| 797 | 797 |
return &buildFile{
|
| 798 | 798 |
runtime: srv.runtime, |
| 799 | 799 |
srv: srv, |
| ... | ... |
@@ -4,7 +4,7 @@ import ( |
| 4 | 4 |
"crypto/rand" |
| 5 | 5 |
"encoding/hex" |
| 6 | 6 |
"fmt" |
| 7 |
- "github.com/dotcloud/docker/auth" |
|
| 7 |
+ "github.com/dotcloud/docker/registry" |
|
| 8 | 8 |
"os" |
| 9 | 9 |
"strings" |
| 10 | 10 |
"testing" |
| ... | ... |
@@ -18,13 +18,13 @@ import ( |
| 18 | 18 |
func TestLogin(t *testing.T) {
|
| 19 | 19 |
os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com")
|
| 20 | 20 |
defer os.Setenv("DOCKER_INDEX_URL", "")
|
| 21 |
- authConfig := &auth.AuthConfig{
|
|
| 21 |
+ authConfig := ®istry.AuthConfig{
|
|
| 22 | 22 |
Username: "unittester", |
| 23 | 23 |
Password: "surlautrerivejetattendrai", |
| 24 | 24 |
Email: "noise+unittester@docker.com", |
| 25 | 25 |
ServerAddress: "https://indexstaging-docker.dotcloud.com/v1/", |
| 26 | 26 |
} |
| 27 |
- status, err := auth.Login(authConfig, nil) |
|
| 27 |
+ status, err := registry.Login(authConfig, nil) |
|
| 28 | 28 |
if err != nil {
|
| 29 | 29 |
t.Fatal(err) |
| 30 | 30 |
} |
| ... | ... |
@@ -41,13 +41,13 @@ func TestCreateAccount(t *testing.T) {
|
| 41 | 41 |
} |
| 42 | 42 |
token := hex.EncodeToString(tokenBuffer)[:12] |
| 43 | 43 |
username := "ut" + token |
| 44 |
- authConfig := &auth.AuthConfig{
|
|
| 44 |
+ authConfig := ®istry.AuthConfig{
|
|
| 45 | 45 |
Username: username, |
| 46 | 46 |
Password: "test42", |
| 47 | 47 |
Email: fmt.Sprintf("docker-ut+%s@example.com", token),
|
| 48 | 48 |
ServerAddress: "https://indexstaging-docker.dotcloud.com/v1/", |
| 49 | 49 |
} |
| 50 |
- status, err := auth.Login(authConfig, nil) |
|
| 50 |
+ status, err := registry.Login(authConfig, nil) |
|
| 51 | 51 |
if err != nil {
|
| 52 | 52 |
t.Fatal(err) |
| 53 | 53 |
} |
| ... | ... |
@@ -59,7 +59,7 @@ func TestCreateAccount(t *testing.T) {
|
| 59 | 59 |
t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status)
|
| 60 | 60 |
} |
| 61 | 61 |
|
| 62 |
- status, err = auth.Login(authConfig, nil) |
|
| 62 |
+ status, err = registry.Login(authConfig, nil) |
|
| 63 | 63 |
if err == nil {
|
| 64 | 64 |
t.Fatalf("Expected error but found nil instead")
|
| 65 | 65 |
} |
| 66 | 66 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,290 @@ |
| 0 |
+package registry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/base64" |
|
| 4 |
+ "encoding/json" |
|
| 5 |
+ "errors" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "github.com/dotcloud/docker/utils" |
|
| 8 |
+ "io/ioutil" |
|
| 9 |
+ "net/http" |
|
| 10 |
+ "os" |
|
| 11 |
+ "path" |
|
| 12 |
+ "strings" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+// Where we store the config file |
|
| 16 |
+const CONFIGFILE = ".dockercfg" |
|
| 17 |
+ |
|
| 18 |
+// Only used for user auth + account creation |
|
| 19 |
+const INDEXSERVER = "https://index.docker.io/v1/" |
|
| 20 |
+ |
|
| 21 |
+//const INDEXSERVER = "https://indexstaging-docker.dotcloud.com/v1/" |
|
| 22 |
+ |
|
| 23 |
+var ( |
|
| 24 |
+ ErrConfigFileMissing = errors.New("The Auth config file is missing")
|
|
| 25 |
+) |
|
| 26 |
+ |
|
| 27 |
+type AuthConfig struct {
|
|
| 28 |
+ Username string `json:"username,omitempty"` |
|
| 29 |
+ Password string `json:"password,omitempty"` |
|
| 30 |
+ Auth string `json:"auth"` |
|
| 31 |
+ Email string `json:"email"` |
|
| 32 |
+ ServerAddress string `json:"serveraddress,omitempty"` |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+type ConfigFile struct {
|
|
| 36 |
+ Configs map[string]AuthConfig `json:"configs,omitempty"` |
|
| 37 |
+ rootPath string |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+func IndexServerAddress() string {
|
|
| 41 |
+ return INDEXSERVER |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// create a base64 encoded auth string to store in config |
|
| 45 |
+func encodeAuth(authConfig *AuthConfig) string {
|
|
| 46 |
+ authStr := authConfig.Username + ":" + authConfig.Password |
|
| 47 |
+ msg := []byte(authStr) |
|
| 48 |
+ encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg))) |
|
| 49 |
+ base64.StdEncoding.Encode(encoded, msg) |
|
| 50 |
+ return string(encoded) |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// decode the auth string |
|
| 54 |
+func decodeAuth(authStr string) (string, string, error) {
|
|
| 55 |
+ decLen := base64.StdEncoding.DecodedLen(len(authStr)) |
|
| 56 |
+ decoded := make([]byte, decLen) |
|
| 57 |
+ authByte := []byte(authStr) |
|
| 58 |
+ n, err := base64.StdEncoding.Decode(decoded, authByte) |
|
| 59 |
+ if err != nil {
|
|
| 60 |
+ return "", "", err |
|
| 61 |
+ } |
|
| 62 |
+ if n > decLen {
|
|
| 63 |
+ return "", "", fmt.Errorf("Something went wrong decoding auth config")
|
|
| 64 |
+ } |
|
| 65 |
+ arr := strings.SplitN(string(decoded), ":", 2) |
|
| 66 |
+ if len(arr) != 2 {
|
|
| 67 |
+ return "", "", fmt.Errorf("Invalid auth configuration file")
|
|
| 68 |
+ } |
|
| 69 |
+ password := strings.Trim(arr[1], "\x00") |
|
| 70 |
+ return arr[0], password, nil |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+// load up the auth config information and return values |
|
| 74 |
+// FIXME: use the internal golang config parser |
|
| 75 |
+func LoadConfig(rootPath string) (*ConfigFile, error) {
|
|
| 76 |
+ configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath}
|
|
| 77 |
+ confFile := path.Join(rootPath, CONFIGFILE) |
|
| 78 |
+ if _, err := os.Stat(confFile); err != nil {
|
|
| 79 |
+ return &configFile, nil //missing file is not an error |
|
| 80 |
+ } |
|
| 81 |
+ b, err := ioutil.ReadFile(confFile) |
|
| 82 |
+ if err != nil {
|
|
| 83 |
+ return &configFile, err |
|
| 84 |
+ } |
|
| 85 |
+ |
|
| 86 |
+ if err := json.Unmarshal(b, &configFile.Configs); err != nil {
|
|
| 87 |
+ arr := strings.Split(string(b), "\n") |
|
| 88 |
+ if len(arr) < 2 {
|
|
| 89 |
+ return &configFile, fmt.Errorf("The Auth config file is empty")
|
|
| 90 |
+ } |
|
| 91 |
+ authConfig := AuthConfig{}
|
|
| 92 |
+ origAuth := strings.Split(arr[0], " = ") |
|
| 93 |
+ if len(origAuth) != 2 {
|
|
| 94 |
+ return &configFile, fmt.Errorf("Invalid Auth config file")
|
|
| 95 |
+ } |
|
| 96 |
+ authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1]) |
|
| 97 |
+ if err != nil {
|
|
| 98 |
+ return &configFile, err |
|
| 99 |
+ } |
|
| 100 |
+ origEmail := strings.Split(arr[1], " = ") |
|
| 101 |
+ if len(origEmail) != 2 {
|
|
| 102 |
+ return &configFile, fmt.Errorf("Invalid Auth config file")
|
|
| 103 |
+ } |
|
| 104 |
+ authConfig.Email = origEmail[1] |
|
| 105 |
+ authConfig.ServerAddress = IndexServerAddress() |
|
| 106 |
+ configFile.Configs[IndexServerAddress()] = authConfig |
|
| 107 |
+ } else {
|
|
| 108 |
+ for k, authConfig := range configFile.Configs {
|
|
| 109 |
+ authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth) |
|
| 110 |
+ if err != nil {
|
|
| 111 |
+ return &configFile, err |
|
| 112 |
+ } |
|
| 113 |
+ authConfig.Auth = "" |
|
| 114 |
+ configFile.Configs[k] = authConfig |
|
| 115 |
+ authConfig.ServerAddress = k |
|
| 116 |
+ } |
|
| 117 |
+ } |
|
| 118 |
+ return &configFile, nil |
|
| 119 |
+} |
|
| 120 |
+ |
|
| 121 |
+// save the auth config |
|
| 122 |
+func SaveConfig(configFile *ConfigFile) error {
|
|
| 123 |
+ confFile := path.Join(configFile.rootPath, CONFIGFILE) |
|
| 124 |
+ if len(configFile.Configs) == 0 {
|
|
| 125 |
+ os.Remove(confFile) |
|
| 126 |
+ return nil |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ configs := make(map[string]AuthConfig, len(configFile.Configs)) |
|
| 130 |
+ for k, authConfig := range configFile.Configs {
|
|
| 131 |
+ authCopy := authConfig |
|
| 132 |
+ |
|
| 133 |
+ authCopy.Auth = encodeAuth(&authCopy) |
|
| 134 |
+ authCopy.Username = "" |
|
| 135 |
+ authCopy.Password = "" |
|
| 136 |
+ authCopy.ServerAddress = "" |
|
| 137 |
+ configs[k] = authCopy |
|
| 138 |
+ } |
|
| 139 |
+ |
|
| 140 |
+ b, err := json.Marshal(configs) |
|
| 141 |
+ if err != nil {
|
|
| 142 |
+ return err |
|
| 143 |
+ } |
|
| 144 |
+ err = ioutil.WriteFile(confFile, b, 0600) |
|
| 145 |
+ if err != nil {
|
|
| 146 |
+ return err |
|
| 147 |
+ } |
|
| 148 |
+ return nil |
|
| 149 |
+} |
|
| 150 |
+ |
|
| 151 |
+// try to register/login to the registry server |
|
| 152 |
+func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, error) {
|
|
| 153 |
+ var ( |
|
| 154 |
+ status string |
|
| 155 |
+ reqBody []byte |
|
| 156 |
+ err error |
|
| 157 |
+ client = &http.Client{}
|
|
| 158 |
+ reqStatusCode = 0 |
|
| 159 |
+ serverAddress = authConfig.ServerAddress |
|
| 160 |
+ ) |
|
| 161 |
+ |
|
| 162 |
+ if serverAddress == "" {
|
|
| 163 |
+ serverAddress = IndexServerAddress() |
|
| 164 |
+ } |
|
| 165 |
+ |
|
| 166 |
+ loginAgainstOfficialIndex := serverAddress == IndexServerAddress() |
|
| 167 |
+ |
|
| 168 |
+ // to avoid sending the server address to the server it should be removed before being marshalled |
|
| 169 |
+ authCopy := *authConfig |
|
| 170 |
+ authCopy.ServerAddress = "" |
|
| 171 |
+ |
|
| 172 |
+ jsonBody, err := json.Marshal(authCopy) |
|
| 173 |
+ if err != nil {
|
|
| 174 |
+ return "", fmt.Errorf("Config Error: %s", err)
|
|
| 175 |
+ } |
|
| 176 |
+ |
|
| 177 |
+ // using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status. |
|
| 178 |
+ b := strings.NewReader(string(jsonBody)) |
|
| 179 |
+ req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b) |
|
| 180 |
+ if err != nil {
|
|
| 181 |
+ return "", fmt.Errorf("Server Error: %s", err)
|
|
| 182 |
+ } |
|
| 183 |
+ reqStatusCode = req1.StatusCode |
|
| 184 |
+ defer req1.Body.Close() |
|
| 185 |
+ reqBody, err = ioutil.ReadAll(req1.Body) |
|
| 186 |
+ if err != nil {
|
|
| 187 |
+ return "", fmt.Errorf("Server Error: [%#v] %s", reqStatusCode, err)
|
|
| 188 |
+ } |
|
| 189 |
+ |
|
| 190 |
+ if reqStatusCode == 201 {
|
|
| 191 |
+ if loginAgainstOfficialIndex {
|
|
| 192 |
+ status = "Account created. Please use the confirmation link we sent" + |
|
| 193 |
+ " to your e-mail to activate it." |
|
| 194 |
+ } else {
|
|
| 195 |
+ status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it." |
|
| 196 |
+ } |
|
| 197 |
+ } else if reqStatusCode == 400 {
|
|
| 198 |
+ if string(reqBody) == "\"Username or email already exists\"" {
|
|
| 199 |
+ req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
|
|
| 200 |
+ req.SetBasicAuth(authConfig.Username, authConfig.Password) |
|
| 201 |
+ resp, err := client.Do(req) |
|
| 202 |
+ if err != nil {
|
|
| 203 |
+ return "", err |
|
| 204 |
+ } |
|
| 205 |
+ defer resp.Body.Close() |
|
| 206 |
+ body, err := ioutil.ReadAll(resp.Body) |
|
| 207 |
+ if err != nil {
|
|
| 208 |
+ return "", err |
|
| 209 |
+ } |
|
| 210 |
+ if resp.StatusCode == 200 {
|
|
| 211 |
+ status = "Login Succeeded" |
|
| 212 |
+ } else if resp.StatusCode == 401 {
|
|
| 213 |
+ return "", fmt.Errorf("Wrong login/password, please try again")
|
|
| 214 |
+ } else if resp.StatusCode == 403 {
|
|
| 215 |
+ if loginAgainstOfficialIndex {
|
|
| 216 |
+ return "", fmt.Errorf("Login: Account is not Active. Please check your e-mail for a confirmation link.")
|
|
| 217 |
+ } |
|
| 218 |
+ return "", fmt.Errorf("Login: Account is not Active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
|
|
| 219 |
+ } else {
|
|
| 220 |
+ return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, resp.StatusCode, resp.Header)
|
|
| 221 |
+ } |
|
| 222 |
+ } else {
|
|
| 223 |
+ return "", fmt.Errorf("Registration: %s", reqBody)
|
|
| 224 |
+ } |
|
| 225 |
+ } else if reqStatusCode == 401 {
|
|
| 226 |
+ // This case would happen with private registries where /v1/users is |
|
| 227 |
+ // protected, so people can use `docker login` as an auth check. |
|
| 228 |
+ req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
|
|
| 229 |
+ req.SetBasicAuth(authConfig.Username, authConfig.Password) |
|
| 230 |
+ resp, err := client.Do(req) |
|
| 231 |
+ if err != nil {
|
|
| 232 |
+ return "", err |
|
| 233 |
+ } |
|
| 234 |
+ defer resp.Body.Close() |
|
| 235 |
+ body, err := ioutil.ReadAll(resp.Body) |
|
| 236 |
+ if err != nil {
|
|
| 237 |
+ return "", err |
|
| 238 |
+ } |
|
| 239 |
+ if resp.StatusCode == 200 {
|
|
| 240 |
+ status = "Login Succeeded" |
|
| 241 |
+ } else if resp.StatusCode == 401 {
|
|
| 242 |
+ return "", fmt.Errorf("Wrong login/password, please try again")
|
|
| 243 |
+ } else {
|
|
| 244 |
+ return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
|
|
| 245 |
+ resp.StatusCode, resp.Header) |
|
| 246 |
+ } |
|
| 247 |
+ } else {
|
|
| 248 |
+ return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
|
|
| 249 |
+ } |
|
| 250 |
+ return status, nil |
|
| 251 |
+} |
|
| 252 |
+ |
|
| 253 |
+// this method matches a auth configuration to a server address or a url |
|
| 254 |
+func (config *ConfigFile) ResolveAuthConfig(hostname string) AuthConfig {
|
|
| 255 |
+ if hostname == IndexServerAddress() || len(hostname) == 0 {
|
|
| 256 |
+ // default to the index server |
|
| 257 |
+ return config.Configs[IndexServerAddress()] |
|
| 258 |
+ } |
|
| 259 |
+ |
|
| 260 |
+ // First try the happy case |
|
| 261 |
+ if c, found := config.Configs[hostname]; found {
|
|
| 262 |
+ return c |
|
| 263 |
+ } |
|
| 264 |
+ |
|
| 265 |
+ convertToHostname := func(url string) string {
|
|
| 266 |
+ stripped := url |
|
| 267 |
+ if strings.HasPrefix(url, "http://") {
|
|
| 268 |
+ stripped = strings.Replace(url, "http://", "", 1) |
|
| 269 |
+ } else if strings.HasPrefix(url, "https://") {
|
|
| 270 |
+ stripped = strings.Replace(url, "https://", "", 1) |
|
| 271 |
+ } |
|
| 272 |
+ |
|
| 273 |
+ nameParts := strings.SplitN(stripped, "/", 2) |
|
| 274 |
+ |
|
| 275 |
+ return nameParts[0] |
|
| 276 |
+ } |
|
| 277 |
+ |
|
| 278 |
+ // Maybe they have a legacy config file, we will iterate the keys converting |
|
| 279 |
+ // them to the new format and testing |
|
| 280 |
+ normalizedHostename := convertToHostname(hostname) |
|
| 281 |
+ for registry, config := range config.Configs {
|
|
| 282 |
+ if registryHostname := convertToHostname(registry); registryHostname == normalizedHostename {
|
|
| 283 |
+ return config |
|
| 284 |
+ } |
|
| 285 |
+ } |
|
| 286 |
+ |
|
| 287 |
+ // When all else fails, return an empty auth config |
|
| 288 |
+ return AuthConfig{}
|
|
| 289 |
+} |
| 0 | 290 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,149 @@ |
| 0 |
+package registry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "io/ioutil" |
|
| 4 |
+ "os" |
|
| 5 |
+ "testing" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func TestEncodeAuth(t *testing.T) {
|
|
| 9 |
+ newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
|
|
| 10 |
+ authStr := encodeAuth(newAuthConfig) |
|
| 11 |
+ decAuthConfig := &AuthConfig{}
|
|
| 12 |
+ var err error |
|
| 13 |
+ decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr) |
|
| 14 |
+ if err != nil {
|
|
| 15 |
+ t.Fatal(err) |
|
| 16 |
+ } |
|
| 17 |
+ if newAuthConfig.Username != decAuthConfig.Username {
|
|
| 18 |
+ t.Fatal("Encode Username doesn't match decoded Username")
|
|
| 19 |
+ } |
|
| 20 |
+ if newAuthConfig.Password != decAuthConfig.Password {
|
|
| 21 |
+ t.Fatal("Encode Password doesn't match decoded Password")
|
|
| 22 |
+ } |
|
| 23 |
+ if authStr != "a2VuOnRlc3Q=" {
|
|
| 24 |
+ t.Fatal("AuthString encoding isn't correct.")
|
|
| 25 |
+ } |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func setupTempConfigFile() (*ConfigFile, error) {
|
|
| 29 |
+ root, err := ioutil.TempDir("", "docker-test-auth")
|
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ return nil, err |
|
| 32 |
+ } |
|
| 33 |
+ configFile := &ConfigFile{
|
|
| 34 |
+ rootPath: root, |
|
| 35 |
+ Configs: make(map[string]AuthConfig), |
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ for _, registry := range []string{"testIndex", IndexServerAddress()} {
|
|
| 39 |
+ configFile.Configs[registry] = AuthConfig{
|
|
| 40 |
+ Username: "docker-user", |
|
| 41 |
+ Password: "docker-pass", |
|
| 42 |
+ Email: "docker@docker.io", |
|
| 43 |
+ } |
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ return configFile, nil |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func TestSameAuthDataPostSave(t *testing.T) {
|
|
| 50 |
+ configFile, err := setupTempConfigFile() |
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ t.Fatal(err) |
|
| 53 |
+ } |
|
| 54 |
+ defer os.RemoveAll(configFile.rootPath) |
|
| 55 |
+ |
|
| 56 |
+ err = SaveConfig(configFile) |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ t.Fatal(err) |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ authConfig := configFile.Configs["testIndex"] |
|
| 62 |
+ if authConfig.Username != "docker-user" {
|
|
| 63 |
+ t.Fail() |
|
| 64 |
+ } |
|
| 65 |
+ if authConfig.Password != "docker-pass" {
|
|
| 66 |
+ t.Fail() |
|
| 67 |
+ } |
|
| 68 |
+ if authConfig.Email != "docker@docker.io" {
|
|
| 69 |
+ t.Fail() |
|
| 70 |
+ } |
|
| 71 |
+ if authConfig.Auth != "" {
|
|
| 72 |
+ t.Fail() |
|
| 73 |
+ } |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+func TestResolveAuthConfigIndexServer(t *testing.T) {
|
|
| 77 |
+ configFile, err := setupTempConfigFile() |
|
| 78 |
+ if err != nil {
|
|
| 79 |
+ t.Fatal(err) |
|
| 80 |
+ } |
|
| 81 |
+ defer os.RemoveAll(configFile.rootPath) |
|
| 82 |
+ |
|
| 83 |
+ for _, registry := range []string{"", IndexServerAddress()} {
|
|
| 84 |
+ resolved := configFile.ResolveAuthConfig(registry) |
|
| 85 |
+ if resolved != configFile.Configs[IndexServerAddress()] {
|
|
| 86 |
+ t.Fail() |
|
| 87 |
+ } |
|
| 88 |
+ } |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+func TestResolveAuthConfigFullURL(t *testing.T) {
|
|
| 92 |
+ configFile, err := setupTempConfigFile() |
|
| 93 |
+ if err != nil {
|
|
| 94 |
+ t.Fatal(err) |
|
| 95 |
+ } |
|
| 96 |
+ defer os.RemoveAll(configFile.rootPath) |
|
| 97 |
+ |
|
| 98 |
+ registryAuth := AuthConfig{
|
|
| 99 |
+ Username: "foo-user", |
|
| 100 |
+ Password: "foo-pass", |
|
| 101 |
+ Email: "foo@example.com", |
|
| 102 |
+ } |
|
| 103 |
+ localAuth := AuthConfig{
|
|
| 104 |
+ Username: "bar-user", |
|
| 105 |
+ Password: "bar-pass", |
|
| 106 |
+ Email: "bar@example.com", |
|
| 107 |
+ } |
|
| 108 |
+ configFile.Configs["https://registry.example.com/v1/"] = registryAuth |
|
| 109 |
+ configFile.Configs["http://localhost:8000/v1/"] = localAuth |
|
| 110 |
+ configFile.Configs["registry.com"] = registryAuth |
|
| 111 |
+ |
|
| 112 |
+ validRegistries := map[string][]string{
|
|
| 113 |
+ "https://registry.example.com/v1/": {
|
|
| 114 |
+ "https://registry.example.com/v1/", |
|
| 115 |
+ "http://registry.example.com/v1/", |
|
| 116 |
+ "registry.example.com", |
|
| 117 |
+ "registry.example.com/v1/", |
|
| 118 |
+ }, |
|
| 119 |
+ "http://localhost:8000/v1/": {
|
|
| 120 |
+ "https://localhost:8000/v1/", |
|
| 121 |
+ "http://localhost:8000/v1/", |
|
| 122 |
+ "localhost:8000", |
|
| 123 |
+ "localhost:8000/v1/", |
|
| 124 |
+ }, |
|
| 125 |
+ "registry.com": {
|
|
| 126 |
+ "https://registry.com/v1/", |
|
| 127 |
+ "http://registry.com/v1/", |
|
| 128 |
+ "registry.com", |
|
| 129 |
+ "registry.com/v1/", |
|
| 130 |
+ }, |
|
| 131 |
+ } |
|
| 132 |
+ |
|
| 133 |
+ for configKey, registries := range validRegistries {
|
|
| 134 |
+ for _, registry := range registries {
|
|
| 135 |
+ var ( |
|
| 136 |
+ configured AuthConfig |
|
| 137 |
+ ok bool |
|
| 138 |
+ ) |
|
| 139 |
+ resolved := configFile.ResolveAuthConfig(registry) |
|
| 140 |
+ if configured, ok = configFile.Configs[configKey]; !ok {
|
|
| 141 |
+ t.Fail() |
|
| 142 |
+ } |
|
| 143 |
+ if resolved.Email != configured.Email {
|
|
| 144 |
+ t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
|
|
| 145 |
+ } |
|
| 146 |
+ } |
|
| 147 |
+ } |
|
| 148 |
+} |
| ... | ... |
@@ -6,7 +6,6 @@ import ( |
| 6 | 6 |
"encoding/json" |
| 7 | 7 |
"errors" |
| 8 | 8 |
"fmt" |
| 9 |
- "github.com/dotcloud/docker/auth" |
|
| 10 | 9 |
"github.com/dotcloud/docker/utils" |
| 11 | 10 |
"io" |
| 12 | 11 |
"io/ioutil" |
| ... | ... |
@@ -27,7 +26,7 @@ var ( |
| 27 | 27 |
) |
| 28 | 28 |
|
| 29 | 29 |
func pingRegistryEndpoint(endpoint string) (bool, error) {
|
| 30 |
- if endpoint == auth.IndexServerAddress() {
|
|
| 30 |
+ if endpoint == IndexServerAddress() {
|
|
| 31 | 31 |
// Skip the check, we now this one is valid |
| 32 | 32 |
// (and we never want to fallback to http in case of error) |
| 33 | 33 |
return false, nil |
| ... | ... |
@@ -103,7 +102,7 @@ func ResolveRepositoryName(reposName string) (string, string, error) {
|
| 103 | 103 |
nameParts[0] != "localhost" {
|
| 104 | 104 |
// This is a Docker Index repos (ex: samalba/hipache or ubuntu) |
| 105 | 105 |
err := validateRepositoryName(reposName) |
| 106 |
- return auth.IndexServerAddress(), reposName, err |
|
| 106 |
+ return IndexServerAddress(), reposName, err |
|
| 107 | 107 |
} |
| 108 | 108 |
if len(nameParts) < 2 {
|
| 109 | 109 |
// There is a dot in repos name (and no registry address) |
| ... | ... |
@@ -601,7 +600,7 @@ func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validat |
| 601 | 601 |
|
| 602 | 602 |
func (r *Registry) SearchRepositories(term string) (*SearchResults, error) {
|
| 603 | 603 |
utils.Debugf("Index server: %s", r.indexEndpoint)
|
| 604 |
- u := auth.IndexServerAddress() + "search?q=" + url.QueryEscape(term) |
|
| 604 |
+ u := IndexServerAddress() + "search?q=" + url.QueryEscape(term) |
|
| 605 | 605 |
req, err := r.reqFactory.NewRequest("GET", u, nil)
|
| 606 | 606 |
if err != nil {
|
| 607 | 607 |
return nil, err |
| ... | ... |
@@ -627,12 +626,12 @@ func (r *Registry) SearchRepositories(term string) (*SearchResults, error) {
|
| 627 | 627 |
return result, err |
| 628 | 628 |
} |
| 629 | 629 |
|
| 630 |
-func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig {
|
|
| 630 |
+func (r *Registry) GetAuthConfig(withPasswd bool) *AuthConfig {
|
|
| 631 | 631 |
password := "" |
| 632 | 632 |
if withPasswd {
|
| 633 | 633 |
password = r.authConfig.Password |
| 634 | 634 |
} |
| 635 |
- return &auth.AuthConfig{
|
|
| 635 |
+ return &AuthConfig{
|
|
| 636 | 636 |
Username: r.authConfig.Username, |
| 637 | 637 |
Password: password, |
| 638 | 638 |
Email: r.authConfig.Email, |
| ... | ... |
@@ -668,12 +667,12 @@ type ImgData struct {
|
| 668 | 668 |
|
| 669 | 669 |
type Registry struct {
|
| 670 | 670 |
client *http.Client |
| 671 |
- authConfig *auth.AuthConfig |
|
| 671 |
+ authConfig *AuthConfig |
|
| 672 | 672 |
reqFactory *utils.HTTPRequestFactory |
| 673 | 673 |
indexEndpoint string |
| 674 | 674 |
} |
| 675 | 675 |
|
| 676 |
-func NewRegistry(authConfig *auth.AuthConfig, factory *utils.HTTPRequestFactory, indexEndpoint string) (r *Registry, err error) {
|
|
| 676 |
+func NewRegistry(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, indexEndpoint string) (r *Registry, err error) {
|
|
| 677 | 677 |
httpTransport := &http.Transport{
|
| 678 | 678 |
DisableKeepAlives: true, |
| 679 | 679 |
Proxy: http.ProxyFromEnvironment, |
| ... | ... |
@@ -693,13 +692,13 @@ func NewRegistry(authConfig *auth.AuthConfig, factory *utils.HTTPRequestFactory, |
| 693 | 693 |
|
| 694 | 694 |
// If we're working with a standalone private registry over HTTPS, send Basic Auth headers |
| 695 | 695 |
// alongside our requests. |
| 696 |
- if indexEndpoint != auth.IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") {
|
|
| 696 |
+ if indexEndpoint != IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") {
|
|
| 697 | 697 |
standalone, err := pingRegistryEndpoint(indexEndpoint) |
| 698 | 698 |
if err != nil {
|
| 699 | 699 |
return nil, err |
| 700 | 700 |
} |
| 701 | 701 |
if standalone {
|
| 702 |
- utils.Debugf("Endpoint %s is eligible for private registry auth. Enabling decorator.", indexEndpoint)
|
|
| 702 |
+ utils.Debugf("Endpoint %s is eligible for private registry registry. Enabling decorator.", indexEndpoint)
|
|
| 703 | 703 |
dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password) |
| 704 | 704 |
factory.AddDecorator(dec) |
| 705 | 705 |
} |
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 | 1 |
package registry |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "github.com/dotcloud/docker/auth" |
|
| 5 | 4 |
"github.com/dotcloud/docker/utils" |
| 6 | 5 |
"strings" |
| 7 | 6 |
"testing" |
| ... | ... |
@@ -14,7 +13,7 @@ var ( |
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 | 16 |
func spawnTestRegistry(t *testing.T) *Registry {
|
| 17 |
- authConfig := &auth.AuthConfig{}
|
|
| 17 |
+ authConfig := &AuthConfig{}
|
|
| 18 | 18 |
r, err := NewRegistry(authConfig, utils.NewHTTPRequestFactory(), makeURL("/v1/"))
|
| 19 | 19 |
if err != nil {
|
| 20 | 20 |
t.Fatal(err) |
| ... | ... |
@@ -137,7 +136,7 @@ func TestResolveRepositoryName(t *testing.T) {
|
| 137 | 137 |
if err != nil {
|
| 138 | 138 |
t.Fatal(err) |
| 139 | 139 |
} |
| 140 |
- assertEqual(t, ep, auth.IndexServerAddress(), "Expected endpoint to be index server address") |
|
| 140 |
+ assertEqual(t, ep, IndexServerAddress(), "Expected endpoint to be index server address") |
|
| 141 | 141 |
assertEqual(t, repo, "fooo/bar", "Expected resolved repo to be foo/bar") |
| 142 | 142 |
|
| 143 | 143 |
u := makeURL("")[7:]
|
| ... | ... |
@@ -4,7 +4,6 @@ import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"fmt" |
| 6 | 6 |
"github.com/dotcloud/docker/archive" |
| 7 |
- "github.com/dotcloud/docker/auth" |
|
| 8 | 7 |
"github.com/dotcloud/docker/daemonconfig" |
| 9 | 8 |
"github.com/dotcloud/docker/dockerversion" |
| 10 | 9 |
"github.com/dotcloud/docker/engine" |
| ... | ... |
@@ -199,19 +198,19 @@ func (srv *Server) ContainerKill(job *engine.Job) engine.Status {
|
| 199 | 199 |
func (srv *Server) Auth(job *engine.Job) engine.Status {
|
| 200 | 200 |
var ( |
| 201 | 201 |
err error |
| 202 |
- authConfig = &auth.AuthConfig{}
|
|
| 202 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 203 | 203 |
) |
| 204 | 204 |
|
| 205 | 205 |
job.GetenvJson("authConfig", authConfig)
|
| 206 | 206 |
// TODO: this is only done here because auth and registry need to be merged into one pkg |
| 207 |
- if addr := authConfig.ServerAddress; addr != "" && addr != auth.IndexServerAddress() {
|
|
| 207 |
+ if addr := authConfig.ServerAddress; addr != "" && addr != registry.IndexServerAddress() {
|
|
| 208 | 208 |
addr, err = registry.ExpandAndVerifyRegistryUrl(addr) |
| 209 | 209 |
if err != nil {
|
| 210 | 210 |
return job.Error(err) |
| 211 | 211 |
} |
| 212 | 212 |
authConfig.ServerAddress = addr |
| 213 | 213 |
} |
| 214 |
- status, err := auth.Login(authConfig, srv.HTTPRequestFactory(nil)) |
|
| 214 |
+ status, err := registry.Login(authConfig, srv.HTTPRequestFactory(nil)) |
|
| 215 | 215 |
if err != nil {
|
| 216 | 216 |
return job.Error(err) |
| 217 | 217 |
} |
| ... | ... |
@@ -431,8 +430,8 @@ func (srv *Server) Build(job *engine.Job) engine.Status {
|
| 431 | 431 |
suppressOutput = job.GetenvBool("q")
|
| 432 | 432 |
noCache = job.GetenvBool("nocache")
|
| 433 | 433 |
rm = job.GetenvBool("rm")
|
| 434 |
- authConfig = &auth.AuthConfig{}
|
|
| 435 |
- configFile = &auth.ConfigFile{}
|
|
| 434 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 435 |
+ configFile = ®istry.ConfigFile{}
|
|
| 436 | 436 |
tag string |
| 437 | 437 |
context io.ReadCloser |
| 438 | 438 |
) |
| ... | ... |
@@ -611,12 +610,12 @@ func (srv *Server) ImagesSearch(job *engine.Job) engine.Status {
|
| 611 | 611 |
var ( |
| 612 | 612 |
term = job.Args[0] |
| 613 | 613 |
metaHeaders = map[string][]string{}
|
| 614 |
- authConfig = &auth.AuthConfig{}
|
|
| 614 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 615 | 615 |
) |
| 616 | 616 |
job.GetenvJson("authConfig", authConfig)
|
| 617 | 617 |
job.GetenvJson("metaHeaders", metaHeaders)
|
| 618 | 618 |
|
| 619 |
- r, err := registry.NewRegistry(authConfig, srv.HTTPRequestFactory(metaHeaders), auth.IndexServerAddress()) |
|
| 619 |
+ r, err := registry.NewRegistry(authConfig, srv.HTTPRequestFactory(metaHeaders), registry.IndexServerAddress()) |
|
| 620 | 620 |
if err != nil {
|
| 621 | 621 |
return job.Error(err) |
| 622 | 622 |
} |
| ... | ... |
@@ -827,7 +826,7 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status {
|
| 827 | 827 |
v.Set("ExecutionDriver", srv.runtime.ExecutionDriver().Name())
|
| 828 | 828 |
v.SetInt("NEventsListener", len(srv.listeners))
|
| 829 | 829 |
v.Set("KernelVersion", kernelVersion)
|
| 830 |
- v.Set("IndexServerAddress", auth.IndexServerAddress())
|
|
| 830 |
+ v.Set("IndexServerAddress", registry.IndexServerAddress())
|
|
| 831 | 831 |
v.Set("InitSha1", dockerversion.INITSHA1)
|
| 832 | 832 |
v.Set("InitPath", initPath)
|
| 833 | 833 |
if _, err := v.WriteTo(job.Stdout); err != nil {
|
| ... | ... |
@@ -1312,7 +1311,7 @@ func (srv *Server) ImagePull(job *engine.Job) engine.Status {
|
| 1312 | 1312 |
localName = job.Args[0] |
| 1313 | 1313 |
tag string |
| 1314 | 1314 |
sf = utils.NewStreamFormatter(job.GetenvBool("json"))
|
| 1315 |
- authConfig = &auth.AuthConfig{}
|
|
| 1315 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 1316 | 1316 |
metaHeaders map[string][]string |
| 1317 | 1317 |
) |
| 1318 | 1318 |
if len(job.Args) > 1 {
|
| ... | ... |
@@ -1350,7 +1349,7 @@ func (srv *Server) ImagePull(job *engine.Job) engine.Status {
|
| 1350 | 1350 |
return job.Error(err) |
| 1351 | 1351 |
} |
| 1352 | 1352 |
|
| 1353 |
- if endpoint == auth.IndexServerAddress() {
|
|
| 1353 |
+ if endpoint == registry.IndexServerAddress() {
|
|
| 1354 | 1354 |
// If pull "index.docker.io/foo/bar", it's stored locally under "foo/bar" |
| 1355 | 1355 |
localName = remoteName |
| 1356 | 1356 |
} |
| ... | ... |
@@ -1531,7 +1530,7 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status {
|
| 1531 | 1531 |
var ( |
| 1532 | 1532 |
localName = job.Args[0] |
| 1533 | 1533 |
sf = utils.NewStreamFormatter(job.GetenvBool("json"))
|
| 1534 |
- authConfig = &auth.AuthConfig{}
|
|
| 1534 |
+ authConfig = ®istry.AuthConfig{}
|
|
| 1535 | 1535 |
metaHeaders map[string][]string |
| 1536 | 1536 |
) |
| 1537 | 1537 |
|