Skip to content

feat(compute): add compute instance create replicated boot disk sample #9735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* 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;

// [START compute_instance_create_replicated_boot_disk]
import com.google.cloud.compute.v1.AttachedDisk;
import com.google.cloud.compute.v1.AttachedDiskInitializeParams;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.NetworkInterface;
import com.google.cloud.compute.v1.Operation;
import com.google.cloud.compute.v1.Operation.Status;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CreateInstanceWithRegionalDiskFromSnapshot {

public static void main(String[] args) throws IOException, ExecutionException,
InterruptedException, 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 in which you want to create the instance.
String zone = "us-central1-a";
// Name of the instance you want to create.
String instanceName = "YOUR_INSTANCE_NAME";
// Name for the replicated disk.
String diskName = "YOUR_REPLICATED_DISK_NAME";
String region = zone.substring(0, zone.length() - 2);
// Type of the disk.
String diskType = String.format(
"projects/%s/regions/%s/diskTypes/pd-standard", projectId, region);
// The full path and name of the snapshot that you want to use as the source for the new disk.
String snapshotLink = String.format("projects/%s/global/snapshots/%s", projectId,
"SNAPSHOT_NAME");
// An iterable collection of zone names in which you want to keep
// the new disks' replicas. One of the replica zones of the clone must match
// the zone of the source disk.
List<String> replicaZones = new ArrayList<>();

createInstanceWithRegionalDiskFromSnapshot(projectId, zone, instanceName, diskName, diskType,
snapshotLink, replicaZones);
}

// Creates a new VM instance with regional disk from a snapshot and specifies replica zones.
public static Status createInstanceWithRegionalDiskFromSnapshot(
String projectId, String zone, String instanceName, String diskName,
String diskType, String snapshotLink, List<String> replicaZones)
throws IOException, ExecutionException, InterruptedException, TimeoutException {
// 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()) {
AttachedDiskInitializeParams initializeParams = AttachedDiskInitializeParams.newBuilder()
.setSourceSnapshot(snapshotLink)
.setDiskType(diskType)
.setDiskName(diskName)
.addAllReplicaZones(replicaZones)
.build();

// Boot disk configuration
AttachedDisk bootDisk = AttachedDisk.newBuilder()
.setBoot(true)
.setAutoDelete(true) // Optional: Delete disk when instance is deleted.
.setType(AttachedDisk.Type.PERSISTENT.toString())
.setInitializeParams(initializeParams)
.build();

// Network interface configuration (using the default network)
NetworkInterface networkInterface = NetworkInterface.newBuilder()
.setNetwork("global/networks/default")
.build();

// Create the instance resource
Instance instanceResource = Instance.newBuilder()
.setName(instanceName)
.setMachineType(String.format("zones/%s/machineTypes/n1-standard-1", zone))
.addDisks(bootDisk)
.addNetworkInterfaces(networkInterface)
.build();

Operation response = instancesClient.insertAsync(projectId, zone, instanceResource).get(3,
TimeUnit.MINUTES);

if (response.hasError()) {
throw new Error("Error creating instance! " + response.getError());
}
return response.getStatus();
}
}
}
// [END compute_instance_create_replicated_boot_disk]
106 changes: 71 additions & 35 deletions compute/cloud-client/src/test/java/compute/InstanceOperationsIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,34 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static compute.Util.getZone;

import com.google.cloud.compute.v1.CreateSnapshotRegionDiskRequest;
import com.google.cloud.compute.v1.Disk;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.Instance.Status;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.Operation;
import com.google.cloud.compute.v1.RegionDisksClient;
import com.google.cloud.compute.v1.Snapshot;
import compute.disks.CloneEncryptedDisk;
import compute.disks.CreateEncryptedDisk;
import compute.disks.DeleteDisk;
import compute.disks.DeleteSnapshot;
import compute.disks.RegionalCreateFromSource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.Assert;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.runner.RunWith;
Expand All @@ -52,13 +59,21 @@ public class InstanceOperationsIT {

private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT");
private static final String ZONE = getZone();
private static final String REGION = ZONE.substring(0, ZONE.length() - 2);
private static String MACHINE_NAME;
private static String MACHINE_NAME_ENCRYPTED;
private static String DISK_NAME;
private static String ENCRYPTED_DISK_NAME;
private static String RAW_KEY;

private ByteArrayOutputStream stdOut;
private static String INSTANCE_NAME;
private static final String DISK_TYPE = String.format("regions/%s/diskTypes/pd-standard", REGION);
private static String REPLICATED_DISK_NAME;
private static String SNAPSHOT_NAME;
private static final String DISK_SNAPSHOT_LINK =
String.format("projects/%s/global/snapshots/%s", PROJECT_ID, SNAPSHOT_NAME);
private static final List<String> REPLICA_ZONES = Arrays.asList(
String.format("projects/%s/zones/%s-a", PROJECT_ID, REGION),
String.format("projects/%s/zones/%s-b", PROJECT_ID, REGION));

// Check if the required environment variables are set.
public static void requireEnvVar(String envVarName) {
Expand All @@ -72,47 +87,42 @@ public static void setUp()
requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS");
requireEnvVar("GOOGLE_CLOUD_PROJECT");

final PrintStream out = System.out;
ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(stdOut));

MACHINE_NAME = "test-instance-operation-" + UUID.randomUUID();
MACHINE_NAME_ENCRYPTED = "test-instance-encrypted-" + UUID.randomUUID();
DISK_NAME = "test-clone-disk-enc-" + UUID.randomUUID();
ENCRYPTED_DISK_NAME = "test-disk-enc-" + UUID.randomUUID();
RAW_KEY = Util.getBase64EncodedKey();

// Cleanup existing stale resources.
Util.cleanUpExistingInstances("test-instance-", PROJECT_ID, ZONE);
Util.cleanUpExistingDisks("test-clone-disk-enc-", PROJECT_ID, ZONE);
Util.cleanUpExistingDisks("test-disk-enc-", PROJECT_ID, ZONE);
INSTANCE_NAME = "test-instance-" + UUID.randomUUID();
REPLICATED_DISK_NAME = "test-disk-replicated-" + UUID.randomUUID();
SNAPSHOT_NAME = "test-snapshot-" + UUID.randomUUID().toString().split("-")[0];

compute.CreateInstance.createInstance(PROJECT_ID, ZONE, MACHINE_NAME);
compute.CreateEncryptedInstance
.createEncryptedInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED, RAW_KEY);
RegionalCreateFromSource.createRegionalDisk(PROJECT_ID, REGION, REPLICA_ZONES,
REPLICATED_DISK_NAME, DISK_TYPE, 200, Optional.empty(), Optional.empty());
createDiskSnapshot(PROJECT_ID, REGION, REPLICATED_DISK_NAME, SNAPSHOT_NAME);

TimeUnit.SECONDS.sleep(30);

stdOut.close();
System.setOut(out);
}


@AfterAll
public static void cleanup()
throws IOException, InterruptedException, ExecutionException, TimeoutException {
final PrintStream out = System.out;
ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(stdOut));
// Cleanup existing stale resources.
Util.cleanUpExistingInstances("test-instance-", PROJECT_ID, ZONE);
Util.cleanUpExistingDisks("test-clone-disk-enc-", PROJECT_ID, ZONE);
Util.cleanUpExistingDisks("test-disk-enc-", PROJECT_ID, ZONE);
Util.cleanUpExistingRegionalDisks("test-disk-replicated-", PROJECT_ID, REGION);
Util.cleanUpExistingSnapshots("test-snapshot-", PROJECT_ID);

// Delete all instances created for testing.
compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED);
compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME);
compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, INSTANCE_NAME);
DeleteDisk.deleteDisk(PROJECT_ID, ZONE, DISK_NAME);
DeleteDisk.deleteDisk(PROJECT_ID, ZONE, ENCRYPTED_DISK_NAME);

stdOut.close();
System.setOut(out);
DeleteSnapshot.deleteSnapshot(PROJECT_ID, SNAPSHOT_NAME);
}

private static Instance getInstance(String machineName) throws IOException {
Expand All @@ -121,16 +131,28 @@ private static Instance getInstance(String machineName) throws IOException {
}
}

@BeforeEach
public void beforeEach() {
stdOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(stdOut));
}

@AfterEach
public void afterEach() {
stdOut = null;
System.setOut(null);
public static void createDiskSnapshot(String project, String region, String diskName,
String snapshotName)
throws IOException, ExecutionException, InterruptedException, TimeoutException {
try (RegionDisksClient disksClient = RegionDisksClient.create()) {

CreateSnapshotRegionDiskRequest createSnapshotDiskRequest =
CreateSnapshotRegionDiskRequest.newBuilder()
.setProject(project)
.setRegion(region)
.setDisk(diskName)
.setSnapshotResource(Snapshot.newBuilder()
.setName(snapshotName)
.build())
.build();

Operation operation = disksClient.createSnapshotAsync(createSnapshotDiskRequest)
.get(3, TimeUnit.MINUTES);

if (operation.hasError()) {
throw new Error("Failed to create the snapshot");
}
}
}

@Test
Expand Down Expand Up @@ -204,14 +226,17 @@ public void testEncryptedInstanceOperations()
@Test
public void testCloneEncryptedDisk()
throws IOException, ExecutionException, InterruptedException, TimeoutException {
Assert.assertEquals(Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED),
"RUNNING");
ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(stdOut));

Instance instance = getInstance(MACHINE_NAME_ENCRYPTED);
String diskType = String.format("zones/%s/diskTypes/pd-standard", ZONE);
CloneEncryptedDisk.createDiskFromCustomerEncryptedKey(PROJECT_ID, ZONE, DISK_NAME, diskType, 10,
instance.getDisks(0).getSource(), RAW_KEY.getBytes(
StandardCharsets.UTF_8));
assertThat(stdOut.toString()).contains("Disk cloned with customer encryption key.");

stdOut.close();
}

@Test
Expand All @@ -228,4 +253,15 @@ public void testCreateEncryptedDisk()
Assert.assertNotNull(encryptedDisk.getDiskEncryptionKey());
Assert.assertNotNull(encryptedDisk.getDiskEncryptionKey().getSha256());
}

@Test
public void testCreateInstanceWithRegionalDiskFromSnapshot()
throws IOException, ExecutionException, InterruptedException, TimeoutException {
Operation.Status status = CreateInstanceWithRegionalDiskFromSnapshot
.createInstanceWithRegionalDiskFromSnapshot(
PROJECT_ID, ZONE, INSTANCE_NAME, REPLICATED_DISK_NAME,
DISK_TYPE, DISK_SNAPSHOT_LINK, REPLICA_ZONES);

assertThat(status).isEqualTo(Operation.Status.DONE);
}
}
Loading