Browse code

choose rpc code to determine status code

Signed-off-by: allencloud <allen.sun@daocloud.io>

allencloud authored on 2017/03/24 00:28:45
Showing 10 changed files
... ...
@@ -186,7 +186,7 @@ RUN set -x \
186 186
 	&& rm -rf "$GOPATH"
187 187
 
188 188
 # Get the "docker-py" source so we can run their integration tests
189
-ENV DOCKER_PY_COMMIT 4a08d04aef0595322e1b5ac7c52f28a931da85a5
189
+ENV DOCKER_PY_COMMIT dc2b24dcdd4ed7c8f6874ee5dd95716761ab6c93
190 190
 # To run integration tests docker-pycreds is required.
191 191
 # Before running the integration tests conftest.py is
192 192
 # loaded which results in loads auth.py that
... ...
@@ -142,7 +142,7 @@ RUN set -x \
142 142
 	&& rm -rf "$GOPATH"
143 143
 
144 144
 # Get the "docker-py" source so we can run their integration tests
145
-ENV DOCKER_PY_COMMIT 4a08d04aef0595322e1b5ac7c52f28a931da85a5
145
+ENV DOCKER_PY_COMMIT dc2b24dcdd4ed7c8f6874ee5dd95716761ab6c93
146 146
 # Before running the integration tests conftest.py is
147 147
 # loaded which results in loads auth.py that
148 148
 # imports the docker-pycreds module.
... ...
@@ -137,7 +137,7 @@ RUN set -x \
137 137
 	&& rm -rf "$GOPATH"
138 138
 
139 139
 # Get the "docker-py" source so we can run their integration tests
140
-ENV DOCKER_PY_COMMIT e2655f658408f9ad1f62abdef3eb6ed43c0cf324
140
+ENV DOCKER_PY_COMMIT dc2b24dcdd4ed7c8f6874ee5dd95716761ab6c93
141 141
 RUN git clone https://github.com/docker/docker-py.git /docker-py \
142 142
 	&& cd /docker-py \
143 143
 	&& git checkout -q $DOCKER_PY_COMMIT \
... ...
@@ -143,7 +143,7 @@ RUN set -x \
143 143
 	&& rm -rf "$GOPATH"
144 144
 
145 145
 # Get the "docker-py" source so we can run their integration tests
146
-ENV DOCKER_PY_COMMIT e2655f658408f9ad1f62abdef3eb6ed43c0cf324
146
+ENV DOCKER_PY_COMMIT dc2b24dcdd4ed7c8f6874ee5dd95716761ab6c93
147 147
 RUN git clone https://github.com/docker/docker-py.git /docker-py \
148 148
 	&& cd /docker-py \
149 149
 	&& git checkout -q $DOCKER_PY_COMMIT \
... ...
@@ -136,7 +136,7 @@ RUN set -x \
136 136
 	&& rm -rf "$GOPATH"
137 137
 
138 138
 # Get the "docker-py" source so we can run their integration tests
139
-ENV DOCKER_PY_COMMIT e2655f658408f9ad1f62abdef3eb6ed43c0cf324
139
+ENV DOCKER_PY_COMMIT dc2b24dcdd4ed7c8f6874ee5dd95716761ab6c93
140 140
 RUN git clone https://github.com/docker/docker-py.git /docker-py \
141 141
 	&& cd /docker-py \
142 142
 	&& git checkout -q $DOCKER_PY_COMMIT \
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"github.com/docker/docker/api/types/versions"
10 10
 	"github.com/gorilla/mux"
11 11
 	"google.golang.org/grpc"
12
+	"google.golang.org/grpc/codes"
12 13
 )
13 14
 
14 15
 // httpStatusError is an interface
... ...
@@ -44,11 +45,17 @@ func GetHTTPErrorStatusCode(err error) int {
44 44
 	case inputValidationError:
45 45
 		statusCode = http.StatusBadRequest
46 46
 	default:
47
+		statusCode = statusCodeFromGRPCError(err)
48
+		if statusCode != http.StatusInternalServerError {
49
+			return statusCode
50
+		}
51
+
47 52
 		// FIXME: this is brittle and should not be necessary, but we still need to identify if
48 53
 		// there are errors falling back into this logic.
49 54
 		// If we need to differentiate between different possible error types,
50 55
 		// we should create appropriate error types that implement the httpStatusError interface.
51 56
 		errStr := strings.ToLower(errMsg)
57
+
52 58
 		for _, status := range []struct {
53 59
 			keyword string
54 60
 			code    int
... ...
@@ -102,3 +109,36 @@ func MakeErrorHandler(err error) http.HandlerFunc {
102 102
 		}
103 103
 	}
104 104
 }
105
+
106
+// statusCodeFromGRPCError returns status code according to gRPC error
107
+func statusCodeFromGRPCError(err error) int {
108
+	switch grpc.Code(err) {
109
+	case codes.InvalidArgument: // code 3
110
+		return http.StatusBadRequest
111
+	case codes.NotFound: // code 5
112
+		return http.StatusNotFound
113
+	case codes.AlreadyExists: // code 6
114
+		return http.StatusConflict
115
+	case codes.PermissionDenied: // code 7
116
+		return http.StatusForbidden
117
+	case codes.FailedPrecondition: // code 9
118
+		return http.StatusBadRequest
119
+	case codes.Unauthenticated: // code 16
120
+		return http.StatusUnauthorized
121
+	case codes.OutOfRange: // code 11
122
+		return http.StatusBadRequest
123
+	case codes.Unimplemented: // code 12
124
+		return http.StatusNotImplemented
125
+	case codes.Unavailable: // code 14
126
+		return http.StatusServiceUnavailable
127
+	default:
128
+		// codes.Canceled(1)
129
+		// codes.Unknown(2)
130
+		// codes.DeadlineExceeded(4)
131
+		// codes.ResourceExhausted(8)
132
+		// codes.Aborted(10)
133
+		// codes.Internal(13)
134
+		// codes.DataLoss(15)
135
+		return http.StatusInternalServerError
136
+	}
137
+}
... ...
@@ -29,6 +29,10 @@ keywords: "API, Docker, rcli, REST, documentation"
29 29
  generate and rotate to a new CA certificate/key pair.
30 30
 * `POST /service/create` and `POST /services/(id or name)/update` now take the field `Platforms` as part of the service `Placement`, allowing to specify platforms supported by the service.
31 31
 * `POST /containers/(name)/wait` now accepts a `condition` query parameter to indicate which state change condition to wait for. Also, response headers are now returned immediately to acknowledge that the server has registered a wait callback for the client.
32
+* `DELETE /secrets/(name)` now returns status code 404 instead of 500 when the secret does not exist.
33
+* `POST /secrets/create` now returns status code 409 instead of 500 when creating an already existing secret.
34
+* `POST /secrets/(name)/update` now returns status code 400 instead of 500 when updating a secret's content which is not the labels.
35
+* `POST /nodes/(name)/update` now returns status code 400 instead of 500 when demoting last node fails.
32 36
 
33 37
 ## v1.29 API changes
34 38
 
... ...
@@ -114,5 +114,5 @@ func (s *DockerSwarmSuite) TestAPISwarmConfigsUpdate(c *check.C) {
114 114
 	status, out, err := d.SockRequest("POST", url, config.Spec)
115 115
 
116 116
 	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
117
-	c.Assert(status, checker.Equals, http.StatusInternalServerError, check.Commentf("output: %q", string(out)))
117
+	c.Assert(status, checker.Equals, http.StatusBadRequest, check.Commentf("output: %q", string(out)))
118 118
 }
... ...
@@ -23,18 +23,25 @@ func (s *DockerSwarmSuite) TestAPISwarmSecretsCreate(c *check.C) {
23 23
 	d := s.AddDaemon(c, true, true)
24 24
 
25 25
 	testName := "test_secret"
26
-	id := d.CreateSecret(c, swarm.SecretSpec{
26
+	secretSpec := swarm.SecretSpec{
27 27
 		Annotations: swarm.Annotations{
28 28
 			Name: testName,
29 29
 		},
30 30
 		Data: []byte("TESTINGDATA"),
31
-	})
31
+	}
32
+
33
+	id := d.CreateSecret(c, secretSpec)
32 34
 	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
33 35
 
34 36
 	secrets := d.ListSecrets(c)
35 37
 	c.Assert(len(secrets), checker.Equals, 1, check.Commentf("secrets: %#v", secrets))
36 38
 	name := secrets[0].Spec.Annotations.Name
37 39
 	c.Assert(name, checker.Equals, testName, check.Commentf("secret: %s", name))
40
+
41
+	// create an already existing secret, daemon should return a status code of 409
42
+	status, out, err := d.SockRequest("POST", "/secrets/create", secretSpec)
43
+	c.Assert(err, checker.IsNil)
44
+	c.Assert(status, checker.Equals, http.StatusConflict, check.Commentf("secret create: %s", string(out)))
38 45
 }
39 46
 
40 47
 func (s *DockerSwarmSuite) TestAPISwarmSecretsDelete(c *check.C) {
... ...
@@ -55,6 +62,13 @@ func (s *DockerSwarmSuite) TestAPISwarmSecretsDelete(c *check.C) {
55 55
 	status, out, err := d.SockRequest("GET", "/secrets/"+id, nil)
56 56
 	c.Assert(err, checker.IsNil)
57 57
 	c.Assert(status, checker.Equals, http.StatusNotFound, check.Commentf("secret delete: %s", string(out)))
58
+
59
+	// delete non-existing secret, daemon should return a status code of 404
60
+	id = "non-existing"
61
+	status, out, err = d.SockRequest("DELETE", "/secrets/"+id, nil)
62
+	c.Assert(err, checker.IsNil)
63
+	c.Assert(status, checker.Equals, http.StatusNotFound, check.Commentf("secret delete: %s", string(out)))
64
+
58 65
 }
59 66
 
60 67
 func (s *DockerSwarmSuite) TestAPISwarmSecretsUpdate(c *check.C) {
... ...
@@ -114,5 +128,5 @@ func (s *DockerSwarmSuite) TestAPISwarmSecretsUpdate(c *check.C) {
114 114
 	status, out, err := d.SockRequest("POST", url, secret.Spec)
115 115
 
116 116
 	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
117
-	c.Assert(status, checker.Equals, http.StatusInternalServerError, check.Commentf("output: %q", string(out)))
117
+	c.Assert(status, checker.Equals, http.StatusBadRequest, check.Commentf("output: %q", string(out)))
118 118
 }
... ...
@@ -229,7 +229,7 @@ func (s *DockerSwarmSuite) TestAPISwarmPromoteDemote(c *check.C) {
229 229
 	url := fmt.Sprintf("/nodes/%s/update?version=%d", node.ID, node.Version.Index)
230 230
 	status, out, err := d1.SockRequest("POST", url, node.Spec)
231 231
 	c.Assert(err, checker.IsNil)
232
-	c.Assert(status, checker.Equals, http.StatusInternalServerError, check.Commentf("output: %q", string(out)))
232
+	c.Assert(status, checker.Equals, http.StatusBadRequest, check.Commentf("output: %q", string(out)))
233 233
 	// The warning specific to demoting the last manager is best-effort and
234 234
 	// won't appear until the Role field of the demoted manager has been
235 235
 	// updated.