Skip to content

Commit c8957a2

Browse files
committed
feat: create shared reservation
1 parent fcc49d3 commit c8957a2

File tree

4 files changed

+224
-46
lines changed

4 files changed

+224
-46
lines changed

compute/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
cloud.google.com/go/storage v1.43.0
88
github.com/GoogleCloudPlatform/golang-samples v0.0.0-20240724083556-7f760db013b7
99
github.com/google/uuid v1.6.0
10+
github.com/googleapis/gax-go/v2 v2.13.0
1011
google.golang.org/api v0.193.0
1112
google.golang.org/protobuf v1.34.2
1213
)
@@ -23,7 +24,6 @@ require (
2324
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
2425
github.com/google/s2a-go v0.1.8 // indirect
2526
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
26-
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
2727
go.opencensus.io v0.24.0 // indirect
2828
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect
2929
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package snippets
16+
17+
// [START compute_reservation_create_shared]
18+
import (
19+
"context"
20+
"fmt"
21+
"io"
22+
23+
compute "cloud.google.com/go/compute/apiv1"
24+
computepb "cloud.google.com/go/compute/apiv1/computepb"
25+
gax "github.com/googleapis/gax-go/v2"
26+
"google.golang.org/protobuf/proto"
27+
)
28+
29+
type ClientInterface interface {
30+
Close() error
31+
Delete(context.Context, *computepb.DeleteReservationRequest, ...gax.CallOption) (*compute.Operation, error)
32+
Insert(context.Context, *computepb.InsertReservationRequest, ...gax.CallOption) (*compute.Operation, error)
33+
}
34+
35+
// Creates shared reservation from given template in particular zone
36+
func createSharedReservation(w io.Writer, client ClientInterface, projectID, baseProjectId, zone, reservationName, sourceTemplate string) error {
37+
// client, err := compute.NewReservationsRESTClient(ctx)
38+
// projectID := "your_project_id". Destination of sharing.
39+
// baseProjectId := "your_project_id2". Project where the reservation will be created.
40+
// zone := "us-west3-a"
41+
// reservationName := "your_reservation_name"
42+
// sourceTemplate: existing template path. Following formats are allowed:
43+
// - projects/{project_id}/global/instanceTemplates/{template_name}
44+
// - projects/{project_id}/regions/{region}/instanceTemplates/{template_name}
45+
// - https://www.googleapis.com/compute/v1/projects/{project_id}/global/instanceTemplates/instanceTemplate
46+
// - https://www.googleapis.com/compute/v1/projects/{project_id}/regions/{region}/instanceTemplates/instanceTemplate
47+
48+
ctx := context.Background()
49+
50+
shareSettings := map[string]*computepb.ShareSettingsProjectConfig{
51+
projectID: {ProjectId: proto.String(projectID)},
52+
}
53+
54+
req := &computepb.InsertReservationRequest{
55+
Project: baseProjectId,
56+
ReservationResource: &computepb.Reservation{
57+
Name: proto.String(reservationName),
58+
Zone: proto.String(zone),
59+
SpecificReservation: &computepb.AllocationSpecificSKUReservation{
60+
Count: proto.Int64(2),
61+
SourceInstanceTemplate: proto.String(sourceTemplate),
62+
},
63+
ShareSettings: &computepb.ShareSettings{
64+
ProjectMap: shareSettings,
65+
ShareType: proto.String("SPECIFIC_PROJECTS"),
66+
},
67+
},
68+
Zone: zone,
69+
}
70+
71+
op, err := client.Insert(ctx, req)
72+
if err != nil {
73+
return fmt.Errorf("unable to create reservation: %w", err)
74+
}
75+
76+
if op != nil {
77+
if err = op.Wait(ctx); err != nil {
78+
return fmt.Errorf("unable to wait for the operation: %w", err)
79+
}
80+
}
81+
82+
fmt.Fprintf(w, "Reservation created\n")
83+
84+
return nil
85+
}
86+
87+
// [END compute_reservation_create_shared]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Mock objects for shared project
16+
17+
package snippets
18+
19+
import (
20+
"context"
21+
22+
compute "cloud.google.com/go/compute/apiv1"
23+
computepb "cloud.google.com/go/compute/apiv1/computepb"
24+
gax "github.com/googleapis/gax-go/v2"
25+
)
26+
27+
type ReservationsClient struct{}
28+
29+
func (client ReservationsClient) Close() error {
30+
return nil
31+
}
32+
33+
func (client ReservationsClient) Insert(context.Context, *computepb.InsertReservationRequest, ...gax.CallOption) (*compute.Operation, error) {
34+
return nil, nil
35+
}
36+
37+
func (client ReservationsClient) Delete(context.Context, *computepb.DeleteReservationRequest, ...gax.CallOption) (*compute.Operation, error) {
38+
return nil, nil
39+
}

compute/reservations/reservations_test.go

Lines changed: 97 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -108,49 +108,101 @@ func TestReservations(t *testing.T) {
108108

109109
var buf bytes.Buffer
110110

111-
err := createTemplate(tc.ProjectID, templateName)
112-
if err != nil {
113-
t.Errorf("createTemplate got err: %v", err)
114-
}
115-
defer deleteTemplate(tc.ProjectID, templateName)
116-
117-
sourceTemplate, err := getTemplate(tc.ProjectID, templateName)
118-
if err != nil {
119-
t.Errorf("getTemplate got err: %v", err)
120-
}
121-
122-
want := "Reservation created"
123-
if err := createReservation(&buf, tc.ProjectID, zone, reservationName, *sourceTemplate.SelfLink); err != nil {
124-
t.Errorf("createReservation got err: %v", err)
125-
}
126-
if got := buf.String(); !strings.Contains(got, want) {
127-
t.Errorf("createReservation got %s, want %s", got, want)
128-
}
129-
buf.Reset()
130-
131-
want = fmt.Sprintf("Reservation: %s", reservationName)
132-
if err := getReservation(&buf, tc.ProjectID, zone, reservationName); err != nil {
133-
t.Errorf("getReservation got err: %v", err)
134-
}
135-
if got := buf.String(); !strings.Contains(got, want) {
136-
t.Errorf("getReservation got %s, want %s", got, want)
137-
}
138-
buf.Reset()
139-
140-
want = fmt.Sprintf("- %s %d", reservationName, 2)
141-
if err := listReservations(&buf, tc.ProjectID, zone); err != nil {
142-
t.Errorf("listReservations got err: %v", err)
143-
}
144-
if got := buf.String(); !strings.Contains(got, want) {
145-
t.Errorf("listReservations got %s, want %s", got, want)
146-
}
147-
buf.Reset()
148-
149-
want = "Reservation deleted"
150-
if err := deleteReservation(&buf, tc.ProjectID, zone, reservationName); err != nil {
151-
t.Errorf("deleteReservation got err: %v", err)
152-
}
153-
if got := buf.String(); !strings.Contains(got, want) {
154-
t.Errorf("deleteReservation got %s, want %s", got, want)
155-
}
111+
t.Run("Reservation CRUD", func(t *testing.T) {
112+
err := createTemplate(tc.ProjectID, templateName)
113+
if err != nil {
114+
t.Errorf("createTemplate got err: %v", err)
115+
}
116+
defer deleteTemplate(tc.ProjectID, templateName)
117+
118+
sourceTemplate, err := getTemplate(tc.ProjectID, templateName)
119+
if err != nil {
120+
t.Errorf("getTemplate got err: %v", err)
121+
}
122+
123+
want := "Reservation created"
124+
if err := createReservation(&buf, tc.ProjectID, zone, reservationName, *sourceTemplate.SelfLink); err != nil {
125+
t.Errorf("createReservation got err: %v", err)
126+
}
127+
if got := buf.String(); !strings.Contains(got, want) {
128+
t.Errorf("createReservation got %s, want %s", got, want)
129+
}
130+
buf.Reset()
131+
132+
want = fmt.Sprintf("Reservation: %s", reservationName)
133+
if err := getReservation(&buf, tc.ProjectID, zone, reservationName); err != nil {
134+
t.Errorf("getReservation got err: %v", err)
135+
}
136+
if got := buf.String(); !strings.Contains(got, want) {
137+
t.Errorf("getReservation got %s, want %s", got, want)
138+
}
139+
buf.Reset()
140+
141+
want = fmt.Sprintf("- %s %d", reservationName, 2)
142+
if err := listReservations(&buf, tc.ProjectID, zone); err != nil {
143+
t.Errorf("listReservations got err: %v", err)
144+
}
145+
if got := buf.String(); !strings.Contains(got, want) {
146+
t.Errorf("listReservations got %s, want %s", got, want)
147+
}
148+
buf.Reset()
149+
150+
want = "Reservation deleted"
151+
if err := deleteReservation(&buf, tc.ProjectID, zone, reservationName); err != nil {
152+
t.Errorf("deleteReservation got err: %v", err)
153+
}
154+
if got := buf.String(); !strings.Contains(got, want) {
155+
t.Errorf("deleteReservation got %s, want %s", got, want)
156+
}
157+
})
158+
159+
t.Run("Shared reservation CRUD", func(t *testing.T) {
160+
baseProjectID := tc.ProjectID
161+
// This test require 2 projects, therefore one of them is mocked.
162+
// If you want to make a real test, please adjust projectID accordingly and uncomment reservationsClient creation.
163+
// Make sure that base project has proper permissions to share reservations.
164+
// See: https://cloud.google.com/compute/docs/instances/reservations-shared#shared_reservation_constraint
165+
destinationProjectID := "some-project"
166+
err := createTemplate(baseProjectID, templateName)
167+
if err != nil {
168+
t.Errorf("createTemplate got err: %v", err)
169+
}
170+
defer deleteTemplate(baseProjectID, templateName)
171+
172+
sourceTemplate, err := getTemplate(baseProjectID, templateName)
173+
if err != nil {
174+
t.Errorf("getTemplate got err: %v", err)
175+
}
176+
177+
want := "Reservation created"
178+
179+
ctx := context.Background()
180+
181+
// Uncomment line below if you want to run the test without mocks
182+
// reservationsClient, err := compute.NewReservationsRESTClient(ctx)
183+
reservationsClient := ReservationsClient{}
184+
if err != nil {
185+
t.Errorf("Couldn't create reservationsClient, err: %v", err)
186+
}
187+
defer reservationsClient.Close()
188+
189+
if err := createSharedReservation(&buf, reservationsClient, destinationProjectID, baseProjectID, zone, reservationName, *sourceTemplate.SelfLink); err != nil {
190+
t.Errorf("createSharedReservation got err: %v", err)
191+
}
192+
if got := buf.String(); !strings.Contains(got, want) {
193+
t.Errorf("createSharedReservation got %s, want %s", got, want)
194+
}
195+
buf.Reset()
196+
197+
req := &computepb.DeleteReservationRequest{
198+
Project: baseProjectID,
199+
Reservation: reservationName,
200+
Zone: zone,
201+
}
202+
203+
_, err = reservationsClient.Delete(ctx, req)
204+
if err != nil {
205+
t.Errorf("unable to delete reservation: %v", err)
206+
}
207+
})
156208
}

0 commit comments

Comments
 (0)