diff --git a/compute/go.mod b/compute/go.mod index 0ff3aa54f1..0a091c0f52 100644 --- a/compute/go.mod +++ b/compute/go.mod @@ -7,6 +7,7 @@ require ( cloud.google.com/go/storage v1.43.0 github.com/GoogleCloudPlatform/golang-samples v0.0.0-20240724083556-7f760db013b7 github.com/google/uuid v1.6.0 + github.com/googleapis/gax-go/v2 v2.13.0 google.golang.org/api v0.193.0 google.golang.org/protobuf v1.34.2 ) @@ -23,7 +24,6 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.13.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect diff --git a/compute/reservations/create_shared_reservation.go b/compute/reservations/create_shared_reservation.go new file mode 100644 index 0000000000..3e4bfe5b08 --- /dev/null +++ b/compute/reservations/create_shared_reservation.go @@ -0,0 +1,79 @@ +// 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 +// +// https://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 snippets + +// [START compute_reservation_create_shared] +import ( + "context" + "fmt" + "io" + + computepb "cloud.google.com/go/compute/apiv1/computepb" + "google.golang.org/protobuf/proto" +) + +// Creates shared reservation from given template in particular zone +func createSharedReservation(w io.Writer, client ClientInterface, projectID, baseProjectId, zone, reservationName, sourceTemplate string) error { + // client, err := compute.NewReservationsRESTClient(ctx) + // projectID := "your_project_id". Destination of sharing. + // baseProjectId := "your_project_id2". Project where the reservation will be created. + // zone := "us-west3-a" + // reservationName := "your_reservation_name" + // sourceTemplate: existing template path. Following formats are allowed: + // - projects/{project_id}/global/instanceTemplates/{template_name} + // - projects/{project_id}/regions/{region}/instanceTemplates/{template_name} + // - https://www.googleapis.com/compute/v1/projects/{project_id}/global/instanceTemplates/instanceTemplate + // - https://www.googleapis.com/compute/v1/projects/{project_id}/regions/{region}/instanceTemplates/instanceTemplate + + ctx := context.Background() + + shareSettings := map[string]*computepb.ShareSettingsProjectConfig{ + projectID: {ProjectId: proto.String(projectID)}, + } + + req := &computepb.InsertReservationRequest{ + Project: baseProjectId, + ReservationResource: &computepb.Reservation{ + Name: proto.String(reservationName), + Zone: proto.String(zone), + SpecificReservation: &computepb.AllocationSpecificSKUReservation{ + Count: proto.Int64(2), + SourceInstanceTemplate: proto.String(sourceTemplate), + }, + ShareSettings: &computepb.ShareSettings{ + ProjectMap: shareSettings, + ShareType: proto.String("SPECIFIC_PROJECTS"), + }, + }, + Zone: zone, + } + + op, err := client.Insert(ctx, req) + if err != nil { + return fmt.Errorf("unable to create reservation: %w", err) + } + + if op != nil { + if err = op.Wait(ctx); err != nil { + return fmt.Errorf("unable to wait for the operation: %w", err) + } + } + + fmt.Fprintf(w, "Reservation created\n") + + return nil +} + +// [END compute_reservation_create_shared] diff --git a/compute/reservations/mock_shared_project.go b/compute/reservations/mock_shared_project.go new file mode 100644 index 0000000000..53bc413c3c --- /dev/null +++ b/compute/reservations/mock_shared_project.go @@ -0,0 +1,45 @@ +// 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 +// +// https://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. + +// Mock objects for shared project + +package snippets + +import ( + "context" + + compute "cloud.google.com/go/compute/apiv1" + computepb "cloud.google.com/go/compute/apiv1/computepb" + gax "github.com/googleapis/gax-go/v2" +) + +type ClientInterface interface { + Close() error + Delete(context.Context, *computepb.DeleteReservationRequest, ...gax.CallOption) (*compute.Operation, error) + Insert(context.Context, *computepb.InsertReservationRequest, ...gax.CallOption) (*compute.Operation, error) +} + +type ReservationsClient struct{} + +func (client ReservationsClient) Close() error { + return nil +} + +func (client ReservationsClient) Insert(context.Context, *computepb.InsertReservationRequest, ...gax.CallOption) (*compute.Operation, error) { + return nil, nil +} + +func (client ReservationsClient) Delete(context.Context, *computepb.DeleteReservationRequest, ...gax.CallOption) (*compute.Operation, error) { + return nil, nil +} diff --git a/compute/reservations/reservations_test.go b/compute/reservations/reservations_test.go index 01ede11f95..41a4f6b0c2 100644 --- a/compute/reservations/reservations_test.go +++ b/compute/reservations/reservations_test.go @@ -106,7 +106,6 @@ func TestReservations(t *testing.T) { templateName := fmt.Sprintf("test-template-%v-%v", time.Now().Format("01-02-2006"), r.Int()) var buf bytes.Buffer - err := createTemplate(tc.ProjectID, templateName) if err != nil { t.Errorf("createTemplate got err: %v", err) @@ -200,4 +199,44 @@ func TestReservations(t *testing.T) { t.Errorf("deleteReservation got err: %v", err) } }) + + t.Run("Shared reservation CRUD", func(t *testing.T) { + reservationName := fmt.Sprintf("test-reservation-%v-%v", time.Now().Format("01-02-2006"), r.Int()) + baseProjectID := tc.ProjectID + // This test require 2 projects, therefore one of them is mocked. + // If you want to make a real test, please adjust projectID accordingly and uncomment reservationsClient creation. + // Make sure that base project has proper permissions to share reservations. + // See: https://cloud.google.com/compute/docs/instances/reservations-shared#shared_reservation_constraint + destinationProjectID := "some-project" + ctx := context.Background() + + want := "Reservation created" + + // Uncomment line below if you want to run the test without mocks + // reservationsClient, err := compute.NewReservationsRESTClient(ctx) + reservationsClient := ReservationsClient{} + if err != nil { + t.Errorf("Couldn't create reservationsClient, err: %v", err) + } + defer reservationsClient.Close() + + if err := createSharedReservation(&buf, reservationsClient, destinationProjectID, baseProjectID, zone, reservationName, *sourceTemplate.SelfLink); err != nil { + t.Errorf("createSharedReservation got err: %v", err) + } + if got := buf.String(); !strings.Contains(got, want) { + t.Errorf("createSharedReservation got %s, want %s", got, want) + } + buf.Reset() + + req := &computepb.DeleteReservationRequest{ + Project: baseProjectID, + Reservation: reservationName, + Zone: zone, + } + + _, err = reservationsClient.Delete(ctx, req) + if err != nil { + t.Errorf("unable to delete reservation: %v", err) + } + }) }