Plugin repository pinning
Victor Vieux authored on 2016/12/09 04:28:52... | ... |
@@ -60,21 +60,25 @@ func shouldV2Fallback(err errcode.Error) bool { |
60 | 60 |
return false |
61 | 61 |
} |
62 | 62 |
|
63 |
-func translatePullError(err error, ref reference.Named) error { |
|
63 |
+// TranslatePullError is used to convert an error from a registry pull |
|
64 |
+// operation to an error representing the entire pull operation. Any error |
|
65 |
+// information which is not used by the returned error gets output to |
|
66 |
+// log at info level. |
|
67 |
+func TranslatePullError(err error, ref reference.Named) error { |
|
64 | 68 |
switch v := err.(type) { |
65 | 69 |
case errcode.Errors: |
66 | 70 |
if len(v) != 0 { |
67 | 71 |
for _, extra := range v[1:] { |
68 | 72 |
logrus.Infof("Ignoring extra error returned from registry: %v", extra) |
69 | 73 |
} |
70 |
- return translatePullError(v[0], ref) |
|
74 |
+ return TranslatePullError(v[0], ref) |
|
71 | 75 |
} |
72 | 76 |
case errcode.Error: |
73 | 77 |
var newErr error |
74 | 78 |
switch v.Code { |
75 | 79 |
case errcode.ErrorCodeDenied: |
76 | 80 |
// ErrorCodeDenied is used when access to the repository was denied |
77 |
- newErr = errors.Errorf("repository %s not found: does not exist or no read access", ref.Name()) |
|
81 |
+ newErr = errors.Errorf("repository %s not found: does not exist or no pull access", ref.Name()) |
|
78 | 82 |
case v2.ErrorCodeManifestUnknown: |
79 | 83 |
newErr = errors.Errorf("manifest for %s not found", ref.String()) |
80 | 84 |
case v2.ErrorCodeNameUnknown: |
... | ... |
@@ -85,7 +89,7 @@ func translatePullError(err error, ref reference.Named) error { |
85 | 85 |
return newErr |
86 | 86 |
} |
87 | 87 |
case xfer.DoNotRetry: |
88 |
- return translatePullError(v.Err, ref) |
|
88 |
+ return TranslatePullError(v.Err, ref) |
|
89 | 89 |
} |
90 | 90 |
|
91 | 91 |
return err |
... | ... |
@@ -168,7 +168,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo |
168 | 168 |
continue |
169 | 169 |
} |
170 | 170 |
logrus.Errorf("Not continuing with pull after error: %v", err) |
171 |
- return translatePullError(err, ref) |
|
171 |
+ return TranslatePullError(err, ref) |
|
172 | 172 |
} |
173 | 173 |
|
174 | 174 |
imagePullConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "pull") |
... | ... |
@@ -179,7 +179,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo |
179 | 179 |
lastErr = fmt.Errorf("no endpoints found for %s", ref.String()) |
180 | 180 |
} |
181 | 181 |
|
182 |
- return translatePullError(lastErr, ref) |
|
182 |
+ return TranslatePullError(lastErr, ref) |
|
183 | 183 |
} |
184 | 184 |
|
185 | 185 |
// writeStatus writes a status message to out. If layersDownloaded is true, the |
... | ... |
@@ -70,17 +70,22 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end |
70 | 70 |
passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken} |
71 | 71 |
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) |
72 | 72 |
} else { |
73 |
+ scope := auth.RepositoryScope{ |
|
74 |
+ Repository: repoName, |
|
75 |
+ Actions: actions, |
|
76 |
+ } |
|
77 |
+ |
|
78 |
+ // Keep image repositories blank for scope compatibility |
|
79 |
+ if repoInfo.Class != "image" { |
|
80 |
+ scope.Class = repoInfo.Class |
|
81 |
+ } |
|
82 |
+ |
|
73 | 83 |
creds := registry.NewStaticCredentialStore(authConfig) |
74 | 84 |
tokenHandlerOptions := auth.TokenHandlerOptions{ |
75 | 85 |
Transport: authTransport, |
76 | 86 |
Credentials: creds, |
77 |
- Scopes: []auth.Scope{ |
|
78 |
- auth.RepositoryScope{ |
|
79 |
- Repository: repoName, |
|
80 |
- Actions: actions, |
|
81 |
- }, |
|
82 |
- }, |
|
83 |
- ClientID: registry.AuthClientID, |
|
87 |
+ Scopes: []auth.Scope{scope}, |
|
88 |
+ ClientID: registry.AuthClientID, |
|
84 | 89 |
} |
85 | 90 |
tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) |
86 | 91 |
basicHandler := auth.NewBasicHandler(creds) |
... | ... |
@@ -98,7 +98,7 @@ func (s *DockerHubPullSuite) TestPullNonExistingImage(c *check.C) { |
98 | 98 |
for record := range recordChan { |
99 | 99 |
if len(record.option) == 0 { |
100 | 100 |
c.Assert(record.err, checker.NotNil, check.Commentf("expected non-zero exit status when pulling non-existing image: %s", record.out)) |
101 |
- c.Assert(record.out, checker.Contains, fmt.Sprintf("repository %s not found: does not exist or no read access", record.e.repo), check.Commentf("expected image not found error messages")) |
|
101 |
+ c.Assert(record.out, checker.Contains, fmt.Sprintf("repository %s not found: does not exist or no pull access", record.e.repo), check.Commentf("expected image not found error messages")) |
|
102 | 102 |
} else { |
103 | 103 |
// pull -a on a nonexistent registry should fall back as well |
104 | 104 |
c.Assert(record.err, checker.NotNil, check.Commentf("expected non-zero exit status when pulling non-existing image: %s", record.out)) |
... | ... |
@@ -85,6 +85,7 @@ func Pull(ref reference.Named, rs registry.Service, metaheader http.Header, auth |
85 | 85 |
logrus.Debugf("pull.go: error in ResolveRepository: %v", err) |
86 | 86 |
return nil, err |
87 | 87 |
} |
88 |
+ repoInfo.Class = "plugin" |
|
88 | 89 |
|
89 | 90 |
if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil { |
90 | 91 |
logrus.Debugf("pull.go: error in ValidateRepoName: %v", err) |
... | ... |
@@ -138,9 +139,8 @@ func Pull(ref reference.Named, rs registry.Service, metaheader http.Header, auth |
138 | 138 |
} |
139 | 139 |
manifest, err := msv.Get(context.Background(), "", distribution.WithTag(tag)) |
140 | 140 |
if err != nil { |
141 |
- // TODO: change 401 to 404 |
|
142 | 141 |
logrus.Debugf("pull.go: error in msv.Get(): %v", err) |
143 |
- return nil, err |
|
142 |
+ return nil, dockerdist.TranslatePullError(err, repoInfo) |
|
144 | 143 |
} |
145 | 144 |
|
146 | 145 |
_, pl, err := manifest.Payload() |
... | ... |
@@ -280,7 +280,11 @@ func newRepositoryInfo(config *serviceConfig, name reference.Named) (*Repository |
280 | 280 |
return nil, err |
281 | 281 |
} |
282 | 282 |
official := !strings.ContainsRune(name.Name(), '/') |
283 |
- return &RepositoryInfo{name, index, official}, nil |
|
283 |
+ return &RepositoryInfo{ |
|
284 |
+ Named: name, |
|
285 |
+ Index: index, |
|
286 |
+ Official: official, |
|
287 |
+ }, nil |
|
284 | 288 |
} |
285 | 289 |
|
286 | 290 |
// ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but |
... | ... |
@@ -67,4 +67,7 @@ type RepositoryInfo struct { |
67 | 67 |
// If the registry is official, and the normalized name does not |
68 | 68 |
// contain a '/' (e.g. "foo"), then it is considered an official repo. |
69 | 69 |
Official bool |
70 |
+ // Class represents the class of the repository, such as "plugin" |
|
71 |
+ // or "image". |
|
72 |
+ Class string |
|
70 | 73 |
} |
... | ... |
@@ -44,7 +44,7 @@ github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904 |
44 | 44 |
github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7 |
45 | 45 |
|
46 | 46 |
# get graph and distribution packages |
47 |
-github.com/docker/distribution d22e09a6686c32be8c17b684b639da4b90efe320 |
|
47 |
+github.com/docker/distribution a6bf3dd064f15598166bca2d66a9962a9555139e |
|
48 | 48 |
github.com/vbatts/tar-split v0.10.1 |
49 | 49 |
|
50 | 50 |
# get go-zfs packages |
... | ... |
@@ -147,13 +147,18 @@ type Scope interface { |
147 | 147 |
// to a repository. |
148 | 148 |
type RepositoryScope struct { |
149 | 149 |
Repository string |
150 |
+ Class string |
|
150 | 151 |
Actions []string |
151 | 152 |
} |
152 | 153 |
|
153 | 154 |
// String returns the string representation of the repository |
154 | 155 |
// using the scope grammar |
155 | 156 |
func (rs RepositoryScope) String() string { |
156 |
- return fmt.Sprintf("repository:%s:%s", rs.Repository, strings.Join(rs.Actions, ",")) |
|
157 |
+ repoType := "repository" |
|
158 |
+ if rs.Class != "" { |
|
159 |
+ repoType = fmt.Sprintf("%s(%s)", repoType, rs.Class) |
|
160 |
+ } |
|
161 |
+ return fmt.Sprintf("%s:%s:%s", repoType, rs.Repository, strings.Join(rs.Actions, ",")) |
|
157 | 162 |
} |
158 | 163 |
|
159 | 164 |
// RegistryScope represents a token scope for access |