[Feature] Reduce the amount of auth calls needed.
mgazza opened this issue · comments
Current Behavior
Not all registries are created equal. I have encountered a situation where-by a registry was created as a dumping ground of a lot of images from other registries. When I copy image from all of these repos to a path on the uber registry reg client re-auths for each sub path. This is because reg client treats each of the sub paths as separate repo's and adds and rebuilds the scope which invalidates the current token.
Expected Behavior
Reg client would identify that these are all part of the same registry and that its current auth token is valid.
Example Solution
Someway to pre-warm the bearer handler with a registry so that AddScope performs a NoOp when a sub registry is added
Version
devel 0.5.7
Environment
Please include:
-
How you are running the tool: ( via an import )
-
Your platform: Linux, or Mac, x86 or ARM.
-
Your registry: all of them, target right now is gitlab which is GCP
-
Running as binary or container: Both
-
Host platform: Linux, or Mac, x86 or ARM.
-
Registry description: ALL - Docker Hub, ECR, GCR, ACR, Harbor, registry:2.
Unfortunately I don't believe there's a way around this. Some registries like GCR will fail on a request if a token for a different repository is used. And not fail with a request to reauth with a new scope, but fail with a forbidden response even through the user would otherwise have access. To avoid those behaviors, regclient does the best it can to mimic the behavior of other registry clients, which is to seed the scope with each repo it attempts to access.
I've tried to add a work around in this particular case where-by when parsing the ref I would move a portion of the registry to the path. however it doesn't look like the path field is used
func (c *Client) MapRef(image models.DockerImage) (ref.Ref, error) {
r, err := ref.New(image.String())
if err != nil {
return ref.Ref{}, fmt.Errorf("error parsing ref: %w", err)
}
for _, s := range c.ParentRegistries {
p := path.Join(r.Registry, r.Repository)
i := strings.Index(p, s)
if i > -1 {
r.Path = p[len(s):]
r.Repository = strings.TrimRight(s[len(r.Registry)+1:], "/")
}
}
return r, nil
}
func TestClient_MapRef(t *testing.T) {
type fields struct {
RegClient *regclient.RegClient
ParentRegistries []string
}
type args struct {
image models.DockerImage
}
tests := map[string]struct {
fields fields
args args
want ref.Ref
wantErr assert.ErrorAssertionFunc
}{
"when using sub registries": {
fields: fields{
RegClient: nil,
ParentRegistries: []string{"registry.example.com/myrepo/"},
},
args: args{
image: util.ParseDockerImage("registry.example.com/myrepo/somepath/myimage:latest"),
},
want: ref.Ref{
Scheme: "reg",
Reference: "registry.example.com/myrepo/somepath/myimage:latest",
Registry: "registry.example.com",
Repository: "myrepo",
Tag: "latest",
Digest: "",
Path: "somepath/myimage",
},
wantErr: assert.NoError,
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
c := &docker.Client{
RegClient: tt.fields.RegClient,
ParentRegistries: tt.fields.ParentRegistries,
}
got, err := c.MapRef(tt.args.image)
if !tt.wantErr(t, err, fmt.Sprintf("MapRef(%v)", tt.args.image)) {
return
}
assert.Equalf(t, tt.want, got, "MapRef(%v)", tt.args.image)
})
}
}
I've fat thumbed a PR to fix this I think, I need to add some tests and other stuff but to illustrate the point right now its ok.
Ah I just tried the work around an the registry did indeed give me an error -> insufficient_scope
.
Feel free the close the PR and this Issue :D