Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup: Replace ResolveRepositoryName with RepositoryInfo{} #8456

Merged
merged 3 commits into from
Jan 8, 2015
Merged

Cleanup: Replace ResolveRepositoryName with RepositoryInfo{} #8456

merged 3 commits into from
Jan 8, 2015

Conversation

dkjer
Copy link
Contributor

@dkjer dkjer commented Oct 7, 2014

Reason for cleanup

Currently, we breakdown a repository name using a combination of ResolveRepositoryName, and NewEndpoint as follows:

  debian
  \____/
     |
  repoName  =>  ResolveRepositoryName()
                      |
               ______/ \_____________________
              /                              \
  https://index.docker.io/v1/              debian
  \_________________________/              \____/
        |                                     |
     hostname  =>  NewEndpoint()          remoteName
                       |                      |
                    endpoint              ___/\______
                       |                 /           \
 https://index.docker.io/v1/      library/debian    debian
 \______/\_____________/\__/      \____________/     \___/
     |          |         |              |             |
 URL.Scheme  URL.Host  Version      remoteName~    localName



  127.0.0.1:5000/myapp _________________________________
  \__________________/                                  \
     |                                                  |
  repoName  => ResolveRepositoryName()                  |
                      |                                 |
               ______/ \______________________          |
              /                               \         |
       127.0.0.1:5000                       myapp       |
       \____________/                       \____/      |
         |                                    |         |
     hostname  =>  NewEndpoint()              |         |
                       |                      |         |
      GET https://127.0.0.1:5000/v1/_ping     |         |
                       |                      |         |
                       x (Error)              |         |
                       |                      |         |
      GET http://127.0.0.1:5000/v1/_ping      |         |
                       |                      |         |
                    endpoint                  |         |
                       |                      |         |
 http://127.0.0.1:5000/v1/                    |         |
 \_____/\____________/\__/                    |         |
     |        |         |                     |         |
 URL.Scheme  URL.Host  Version          remoteName  localName

However, policies around whether or not repository is using the 'official index' or an 'official repository' are sprinkled throughout the code. Also, rules regarding remoteName for the official repositories ('library/ prefix'), localName, which mirrors to use, etc, are also sprinkled throughout the code.

This results in awkward follow-up checks against repository names, such as

if len(strings.SplitN(name, "/", 2)) == 1 { ... "You cannot push a \"root\" repository." ...}
if endpoint.VersionString(1) == registry.IndexServerAddress() { ... [clean up names and set up mirrors] ... }

Having to track down these pieces of logic throughout the code makes modifications to repository name parsing fragile, and hard to test.

Refactors made

ResolveRepositoryName(repoName) deprecated.

Replaced with the following:

  • ValidateRepositoryName(repoName)
    • Used when a RepositoryInfo breakdown is not needed, just a validation check.
  • ParseRepositoryInfo(repoName)
    • Used when RegistryServiceConfig is not available, and just performs the breakdown (above)
  • NormalizeLocalName
    • Transforms a name for use as a key into TagStore.Repositories.
    • Used to avoid multiple official image names referring to the same image (e.g. 'library/debian' vs 'debian')
  • ResolveRepositoryInfo
    • Calls a Job("resolve_repository") to fully resolve a repository name into a RepositoryInfo
    • Result includes index info.
  • ResolveIndexInfo
    • Calls a Job("resolve_index") to resolve an index name into an IndexInfo
    • Result includes repository configuration (for now, only Mirrors are defined).

RepositoryInfo

type IndexInfo struct {
        Name       string   `json:"name"`
        Mirrors    []string `json:"mirrors"`
        IsSecure   bool     `json:"is_secure"`
        IsOfficial bool     `json:"is_official"`
}

type RepositoryInfo struct {
        Index         *IndexInfo `json:"index"`
        RemoteName    string     `json:"remote_name"`
        LocalName     string     `json:"local_name"`
        CanonicalName string     `json:"canonical_name"`
        IsOfficial    bool       `json:"is_official"`
}

Returned by ParseRepositoryInfo, or ResolveRepositoryInfo.

  • Index.Name: Replaces 'hostname'.
    • Examples: 'docker.io' (official), '127.0.0.1:8000' (private)
  • Index.Mirrors: List of mirrors for use in a pull.
    • Only configurable for official index, currently.
  • Index.IsSecure:
    • True for official index
    • Value for private index depends on --insecure-registry flag
  • Index.IsOfficial: Whether or not repository refers to the official docker.io index.
  • RemoteName: Remote repository name
    • Examples: 'library/debian', 'user/repo'
    • Policies for official repo names are resolved.
  • LocalName: Name to use in TagStore.Repositories
    • Examples: 'debian', '127.0.0.1:8000/user/repo'
    • Policies for official vs private repo names are resolved.
  • CanonicalName: 'global' name of repository.
    • Displayed to the user in information messages, e.g.: "Pulling image from "
    • For now, the CanonicalName matches LocalName rules.
  • IsOfficial: Whether or not repository refers to a library repository on the official docker.io index.

@LK4D4
Copy link
Contributor

LK4D4 commented Oct 7, 2014

Pretty big commit :/

@LK4D4
Copy link
Contributor

LK4D4 commented Oct 7, 2014

Also TestSearchOnCentralRegistry failed

@dkjer
Copy link
Contributor Author

dkjer commented Oct 7, 2014

Fix for TestSearchOnCentralRegistry committed

@LK4D4
Copy link
Contributor

LK4D4 commented Oct 7, 2014

I hope that +700 lines of code is mostly because of tests :)

@dkjer
Copy link
Contributor Author

dkjer commented Oct 7, 2014

+522 -53 lines are from tests ;)

@LK4D4
Copy link
Contributor

LK4D4 commented Oct 8, 2014

@dkjer Hehe, now we have panic in tests :)

@jessfraz
Copy link
Contributor

jessfraz commented Oct 8, 2014

@dkjer @LK4D4 this is something else, disregard for now

for _, name := range names {
pullCmd := exec.Command(dockerBinary, "pull", name)
out, exitCode, err := runCommandWithOutput(pullCmd)
errorOut(err, t, fmt.Sprintf("%s %s", out, err))
Copy link
Contributor

Choose a reason for hiding this comment

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

just a note, we removed the errorOut() function in a cleanup PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @jfrazelle I have updated the PR

@jessfraz
Copy link
Contributor

I like this a lot btw, it's a lot more readable now :) pinging @dmp42 and @dmcgowan because of the registry code cleanup

@tiborvass
Copy link
Contributor

@dkjer needs a rebase :)

@dkjer
Copy link
Contributor Author

dkjer commented Oct 21, 2014

@tiborvass rebased

@dmcgowan
Copy link
Member

I like this change as well after a quick glance through. I will give it a more thorough look later in the week.

@@ -35,7 +35,7 @@ const (
ConnectTimeout
)

func newClient(jar http.CookieJar, roots *x509.CertPool, cert *tls.Certificate, timeout TimeoutType) *http.Client {
var newClient = func(jar http.CookieJar, roots *x509.CertPool, cert *tls.Certificate, timeout TimeoutType) *http.Client {
Copy link
Member

Choose a reason for hiding this comment

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

Is this intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that was needed for wrapping newClient calls during tests.

See registry_mock_test.go func useInsecureHttpsClient()

Copy link
Member

Choose a reason for hiding this comment

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

k, thanks for pointing me there

// First try the happy case
if c, found := config.Configs[hostname]; found {
if c, found := config.Configs[configKey]; found || repoInfo.IsOfficialIndex {
Copy link
Member

Choose a reason for hiding this comment

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

When is found false while IsOfficialIndex is true and why return an empty config in that case?

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 was just to preserve the existing behavior. If we are using the IndexServerAddress we return Configs[IndexServerAddress()] even if it is empty:

func (config *ConfigFile) ResolveAuthConfig(hostname string) AuthConfig {
    if hostname == IndexServerAddress() || len(hostname) == 0 {
        // default to the index server
        return config.Configs[IndexServerAddress()]
    }

Copy link
Member

Choose a reason for hiding this comment

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

I thought perhaps you understood why that is the behavior. I agree there is no behavior change and no need to address further.

@dmcgowan
Copy link
Member

Completed my review and testing, the code and new code layout looks good. Just need to address some of the questions I couldn't answer for myself.

Ping @vbatts and @jlhawn this might interest you guys as well

@jlhawn
Copy link
Contributor

jlhawn commented Oct 30, 2014

This is great. The RepositoryInfo struct should be very helpful going forward. I wonder though if there has yet been a proposal issue opened about separating image naming from transport, and another formal design proposal on how registry mirroring should work. @dmp42 should we open some proposal issues now to start discussing ideas openly?

@dmp42
Copy link
Contributor

dmp42 commented Oct 30, 2014

Yes, we should.

Separation of naming and transport: what was floated around before was going with something like docker pull foo/bar --from http://somewhereregistry/ - or a similar way that would put the accent on what you are expecting to pull ("name"), and from where you are pulling it ("transport").

In that scenario, mirroring suddenly becomes trivial (from a client perspective).

Also, this opens the door for different transport types, eg:

docker pull foo/bar --from file:///somefolder (or docker pull foo/bar --from otherprotocol:///thing)

Indeed, let's get to a proposal.

@dmcgowan
Copy link
Member

There is a pull behavioral change with this PR. Before the name as given on the command line is used as the local name of the repository, now the name is always canonicalized and that is used locally. The effect being git pull library/ubuntu pulls the image as ubuntu and not library/ubuntu. I am not convinced this is a bad thing but I think it should be discussed. @dmp42 initial thoughts on this?

@dkjer
Copy link
Contributor Author

dkjer commented Nov 2, 2014

This PR had a lot of conflicts with 380c832 and afade42 . I ended up splitting index-related info from RepositoryInfo into IndexInfo, and reimplementing --insecure-registry.

@dmp42
Copy link
Contributor

dmp42 commented Nov 3, 2014

@dkjer careful with --insecure-registry - it's a problematic flag in its current state (ping @tiborvass @proppy )

ping @dmcgowan for additional review

@dmp42
Copy link
Contributor

dmp42 commented Nov 3, 2014

@dkjer #8898

@dmp42
Copy link
Contributor

dmp42 commented Nov 3, 2014

Also relevant: #8887

@dkjer
Copy link
Contributor Author

dkjer commented Nov 3, 2014

@dmp42 Yeah, --insecure-registry was also missing tests (everything passed, before reimplementing). I tried to just preserve existing functionality; however this sets me up to resolve conflicts for each --insecure-registry change that happens

log.Fatal(err)
}

// load registry service
Copy link
Member

Choose a reason for hiding this comment

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

Could the original flow stay the same, Register + Install and rather just update the Install call to
registry.NewService(registryCfg).Install(eng). This would keep builtins from needing to be changed and match the refactor change from the linked commit.

@LK4D4
Copy link
Contributor

LK4D4 commented Dec 23, 2014

@jfrazelle @tiborvass wanted to test this thoroughly because we almost have no tests for registry.

@jessfraz
Copy link
Contributor

ah cool carry on :)

On Mon, Dec 22, 2014 at 9:22 PM, Alexander Morozov <notifications@github.com

wrote:

@jfrazelle https://github.com/jfrazelle @tiborvass
https://github.com/tiborvass wanted to test this thoroughly because we
almost have no tests for registry.


Reply to this email directly or view it on GitHub
#8456 (comment).

@@ -232,6 +738,7 @@ func TestSearchRepositories(t *testing.T) {
assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' a ot hae 42 stars")
}

<<<<<<< HEAD
Copy link
Contributor

Choose a reason for hiding this comment

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

@dkjer bad rebase :)

Passing RepositoryInfo to ResolveAuthConfig, pullRepository, and pushRepository

Moving --registry-mirror configuration to registry config

Created resolve_repository job

Repo names with 'index.docker.io' or 'docker.io' are now synonymous with omitting an index name.

Adding test for RepositoryInfo

Adding tests for opts.StringSetOpts and registry.ValidateMirror

Fixing search term use of repoInfo

Adding integration tests for registry mirror configuration

Normalizing LookupImage image name to match LocalName parsing rules

Normalizing repository LocalName to avoid multiple references to an official image

Removing errorOut use in tests

Removing TODO comment

gofmt changes

golint comments cleanup.  renaming RegistryOptions => registry.Options, and RegistryServiceConfig => registry.ServiceConfig

Splitting out builtins.Registry and registry.NewService calls

Stray whitespace cleanup

Moving integration tests for Mirrors and InsecureRegistries into TestNewIndexInfo unit test

Factoring out ValidateRepositoryName from NewRepositoryInfo

Removing unused IndexServerURL

Allowing json marshaling of ServiceConfig.  Exposing ServiceConfig in /info

Switching to CamelCase for json marshaling

PR cleanup; removing 'Is' prefix from boolean members.  Removing unneeded json tags.

Removing non-cleanup related fix for 'localhost:[port]' in splitReposName

Merge fixes for gh9735

Fixing integration test

Reapplying #9754

Adding comment on config.IndexConfigs use from isSecureIndex

Remove unused error return value from isSecureIndex

Signed-off-by: Don Kjer <don.kjer@gmail.com>

Adding back comment in isSecureIndex

Signed-off-by: Don Kjer <don.kjer@gmail.com>
…nfig.go

Signed-off-by: Don Kjer <don.kjer@gmail.com>
@tiborvass
Copy link
Contributor

LGTM

Tested locally, cause drone doesn't like go1.3 syntax since it's using go1.4 gofmt.

Signed-off-by: Don Kjer <don.kjer@gmail.com>
@LK4D4
Copy link
Contributor

LK4D4 commented Jan 8, 2015

Haha, drone is happy.

tiborvass added a commit that referenced this pull request Jan 8, 2015
Cleanup: Replace ResolveRepositoryName with RepositoryInfo{}
@tiborvass tiborvass merged commit 6870bde into moby:master Jan 8, 2015
@tiborvass
Copy link
Contributor

Thanks so much @dkjer for your awesome contribution and infinite patience! Happy new year :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet