Browse code

Merge pull request #28459 from dmcgowan/plugin-repository-pinning

Plugin repository pinning

Victor Vieux authored on 2016/12/09 04:28:52
Showing 10 changed files
... ...
@@ -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()
... ...
@@ -27,6 +27,7 @@ func Push(name string, rs registry.Service, metaHeader http.Header, authConfig *
27 27
 	if err != nil {
28 28
 		return "", err
29 29
 	}
30
+	repoInfo.Class = "plugin"
30 31
 
31 32
 	if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil {
32 33
 		return "", err
... ...
@@ -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