Signed-off-by: allencloud <allen.sun@daocloud.io>
| ... | ... |
@@ -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. |