0
votes

I'm trying to modify an instance's tags list using the goolge compute engine API for Java. My pom.xml imports this dependency:

<dependency>
  <groupId>com.google.apis</groupId>
  <artifactId>google-api-services-compute</artifactId>
  <version>v1-rev173-1.23.0</version>
</dependency>

I can execute the action that will update the tags associated with a VM successfully:

public boolean setInstanceTags(Compute computeConnection, ArrayList<String> nwTags, String projectId, String zone, String instanceName) {
    Instance instance = computeConnection.instances().get(projectId, zone, instanceName).execute();

    Tags tagsToSet = new Tags();
    tagsToSet.setFingerprint(instance.getTags().getFingerprint());
    tagsToSet.setItems(new ArrayList<String>());

    for (String tag: nwTags) {
        tagsToSet.getItems().add(tag);
    }

    Operation setTagsOperation = computeConnection.instances().setTags(projectId, zone, instanceName, tagsToSet).execute();

In order to get feedback on whether that operation succeeded I would like to pull the operation status as follows:

String setTagsOperationId = setTagsOperation.getName();   
setTagsOperation = computeConnection.globalOperations().get(projectId, setTagsOperationId).execute();

This throws this error:

"code" : 403, "errors" : [ { "domain" : "global", "message" : "Required 'compute.globalOperations.get' permission for 'projects/myproject/global/operations/operation-1523604756600-569b5e04b94c3-a87939f4-4e293939'", "reason" : "forbidden" } ], "message" : "Required 'compute.globalOperations.get' permission for 'projects/myproject/global/operations/operation-1523604756600-569b5e04b94c3-a87939f4-4e293939'"

But the service account I'm using does have the "Compute Admin" IAM role and my code is also setting the admin scope:

SCOPES = Arrays.asList(ComputeScopes.COMPUTE);

I'm using the same account/permissions to create firewall rules and pull the status on those operations successfully. Not sure why there is a difference in permissions for pulling operation status for instances.setTags operations and firewalls.insert. The only hint I found is when pulling data on the firewalls.insert operation the 'selfLink' shows that the operation is located in the global scope:

"https://www.googleapis.com/compute/v1/projects/myproject/global/operations/operation-1523604247193-569b5c1eea5a8-2ccf40e9-8815af38"

where as the instances.setTags operation selfLink shows that this operation is located in a specific zone:

"https://www.googleapis.com/compute/v1/projects/myproject/zones/us-central1-c/operations/operation-1523604346365-569b5c7d7e449-dc64de03-fdb77847"

2

2 Answers

0
votes

You can modify the tag of an instance from console and check that this operation is a zonal operation, not a global operation. Below selfLink can be found from Equivalent REST response from any modify tag operation.

"selfLink": "https://www.googleapis.com/compute/v1/projects/[project_name]/zones/us-central1-f/operations/operation-0000000000000-0000000000000-00000000-00000000",

I believe for this reason, it is needed to use a zonal method rather than global method. You can use the below method to resolve this issue.

Method: zoneOperations.get

That said, Compute Admin role already has all required permissions including compute.zoneOperations permission to work in this case.

0
votes

I have tested the following API [1] call with a service account with role "Compute Admin":

$ curl -H"Authorization: Bearer "$(gcloud auth print-access-token) https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/global/operations/[OPERATION_NAME]

And it returned me the expected value with no error.

In order to know if your code and service account have the right role, you can try the following code extracted from the official documentation [2] with the value set to "compute.globalOperations.get".

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.cloudresourcemanager.CloudResourceManager;
import com.google.api.services.cloudresourcemanager.model.TestIamPermissionsRequest;
import com.google.api.services.cloudresourcemanager.model.TestIamPermissionsResponse;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;

public class CloudResourceManagerExample {
  public static void main(String args[]) throws IOException, GeneralSecurityException {
    // REQUIRED: The resource for which the policy detail is being requested.
    // See the operation documentation for the appropriate value for this field.
    String resource = "my-resource"; // TODO: Update placeholder value.

    // TODO: Assign values to desired fields of `requestBody`:
    TestIamPermissionsRequest requestBody = new TestIamPermissionsRequest();

    CloudResourceManager cloudResourceManagerService = createCloudResourceManagerService();
    CloudResourceManager.Projects.TestIamPermissions request =
        cloudResourceManagerService.projects().testIamPermissions(resource, requestBody);

    TestIamPermissionsResponse response = request.execute();

    // TODO: Change code below to process the `response` object:
    System.out.println(response);
  }

  public static CloudResourceManager createCloudResourceManagerService()
      throws IOException, GeneralSecurityException {
    HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();

    GoogleCredential credential = GoogleCredential.getApplicationDefault();
    if (credential.createScopedRequired()) {
      credential =
          credential.createScoped(Arrays.asList("https://www.googleapis.com/auth/cloud-platform"));
    }

    return new CloudResourceManager.Builder(httpTransport, jsonFactory, credential)
        .setApplicationName("Google-CloudResourceManagerSample/0.1")
        .build();
  }
}

This way you will know if your code is using the correct service account or if there is an issue.

[1] https://cloud.google.com/compute/docs/reference/rest/beta/globalOperations/get

[2] https://cloud.google.com/resource-manager/reference/rest/v1/projects/testIamPermissions