diff --git a/compute/cloud-client/src/main/java/compute/disks/AttachRegionalDiskForce.java b/compute/cloud-client/src/main/java/compute/disks/AttachRegionalDiskForce.java new file mode 100644 index 00000000000..20e13376e5e --- /dev/null +++ b/compute/cloud-client/src/main/java/compute/disks/AttachRegionalDiskForce.java @@ -0,0 +1,81 @@ +/* + * Copyright 2024 Google LLC + * + * 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 compute.disks; + +// [START compute_instance_attach_regional_disk_force] +import com.google.cloud.compute.v1.AttachDiskInstanceRequest; +import com.google.cloud.compute.v1.AttachedDisk; +import com.google.cloud.compute.v1.InstancesClient; +import com.google.cloud.compute.v1.Operation; +import com.google.cloud.compute.v1.Operation.Status; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class AttachRegionalDiskForce { + public static void main(String[] args) + throws IOException, InterruptedException, ExecutionException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + // Project ID or project number of the Cloud project you want to use. + String projectId = "YOUR_PROJECT_ID"; + // Name of the zone of your compute instance. + String zone = "us-central1-a"; + // The name of the compute instance where you are adding the replicated disk. + String instanceName = "YOUR_INSTANCE_NAME"; + // The region where your replicated disk is located. + String region = "us-central1"; + // The name of the replicated disk. + String diskName = "YOUR_DISK_NAME"; + + attachRegionalDiskForce(projectId, zone, instanceName, region, diskName); + } + + // Attaches a regional disk to the instance, + // forcing the attachment even if other VMs are using the disk. + public static Status attachRegionalDiskForce(String projectId, + String zone, String instanceName, String region, String diskName) + throws IOException, InterruptedException, ExecutionException, TimeoutException { + String diskLink = String.format("projects/%s/regions/%s/disks/%s", + projectId, region, diskName); + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (InstancesClient instancesClient = InstancesClient.create()) { + AttachedDisk attachedDisk = AttachedDisk.newBuilder() + .setSource(diskLink) + .setMode(AttachedDisk.Mode.READ_WRITE.toString()) + .build(); + + AttachDiskInstanceRequest attachDiskRequest = AttachDiskInstanceRequest.newBuilder() + .setProject(projectId) + .setZone(zone) + .setInstance(instanceName) + .setAttachedDiskResource(attachedDisk) + .setForceAttach(true) // Force the attachment + .build(); + + Operation response = instancesClient.attachDiskAsync(attachDiskRequest) + .get(3, TimeUnit.MINUTES); + + if (response.hasError()) { + throw new Error("Error attaching regional disk! " + response); + } + return response.getStatus(); + } + } +} +// [END compute_instance_attach_regional_disk_force] diff --git a/compute/cloud-client/src/test/java/compute/disks/InstanceAttachDiskIT.java b/compute/cloud-client/src/test/java/compute/disks/InstanceAttachDiskIT.java new file mode 100644 index 00000000000..47ba47b66df --- /dev/null +++ b/compute/cloud-client/src/test/java/compute/disks/InstanceAttachDiskIT.java @@ -0,0 +1,75 @@ +/* + * Copyright 2024 Google LLC + * + * 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 compute.disks; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.compute.v1.AttachDiskInstanceRequest; +import com.google.cloud.compute.v1.InstancesClient; +import com.google.cloud.compute.v1.Operation; +import com.google.cloud.compute.v1.Operation.Status; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.MockedStatic; + +@RunWith(JUnit4.class) +@Timeout(value = 4, unit = TimeUnit.MINUTES) +public class InstanceAttachDiskIT { + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static final String ZONE = "us-west1-a"; + private static final String REGION = "us-west1"; + private static final String ATTACHED_DISK = "disk-regional"; + private static final String INSTANCE_NAME = "instance"; + + @Test + public void testAttachRegionalDiskForceAttach() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + try (MockedStatic mockedResourcePoliciesClient = + mockStatic(InstancesClient.class)) { + Operation operation = mock(Operation.class); + InstancesClient mockClient = mock(InstancesClient.class); + OperationFuture mockFuture = mock(OperationFuture.class); + + mockedResourcePoliciesClient.when(InstancesClient::create).thenReturn(mockClient); + when(mockClient.attachDiskAsync(any(AttachDiskInstanceRequest.class))) + .thenReturn(mockFuture); + when(mockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(operation); + when(operation.getStatus()).thenReturn(Status.DONE); + + Status status = AttachRegionalDiskForce + .attachRegionalDiskForce(PROJECT_ID, ZONE, INSTANCE_NAME, REGION, ATTACHED_DISK); + + verify(mockClient, times(1)).attachDiskAsync(any(AttachDiskInstanceRequest.class)); + verify(mockFuture, times(1)).get(anyLong(), any(TimeUnit.class)); + assertEquals(Status.DONE, status); + } + } +}