0

I'm trying to write an application to manage GCP deployments using the Deployment Manager SDK for Java. I'm able to list deployments successfully but unable to update a deployment as the SDK throws an exception shown below.

com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 Not Found
{
  "code" : 404,
  "errors" : [ {
    "domain" : "global",
    "message" : "The object 'projects/--redacted-project--/global/deployments' is not found.",
    "reason" : "notFound"
  } ],
  "message" : "The object 'projects/--redacted-project--/global/deployments' is not found."
}
        at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:150)
        at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
        at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
        at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1067)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)

My calling code is:

val content = new Deployment().setTarget(target)

val updateResult = client.deployments().update(project, name, content)
          .setCreatePolicy("CREATE_OR_ACQUIRE")
          .setDeletePolicy("DELETE")
          .setPreview(false)
          .execute()

When I switch on debug for the SDK I see the following exchange:

2020-04-13 17:52:36,353 [deploy-akka.deploy-dispatcher-10] INFO  c.g.a.c.h.HttpTransport - -------------- REQUEST  --------------
PUT https://www.googleapis.com/deploymentmanager/v2/projects/--redacted-project--/global/deployments/--redacted-deployment--
Accept-Encoding: gzip
Authorization: <Not Logged>
User-Agent: Google-API-Java-Client Google-HTTP-Java-Client/1.25.0 (gzip)
Content-Type: application/json; charset=UTF-8
Content-Encoding: gzip
Content-Length: 565

2020-04-13 17:52:36,353 [deploy-akka.deploy-dispatcher-10] INFO  c.g.a.c.h.HttpTransport - curl -v --compressed -X PUT -H 'Accept-Encoding: gzip' -H 'Authorization: <Not Logged>' -H 'User-Agent: Google-API-Java-Client Google-HTTP-Java-Client/1.25.0 (gzip)' -H 'Content-Type: application/json; charset=UTF-8' -H 'Content-Encoding: gzip' -d '@-' -- 'https://www.googleapis.com/deploymentmanager/v2/projects/--redacted-project--/global/deployments/--redacted-deployment--' << $$$

2020-04-13 17:52:36,354 [deploy-akka.deploy-dispatcher-10] INFO  c.g.a.c.h.HttpTransport - Total: 1,449 bytes

2020-04-13 17:52:36,354 [deploy-akka.deploy-dispatcher-10] INFO  c.g.a.c.h.HttpTransport - {"target":{"config":{"content":"imports:\n  - path: dep.jinja\n\nresources:\n  - name: testing\n    type: dep.jinja\n    properties:\n      env: prod\n      banana: yes\n      retentionInDays: 5"},"imports":[{"content":"resources:\n- name: riffraff-dataset\n  type: bigquery.v2.dataset\n  properties:\n    location: europe-west2\n    datasetReference:\n      datasetId: riffraff_test\n- name: test-table\n  type: bigquery.v2.table\n  metadata:\n    dependsOn:\n    - riffraff-dataset\n  properties:\n    datasetId: riffraff_test\n    tableReference:\n      tableId: banana\n    schema:\n      fields:\n        - name: species\n          type: string\n        - name: plantation\n          type: string\n        - name: harvested\n          type: date\n        - name: quantity\n          type: numeric\n        - name: edible\n          type: boolean\n{% if properties['banana'] == \"yes\" %}\n- name: test-topic\n  type: gcp-types/pubsub-v1:projects.topics\n  properties:\n    topic: riffraff-topic\n{% endif %}\n- name: riffraff-bucket\n  type: storage.v1.bucket\n  properties:\n    location: europe-west2\n    storageClass: STANDARD\n    iamConfiguration:\n      uniformBucketLevelAccess:\n        enabled: True\n    {% if properties['retentionInDays'] %}\n    lifecycle:\n      rule:\n        - action:\n            type: Delete\n          condition:\n            age: {{ properties['retentionInDays'] }}\n    {% endif %}","name":"dep.jinja"}]}}

2020-04-13 17:52:37,174 [deploy-akka.deploy-dispatcher-10] INFO  c.g.a.c.h.HttpTransport - -------------- RESPONSE --------------
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=UTF-8
Vary: Origin
Vary: X-Origin
Vary: Referer
Content-Encoding: gzip
Date: Mon, 13 Apr 2020 16:52:37 GMT
Server: ESF
Cache-Control: private
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Alt-Svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,h3-T050=":443"; ma=2592000
Transfer-Encoding: chunked

So it looks like an invalid URL or object.

However if I use the gcloud CLI tool to do the same thing with --log-http I see that it is hitting exactly the same endpoint but not getting a 404:

==== request start ====
uri: https://www.googleapis.com/deploymentmanager/v2/projects/--redacted-project--/global/deployments/--redacted-deployment--?alt=json&preview=False&createPolicy=CREATE_OR_ACQUIRE&deletePolicy=DELETE
method: PUT

Not sure where to go from here.

sihil
  • 2,563
  • 1
  • 17
  • 24

1 Answers1

0

After further digging I noticed that the fields in the body JSON in the PUT request were different. The gcloud CLI was sending fingerprint, name and target whilst the Java SDK was only sending target.

Tucked away in the documentation for the request body it says that you need to set fingerprint for update and a handful of other methods.

I modified my code to GET the deployment first and include the fingerprint in the request and it began to work.

val content = new Deployment().setName(name).setFingerprint(fingerprint).setTarget(target)

val updateResult = client.deployments().update(project, name, content)
  .setCreatePolicy("CREATE_OR_ACQUIRE")
  .setDeletePolicy("DELETE")
  .setPreview(false)
  .execute()

In my view 404 is the wrong error for this. Google should be returning a 400 as the client is not including a required field in the request.

In addition the SDK does a large number of precondition checks to catch invalid requests but does not include this one. I've opened an issue with Google to suggest this.

sihil
  • 2,563
  • 1
  • 17
  • 24