Skip to content

Commit c6ae893

Browse files
feat(compute): add compute reservation create shared (#9535)
* Implemented compute_reservation_create_shared sample, created test * Implemented compute_reservation_create_shared sample * Added test * Added dependency * Fixed code * Fixed code * Fixed test mocking reservationClient * Disabled Hyperdisk tests * Fixed Hyperdisk tests * Fixed pom file * Fixed imports * Created cleanup method for reservation, fixed tests order * Changed zone * Changed zone * Faxed naming and test * Fixed test * Fixed time in Util class * Fixed Util class * Returned CrudOperationReservationIT class * Returned getZone() * Fixed comment * Fixed Util * Fixed comment * Added comments * MeFixed comment
1 parent 78de6d3 commit c6ae893

File tree

3 files changed

+209
-0
lines changed

3 files changed

+209
-0
lines changed

compute/cloud-client/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@
5959
<groupId>com.google.cloud</groupId>
6060
<scope>test</scope>
6161
</dependency>
62+
<dependency>
63+
<groupId>org.mockito</groupId>
64+
<artifactId>mockito-core</artifactId>
65+
<version>5.13.0</version>
66+
<scope>test</scope>
67+
</dependency>
68+
6269

6370
<dependency>
6471
<artifactId>truth</artifactId>
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package compute.reservation;
18+
19+
// [START compute_reservation_create_shared]
20+
21+
import com.google.cloud.compute.v1.AllocationSpecificSKUReservation;
22+
import com.google.cloud.compute.v1.Operation;
23+
import com.google.cloud.compute.v1.Reservation;
24+
import com.google.cloud.compute.v1.ReservationsClient;
25+
import com.google.cloud.compute.v1.ShareSettings;
26+
import com.google.cloud.compute.v1.ShareSettingsProjectConfig;
27+
import java.io.IOException;
28+
import java.util.concurrent.ExecutionException;
29+
import java.util.concurrent.TimeUnit;
30+
import java.util.concurrent.TimeoutException;
31+
32+
public class CreateSharedReservation {
33+
private final ReservationsClient reservationsClient;
34+
35+
// Constructor to inject the ReservationsClient
36+
public CreateSharedReservation(ReservationsClient reservationsClient) {
37+
this.reservationsClient = reservationsClient;
38+
}
39+
40+
public static void main(String[] args)
41+
throws IOException, ExecutionException, InterruptedException, TimeoutException {
42+
// TODO(developer): Replace these variables before running the sample.
43+
// The ID of the project where you want to reserve resources
44+
// and where the instance template exists.
45+
// By default, no projects are allowed to create or modify shared reservations
46+
// in an organization. Add projects to the Shared Reservations Owner Projects
47+
// (compute.sharedReservationsOwnerProjects) organization policy constraint
48+
// to allow them to create and modify shared reservations.
49+
// For more information visit this page:
50+
// https://cloud.google.com/compute/docs/instances/reservations-shared#shared_reservation_constraint
51+
String projectId = "YOUR_PROJECT_ID";
52+
// Zone in which the reservation resides.
53+
String zone = "us-central1-a";
54+
// Name of the reservation to be created.
55+
String reservationName = "YOUR_RESERVATION_NAME";
56+
// The URI of the global instance template to be used for creating the reservation.
57+
String instanceTemplateUri = String.format(
58+
"projects/%s/global/instanceTemplates/YOUR_INSTANCE_TEMPLATE_NAME", projectId);
59+
// Number of instances for which capacity needs to be reserved.
60+
int vmCount = 3;
61+
// In your main method, create ReservationsClient
62+
ReservationsClient client = ReservationsClient.create();
63+
// Create an instance of your class, passing in the client
64+
CreateSharedReservation creator = new CreateSharedReservation(client);
65+
66+
creator.createSharedReservation(projectId, zone, reservationName, instanceTemplateUri, vmCount);
67+
}
68+
69+
// Creates a shared reservation with the given name in the given zone.
70+
public void createSharedReservation(
71+
String projectId, String zone,
72+
String reservationName, String instanceTemplateUri, int vmCount)
73+
throws ExecutionException, InterruptedException, TimeoutException {
74+
75+
ShareSettings shareSettings = ShareSettings.newBuilder()
76+
.setShareType(String.valueOf(ShareSettings.ShareType.SPECIFIC_PROJECTS))
77+
// The IDs of projects that can consume this reservation. You can include up to 100
78+
// consumer projects. These projects must be in the same organization as
79+
// the owner project. Don't include the owner project. By default, it is already allowed
80+
// to consume the reservation.
81+
.putProjectMap("CONSUMER_PROJECT_ID_1", ShareSettingsProjectConfig.newBuilder().build())
82+
.putProjectMap("CONSUMER_PROJECT_ID_2", ShareSettingsProjectConfig.newBuilder().build())
83+
.build();
84+
85+
// Create the reservation.
86+
Reservation reservation =
87+
Reservation.newBuilder()
88+
.setName(reservationName)
89+
.setZone(zone)
90+
.setSpecificReservationRequired(true)
91+
.setShareSettings(shareSettings)
92+
.setSpecificReservation(
93+
AllocationSpecificSKUReservation.newBuilder()
94+
.setCount(vmCount)
95+
.setSourceInstanceTemplate(instanceTemplateUri)
96+
.build())
97+
.build();
98+
99+
// Wait for the create reservation operation to complete.
100+
Operation response =
101+
this.reservationsClient.insertAsync(projectId, zone, reservation).get(3, TimeUnit.MINUTES);
102+
103+
if (response.hasError()) {
104+
System.out.println("Reservation creation failed!" + response);
105+
return;
106+
}
107+
System.out.println("Reservation created. Operation Status: " + response.getStatus());
108+
}
109+
}
110+
// [END compute_reservation_create_shared]

compute/cloud-client/src/test/java/compute/reservation/ReservationIT.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,19 @@
1818

1919
import static com.google.common.truth.Truth.assertThat;
2020
import static com.google.common.truth.Truth.assertWithMessage;
21+
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.times;
23+
import static org.mockito.Mockito.verify;
24+
import static org.mockito.Mockito.when;
2125

26+
import com.google.api.gax.longrunning.OperationFuture;
2227
import com.google.api.gax.rpc.NotFoundException;
28+
import com.google.cloud.compute.v1.AllocationSpecificSKUReservation;
29+
import com.google.cloud.compute.v1.Operation;
2330
import com.google.cloud.compute.v1.Reservation;
2431
import com.google.cloud.compute.v1.ReservationsClient;
32+
import com.google.cloud.compute.v1.ShareSettings;
33+
import com.google.cloud.compute.v1.ShareSettingsProjectConfig;
2534
import compute.CreateInstanceTemplate;
2635
import compute.CreateRegionalInstanceTemplate;
2736
import compute.DeleteInstanceTemplate;
@@ -36,8 +45,10 @@
3645
import java.util.concurrent.TimeoutException;
3746
import org.junit.Assert;
3847
import org.junit.jupiter.api.AfterAll;
48+
import org.junit.jupiter.api.AfterEach;
3949
import org.junit.jupiter.api.Assertions;
4050
import org.junit.jupiter.api.BeforeAll;
51+
import org.junit.jupiter.api.BeforeEach;
4152
import org.junit.jupiter.api.MethodOrderer;
4253
import org.junit.jupiter.api.Order;
4354
import org.junit.jupiter.api.Test;
@@ -69,7 +80,16 @@ public class ReservationIT {
6980
private static final String REGIONAL_INSTANCE_TEMPLATE_URI =
7081
String.format("projects/%s/regions/%s/instanceTemplates/%s",
7182
PROJECT_ID, REGION, REGIONAL_INSTANCE_TEMPLATE_NAME);
83+
private static final String SPECIFIC_SHARED_INSTANCE_TEMPLATE_NAME =
84+
"test-shared-inst-temp-" + javaVersion + "-"
85+
+ UUID.randomUUID().toString().substring(0, 8);
86+
private static final String INSTANCE_TEMPLATE_SHARED_RESERV_URI =
87+
String.format("projects/%s/global/instanceTemplates/%s",
88+
PROJECT_ID, SPECIFIC_SHARED_INSTANCE_TEMPLATE_NAME);
89+
private static final String RESERVATION_NAME_SHARED = "test-reservation-shared-" + javaVersion
90+
+ "-" + UUID.randomUUID().toString().substring(0, 8);
7291
private static final int NUMBER_OF_VMS = 3;
92+
private ByteArrayOutputStream stdOut;
7393

7494
// Check if the required environment variables are set.
7595
public static void requireEnvVar(String envVarName) {
@@ -93,6 +113,7 @@ public static void setUp()
93113
Util.cleanUpExistingReservations(
94114
"test-reservation-global-" + javaVersion, PROJECT_ID, ZONE);
95115
Util.cleanUpExistingReservations("test-reservation-regional-" + javaVersion, PROJECT_ID, ZONE);
116+
Util.cleanUpExistingInstanceTemplates("test-shared-inst-temp-" + javaVersion, PROJECT_ID);
96117

97118
// Initialize the client once for all tests
98119
reservationsClient = ReservationsClient.create();
@@ -105,6 +126,9 @@ public static void setUp()
105126
CreateRegionalInstanceTemplate.createRegionalInstanceTemplate(
106127
PROJECT_ID, REGION, REGIONAL_INSTANCE_TEMPLATE_NAME);
107128
assertThat(stdOut.toString()).contains("Instance Template Operation Status: DONE");
129+
// Create instance template for shares reservation.
130+
CreateInstanceTemplate.createInstanceTemplate(
131+
PROJECT_ID, SPECIFIC_SHARED_INSTANCE_TEMPLATE_NAME);
108132

109133
stdOut.close();
110134
System.setOut(out);
@@ -130,6 +154,13 @@ public static void cleanup()
130154
.contains("Instance template deletion operation status for "
131155
+ REGIONAL_INSTANCE_TEMPLATE_NAME);
132156

157+
// Delete instance template for shared reservation
158+
DeleteInstanceTemplate.deleteInstanceTemplate(
159+
PROJECT_ID, SPECIFIC_SHARED_INSTANCE_TEMPLATE_NAME);
160+
assertThat(stdOut.toString())
161+
.contains("Instance template deletion operation status for "
162+
+ SPECIFIC_SHARED_INSTANCE_TEMPLATE_NAME);
163+
133164
// Delete all reservations created for testing.
134165
DeleteReservation.deleteReservation(PROJECT_ID, ZONE, RESERVATION_NAME_GLOBAL);
135166
DeleteReservation.deleteReservation(PROJECT_ID, ZONE, RESERVATION_NAME_REGIONAL);
@@ -149,6 +180,18 @@ public static void cleanup()
149180
System.setOut(out);
150181
}
151182

183+
@BeforeEach
184+
public void beforeEach() {
185+
stdOut = new ByteArrayOutputStream();
186+
System.setOut(new PrintStream(stdOut));
187+
}
188+
189+
@AfterEach
190+
public void afterEach() {
191+
stdOut = null;
192+
System.setOut(null);
193+
}
194+
152195
@Test
153196
@Order(1)
154197
public void testCreateReservationWithGlobalInstanceTemplate()
@@ -189,4 +232,53 @@ public void testUpdateVmsForReservation()
189232

190233
Assert.assertEquals(newNumberOfVms, reservation.getSpecificReservation().getCount());
191234
}
235+
236+
@Test
237+
public void testCreateSharedReservation()
238+
throws ExecutionException, InterruptedException, TimeoutException {
239+
// Mock the ReservationsClient
240+
ReservationsClient mockReservationsClient = mock(ReservationsClient.class);
241+
242+
// This test require projects in the test environment to share reservation with,
243+
// therefore the operation should be mocked. If you want to make a real test,
244+
// please set the CONSUMER_PROJECT_ID_1 and CONSUMER_PROJECT_ID_2 accordingly.
245+
// Make sure that base project has proper permissions to share reservations.
246+
// See: https://cloud.google.com/compute/docs/instances/reservations-shared#shared_reservation_constraint
247+
ShareSettings shareSettings = ShareSettings.newBuilder()
248+
.setShareType(String.valueOf(ShareSettings.ShareType.SPECIFIC_PROJECTS))
249+
.putProjectMap("CONSUMER_PROJECT_ID_1", ShareSettingsProjectConfig.newBuilder().build())
250+
.putProjectMap("CONSUMER_PROJECT_ID_2", ShareSettingsProjectConfig.newBuilder().build())
251+
.build();
252+
253+
Reservation reservation =
254+
Reservation.newBuilder()
255+
.setName(RESERVATION_NAME_SHARED)
256+
.setZone(ZONE)
257+
.setSpecificReservationRequired(true)
258+
.setShareSettings(shareSettings)
259+
.setSpecificReservation(
260+
AllocationSpecificSKUReservation.newBuilder()
261+
.setCount(NUMBER_OF_VMS)
262+
.setSourceInstanceTemplate(INSTANCE_TEMPLATE_SHARED_RESERV_URI)
263+
.build())
264+
.build();
265+
266+
OperationFuture mockFuture = mock(OperationFuture.class);
267+
when(mockReservationsClient.insertAsync(PROJECT_ID, ZONE, reservation))
268+
.thenReturn(mockFuture);
269+
Operation mockOperation = mock(Operation.class);
270+
when(mockFuture.get(3, TimeUnit.MINUTES)).thenReturn(mockOperation);
271+
when(mockOperation.hasError()).thenReturn(false);
272+
when(mockOperation.getStatus()).thenReturn(Operation.Status.DONE);
273+
274+
// Create an instance, passing in the mock client
275+
CreateSharedReservation creator = new CreateSharedReservation(mockReservationsClient);
276+
277+
creator.createSharedReservation(PROJECT_ID, ZONE,
278+
RESERVATION_NAME_SHARED, INSTANCE_TEMPLATE_SHARED_RESERV_URI, NUMBER_OF_VMS);
279+
280+
verify(mockReservationsClient, times(1))
281+
.insertAsync(PROJECT_ID, ZONE, reservation);
282+
assertThat(stdOut.toString()).contains("Reservation created. Operation Status: DONE");
283+
}
192284
}

0 commit comments

Comments
 (0)