diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/BlobChecker.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/BlobChecker.java index e439a97c61..427f47dfff 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/BlobChecker.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/BlobChecker.java @@ -23,10 +23,6 @@ import com.google.cloud.tools.jib.http.BlobHttpContent; import com.google.cloud.tools.jib.http.Response; import com.google.cloud.tools.jib.image.DescriptorDigest; -import com.google.cloud.tools.jib.json.JsonTemplateMapper; -import com.google.cloud.tools.jib.registry.json.ErrorEntryTemplate; -import com.google.cloud.tools.jib.registry.json.ErrorResponseTemplate; -import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; @@ -71,34 +67,15 @@ public BlobDescriptor handleHttpResponseException(HttpResponseException httpResp } // Finds a BLOB_UNKNOWN error response code. - String errorContent = httpResponseException.getContent(); - if (errorContent == null) { + if (httpResponseException.getContent() == null) { // TODO: The Google HTTP client gives null content for HEAD requests. Make the content never // be null, even for HEAD requests. return null; + } - } else { - try { - ErrorResponseTemplate errorResponse = - JsonTemplateMapper.readJson(errorContent, ErrorResponseTemplate.class); - List errors = errorResponse.getErrors(); - if (errors.size() == 1) { - String errorCodeString = errors.get(0).getCode(); - if (errorCodeString == null) { - // Did not get an error code back. - throw httpResponseException; - } - ErrorCodes errorCode = ErrorCodes.valueOf(errorCodeString); - if (errorCode.equals(ErrorCodes.BLOB_UNKNOWN)) { - return null; - } - } - - } catch (IOException ex) { - throw new RegistryErrorExceptionBuilder(getActionDescription(), ex) - .addReason("Failed to parse registry error response body") - .build(); - } + ErrorCodes errorCode = ErrorResponseUtil.getErrorCode(httpResponseException); + if (errorCode == ErrorCodes.BLOB_UNKNOWN) { + return null; } // BLOB_UNKNOWN was not found as a error response code. diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/ErrorResponseUtil.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/ErrorResponseUtil.java new file mode 100644 index 0000000000..f6407235d2 --- /dev/null +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/ErrorResponseUtil.java @@ -0,0 +1,70 @@ +/* + * Copyright 2018 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.tools.jib.registry; + +import com.google.api.client.http.HttpResponseException; +import com.google.cloud.tools.jib.json.JsonTemplateMapper; +import com.google.cloud.tools.jib.registry.json.ErrorEntryTemplate; +import com.google.cloud.tools.jib.registry.json.ErrorResponseTemplate; +import java.io.IOException; +import java.util.List; + +/** Utility methods for parsing {@link ErrorResponseTemplate JSON-encoded error responses}. */ +public class ErrorResponseUtil { + + /** + * Extract an {@link ErrorCodes} response from the error object encoded in an {@link + * HttpResponseException}. + * + * @param httpResponseException the response exception + * @return the parsed {@link ErrorCodes} if found + * @throws HttpResponseException rethrows the original exception if an error object could not be + * parsed, if there were multiple error objects, or if the error code is unknown. + */ + public static ErrorCodes getErrorCode(HttpResponseException httpResponseException) + throws HttpResponseException { + // Obtain the error response code. + String errorContent = httpResponseException.getContent(); + if (errorContent == null) { + throw httpResponseException; + } + + try { + ErrorResponseTemplate errorResponse = + JsonTemplateMapper.readJson(errorContent, ErrorResponseTemplate.class); + List errors = errorResponse.getErrors(); + // There may be multiple error objects + if (errors.size() == 1) { + String errorCodeString = errors.get(0).getCode(); + // May not get an error code back. + if (errorCodeString != null) { + // throws IllegalArgumentException if unknown error code + return ErrorCodes.valueOf(errorCodeString); + } + } + + } catch (IOException | IllegalArgumentException ex) { + // Parse exception: either isn't an error object or unknown error code + } + + // rethrow the original exception + throw httpResponseException; + } + + // not intended to be instantiated + private ErrorResponseUtil() {} +} diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/ManifestPusher.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/ManifestPusher.java index a92d413805..7dff52623d 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/ManifestPusher.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/ManifestPusher.java @@ -22,9 +22,6 @@ import com.google.cloud.tools.jib.http.Response; import com.google.cloud.tools.jib.image.json.BuildableManifestTemplate; import com.google.cloud.tools.jib.json.JsonTemplateMapper; -import com.google.cloud.tools.jib.registry.json.ErrorEntryTemplate; -import com.google.cloud.tools.jib.registry.json.ErrorResponseTemplate; -import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; @@ -77,40 +74,13 @@ public Void handleHttpResponseException(HttpResponseException httpResponseExcept throw httpResponseException; } - // TODO turn deserializing the error-code into a library method - - // Obtain the error response code. - String errorContent = httpResponseException.getContent(); - if (errorContent == null) { - throw httpResponseException; - } - - try { - ErrorResponseTemplate errorResponse = - JsonTemplateMapper.readJson(errorContent, ErrorResponseTemplate.class); - List errors = errorResponse.getErrors(); - if (errors.size() == 1) { - String errorCodeString = errors.get(0).getCode(); - if (errorCodeString == null) { - // Did not get an error code back. - throw httpResponseException; - } - ErrorCodes errorCode = ErrorCodes.valueOf(errorCodeString); - if (errorCode.equals(ErrorCodes.MANIFEST_INVALID) - || errorCode.equals(ErrorCodes.TAG_INVALID)) { - throw new RegistryErrorExceptionBuilder(getActionDescription(), httpResponseException) - .addReason("Registry may not support Image Manifest Version 2, Schema 2") - .build(); - } - } - - } catch (IOException ex) { + ErrorCodes errorCode = ErrorResponseUtil.getErrorCode(httpResponseException); + if (errorCode == ErrorCodes.MANIFEST_INVALID || errorCode == ErrorCodes.TAG_INVALID) { throw new RegistryErrorExceptionBuilder(getActionDescription(), httpResponseException) - .addReason("Failed to parse registry error response body") + .addReason("Registry may not support Image Manifest Version 2, Schema 2") .build(); } - - // unhandled error response code. + // rethrow: unhandled error response code. throw httpResponseException; } diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/registry/ErrorResponseUtilTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/registry/ErrorResponseUtilTest.java new file mode 100644 index 0000000000..fe15dfdd3a --- /dev/null +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/registry/ErrorResponseUtilTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2018 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.tools.jib.registry; + +import com.google.api.client.http.HttpHeaders; +import com.google.api.client.http.HttpResponseException; +import org.apache.http.HttpStatus; +import org.junit.Assert; +import org.junit.Test; + +/** Test for {@link ErrorReponseUtil}. */ +public class ErrorResponseUtilTest { + + @Test + public void testGetErrorCode_knownErrorCode() throws HttpResponseException { + HttpResponseException httpResponseException = + new HttpResponseException.Builder( + HttpStatus.SC_BAD_REQUEST, "Bad Request", new HttpHeaders()) + .setContent( + "{\"errors\":[{\"code\":\"MANIFEST_INVALID\",\"message\":\"manifest invalid\",\"detail\":{}}]}") + .build(); + + Assert.assertSame( + ErrorCodes.MANIFEST_INVALID, ErrorResponseUtil.getErrorCode(httpResponseException)); + } + + /** An unknown {@link ErrorCodes} should cause original exception to be rethrown. */ + @Test + public void testGetErrorCode_unknownErrorCode() { + HttpResponseException httpResponseException = + new HttpResponseException.Builder( + HttpStatus.SC_BAD_REQUEST, "Bad Request", new HttpHeaders()) + .setContent( + "{\"errors\":[{\"code\":\"INVALID_ERROR_CODE\",\"message\":\"invalid code\",\"detail\":{}}]}") + .build(); + try { + ErrorResponseUtil.getErrorCode(httpResponseException); + Assert.fail(); + } catch (HttpResponseException ex) { + Assert.assertSame(httpResponseException, ex); + } + } + + /** Multiple error objects should cause original exception to be rethrown. */ + @Test + public void testGetErrorCode_multipleErrors() { + HttpResponseException httpResponseException = + new HttpResponseException.Builder( + HttpStatus.SC_BAD_REQUEST, "Bad Request", new HttpHeaders()) + .setContent( + "{\"errors\":[" + + "{\"code\":\"MANIFEST_INVALID\",\"message\":\"message 1\",\"detail\":{}}," + + "{\"code\":\"TAG_INVALID\",\"message\":\"message 2\",\"detail\":{}}" + + "]}") + .build(); + try { + ErrorResponseUtil.getErrorCode(httpResponseException); + Assert.fail(); + } catch (HttpResponseException ex) { + Assert.assertSame(httpResponseException, ex); + } + } + + /** An non-error object should cause original exception to be rethrown. */ + @Test + public void testGetErrorCode_invalidErrorObject() { + HttpResponseException httpResponseException = + new HttpResponseException.Builder( + HttpStatus.SC_BAD_REQUEST, "Bad Request", new HttpHeaders()) + .setContent("{\"type\":\"other\",\"message\":\"some other object\"}") + .build(); + try { + ErrorResponseUtil.getErrorCode(httpResponseException); + Assert.fail(); + } catch (HttpResponseException ex) { + Assert.assertSame(httpResponseException, ex); + } + } +}