From 15239afed7decbebe3dd920b1333579ef619a6eb Mon Sep 17 00:00:00 2001 From: Beraldo Leal Date: Wed, 26 Jun 2024 21:09:09 -0400 Subject: [PATCH 1/4] gcp: implementing provisioning interface This will create the cluster, vpc, image and overlay using the GKE infrastructure. Signed-off-by: Beraldo Leal --- src/cloud-api-adaptor/go.mod | 1 + .../test/provisioner/gcp/cluster.go | 284 ++++++++++++++++++ .../test/provisioner/gcp/image.go | 22 ++ .../test/provisioner/gcp/overlay.go | 88 ++++++ .../test/provisioner/gcp/provision.go | 15 + .../test/provisioner/gcp/provision_common.go | 105 +++++++ .../test/provisioner/gcp/vpc.go | 186 ++++++++++++ 7 files changed, 701 insertions(+) create mode 100644 src/cloud-api-adaptor/test/provisioner/gcp/cluster.go create mode 100644 src/cloud-api-adaptor/test/provisioner/gcp/image.go create mode 100644 src/cloud-api-adaptor/test/provisioner/gcp/overlay.go create mode 100644 src/cloud-api-adaptor/test/provisioner/gcp/provision.go create mode 100644 src/cloud-api-adaptor/test/provisioner/gcp/provision_common.go create mode 100644 src/cloud-api-adaptor/test/provisioner/gcp/vpc.go diff --git a/src/cloud-api-adaptor/go.mod b/src/cloud-api-adaptor/go.mod index 8f62f83bf1..f78cd7b687 100644 --- a/src/cloud-api-adaptor/go.mod +++ b/src/cloud-api-adaptor/go.mod @@ -58,6 +58,7 @@ require ( github.com/spf13/cobra v1.7.0 golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 + google.golang.org/api v0.162.0 google.golang.org/protobuf v1.33.0 k8s.io/api v0.26.2 k8s.io/apimachinery v0.26.2 diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/cluster.go b/src/cloud-api-adaptor/test/provisioner/gcp/cluster.go new file mode 100644 index 0000000000..f3d8e965f3 --- /dev/null +++ b/src/cloud-api-adaptor/test/provisioner/gcp/cluster.go @@ -0,0 +1,284 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package gcp + +import ( + "context" + "fmt" + "os" + "os/exec" + "strconv" + "time" + + log "github.com/sirupsen/logrus" + "google.golang.org/api/container/v1" + "google.golang.org/api/googleapi" + "google.golang.org/api/option" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/retry" + kconf "sigs.k8s.io/e2e-framework/klient/conf" +) + +// GKECluster implements the basic GKE Cluster client operations. +type GKECluster struct { + clusterName string + clusterVersion string + credentials string + machineType string + nodeCount int64 + projectID string + zone string + cluster *container.Cluster +} + +// NewGKECluster creates a new GKECluster with the given properties +func NewGKECluster(properties map[string]string) (*GKECluster, error) { + defaults := map[string]string{ + "cluster_name": "peer-pods", + "cluster_version": "1.27.11-gke.1062004", + "machine_type": "n1-standard-1", + "node_count": "2", + "zone": "us-central1-a", + } + + for key, value := range properties { + defaults[key] = value + } + + requiredFields := []string{"project_id", "credentials"} + for _, field := range requiredFields { + if _, ok := defaults[field]; !ok { + return nil, fmt.Errorf("%s is required", field) + } + } + + nodeCount, err := strconv.ParseInt(defaults["node_count"], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid node_count: %v", err) + } + + return &GKECluster{ + clusterName: defaults["cluster_name"], + clusterVersion: defaults["cluster_version"], + credentials: defaults["credentials"], + machineType: defaults["machine_type"], + nodeCount: nodeCount, + projectID: defaults["project_id"], + zone: defaults["zone"], + cluster: nil, + }, nil +} + +// CreateCluster creates the GKE cluster +func (g *GKECluster) CreateCluster(ctx context.Context) error { + ctx, cancel := context.WithTimeout(ctx, time.Hour) + defer cancel() + + srv, err := container.NewService( + ctx, option.WithCredentialsFile(g.credentials), + ) + if err != nil { + return fmt.Errorf("GKE: container.NewService: %v", err) + } + + cluster := &container.Cluster{ + Name: g.clusterName, + InitialNodeCount: g.nodeCount, + NodeConfig: &container.NodeConfig{ + MachineType: g.machineType, + ImageType: "UBUNTU_CONTAINERD", // Default CO OS has a ro fs. + }, + } + + req := &container.CreateClusterRequest{ + Cluster: cluster, + } + + op, err := srv.Projects.Zones.Clusters.Create( + g.projectID, g.zone, req, + ).Context(ctx).Do() + if err != nil { + return fmt.Errorf("GKE: Projects.Zones.Clusters.Create: %v", err) + } + + log.Infof("GKE: Cluster creation operation: %v\n", op.Name) + + g.cluster, err = g.WaitForClusterActive(ctx, 30*time.Minute) + if err != nil { + return fmt.Errorf("GKE: Error waiting for cluster to become active: %v", err) + } + + err = g.ApplyNodeLabels(ctx) + if err != nil { + return fmt.Errorf("GKE: Error applying node labels: %v", err) + } + return nil +} + +// DeleteCluster deletes the GKE cluster +func (g *GKECluster) DeleteCluster(ctx context.Context) error { + ctx, cancel := context.WithTimeout(ctx, time.Hour) + defer cancel() + + srv, err := container.NewService( + ctx, option.WithCredentialsFile(g.credentials), + ) + if err != nil { + return fmt.Errorf("GKE: container.NewService: %v", err) + } + + op, err := srv.Projects.Zones.Clusters.Delete( + g.projectID, g.zone, g.clusterName, + ).Context(ctx).Do() + if err != nil { + return fmt.Errorf("GKE: Projects.Zones.Clusters.Delete: %v", err) + } + + log.Infof("GKE: Cluster deletion operation: %v\n", op.Name) + + // Wait for the cluster to be deleted + activationTimeout := 30 * time.Minute + err = g.WaitForClusterDeleted(ctx, activationTimeout) + if err != nil { + return fmt.Errorf("GKE: error waiting for cluster to be deleted: %v", err) + } + return nil +} + +// WaitForClusterActive waits until the GKE cluster is active +func (g *GKECluster) WaitForClusterActive( + ctx context.Context, activationTimeout time.Duration, +) (*container.Cluster, error) { + srv, err := container.NewService( + ctx, option.WithCredentialsFile(g.credentials), + ) + if err != nil { + return nil, fmt.Errorf("GKE: container.NewService: %v", err) + } + + timeoutCtx, cancel := context.WithTimeout(ctx, activationTimeout) + defer cancel() + + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-timeoutCtx.Done(): + return nil, fmt.Errorf("GKE: Reached timeout waiting for cluster.") + case <-ticker.C: + cluster, err := srv.Projects.Zones.Clusters.Get(g.projectID, g.zone, g.clusterName).Context(ctx).Do() + if err != nil { + return nil, fmt.Errorf("GKE: Projects.Zones.Clusters.Get: %v", err) + } + + if cluster.Status == "RUNNING" { + log.Info("GKE: Cluster is now active") + return cluster, nil + } + + log.Info("GKE: Waiting for cluster to become active...") + } + } +} + +// WaitForClusterDeleted waits until the GKE cluster is deleted +func (g *GKECluster) WaitForClusterDeleted( + ctx context.Context, activationTimeout time.Duration, +) error { + srv, err := container.NewService( + ctx, option.WithCredentialsFile(g.credentials), + ) + if err != nil { + return fmt.Errorf("GKE: container.NewService: %v", err) + } + + timeoutCtx, cancel := context.WithTimeout(ctx, activationTimeout) + defer cancel() + + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-timeoutCtx.Done(): + return fmt.Errorf("GKE: timeout waiting for cluster deletion") + case <-ticker.C: + _, err := srv.Projects.Zones.Clusters.Get(g.projectID, g.zone, g.clusterName).Context(ctx).Do() + if err != nil { + if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { + log.Info("GKE: Cluster deleted successfully") + return nil + } + return fmt.Errorf("GKE: Projects.Zones.Clusters.Get: %v", err) + } + + log.Info("GKE: Waiting for cluster to be deleted...") + } + } +} + +func (g *GKECluster) ApplyNodeLabels(ctx context.Context) error { + kubeconfigPath, err := g.GetKubeconfigFile(ctx) + if err != nil { + return err + } + + config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + if err != nil { + return fmt.Errorf("failed to build kubeconfig: %v", err) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return fmt.Errorf("failed to create clientset: %v", err) + } + + nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + if err != nil { + return fmt.Errorf("failed to list nodes: %v", err) + } + + for _, node := range nodes.Items { + err := retry.RetryOnConflict(retry.DefaultRetry, func() error { + n, err := clientset.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("failed to get node: %v", err) + } + + n.Labels["node.kubernetes.io/worker"] = "" + n.Labels["node-role.kubernetes.io/worker"] = "" + _, err = clientset.CoreV1().Nodes().Update(ctx, n, metav1.UpdateOptions{}) + return err + }) + if err != nil { + return fmt.Errorf("Failed to label node %s: %v\n", node.Name, err) + } + log.Infof("Successfully labeled node %s\n", node.Name) + } + return nil +} + +// GetKubeconfigFile retrieves the path to the kubeconfig file +func (g *GKECluster) GetKubeconfigFile(ctx context.Context) (string, error) { + cmd := exec.CommandContext(ctx, "gcloud", "container", "clusters", "get-credentials", g.clusterName, "--zone", g.zone, "--project", g.projectID) + output, err := cmd.CombinedOutput() + + if err != nil { + return "", fmt.Errorf("Failed to get cluster credentials: %v\nOutput: %s", err, output) + } + + if g.cluster == nil { + return "", fmt.Errorf("Cluster not found. Call CreateCluster() first.") + } + + kubeconfigPath := kconf.ResolveKubeConfigFile() + _, err = os.Stat(kubeconfigPath) + if err != nil { + return "", fmt.Errorf("Failed to resolve KubeConfigfile: %v", err) + } + return kubeconfigPath, nil +} diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/image.go b/src/cloud-api-adaptor/test/provisioner/gcp/image.go new file mode 100644 index 0000000000..e3263d1c60 --- /dev/null +++ b/src/cloud-api-adaptor/test/provisioner/gcp/image.go @@ -0,0 +1,22 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package gcp + +import ( + "google.golang.org/api/compute/v1" +) + +type GCPImage struct { + Service *compute.Service + Description string + Name string +} + +func NewGCPImage(srv *compute.Service, name string) (*GCPImage, error) { + return &GCPImage{ + Service: srv, + Description: "Peer Pod VM image", + Name: name, + }, nil +} diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/overlay.go b/src/cloud-api-adaptor/test/provisioner/gcp/overlay.go new file mode 100644 index 0000000000..ca2a4db884 --- /dev/null +++ b/src/cloud-api-adaptor/test/provisioner/gcp/overlay.go @@ -0,0 +1,88 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package gcp + +import ( + "context" + pv "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner" + log "github.com/sirupsen/logrus" + "path/filepath" + "sigs.k8s.io/e2e-framework/pkg/envconf" +) + +type GCPInstallOverlay struct { + Overlay *pv.KustomizeOverlay +} + +func NewGCPInstallOverlay(installDir, provider string) (pv.InstallOverlay, error) { + overlay, err := pv.NewKustomizeOverlay(filepath.Join(installDir, "overlays", provider)) + if err != nil { + return nil, err + } + + return &GCPInstallOverlay{ + Overlay: overlay, + }, nil +} + +func (a *GCPInstallOverlay) Apply(ctx context.Context, cfg *envconf.Config) error { + return a.Overlay.Apply(ctx, cfg) +} + +func (a *GCPInstallOverlay) Delete(ctx context.Context, cfg *envconf.Config) error { + return a.Overlay.Delete(ctx, cfg) +} + +func (a *GCPInstallOverlay) Edit(ctx context.Context, cfg *envconf.Config, properties map[string]string) error { + var err error + + image := properties["caa_image_name"] + log.Infof("Updating caa image with %s", image) + if image != "" { + err = a.Overlay.SetKustomizeImage("cloud-api-adaptor", "newName", image) + if err != nil { + return err + } + } + + // Mapping the internal properties to ConfigMapGenerator properties. + mapProps := map[string]string{ + "pause_image": "PAUSE_IMAGE", + "podvm_image_name": "PODVM_IMAGE_NAME", + "machine_type": "GCP_MACHINE_TYPE", + "project_id": "GCP_PROJECT_ID", + "zone": "GCP_ZONE", + "network": "GCP_NETWORK", + "vxlan_port": "VXLAN_PORT", + } + + for k, v := range mapProps { + if properties[k] != "" { + if err = a.Overlay.SetKustomizeConfigMapGeneratorLiteral("peer-pods-cm", + v, properties[k]); err != nil { + return err + } + } + } + + // Mapping the internal properties to SecretGenerator properties. + mapProps = map[string]string{ + "credentials": "GCP_CREDENTIALS", + } + for k, _ := range mapProps { + if properties[k] != "" { + log.Info(properties[k]) + if err = a.Overlay.SetKustomizeSecretGeneratorFile("peer-pods-secret", + properties[k]); err != nil { + return err + } + } + } + + if err = a.Overlay.YamlReload(); err != nil { + return err + } + + return nil +} diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/provision.go b/src/cloud-api-adaptor/test/provisioner/gcp/provision.go new file mode 100644 index 0000000000..ad4399de43 --- /dev/null +++ b/src/cloud-api-adaptor/test/provisioner/gcp/provision.go @@ -0,0 +1,15 @@ +//go:build gcp + +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package gcp + +import ( + pv "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner" +) + +func init() { + pv.NewProvisionerFunctions["gcp"] = NewGCPProvisioner + pv.NewInstallOverlayFunctions["gcp"] = NewGCPInstallOverlay +} diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/provision_common.go b/src/cloud-api-adaptor/test/provisioner/gcp/provision_common.go new file mode 100644 index 0000000000..1144471b38 --- /dev/null +++ b/src/cloud-api-adaptor/test/provisioner/gcp/provision_common.go @@ -0,0 +1,105 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package gcp + +import ( + "context" + "fmt" + "google.golang.org/api/option" + + pv "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner" + // log "github.com/sirupsen/logrus" + "google.golang.org/api/compute/v1" + "sigs.k8s.io/e2e-framework/pkg/envconf" +) + +var GCPProps = &GCPProvisioner{} + +// GCPProvisioner implements the CloudProvisioner interface. +type GCPProvisioner struct { + GkeCluster *GKECluster + GcpVPC *GCPVPC + PodvmImage *GCPImage + CaaImageName string +} + +// NewGCPProvisioner creates a new GCPProvisioner with the given properties. +func NewGCPProvisioner(properties map[string]string) (pv.CloudProvisioner, error) { + ctx := context.Background() + + srv, err := compute.NewService( + ctx, option.WithCredentialsFile(properties["credentials"])) + if err != nil { + return nil, fmt.Errorf("GCP: compute.NewService: %v", err) + } + + gkeCluster, err := NewGKECluster(properties) + if err != nil { + return nil, err + } + + gcpVPC, err := NewGCPVPC(properties) + if err != nil { + return nil, err + } + + image, err := NewGCPImage(srv, properties["podvm_image_name"]) + if err != nil { + return nil, err + } + + GCPProps = &GCPProvisioner{ + GkeCluster: gkeCluster, + GcpVPC: gcpVPC, + PodvmImage: image, + CaaImageName: properties["caa_image_name"], + } + return GCPProps, nil +} + +// CreateCluster creates a new GKE cluster. +func (p *GCPProvisioner) CreateCluster(ctx context.Context, cfg *envconf.Config) error { + err := p.GkeCluster.CreateCluster(ctx) + if err != nil { + return err + } + + kubeconfigPath, err := p.GkeCluster.GetKubeconfigFile(ctx) + if err != nil { + return err + } + *cfg = *envconf.NewWithKubeConfig(kubeconfigPath) + + return nil +} + +// CreateVPC creates a new VPC in Google Cloud. +func (p *GCPProvisioner) CreateVPC(ctx context.Context, cfg *envconf.Config) error { + return p.GcpVPC.CreateVPC(ctx, cfg) +} + +// DeleteCluster deletes the GKE cluster. +func (p *GCPProvisioner) DeleteCluster(ctx context.Context, cfg *envconf.Config) error { + return p.GkeCluster.DeleteCluster(ctx) +} + +// DeleteVPC deletes the VPC in Google Cloud. +func (p *GCPProvisioner) DeleteVPC(ctx context.Context, cfg *envconf.Config) error { + return p.GcpVPC.DeleteVPC(ctx, cfg) +} + +func (p *GCPProvisioner) GetProperties(ctx context.Context, cfg *envconf.Config) map[string]string { + return map[string]string{ + "podvm_image_name": p.PodvmImage.Name, + "machine_type": p.GkeCluster.machineType, + "project_id": p.GkeCluster.projectID, + "zone": p.GkeCluster.zone, + "network": p.GcpVPC.vpcName, + "caa_image_name": p.CaaImageName, + } +} + +func (p *GCPProvisioner) UploadPodvm(imagePath string, ctx context.Context, cfg *envconf.Config) error { + return nil +} diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/vpc.go b/src/cloud-api-adaptor/test/provisioner/gcp/vpc.go new file mode 100644 index 0000000000..bff9bb0ac7 --- /dev/null +++ b/src/cloud-api-adaptor/test/provisioner/gcp/vpc.go @@ -0,0 +1,186 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package gcp + +import ( + "context" + "fmt" + "time" + + log "github.com/sirupsen/logrus" + "google.golang.org/api/compute/v1" + "google.golang.org/api/googleapi" + "google.golang.org/api/option" + "sigs.k8s.io/e2e-framework/pkg/envconf" +) + +// GCPVPC implements the Google Compute VPC interface. +type GCPVPC struct { + vpcName string + credentials string + projectID string +} + +// NewGCPVPC creates a new GCPVPC object. +func NewGCPVPC(properties map[string]string) (*GCPVPC, error) { + defaults := map[string]string{ + "vpc_name": "default", + } + + for key, value := range properties { + defaults[key] = value + } + + requiredFields := []string{"project_id", "credentials"} + for _, field := range requiredFields { + if _, ok := defaults[field]; !ok { + return nil, fmt.Errorf("%s is required", field) + } + } + + return &GCPVPC{ + vpcName: defaults["vpc_name"], + credentials: defaults["credentials"], + projectID: defaults["project_id"], + }, nil +} + +// CreateVPC creates a new VPC in Google Cloud. +func (g *GCPVPC) CreateVPC( + ctx context.Context, cfg *envconf.Config, +) error { + ctx, cancel := context.WithTimeout(ctx, time.Hour) + defer cancel() + + srv, err := compute.NewService(ctx, option.WithCredentialsFile(g.credentials)) + if err != nil { + return fmt.Errorf("GKE: compute.NewService: %v", err) + } + + _, err = srv.Networks.Get(g.projectID, g.vpcName).Context(ctx).Do() + if err == nil { + log.Infof("GKE: Using existing VPC %s.\n", g.vpcName) + return nil + } + + network := &compute.Network{ + Name: g.vpcName, + AutoCreateSubnetworks: true, + } + + op, err := srv.Networks.Insert(g.projectID, network).Context(ctx).Do() + if err != nil { + return fmt.Errorf("GKE: Networks.Insert: %v", err) + } + + log.Infof("GKE: VPC creation operation started: %v\n", op.Name) + + err = g.WaitForVPCCreation(ctx, 30*time.Minute) + if err != nil { + return fmt.Errorf("GKE: Error waiting for VPC to be created: %v", err) + } + + // subnetwork := &compute.Subnetwork{ + // Name: "peer-pods-subnet", + // Network: op.SelfLink, + // Region: "us-west1", + // } + // + // _, err = srv.Subnetworks.Insert(g.projectID, "us-west1", subnetwork).Context(ctx).Do() + // if err != nil { + // return fmt.Errorf("GKE: Subnetworks.Insert: %v", err) + // } + return nil +} + +// DeleteVPC deletes a VPC in Google Cloud. +func (g *GCPVPC) DeleteVPC(ctx context.Context, cfg *envconf.Config) error { + srv, err := compute.NewService(ctx, option.WithCredentialsFile(g.credentials)) + if err != nil { + return fmt.Errorf("GKE: compute.NewService: %v", err) + } + + op, err := srv.Networks.Delete(g.projectID, g.vpcName).Context(ctx).Do() + if err != nil { + return fmt.Errorf("GKE: Networks.Delete: %v", err) + } + + log.Infof("GKE: VPC deletion operation started: %v\n", op.Name) + + err = g.WaitForVPCDeleted(ctx, 30*time.Minute) + if err != nil { + return fmt.Errorf("GKE: Error waiting for VPC to be deleted: %v", err) + } + + return nil +} + +// WaitForVPCCreation waits until the VPC is created and available. +func (g *GCPVPC) WaitForVPCCreation( + ctx context.Context, timeout time.Duration, +) error { + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + srv, err := compute.NewService(ctx, option.WithCredentialsFile(g.credentials)) + if err != nil { + return fmt.Errorf("compute.NewService: %v", err) + } + + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return fmt.Errorf("timeout waiting for VPC creation") + case <-ticker.C: + network, err := srv.Networks.Get(g.projectID, g.vpcName).Context(ctx).Do() + if err != nil { + if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Code == 404 { + log.Info("Waiting for VPC to be created...") + continue + } + return fmt.Errorf("Networks.Get: %v", err) + } + if network.SelfLink != "" { + log.Info("VPC created successfully") + return nil + } + } + } +} + +// WaitForVPCDeleted waits until the VPC is deleted. +func (g *GCPVPC) WaitForVPCDeleted( + ctx context.Context, timeout time.Duration, +) error { + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + srv, err := compute.NewService(ctx, option.WithCredentialsFile(g.credentials)) + if err != nil { + return fmt.Errorf("GKE: compute.NewService: %v", err) + } + + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return fmt.Errorf("GKE: timeout waiting for VPC deletion") + case <-ticker.C: + _, err := srv.Networks.Get(g.projectID, g.vpcName).Context(ctx).Do() + if err != nil { + if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Code == 404 { + log.Info("GKE: VPC deleted successfully") + return nil + } + return fmt.Errorf("GKE: Networks.Get: %v", err) + } + log.Info("GKE: Waiting for VPC to be deleted...") + } + } +} From d946ee1ba14d9f748e774c232614be7c57e8b7b1 Mon Sep 17 00:00:00 2001 From: Beraldo Leal Date: Thu, 3 Oct 2024 14:07:23 -0400 Subject: [PATCH 2/4] gcp: adding basic e2e tests Those are just a basic set of sets, more to come in future PRs. Signed-off-by: Beraldo Leal --- src/cloud-api-adaptor/test/e2e/gcp_common.go | 56 ++++++++++ src/cloud-api-adaptor/test/e2e/gcp_test.go | 104 +++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 src/cloud-api-adaptor/test/e2e/gcp_common.go create mode 100644 src/cloud-api-adaptor/test/e2e/gcp_test.go diff --git a/src/cloud-api-adaptor/test/e2e/gcp_common.go b/src/cloud-api-adaptor/test/e2e/gcp_common.go new file mode 100644 index 0000000000..36f68235b8 --- /dev/null +++ b/src/cloud-api-adaptor/test/e2e/gcp_common.go @@ -0,0 +1,56 @@ +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "testing" + "time" + + pv "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner/gcp" +) + +// GCPAssert implements the CloudAssert interface. +type GCPAssert struct { + Vpc *pv.GCPVPC +} + +func NewGCPAssert() GCPAssert { + return GCPAssert{ + Vpc: pv.GCPProps.GcpVPC, + } +} + +func (aa GCPAssert) DefaultTimeout() time.Duration { + return 1 * time.Minute +} + +func (aa GCPAssert) HasPodVM(t *testing.T, id string) { + podvmPrefix := "podvm-" + id + + // Create a request to list instances in the specified project and zone. + req := aa.ComputeService.Instances.List(aa.ProjectID, aa.Zone) + instances, err := req.Do() + if err != nil { + t.Errorf("Failed to list instances: %v", err) + return + } + + found := false + for _, instance := range instances.Items { + if instance.Status != "TERMINATED" { + if strings.HasPrefix(instance.Name, podvmPrefix) { + found = true + break + } + } + } + + if !found { + t.Errorf("Podvm name=%s not found", id) + } +} + +func (aa GCPAssert) GetInstanceType(t *testing.T, podName string) (string, error) { + return "", nil +} diff --git a/src/cloud-api-adaptor/test/e2e/gcp_test.go b/src/cloud-api-adaptor/test/e2e/gcp_test.go new file mode 100644 index 0000000000..b637040300 --- /dev/null +++ b/src/cloud-api-adaptor/test/e2e/gcp_test.go @@ -0,0 +1,104 @@ +//go:build gcp + +// (C) Copyright Confidential Containers Contributors +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "testing" +) + +func TestGCPCreateSimplePod(t *testing.T) { + assert := GCPAssert{} + DoTestCreateSimplePod(t, testEnv, assert) +} + +func TestGCPCreatePodWithConfigMap(t *testing.T) { + t.Skip("Test not passing") + assert := NewGCPAssert() + + DoTestCreatePodWithConfigMap(t, testEnv, assert) +} + +func TestGCPCreatePodWithSecret(t *testing.T) { + t.Skip("Test not passing") + assert := NewGCPAssert() + + DoTestCreatePodWithSecret(t, testEnv, assert) +} + +// func TestAwsCreatePeerPodContainerWithExternalIPAccess(t *testing.T) { +// t.Skip("Test not passing") +// assert := NewAWSAssert() +// +// DoTestCreatePeerPodContainerWithExternalIPAccess(t, testEnv, assert) +// } +// +// func TestAwsCreatePeerPodWithJob(t *testing.T) { +// assert := NewAWSAssert() +// +// DoTestCreatePeerPodWithJob(t, testEnv, assert) +// } +// +// func TestAwsCreatePeerPodAndCheckUserLogs(t *testing.T) { +// assert := NewAWSAssert() +// +// DoTestCreatePeerPodAndCheckUserLogs(t, testEnv, assert) +// } +// +// func TestAwsCreatePeerPodAndCheckWorkDirLogs(t *testing.T) { +// assert := NewAWSAssert() +// +// DoTestCreatePeerPodAndCheckWorkDirLogs(t, testEnv, assert) +// } +// +// func TestAwsCreatePeerPodAndCheckEnvVariableLogsWithImageOnly(t *testing.T) { +// assert := NewAWSAssert() +// +// DoTestCreatePeerPodAndCheckEnvVariableLogsWithImageOnly(t, testEnv, assert) +// } +// +// func TestAwsCreatePeerPodAndCheckEnvVariableLogsWithDeploymentOnly(t *testing.T) { +// assert := NewAWSAssert() +// +// DoTestCreatePeerPodAndCheckEnvVariableLogsWithDeploymentOnly(t, testEnv, assert) +// } +// +// func TestAwsCreatePeerPodAndCheckEnvVariableLogsWithImageAndDeployment(t *testing.T) { +// assert := NewAWSAssert() +// +// DoTestCreatePeerPodAndCheckEnvVariableLogsWithImageAndDeployment(t, testEnv, assert) +// } +// +// func TestAwsCreatePeerPodWithLargeImage(t *testing.T) { +// assert := NewAWSAssert() +// +// DoTestCreatePeerPodWithLargeImage(t, testEnv, assert) +// } +// +// func TestAwsCreatePeerPodWithPVC(t *testing.T) { +// t.Skip("To be implemented") +// } +// +// func TestAwsCreatePeerPodWithAuthenticatedImagewithValidCredentials(t *testing.T) { +// t.Skip("To be implemented") +// } +// +// func TestAwsCreatePeerPodWithAuthenticatedImageWithInvalidCredentials(t *testing.T) { +// t.Skip("To be implemented") +// } +// +// func TestAwsCreatePeerPodWithAuthenticatedImageWithoutCredentials(t *testing.T) { +// t.Skip("To be implemented") +// } +// +// func TestAwsDeletePod(t *testing.T) { +// assert := NewAWSAssert() +// DoTestDeleteSimplePod(t, testEnv, assert) +// } +// +// func TestAwsCreateNginxDeployment(t *testing.T) { +// assert := NewAWSAssert() +// DoTestNginxDeployment(t, testEnv, assert) +// } From 3d4ba13877835095553400d9e3a38ce0934d6c0c Mon Sep 17 00:00:00 2001 From: Beraldo Leal Date: Fri, 4 Oct 2024 13:33:05 +0000 Subject: [PATCH 3/4] wip: changes to the provisioner Signed-off-by: Beraldo Leal --- src/cloud-api-adaptor/test/e2e/gcp_common.go | 12 +- .../test/provisioner/gcp/cluster.go | 109 ++++-------------- .../test/provisioner/gcp/image.go | 17 +-- .../test/provisioner/gcp/provision_common.go | 28 ++--- .../test/provisioner/gcp/vpc.go | 99 +++++----------- 5 files changed, 79 insertions(+), 186 deletions(-) diff --git a/src/cloud-api-adaptor/test/e2e/gcp_common.go b/src/cloud-api-adaptor/test/e2e/gcp_common.go index 36f68235b8..fd581d6203 100644 --- a/src/cloud-api-adaptor/test/e2e/gcp_common.go +++ b/src/cloud-api-adaptor/test/e2e/gcp_common.go @@ -5,9 +5,11 @@ package e2e import ( "testing" + "strings" "time" pv "github.com/confidential-containers/cloud-api-adaptor/src/cloud-api-adaptor/test/provisioner/gcp" + "google.golang.org/api/compute/v1" ) // GCPAssert implements the CloudAssert interface. @@ -29,7 +31,7 @@ func (aa GCPAssert) HasPodVM(t *testing.T, id string) { podvmPrefix := "podvm-" + id // Create a request to list instances in the specified project and zone. - req := aa.ComputeService.Instances.List(aa.ProjectID, aa.Zone) + req := pv.GCPProps.ComputeService.Instances.List(pv.GCPProps.ProjectID, pv.GCPProps.Zone) instances, err := req.Do() if err != nil { t.Errorf("Failed to list instances: %v", err) @@ -38,11 +40,9 @@ func (aa GCPAssert) HasPodVM(t *testing.T, id string) { found := false for _, instance := range instances.Items { - if instance.Status != "TERMINATED" { - if strings.HasPrefix(instance.Name, podvmPrefix) { - found = true - break - } + if instance.Status != "TERMINATED" && strings.HasPrefix(instance.Name, podvmPrefix) { + found = true + break } } diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/cluster.go b/src/cloud-api-adaptor/test/provisioner/gcp/cluster.go index f3d8e965f3..23fd311113 100644 --- a/src/cloud-api-adaptor/test/provisioner/gcp/cluster.go +++ b/src/cloud-api-adaptor/test/provisioner/gcp/cluster.go @@ -24,66 +24,31 @@ import ( // GKECluster implements the basic GKE Cluster client operations. type GKECluster struct { - clusterName string - clusterVersion string - credentials string - machineType string - nodeCount int64 - projectID string - zone string - cluster *container.Cluster + Srv *container.Service + Properties map[string]string } // NewGKECluster creates a new GKECluster with the given properties func NewGKECluster(properties map[string]string) (*GKECluster, error) { - defaults := map[string]string{ - "cluster_name": "peer-pods", - "cluster_version": "1.27.11-gke.1062004", - "machine_type": "n1-standard-1", - "node_count": "2", - "zone": "us-central1-a", - } - - for key, value := range properties { - defaults[key] = value - } - - requiredFields := []string{"project_id", "credentials"} - for _, field := range requiredFields { - if _, ok := defaults[field]; !ok { - return nil, fmt.Errorf("%s is required", field) - } - } - - nodeCount, err := strconv.ParseInt(defaults["node_count"], 10, 64) + srv, err := container.NewService( + context.Background(), + option.WithCredentialsFile(properties["credentialsPath"]), + ) if err != nil { - return nil, fmt.Errorf("invalid node_count: %v", err) + return nil, fmt.Errorf("failed to create GKE service: %w", err) } - return &GKECluster{ - clusterName: defaults["cluster_name"], - clusterVersion: defaults["cluster_version"], - credentials: defaults["credentials"], - machineType: defaults["machine_type"], - nodeCount: nodeCount, - projectID: defaults["project_id"], - zone: defaults["zone"], - cluster: nil, - }, nil + return &GKECluster{ + Srv: srv, + Properties: properties, + }, nil } -// CreateCluster creates the GKE cluster -func (g *GKECluster) CreateCluster(ctx context.Context) error { +// Create creates the GKE cluster +func (g *GKECluster) Create(ctx context.Context) error { ctx, cancel := context.WithTimeout(ctx, time.Hour) defer cancel() - srv, err := container.NewService( - ctx, option.WithCredentialsFile(g.credentials), - ) - if err != nil { - return fmt.Errorf("GKE: container.NewService: %v", err) - } - cluster := &container.Cluster{ Name: g.clusterName, InitialNodeCount: g.nodeCount, @@ -97,7 +62,7 @@ func (g *GKECluster) CreateCluster(ctx context.Context) error { Cluster: cluster, } - op, err := srv.Projects.Zones.Clusters.Create( + op, err := g.Client.Projects.Zones.Clusters.Create( g.projectID, g.zone, req, ).Context(ctx).Do() if err != nil { @@ -106,7 +71,7 @@ func (g *GKECluster) CreateCluster(ctx context.Context) error { log.Infof("GKE: Cluster creation operation: %v\n", op.Name) - g.cluster, err = g.WaitForClusterActive(ctx, 30*time.Minute) + _, err = g.WaitForActive(ctx, 30*time.Minute) if err != nil { return fmt.Errorf("GKE: Error waiting for cluster to become active: %v", err) } @@ -118,19 +83,12 @@ func (g *GKECluster) CreateCluster(ctx context.Context) error { return nil } -// DeleteCluster deletes the GKE cluster -func (g *GKECluster) DeleteCluster(ctx context.Context) error { +// Delete deletes the GKE cluster +func (g *GKECluster) Delete(ctx context.Context) error { ctx, cancel := context.WithTimeout(ctx, time.Hour) defer cancel() - srv, err := container.NewService( - ctx, option.WithCredentialsFile(g.credentials), - ) - if err != nil { - return fmt.Errorf("GKE: container.NewService: %v", err) - } - - op, err := srv.Projects.Zones.Clusters.Delete( + op, err := g.Client.Projects.Zones.Clusters.Delete( g.projectID, g.zone, g.clusterName, ).Context(ctx).Do() if err != nil { @@ -141,23 +99,17 @@ func (g *GKECluster) DeleteCluster(ctx context.Context) error { // Wait for the cluster to be deleted activationTimeout := 30 * time.Minute - err = g.WaitForClusterDeleted(ctx, activationTimeout) + err = g.WaitForDeleted(ctx, activationTimeout) if err != nil { return fmt.Errorf("GKE: error waiting for cluster to be deleted: %v", err) } return nil } -// WaitForClusterActive waits until the GKE cluster is active -func (g *GKECluster) WaitForClusterActive( +// WaitForActive waits until the GKE cluster is active +func (g *GKECluster) WaitForActive( ctx context.Context, activationTimeout time.Duration, ) (*container.Cluster, error) { - srv, err := container.NewService( - ctx, option.WithCredentialsFile(g.credentials), - ) - if err != nil { - return nil, fmt.Errorf("GKE: container.NewService: %v", err) - } timeoutCtx, cancel := context.WithTimeout(ctx, activationTimeout) defer cancel() @@ -170,7 +122,7 @@ func (g *GKECluster) WaitForClusterActive( case <-timeoutCtx.Done(): return nil, fmt.Errorf("GKE: Reached timeout waiting for cluster.") case <-ticker.C: - cluster, err := srv.Projects.Zones.Clusters.Get(g.projectID, g.zone, g.clusterName).Context(ctx).Do() + cluster, err := g.Client.Projects.Zones.Clusters.Get(g.projectID, g.zone, g.clusterName).Context(ctx).Do() if err != nil { return nil, fmt.Errorf("GKE: Projects.Zones.Clusters.Get: %v", err) } @@ -185,17 +137,10 @@ func (g *GKECluster) WaitForClusterActive( } } -// WaitForClusterDeleted waits until the GKE cluster is deleted -func (g *GKECluster) WaitForClusterDeleted( +// WaitForDeleted waits until the GKE cluster is deleted +func (g *GKECluster) WaitForDeleted( ctx context.Context, activationTimeout time.Duration, ) error { - srv, err := container.NewService( - ctx, option.WithCredentialsFile(g.credentials), - ) - if err != nil { - return fmt.Errorf("GKE: container.NewService: %v", err) - } - timeoutCtx, cancel := context.WithTimeout(ctx, activationTimeout) defer cancel() @@ -207,7 +152,7 @@ func (g *GKECluster) WaitForClusterDeleted( case <-timeoutCtx.Done(): return fmt.Errorf("GKE: timeout waiting for cluster deletion") case <-ticker.C: - _, err := srv.Projects.Zones.Clusters.Get(g.projectID, g.zone, g.clusterName).Context(ctx).Do() + _, err := g.Client.Projects.Zones.Clusters.Get(g.projectID, g.zone, g.clusterName).Context(ctx).Do() if err != nil { if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { log.Info("GKE: Cluster deleted successfully") @@ -271,10 +216,6 @@ func (g *GKECluster) GetKubeconfigFile(ctx context.Context) (string, error) { return "", fmt.Errorf("Failed to get cluster credentials: %v\nOutput: %s", err, output) } - if g.cluster == nil { - return "", fmt.Errorf("Cluster not found. Call CreateCluster() first.") - } - kubeconfigPath := kconf.ResolveKubeConfigFile() _, err = os.Stat(kubeconfigPath) if err != nil { diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/image.go b/src/cloud-api-adaptor/test/provisioner/gcp/image.go index e3263d1c60..33ed6e91d1 100644 --- a/src/cloud-api-adaptor/test/provisioner/gcp/image.go +++ b/src/cloud-api-adaptor/test/provisioner/gcp/image.go @@ -8,15 +8,16 @@ import ( ) type GCPImage struct { - Service *compute.Service - Description string - Name string + Client *compute.Service } -func NewGCPImage(srv *compute.Service, name string) (*GCPImage, error) { +func NewGCPImage(credentialsPath string) *GCPImage { + client, err := compute.NewService(context.TODO(), option.WithCredentialsFile(credentialsPath)) + if err != nil { + return nil, fmt.Errorf("GKE: failed to create GCP compute service: %v", err) + } + return &GCPImage{ - Service: srv, - Description: "Peer Pod VM image", - Name: name, - }, nil + Client: client, + } } diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/provision_common.go b/src/cloud-api-adaptor/test/provisioner/gcp/provision_common.go index 1144471b38..944ae47d92 100644 --- a/src/cloud-api-adaptor/test/provisioner/gcp/provision_common.go +++ b/src/cloud-api-adaptor/test/provisioner/gcp/provision_common.go @@ -21,30 +21,23 @@ type GCPProvisioner struct { GkeCluster *GKECluster GcpVPC *GCPVPC PodvmImage *GCPImage - CaaImageName string } // NewGCPProvisioner creates a new GCPProvisioner with the given properties. func NewGCPProvisioner(properties map[string]string) (pv.CloudProvisioner, error) { - ctx := context.Background() + credentials_path = properties['credentials_path'] - srv, err := compute.NewService( - ctx, option.WithCredentialsFile(properties["credentials"])) - if err != nil { - return nil, fmt.Errorf("GCP: compute.NewService: %v", err) - } - - gkeCluster, err := NewGKECluster(properties) + gkeCluster, err := NewGKECluster(credentials_path) if err != nil { return nil, err } - gcpVPC, err := NewGCPVPC(properties) + gcpVPC, err := NewGCPVPC(credentials_path, properties['vpc_name']) if err != nil { return nil, err } - image, err := NewGCPImage(srv, properties["podvm_image_name"]) + gcpImage, err := NewGCPImage(credentials_path) if err != nil { return nil, err } @@ -52,15 +45,14 @@ func NewGCPProvisioner(properties map[string]string) (pv.CloudProvisioner, error GCPProps = &GCPProvisioner{ GkeCluster: gkeCluster, GcpVPC: gcpVPC, - PodvmImage: image, - CaaImageName: properties["caa_image_name"], + PodvmImage: gcpImage, } return GCPProps, nil } // CreateCluster creates a new GKE cluster. func (p *GCPProvisioner) CreateCluster(ctx context.Context, cfg *envconf.Config) error { - err := p.GkeCluster.CreateCluster(ctx) + err := p.GkeCluster.Create(ctx) if err != nil { return err } @@ -76,17 +68,17 @@ func (p *GCPProvisioner) CreateCluster(ctx context.Context, cfg *envconf.Config) // CreateVPC creates a new VPC in Google Cloud. func (p *GCPProvisioner) CreateVPC(ctx context.Context, cfg *envconf.Config) error { - return p.GcpVPC.CreateVPC(ctx, cfg) + return p.GcpVPC.Create(ctx, cfg) } // DeleteCluster deletes the GKE cluster. func (p *GCPProvisioner) DeleteCluster(ctx context.Context, cfg *envconf.Config) error { - return p.GkeCluster.DeleteCluster(ctx) + return p.GkeCluster.Delete(ctx) } // DeleteVPC deletes the VPC in Google Cloud. func (p *GCPProvisioner) DeleteVPC(ctx context.Context, cfg *envconf.Config) error { - return p.GcpVPC.DeleteVPC(ctx, cfg) + return p.GcpVPC.Delete(ctx, cfg) } func (p *GCPProvisioner) GetProperties(ctx context.Context, cfg *envconf.Config) map[string]string { @@ -96,10 +88,10 @@ func (p *GCPProvisioner) GetProperties(ctx context.Context, cfg *envconf.Config) "project_id": p.GkeCluster.projectID, "zone": p.GkeCluster.zone, "network": p.GcpVPC.vpcName, - "caa_image_name": p.CaaImageName, } } func (p *GCPProvisioner) UploadPodvm(imagePath string, ctx context.Context, cfg *envconf.Config) error { + // To be Implemented return nil } diff --git a/src/cloud-api-adaptor/test/provisioner/gcp/vpc.go b/src/cloud-api-adaptor/test/provisioner/gcp/vpc.go index bff9bb0ac7..85ae4873f0 100644 --- a/src/cloud-api-adaptor/test/provisioner/gcp/vpc.go +++ b/src/cloud-api-adaptor/test/provisioner/gcp/vpc.go @@ -17,98 +17,67 @@ import ( // GCPVPC implements the Google Compute VPC interface. type GCPVPC struct { - vpcName string - credentials string - projectID string + Client *compute.Service + CredentialsPath string + Name string } // NewGCPVPC creates a new GCPVPC object. -func NewGCPVPC(properties map[string]string) (*GCPVPC, error) { - defaults := map[string]string{ - "vpc_name": "default", - } - - for key, value := range properties { - defaults[key] = value - } - - requiredFields := []string{"project_id", "credentials"} - for _, field := range requiredFields { - if _, ok := defaults[field]; !ok { - return nil, fmt.Errorf("%s is required", field) - } +func NewGCPVPC(credentialsPath string, name string) (*GCPVPC, error) { + client, err := compute.NewService(context.TODO(), option.WithCredentialsFile(credentialsPath)) + if err != nil { + return nil, fmt.Errorf("GKE: failed to create GCP compute service: %v", err) } return &GCPVPC{ - vpcName: defaults["vpc_name"], - credentials: defaults["credentials"], - projectID: defaults["project_id"], + Client: client, + CredentialsPath: credentialsPath, + Name: name, }, nil } -// CreateVPC creates a new VPC in Google Cloud. -func (g *GCPVPC) CreateVPC( +// Create creates a new VPC in Google Cloud. +func (g *GCPVPC) Create( ctx context.Context, cfg *envconf.Config, ) error { ctx, cancel := context.WithTimeout(ctx, time.Hour) defer cancel() - srv, err := compute.NewService(ctx, option.WithCredentialsFile(g.credentials)) - if err != nil { - return fmt.Errorf("GKE: compute.NewService: %v", err) + _, err = g.Client.Networks.Get(g.projectID, g.Name).Context(ctx).Do() + if err == nil { + log.Infof("GKE: Using existing VPC %s.\n", g.Name) + return nil } - _, err = srv.Networks.Get(g.projectID, g.vpcName).Context(ctx).Do() - if err == nil { - log.Infof("GKE: Using existing VPC %s.\n", g.vpcName) - return nil - } - network := &compute.Network{ - Name: g.vpcName, + Name: g.Name, AutoCreateSubnetworks: true, } - op, err := srv.Networks.Insert(g.projectID, network).Context(ctx).Do() + op, err := g.Client.Networks.Insert(g.projectID, network).Context(ctx).Do() if err != nil { return fmt.Errorf("GKE: Networks.Insert: %v", err) } log.Infof("GKE: VPC creation operation started: %v\n", op.Name) - err = g.WaitForVPCCreation(ctx, 30*time.Minute) + err = g.WaitForCreation(ctx, 30*time.Minute) if err != nil { return fmt.Errorf("GKE: Error waiting for VPC to be created: %v", err) } - - // subnetwork := &compute.Subnetwork{ - // Name: "peer-pods-subnet", - // Network: op.SelfLink, - // Region: "us-west1", - // } - // - // _, err = srv.Subnetworks.Insert(g.projectID, "us-west1", subnetwork).Context(ctx).Do() - // if err != nil { - // return fmt.Errorf("GKE: Subnetworks.Insert: %v", err) - // } return nil } -// DeleteVPC deletes a VPC in Google Cloud. -func (g *GCPVPC) DeleteVPC(ctx context.Context, cfg *envconf.Config) error { - srv, err := compute.NewService(ctx, option.WithCredentialsFile(g.credentials)) - if err != nil { - return fmt.Errorf("GKE: compute.NewService: %v", err) - } - - op, err := srv.Networks.Delete(g.projectID, g.vpcName).Context(ctx).Do() +// Delete deletes a VPC in Google Cloud. +func (g *GCPVPC) Delete(ctx context.Context, cfg *envconf.Config) error { + op, err := g.Client.Networks.Delete(g.projectID, g.Name).Context(ctx).Do() if err != nil { return fmt.Errorf("GKE: Networks.Delete: %v", err) } log.Infof("GKE: VPC deletion operation started: %v\n", op.Name) - err = g.WaitForVPCDeleted(ctx, 30*time.Minute) + err = g.WaitForDeleted(ctx, 30*time.Minute) if err != nil { return fmt.Errorf("GKE: Error waiting for VPC to be deleted: %v", err) } @@ -116,18 +85,13 @@ func (g *GCPVPC) DeleteVPC(ctx context.Context, cfg *envconf.Config) error { return nil } -// WaitForVPCCreation waits until the VPC is created and available. -func (g *GCPVPC) WaitForVPCCreation( +// WaitForCreation waits until the VPC is created and available. +func (g *GCPVPC) WaitForCreation( ctx context.Context, timeout time.Duration, ) error { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() - srv, err := compute.NewService(ctx, option.WithCredentialsFile(g.credentials)) - if err != nil { - return fmt.Errorf("compute.NewService: %v", err) - } - ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() @@ -136,7 +100,7 @@ func (g *GCPVPC) WaitForVPCCreation( case <-ctx.Done(): return fmt.Errorf("timeout waiting for VPC creation") case <-ticker.C: - network, err := srv.Networks.Get(g.projectID, g.vpcName).Context(ctx).Do() + network, err := g.Client.Networks.Get(g.projectID, g.Name).Context(ctx).Do() if err != nil { if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Code == 404 { log.Info("Waiting for VPC to be created...") @@ -152,18 +116,13 @@ func (g *GCPVPC) WaitForVPCCreation( } } -// WaitForVPCDeleted waits until the VPC is deleted. -func (g *GCPVPC) WaitForVPCDeleted( +// WaitForDeleted waits until the VPC is deleted. +func (g *GCPVPC) WaitForDeleted( ctx context.Context, timeout time.Duration, ) error { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() - srv, err := compute.NewService(ctx, option.WithCredentialsFile(g.credentials)) - if err != nil { - return fmt.Errorf("GKE: compute.NewService: %v", err) - } - ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() @@ -172,7 +131,7 @@ func (g *GCPVPC) WaitForVPCDeleted( case <-ctx.Done(): return fmt.Errorf("GKE: timeout waiting for VPC deletion") case <-ticker.C: - _, err := srv.Networks.Get(g.projectID, g.vpcName).Context(ctx).Do() + _, err := g.Client.Networks.Get(g.projectID, g.Name).Context(ctx).Do() if err != nil { if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Code == 404 { log.Info("GKE: VPC deleted successfully") From d9cb1fcba2d146dcbcec31cb8a18b0da2b31ae9d Mon Sep 17 00:00:00 2001 From: "red-hat-konflux[bot]" <126015336+red-hat-konflux[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 20:07:33 +0000 Subject: [PATCH 4/4] chore(deps): update terraform ibm to ~> 1.74.0 Signed-off-by: red-hat-konflux <126015336+red-hat-konflux[bot]@users.noreply.github.com> --- src/cloud-api-adaptor/ibmcloud/cluster/node/version.tf | 2 +- src/cloud-api-adaptor/ibmcloud/cluster/version.tf | 2 +- src/cloud-api-adaptor/ibmcloud/cluster/vpc/version.tf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cloud-api-adaptor/ibmcloud/cluster/node/version.tf b/src/cloud-api-adaptor/ibmcloud/cluster/node/version.tf index 4e80892aa4..f70a2f32b2 100644 --- a/src/cloud-api-adaptor/ibmcloud/cluster/node/version.tf +++ b/src/cloud-api-adaptor/ibmcloud/cluster/node/version.tf @@ -7,7 +7,7 @@ terraform { required_providers { ibm = { source = "IBM-Cloud/ibm" - version = "~> 1.50.0" + version = "~> 1.74.0" } } } diff --git a/src/cloud-api-adaptor/ibmcloud/cluster/version.tf b/src/cloud-api-adaptor/ibmcloud/cluster/version.tf index 4e80892aa4..f70a2f32b2 100644 --- a/src/cloud-api-adaptor/ibmcloud/cluster/version.tf +++ b/src/cloud-api-adaptor/ibmcloud/cluster/version.tf @@ -7,7 +7,7 @@ terraform { required_providers { ibm = { source = "IBM-Cloud/ibm" - version = "~> 1.50.0" + version = "~> 1.74.0" } } } diff --git a/src/cloud-api-adaptor/ibmcloud/cluster/vpc/version.tf b/src/cloud-api-adaptor/ibmcloud/cluster/vpc/version.tf index 4e80892aa4..f70a2f32b2 100644 --- a/src/cloud-api-adaptor/ibmcloud/cluster/vpc/version.tf +++ b/src/cloud-api-adaptor/ibmcloud/cluster/vpc/version.tf @@ -7,7 +7,7 @@ terraform { required_providers { ibm = { source = "IBM-Cloud/ibm" - version = "~> 1.50.0" + version = "~> 1.74.0" } } }