Skip to content

support registry mirror config reload#29650

Merged
thaJeztah merged 1 commit intomoby:masterfrom
allencloud:support-registry-mirror-config-reload
Jan 4, 2017
Merged

support registry mirror config reload#29650
thaJeztah merged 1 commit intomoby:masterfrom
allencloud:support-registry-mirror-config-reload

Conversation

@allencloud
Copy link
Contributor

@allencloud allencloud commented Dec 22, 2016

Signed-off-by: allencloud [email protected]

fixes #29594

this PR makes docker daemon support reload config registry mirror.

- What I did

  1. add registry-mirror enable reload
  2. fix a bug that duplicated mirrors will appear in the daemon and docker info
  3. add a test case for that

- How I did it

- How to verify it

- Description for the changelog

- A picture of a cute animal (not mandatory but encouraged)

@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch 6 times, most recently from af48291 to 16c8d98 Compare December 22, 2016 11:14
@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch from 16c8d98 to 07803d2 Compare December 22, 2016 16:08
daemon/daemon.go Outdated
Copy link
Member

@thaJeztah thaJeztah Dec 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(just quickly peeking at the code changes); these whitespace changes are not related to this change, and not everyone like adding too much whitespace; could you remove these?

@thaJeztah
Copy link
Member

Feature-wise, I'm ok with adding this. I do think that the whole configuration (re)load functionality needs a massive overhaul / refactor though; I think the code is getting quite messy, and a lot of duplication

@thaJeztah
Copy link
Member

Design-wise we're okay with this

@aaronlehmann do you see any possible issues with live-reloading this list?

@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch 5 times, most recently from 75a22f8 to d568ce3 Compare December 23, 2016 06:09
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this test?

Copy link
Contributor Author

@allencloud allencloud Dec 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since it fails in the test, when validateMirror.
Mirror address must have scheme. In all the test file, mirror has scheme, while this has no.

In addition, I need more investigation.

@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch 4 times, most recently from bf4ae87 to 04efd0d Compare December 23, 2016 12:02
Copy link
Contributor Author

@allencloud allencloud Dec 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since every mirror in the daemon no matter from flag, config or reload config, they will all be validated in LoadMirrors.
While ValidateMirror twice will lead to error, since the first ValidateMirror will add / at the end of the mirror address, if the new mirror enter the second ValidateMirror with a postfix of /, it will report error.

So I remove this part of ValidateMirror for flag options.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I think we should fix ValidateMirror(); it basically does two things; validate the value, and "normalise" it to a standard format (to always have a trailing /). It's odd that we don't accept a trailing / as input but actually require it to be set in the output.

This should do the trick;

func ValidateMirror(val string) (string, error) {
                return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
        }

-       if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
+       if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
                return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
        }

-       return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
+       return strings.TrimSuffix(val, "/") + "/", nil
 }

The unit test for ValidateMirror() will need to be updated as well, and some values moved from "invalid" to "valid"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, perhaps we should update those error messages a bit, as they are a bit inconsistent, and don't show which mirror/url was invalid;

// ValidateMirror validates an HTTP(S) registry mirror
func ValidateMirror(val string) (string, error) {
	uri, err := url.Parse(val)
	if err != nil {
		return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
	}
	if uri.Scheme != "http" && uri.Scheme != "https" {
		return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
	}
	if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
		return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
	}
	return strings.TrimSuffix(val, "/") + "/", nil
}
diff --git a/registry/config.go b/registry/config.go
index 79819de..88b62af 100644
--- a/registry/config.go
+++ b/registry/config.go
@@ -247,18 +247,15 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
 func ValidateMirror(val string) (string, error) {
        uri, err := url.Parse(val)
        if err != nil {
-               return "", fmt.Errorf("%s is not a valid URI", val)
+               return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
        }
-
        if uri.Scheme != "http" && uri.Scheme != "https" {
-               return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
+               return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
        }
-
-       if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
-               return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
+       if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
+               return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
        }
-
-       return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
+       return strings.TrimSuffix(val, "/") + "/", nil
 }

Copy link
Member

@thaJeztah thaJeztah Dec 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, interesting; it marks URLs containing a username/password (http://user:[email protected]/) as "valid", but the original version silently strips them from the "normalised" URL

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably do it;

// ValidateMirror validates an HTTP(S) registry mirror
func ValidateMirror(val string) (string, error) {
	uri, err := url.Parse(val)
	if err != nil {
		return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
	}
	if uri.Scheme != "http" && uri.Scheme != "https" {
		return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
	}
	if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
		return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
	}
	if uri.User != nil {
		// strip password from output
		uri.User = url.UserPassword(uri.User.Username(), "xxxxx")
		return "", fmt.Errorf("invalid mirror: username/password not allowed in URI %q", uri)
	}
	return strings.TrimSuffix(val, "/") + "/", nil
}

@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch 2 times, most recently from dcb312a to 621f082 Compare December 23, 2016 13:01
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@allencloud I left some comments, will have another look after it's updated 😊

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like overwriting the configuration, and reverting if the values are invalid. How about something like this;

// LoadMirrors loads mirrors to config, after removing duplicates.
// Returns an error if mirrors contains an invalid mirror.
func (config *serviceConfig) LoadMirrors(mirrors []string) error {
	seen := map[string]struct{}{}
	unique := []string{}
	for _, m := range mirrors {
		m2, err := ValidateMirror(m)
		if err != nil {
			return err
		}
		if _, exist := seen[m2]; exist {
			continue
		}
		unique = append(unique, m2)
		seen[m2] = struct{}{}
	}
	config.Mirrors = unique
	return nil
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I think we should fix ValidateMirror(); it basically does two things; validate the value, and "normalise" it to a standard format (to always have a trailing /). It's odd that we don't accept a trailing / as input but actually require it to be set in the output.

This should do the trick;

func ValidateMirror(val string) (string, error) {
                return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
        }

-       if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
+       if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
                return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
        }

-       return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
+       return strings.TrimSuffix(val, "/") + "/", nil
 }

The unit test for ValidateMirror() will need to be updated as well, and some values moved from "invalid" to "valid"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, perhaps we should update those error messages a bit, as they are a bit inconsistent, and don't show which mirror/url was invalid;

// ValidateMirror validates an HTTP(S) registry mirror
func ValidateMirror(val string) (string, error) {
	uri, err := url.Parse(val)
	if err != nil {
		return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
	}
	if uri.Scheme != "http" && uri.Scheme != "https" {
		return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
	}
	if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
		return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
	}
	return strings.TrimSuffix(val, "/") + "/", nil
}
diff --git a/registry/config.go b/registry/config.go
index 79819de..88b62af 100644
--- a/registry/config.go
+++ b/registry/config.go
@@ -247,18 +247,15 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
 func ValidateMirror(val string) (string, error) {
        uri, err := url.Parse(val)
        if err != nil {
-               return "", fmt.Errorf("%s is not a valid URI", val)
+               return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
        }
-
        if uri.Scheme != "http" && uri.Scheme != "https" {
-               return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
+               return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
        }
-
-       if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
-               return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
+       if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
+               return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
        }
-
-       return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
+       return strings.TrimSuffix(val, "/") + "/", nil
 }

Copy link
Member

@thaJeztah thaJeztah Dec 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, interesting; it marks URLs containing a username/password (http://user:[email protected]/) as "valid", but the original version silently strips them from the "normalised" URL

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably do it;

// ValidateMirror validates an HTTP(S) registry mirror
func ValidateMirror(val string) (string, error) {
	uri, err := url.Parse(val)
	if err != nil {
		return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
	}
	if uri.Scheme != "http" && uri.Scheme != "https" {
		return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
	}
	if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
		return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
	}
	if uri.User != nil {
		// strip password from output
		uri.User = url.UserPassword(uri.User.Username(), "xxxxx")
		return "", fmt.Errorf("invalid mirror: username/password not allowed in URI %q", uri)
	}
	return strings.TrimSuffix(val, "/") + "/", nil
}

@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch from 621f082 to 69cb253 Compare December 23, 2016 17:34
@allencloud
Copy link
Contributor Author

Thanks a lot. Really helpful comments. I updated this PR. PTAL

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This naming part updated. PTAL

@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch 5 times, most recently from 8071ded to 3ad1479 Compare December 26, 2016 15:34
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, looks like I had a review "pending" here; see my comment

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you override the original password here, as I did in my example? #29650 (comment)

We don't want passwords to be printed/logged

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a http://foo:[email protected]/ to the test?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should simplify this test, and have a single slice of test-cases, each with "before", "new", and "expected", and a property that indicates if it should fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is great. I will try that. Thanks a lot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated with clearer test. PTAL 😄

@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch 4 times, most recently from 0222770 to f7d552f Compare January 3, 2017 10:49
daemon/daemon.go Outdated

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking observation: I feel like none of the registry-related settings should be prefixed with "Daemon".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing is reasonable.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: resgitry

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not nil (i.e. omit this line)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, thanks

@aaronlehmann
Copy link

Code LGTM

@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch 2 times, most recently from 29b2e90 to e90f36d Compare January 4, 2017 02:28
@allencloud allencloud force-pushed the support-registry-mirror-config-reload branch from e90f36d to 5b9348c Compare January 4, 2017 03:04
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@thaJeztah thaJeztah merged commit d3f30d6 into moby:master Jan 4, 2017
@GordonTheTurtle GordonTheTurtle added this to the 1.14.0 milestone Jan 4, 2017
@allencloud allencloud deleted the support-registry-mirror-config-reload branch January 4, 2017 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature request] make dockerd support reloading config registry mirror

5 participants