diff --git a/api/cloud-control/v1beta1/vpcpeering_types.go b/api/cloud-control/v1beta1/vpcpeering_types.go index bb4419d38..c890ce037 100644 --- a/api/cloud-control/v1beta1/vpcpeering_types.go +++ b/api/cloud-control/v1beta1/vpcpeering_types.go @@ -148,6 +148,13 @@ type VpcPeeringStatus struct { // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty"` + + //GCP creates operations when creating peerings, since we are creating two peerings (one in each direction), we store the operation names here to track them if needed + //One example is the quota exceeded error that can happen on either side if one of the vpcs is close to the quota limit + // +optional + Operation string `json:"operation,omitempty"` + // +optional + RemoteOperation string `json:"remoteOperation,omitempty"` } //+kubebuilder:object:root=true diff --git a/config/crd/bases/cloud-control.kyma-project.io_vpcpeerings.yaml b/config/crd/bases/cloud-control.kyma-project.io_vpcpeerings.yaml index c8be569cd..7d126dc74 100644 --- a/config/crd/bases/cloud-control.kyma-project.io_vpcpeerings.yaml +++ b/config/crd/bases/cloud-control.kyma-project.io_vpcpeerings.yaml @@ -237,8 +237,15 @@ spec: x-kubernetes-list-type: map id: type: string + operation: + description: |- + GCP creates operations when creating peerings, since we are creating two peerings (one in each direction), we store the operation names here to track them if needed + One example is the quota exceeded error that can happen on either side if one of the vpcs is close to the quota limit + type: string remoteId: type: string + remoteOperation: + type: string state: type: string vpcId: diff --git a/config/dist/kcp/crd/bases/cloud-control.kyma-project.io_vpcpeerings.yaml b/config/dist/kcp/crd/bases/cloud-control.kyma-project.io_vpcpeerings.yaml index c8be569cd..7d126dc74 100644 --- a/config/dist/kcp/crd/bases/cloud-control.kyma-project.io_vpcpeerings.yaml +++ b/config/dist/kcp/crd/bases/cloud-control.kyma-project.io_vpcpeerings.yaml @@ -237,8 +237,15 @@ spec: x-kubernetes-list-type: map id: type: string + operation: + description: |- + GCP creates operations when creating peerings, since we are creating two peerings (one in each direction), we store the operation names here to track them if needed + One example is the quota exceeded error that can happen on either side if one of the vpcs is close to the quota limit + type: string remoteId: type: string + remoteOperation: + type: string state: type: string vpcId: diff --git a/pkg/kcp/provider/gcp/client/gcpClients.go b/pkg/kcp/provider/gcp/client/gcpClients.go index 9e0c41cba..d0295b87b 100644 --- a/pkg/kcp/provider/gcp/client/gcpClients.go +++ b/pkg/kcp/provider/gcp/client/gcpClients.go @@ -32,6 +32,7 @@ type GcpClients struct { // The VpcPeeringClients uses a different service account than the other clients and has different permissions as well. type VpcPeeringClients struct { + ComputeGlobalOperations *compute.GlobalOperationsClient ComputeNetworks *compute.NetworksClient ResourceManagerTagBindings *resourcemanager.TagBindingsClient } @@ -126,8 +127,11 @@ func NewGcpClients(ctx context.Context, saJsonKeyPath string, vpcPeeringSaJsonKe if err != nil { return nil, fmt.Errorf("error creating vpc peering compute networks client: %w", err) } + vpcPeeringComputeGlobalOperations, err := compute.NewGlobalOperationsRESTClient(ctx, option.WithTokenSource(vpcPeeringComputeNetworksTokenSource)) + if err != nil { + return nil, fmt.Errorf("error creating vpc peering compute operations client: %w", err) + } // resource manager client for VPC peering, uses a different service account---------------- - vpcPeeringResourceManagerTokenProvider, err := vpcPeeringClientBuilder.WithScopes(resourcemanager.DefaultAuthScopes()).BuildTokenProvider() if err != nil { return nil, fmt.Errorf("failed to build vpc peering resource manager token provider: %w", err) @@ -148,6 +152,7 @@ func NewGcpClients(ctx context.Context, saJsonKeyPath string, vpcPeeringSaJsonKe RedisCluster: redisCluster, RedisInstance: redisInstance, VpcPeeringClients: &VpcPeeringClients{ + ComputeGlobalOperations: vpcPeeringComputeGlobalOperations, ComputeNetworks: vpcPeeringComputeNetworks, ResourceManagerTagBindings: vpcPeeringresourceManagerTagBindings, }, diff --git a/pkg/kcp/provider/gcp/meta/metadata.go b/pkg/kcp/provider/gcp/meta/metadata.go index 0f52fe927..9420f6938 100644 --- a/pkg/kcp/provider/gcp/meta/metadata.go +++ b/pkg/kcp/provider/gcp/meta/metadata.go @@ -2,6 +2,7 @@ package meta import ( "errors" + "strings" "github.com/googleapis/gax-go/v2/apierror" "google.golang.org/api/googleapi" @@ -74,3 +75,21 @@ func IsTooManyRequests(err error) bool { return false } + +func IsOperationInProgressError(err error) bool { + if err == nil { + return false + } + + var googleapierror *googleapi.Error + if ok := errors.As(err, &googleapierror); ok { + if googleapierror.Code == 400 { + for _, e := range googleapierror.Errors { + if strings.Contains(e.Message, "peering operation in progress") { + return true + } + } + } + } + return false +} diff --git a/pkg/kcp/provider/gcp/mock/vpcPeeringStore.go b/pkg/kcp/provider/gcp/mock/vpcPeeringStore.go index 03e4c7e11..53cbe2f9a 100644 --- a/pkg/kcp/provider/gcp/mock/vpcPeeringStore.go +++ b/pkg/kcp/provider/gcp/mock/vpcPeeringStore.go @@ -13,10 +13,11 @@ type vpcPeeringEntry struct { peering *pb.NetworkPeering } type vpcPeeringStore struct { - m sync.Mutex - items map[string]*vpcPeeringEntry - errorMap map[string]error - tags map[string][]string + m sync.Mutex + items map[string]*vpcPeeringEntry + operations map[string]*pb.Operation + errorMap map[string]error + tags map[string][]string } type VpcPeeringMockClientUtils interface { @@ -30,7 +31,7 @@ func getFullNetworkUrl(project, vpc string) string { return fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/global/networks/%s", project, vpc) } -func (s *vpcPeeringStore) CreateRemoteVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) error { +func (s *vpcPeeringStore) CreateRemoteVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) (*pb.Operation, error) { s.m.Lock() defer s.m.Unlock() remoteNetwork := getFullNetworkUrl(remoteProject, remoteVpc) @@ -42,9 +43,21 @@ func (s *vpcPeeringStore) CreateRemoteVpcPeering(ctx context.Context, remotePeer exportCustomRoutes = true } + if s.operations == nil { + s.operations = make(map[string]*pb.Operation) + } + + if s.operations[remoteVpc] == nil { + operationpb := &pb.Operation{ + Status: ptr.To(pb.Operation_DONE), + Name: ptr.To(remoteVpc), + } + s.operations[remoteVpc] = operationpb + } + _, peeringExists := s.items[remoteNetwork] if peeringExists { - return nil + return s.operations[remoteVpc], nil } item := &vpcPeeringEntry{ @@ -59,10 +72,10 @@ func (s *vpcPeeringStore) CreateRemoteVpcPeering(ctx context.Context, remotePeer } s.items[remoteNetwork] = item - return nil + return s.operations[remoteVpc], nil } -func (s *vpcPeeringStore) CreateKymaVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) error { +func (s *vpcPeeringStore) CreateLocalVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) (*pb.Operation, error) { s.m.Lock() defer s.m.Unlock() @@ -72,9 +85,21 @@ func (s *vpcPeeringStore) CreateKymaVpcPeering(ctx context.Context, remotePeerin exportCustomRoutes := false importCustomRoutes := customRoutes + if s.operations == nil { + s.operations = make(map[string]*pb.Operation) + } + + if s.operations[kymaVpc] == nil { + operationpb := &pb.Operation{ + Status: ptr.To(pb.Operation_DONE), + Name: ptr.To(kymaVpc), + } + s.operations[kymaVpc] = operationpb + } + _, peeringExists := s.items[kymaNetwork] if peeringExists { - return nil + return s.operations[kymaVpc], nil } item := &vpcPeeringEntry{ @@ -90,7 +115,7 @@ func (s *vpcPeeringStore) CreateKymaVpcPeering(ctx context.Context, remotePeerin s.items[kymaNetwork] = item - return nil + return s.operations[kymaVpc], nil } func (s *vpcPeeringStore) GetRemoteNetworkTags(_ context.Context, vpc string, project string) ([]string, error) { @@ -122,6 +147,10 @@ func (s *vpcPeeringStore) GetVpcPeering(ctx context.Context, remotePeeringName s s.tags = make(map[string][]string) } + if s.operations == nil { + s.operations = make(map[string]*pb.Operation) + } + _, peeringExists := s.items[network] if !peeringExists { return nil, nil @@ -143,6 +172,17 @@ func (s *vpcPeeringStore) DeleteVpcPeering(ctx context.Context, remotePeeringNam return nil } +func (s *vpcPeeringStore) GetOperation(ctx context.Context, project string, operationId string) (*pb.Operation, error) { + s.m.Lock() + defer s.m.Unlock() + + _, operationExists := s.operations[operationId] + if !operationExists { + return nil, fmt.Errorf("operation %s not found", operationId) + } + return s.operations[operationId], nil +} + // Fake client implementations to mimic google API calls func (s *vpcPeeringStore) SetMockVpcPeeringLifeCycleState(project string, vpc string, state pb.NetworkPeering_State) { stateString := state.String() diff --git a/pkg/kcp/provider/gcp/vpcpeering/checkLocalOperation.go b/pkg/kcp/provider/gcp/vpcpeering/checkLocalOperation.go new file mode 100644 index 000000000..51a5a8549 --- /dev/null +++ b/pkg/kcp/provider/gcp/vpcpeering/checkLocalOperation.go @@ -0,0 +1,83 @@ +package vpcpeering + +import ( + "context" + "strings" + + pb "cloud.google.com/go/compute/apiv1/computepb" + cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" + "github.com/kyma-project/cloud-manager/api/cloud-resources/v1beta1" + "github.com/kyma-project/cloud-manager/pkg/util" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/kyma-project/cloud-manager/pkg/composed" +) + +func checkLocalOperation(ctx context.Context, st composed.State) (error, context.Context) { + state := st.(*State) + logger := composed.LoggerFromCtx(ctx) + + if composed.MarkedForDeletionPredicate(ctx, state) || (state.localOperation != nil && state.localOperation.GetStatus() != pb.Operation_PENDING) { + return nil, ctx + } + + if state.ObjAsVpcPeering().Status.Operation != "" { + op, err := state.client.GetOperation(ctx, state.LocalNetwork().Spec.Network.Reference.Gcp.GcpProject, state.ObjAsVpcPeering().Status.Operation) + if err != nil { + logger.Error(err, "[KCP GCP VpcPeering checkLocalOperation] Error getting local operation") + meta.SetStatusCondition(state.ObjAsVpcPeering().Conditions(), metav1.Condition{ + Type: v1beta1.ConditionTypeError, + Status: "True", + Reason: v1beta1.ConditionReasonError, + Message: "Error loading Local Vpc Peering Operation: " + state.ObjAsVpcPeering().Status.Operation, + }) + err = state.PatchObjStatus(ctx) + if err != nil { + return composed.LogErrorAndReturn(err, + "Error updating status since it was not possible to load the local Vpc Peering operation.", + composed.StopWithRequeueDelay(util.Timing.T10000ms()), + ctx, + ) + } + return composed.StopWithRequeueDelay(util.Timing.T60000ms()), nil + } + state.localOperation = op + if op != nil { + if op.GetStatus() != pb.Operation_DONE { + logger.Info("[KCP GCP VpcPeering checkLocalOperation] Local operation still in progress", "localOperation", ptr.Deref(op.Name, "OperationUnknown")) + return composed.StopWithRequeueDelay(util.Timing.T60000ms()), nil + } + if op.GetError() != nil { + logger.Error(err, "[KCP GCP VpcPeering checkRemoteOperation] Local operation error "+op.GetName()) + state.ObjAsVpcPeering().Status.State = cloudcontrolv1beta1.VirtualNetworkPeeringStateDisconnected + if strings.Contains(op.GetError().String(), "QUOTA_EXCEEDED") { + return composed.UpdateStatus(state.ObjAsVpcPeering()).SetExclusiveConditions(metav1.Condition{ + Type: v1beta1.ConditionTypeQuotaExceeded, + Status: "True", + Reason: v1beta1.ConditionTypeQuotaExceeded, + Message: "Error creating Local Vpc Peering due to quota limits, please check if your vpc quota limits are not exceeded.", + }). + ErrorLogMessage("Error creating Local VpcPeering due to quota exceeded"). + FailedError(composed.StopWithRequeue). + SuccessError(composed.StopWithRequeueDelay(util.Timing.T300000ms())). + Run(ctx, state) + } + + return composed.UpdateStatus(state.ObjAsVpcPeering()).SetExclusiveConditions(metav1.Condition{ + Type: v1beta1.ConditionTypeError, + Status: "True", + Reason: v1beta1.ConditionTypeError, + Message: "The cloud provider had an error while creating Local Vpc Peering", + }). + ErrorLogMessage("The cloud provider had an error while creating Local Vpc Peering"). + FailedError(composed.StopWithRequeue). + SuccessError(composed.StopWithRequeueDelay(util.Timing.T300000ms())). + Run(ctx, state) + } + } + } + + return nil, nil +} diff --git a/pkg/kcp/provider/gcp/vpcpeering/checkRemoteOperation.go b/pkg/kcp/provider/gcp/vpcpeering/checkRemoteOperation.go new file mode 100644 index 000000000..7206e8977 --- /dev/null +++ b/pkg/kcp/provider/gcp/vpcpeering/checkRemoteOperation.go @@ -0,0 +1,55 @@ +package vpcpeering + +import ( + "context" + + pb "cloud.google.com/go/compute/apiv1/computepb" + "github.com/kyma-project/cloud-manager/api/cloud-resources/v1beta1" + "github.com/kyma-project/cloud-manager/pkg/util" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/kyma-project/cloud-manager/pkg/composed" +) + +func checkRemoteOperation(ctx context.Context, st composed.State) (error, context.Context) { + state := st.(*State) + logger := composed.LoggerFromCtx(ctx) + + if composed.MarkedForDeletionPredicate(ctx, state) || (state.remoteOperation != nil && state.remoteOperation.GetStatus() != pb.Operation_PENDING) { + return nil, ctx + } + + if state.ObjAsVpcPeering().Status.RemoteOperation != "" { + op, err := state.client.GetOperation(ctx, state.RemoteNetwork().Spec.Network.Reference.Gcp.GcpProject, state.ObjAsVpcPeering().Status.RemoteOperation) + if err != nil { + logger.Error(err, "[KCP GCP VpcPeering checkRemoteOperation] Error getting remote operation") + meta.SetStatusCondition(state.ObjAsVpcPeering().Conditions(), metav1.Condition{ + Type: v1beta1.ConditionTypeError, + Status: "True", + Reason: v1beta1.ConditionReasonError, + Message: "Error loading Remote Vpc Peering Operation: " + state.ObjAsVpcPeering().Status.RemoteOperation, + }) + err = state.PatchObjStatus(ctx) + if err != nil { + return composed.LogErrorAndReturn(err, + "Error updating status since it was not possible to load the remote Vpc Peering operation.", + composed.StopWithRequeueDelay(util.Timing.T10000ms()), + ctx, + ) + } + return composed.StopWithRequeueDelay(util.Timing.T60000ms()), nil + } + state.remoteOperation = op + if op != nil { + if op.GetStatus() != pb.Operation_DONE { + logger.Info("[KCP GCP VpcPeering checkRemoteOperation] Remote operation still in progress", "remoteOperation", ptr.Deref(op.Name, "OperationUnknown")) + return composed.StopWithRequeueDelay(util.Timing.T60000ms()), nil + } + return nil, ctx + } + } + + return nil, nil +} diff --git a/pkg/kcp/provider/gcp/vpcpeering/client/vpcPeeringClient.go b/pkg/kcp/provider/gcp/vpcpeering/client/vpcPeeringClient.go index 91422efca..b2f9cc968 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/client/vpcPeeringClient.go +++ b/pkg/kcp/provider/gcp/vpcpeering/client/vpcPeeringClient.go @@ -39,23 +39,25 @@ func NewClientProvider(gcpClients *client.GcpClients) client.GcpClientProvider[V } func NewVpcPeeringClient(gcpClients *client.GcpClients) VpcPeeringClient { - return &gcpVpcPeeringClient{networksClient: gcpClients.VpcPeeringClients.ComputeNetworks, resourceManagerClient: gcpClients.VpcPeeringClients.ResourceManagerTagBindings} + return &gcpVpcPeeringClient{networksClient: gcpClients.VpcPeeringClients.ComputeNetworks, resourceManagerTagBindingsClient: gcpClients.VpcPeeringClients.ResourceManagerTagBindings, operationsClient: gcpClients.VpcPeeringClients.ComputeGlobalOperations} } type gcpVpcPeeringClient struct { - networksClient *compute.NetworksClient - resourceManagerClient *resourcemanager.TagBindingsClient + networksClient *compute.NetworksClient + operationsClient *compute.GlobalOperationsClient + resourceManagerTagBindingsClient *resourcemanager.TagBindingsClient } type VpcPeeringClient interface { DeleteVpcPeering(ctx context.Context, remotePeeringName string, kymaProject string, kymaVpc string) error GetVpcPeering(ctx context.Context, remotePeeringName string, project string, vpc string) (*pb.NetworkPeering, error) - CreateRemoteVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) error - CreateKymaVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) error + CreateRemoteVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) (*pb.Operation, error) + CreateLocalVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) (*pb.Operation, error) GetRemoteNetworkTags(context context.Context, remoteVpc string, remoteProject string) ([]string, error) + GetOperation(context context.Context, project string, operationId string) (*pb.Operation, error) } -func CreateVpcPeeringRequest(ctx context.Context, remotePeeringName string, sourceVpc string, sourceProject string, importCustomRoutes bool, exportCustomRoutes bool, destinationProject string, destinationVpc string, networksClient *compute.NetworksClient) error { +func CreateVpcPeeringRequest(ctx context.Context, remotePeeringName string, sourceVpc string, sourceProject string, importCustomRoutes bool, exportCustomRoutes bool, destinationProject string, destinationVpc string, networksClient *compute.NetworksClient) (*pb.Operation, error) { destinationNetworkUrl := getFullNetworkUrl(destinationProject, destinationVpc) @@ -73,15 +75,15 @@ func CreateVpcPeeringRequest(ctx context.Context, remotePeeringName string, sour }, } - _, err := networksClient.AddPeering(ctx, vpcPeeringRequest) + op, err := networksClient.AddPeering(ctx, vpcPeeringRequest) if err != nil { - return err + return nil, err } - return nil + return op.Proto(), nil } -func (c *gcpVpcPeeringClient) CreateRemoteVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) error { +func (c *gcpVpcPeeringClient) CreateRemoteVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) (*pb.Operation, error) { //peering from remote vpc to kyma //by default exportCustomRoutes is false, but if the remote vpc wants kyma to import custom routes, the peering needs to export them :) exportCustomRoutes := false @@ -92,7 +94,7 @@ func (c *gcpVpcPeeringClient) CreateRemoteVpcPeering(ctx context.Context, remote return CreateVpcPeeringRequest(ctx, remotePeeringName, remoteVpc, remoteProject, importCustomRoutes, exportCustomRoutes, kymaProject, kymaVpc, c.networksClient) } -func (c *gcpVpcPeeringClient) CreateKymaVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) error { +func (c *gcpVpcPeeringClient) CreateLocalVpcPeering(ctx context.Context, remotePeeringName string, remoteVpc string, remoteProject string, customRoutes bool, kymaProject string, kymaVpc string) (*pb.Operation, error) { //peering from kyma to remote vpc //Kyma will not export custom routes to the remote vpc, but if the remote vpc is exporting them, we need to import them exportCustomRoutes := false @@ -122,7 +124,7 @@ func (c *gcpVpcPeeringClient) GetVpcPeering(ctx context.Context, remotePeeringNa if len(peerings) == 0 { logger := composed.LoggerFromCtx(ctx) - logger.Info("Vpc Peering not found") + logger.Info("Vpc Peering not found", "peeringName", remotePeeringName) return nil, nil } return peerings[0], nil @@ -141,7 +143,7 @@ func (c *gcpVpcPeeringClient) GetRemoteNetworkTags(ctx context.Context, remoteVp return nil, err } - tagIterator := c.resourceManagerClient.ListEffectiveTags(ctx, &resourcemanagerpb.ListEffectiveTagsRequest{Parent: strings.Replace(ptr.Deref(remoteNetwork.SelfLinkWithId, ""), "https://www.googleapis.com/compute/v1", "//compute.googleapis.com", 1)}) + tagIterator := c.resourceManagerTagBindingsClient.ListEffectiveTags(ctx, &resourcemanagerpb.ListEffectiveTagsRequest{Parent: strings.Replace(ptr.Deref(remoteNetwork.SelfLinkWithId, ""), "https://www.googleapis.com/compute/v1", "//compute.googleapis.com", 1)}) for { tag, err := tagIterator.Next() if err != nil { @@ -154,3 +156,14 @@ func (c *gcpVpcPeeringClient) GetRemoteNetworkTags(ctx context.Context, remoteVp } return tagsArray, nil } + +func (c *gcpVpcPeeringClient) GetOperation(ctx context.Context, project string, operationId string) (*pb.Operation, error) { + op, err := c.operationsClient.Get(ctx, &pb.GetGlobalOperationRequest{ + Operation: operationId, + Project: project, + }) + if err != nil { + return nil, err + } + return op, nil +} diff --git a/pkg/kcp/provider/gcp/vpcpeering/createKymaVpcPeering.go b/pkg/kcp/provider/gcp/vpcpeering/createLocalVpcPeering.go similarity index 72% rename from pkg/kcp/provider/gcp/vpcpeering/createKymaVpcPeering.go rename to pkg/kcp/provider/gcp/vpcpeering/createLocalVpcPeering.go index 34b76dc17..af53dbd41 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/createKymaVpcPeering.go +++ b/pkg/kcp/provider/gcp/vpcpeering/createLocalVpcPeering.go @@ -14,21 +14,23 @@ package vpcpeering import ( "context" + cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" "github.com/kyma-project/cloud-manager/pkg/composed" "github.com/kyma-project/cloud-manager/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) -func createKymaVpcPeering(ctx context.Context, st composed.State) (error, context.Context) { +func createLocalVpcPeering(ctx context.Context, st composed.State) (error, context.Context) { state := st.(*State) logger := composed.LoggerFromCtx(ctx) - if state.kymaVpcPeering != nil { + if composed.MarkedForDeletionPredicate(ctx, state) || (state.localVpcPeering != nil || (state.localOperation != nil)) { return nil, nil } - err := state.client.CreateKymaVpcPeering( + op, err := state.client.CreateLocalVpcPeering( ctx, state.getKymaVpcPeeringName(), state.RemoteNetwork().Status.Network.Gcp.NetworkName, @@ -51,6 +53,16 @@ func createKymaVpcPeering(ctx context.Context, st composed.State) (error, contex SuccessError(composed.StopWithRequeueDelay(util.Timing.T60000ms())). Run(ctx, state) } - logger.Info("[KCP GCP VPCPeering createKymaVpcPeering] Kyma VPC Peering Connection created") - return composed.StopWithRequeueDelay(3 * util.Timing.T10000ms()), nil + + state.ObjAsVpcPeering().Status.Operation = ptr.Deref(op.Name, "OperationUnknown") + err = state.PatchObjStatus(ctx) + if err != nil { + return composed.LogErrorAndReturn(err, + "Error updating status with Local VPC Peering operation.", + composed.StopWithRequeueDelay(util.Timing.T10000ms()), + ctx, + ) + } + logger.Info("[KCP GCP VPCPeering createLocalVpcPeering] Local VPC Peering Connection created ", "localVpcPeering", state.getKymaVpcPeeringName()) + return composed.StopWithRequeueDelay(3 * util.Timing.T10000ms()), ctx } diff --git a/pkg/kcp/provider/gcp/vpcpeering/createRemoteVpcPeering.go b/pkg/kcp/provider/gcp/vpcpeering/createRemoteVpcPeering.go index cbd8f9866..589b1977a 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/createRemoteVpcPeering.go +++ b/pkg/kcp/provider/gcp/vpcpeering/createRemoteVpcPeering.go @@ -15,24 +15,24 @@ package vpcpeering import ( "context" "fmt" + cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" "github.com/kyma-project/cloud-manager/pkg/composed" gcpmeta "github.com/kyma-project/cloud-manager/pkg/kcp/provider/gcp/meta" "github.com/kyma-project/cloud-manager/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) func createRemoteVpcPeering(ctx context.Context, st composed.State) (error, context.Context) { state := st.(*State) logger := composed.LoggerFromCtx(ctx) - logger.Info("[KCP GCP VpcPeering createRemoteVpcPeering] Creating Remote VPC Peering") - - if state.remoteVpcPeering != nil { + if composed.MarkedForDeletionPredicate(ctx, state) || state.remoteVpcPeering != nil || (state.remoteOperation != nil) { return nil, nil } - err := state.client.CreateRemoteVpcPeering( + op, err := state.client.CreateRemoteVpcPeering( ctx, state.remotePeeringName, state.RemoteNetwork().Status.Network.Gcp.NetworkName, @@ -71,6 +71,16 @@ func createRemoteVpcPeering(ctx context.Context, st composed.State) (error, cont SuccessError(composed.StopWithRequeueDelay(util.Timing.T60000ms())). Run(ctx, state) } - logger.Info("[KCP GCP VpcPeering createRemoteVpcPeering] Remote VPC Peering Connection created") - return composed.StopWithRequeueDelay(3 * util.Timing.T10000ms()), nil + + state.ObjAsVpcPeering().Status.RemoteOperation = ptr.Deref(op.Name, "RemoteOperationUnknown") + err = state.PatchObjStatus(ctx) + if err != nil { + return composed.LogErrorAndReturn(err, + "Error updating status with Remote VPC Peering operation.", + composed.StopWithRequeueDelay(util.Timing.T10000ms()), + ctx, + ) + } + logger.Info("[KCP GCP VpcPeering createRemoteVpcPeering] Remote VPC Peering Connection requested", "operation id", state.ObjAsVpcPeering().Status.RemoteOperation) + return composed.StopWithRequeueDelay(3 * util.Timing.T10000ms()), ctx } diff --git a/pkg/kcp/provider/gcp/vpcpeering/deleteRemoteVpcPeering.go b/pkg/kcp/provider/gcp/vpcpeering/deleteRemoteVpcPeering.go index ea3ed00fd..483e6ca27 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/deleteRemoteVpcPeering.go +++ b/pkg/kcp/provider/gcp/vpcpeering/deleteRemoteVpcPeering.go @@ -1,12 +1,12 @@ package vpcpeering import ( - "cloud.google.com/go/compute/apiv1/computepb" "context" + + "cloud.google.com/go/compute/apiv1/computepb" "github.com/kyma-project/cloud-manager/pkg/composed" gcpmeta "github.com/kyma-project/cloud-manager/pkg/kcp/provider/gcp/meta" "github.com/kyma-project/cloud-manager/pkg/util" - "k8s.io/utils/ptr" ) func deleteRemoteVpcPeering(ctx context.Context, st composed.State) (error, context.Context) { @@ -24,15 +24,15 @@ func deleteRemoteVpcPeering(ctx context.Context, st composed.State) (error, cont return nil, nil } - // If the remote VPC Peering is still active, we need to wait before deleting it. + // If the local VPC Peering is still active, we need to wait before deleting it. // Otherwise, Google will return a 400 error saying, 'There is a peering operation in progress on the local or peer network. Try again later.' // If it's not active, that means the kyma VPC Peering is already deleted, and we can proceed with the deletion of the remote VPC Peering. - if ptr.Deref(state.remoteVpcPeering.State, "") == computepb.NetworkPeering_ACTIVE.String() { - logger.Info("Remote VPC Peering is still active. It should wait for the local VPC Peering to be deleted before proceeding with the deletion of the remote VPC Peering.") + if state.localVpcPeering.GetState() == computepb.NetworkPeering_ACTIVE.String() { + logger.Info("Local VPC Peering is still active. It should wait for the local VPC Peering to be deleted before proceeding with the deletion of the remote VPC Peering.") return composed.StopWithRequeueDelay(util.Timing.T60000ms()), nil } - logger.Info("Deleting Remote GCP VPC Peering " + obj.Spec.Details.PeeringName) + logger.Info("Deleting Remote GCP VPC Peering ", "remoteVpcPeering", obj.Spec.Details.PeeringName) err := state.client.DeleteVpcPeering( ctx, @@ -46,11 +46,18 @@ func deleteRemoteVpcPeering(ctx context.Context, st composed.State) (error, cont logger.Info("Not authorized to delete remote VPC Peering") } if gcpmeta.IsTooManyRequests(err) { - logger.Info("Too many requests. Requeueing") + logger.Info("Too many requests. Re-queueing") return composed.StopWithRequeueDelay(util.Timing.T60000ms()), nil } + if gcpmeta.IsOperationInProgressError(err) { + logger.Info("There is a peering operation in progress on the local or peer network.") + if state.ObjAsVpcPeering().Spec.Details.DeleteRemotePeering { + logger.Info("DeleteRemotePeering is set to true. Re-queueing deletion of remote VPC Peering") + return composed.StopWithRequeueDelay(util.Timing.T10000ms()), nil + } + } logger.Error(err, "Error deleting remote VPC Peering") } - return nil, nil + return nil, ctx } diff --git a/pkg/kcp/provider/gcp/vpcpeering/deleteVpcPeering.go b/pkg/kcp/provider/gcp/vpcpeering/deleteVpcPeering.go index 12becaaea..f4e6d2c68 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/deleteVpcPeering.go +++ b/pkg/kcp/provider/gcp/vpcpeering/deleteVpcPeering.go @@ -2,6 +2,7 @@ package vpcpeering import ( "context" + cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" "github.com/kyma-project/cloud-manager/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -14,12 +15,12 @@ func deleteVpcPeering(ctx context.Context, st composed.State) (error, context.Co obj := state.ObjAsVpcPeering() logger := composed.LoggerFromCtx(ctx) - if state.kymaVpcPeering == nil { - logger.Info("VPC Peering is not loaded") + if state.localVpcPeering == nil { + logger.Info("Local VPC Peering is not loaded ", "localVpcPeering", state.getKymaVpcPeeringName()) return nil, nil } - logger.Info("Deleting GCP VPC Peering " + obj.Spec.Details.PeeringName) + logger.Info("Deleting GCP VPC Peering", "gcpVpcPeering", obj.Spec.Details.PeeringName) err := state.client.DeleteVpcPeering( ctx, @@ -43,5 +44,5 @@ func deleteVpcPeering(ctx context.Context, st composed.State) (error, context.Co Run(ctx, state) } - return nil, nil + return nil, ctx } diff --git a/pkg/kcp/provider/gcp/vpcpeering/loadKymaVpcPeering.go b/pkg/kcp/provider/gcp/vpcpeering/loadLocalVpcPeering.go similarity index 72% rename from pkg/kcp/provider/gcp/vpcpeering/loadKymaVpcPeering.go rename to pkg/kcp/provider/gcp/vpcpeering/loadLocalVpcPeering.go index ba14cd741..348de363a 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/loadKymaVpcPeering.go +++ b/pkg/kcp/provider/gcp/vpcpeering/loadLocalVpcPeering.go @@ -2,6 +2,7 @@ package vpcpeering import ( "context" + "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" "github.com/kyma-project/cloud-manager/pkg/composed" "github.com/kyma-project/cloud-manager/pkg/util" @@ -9,30 +10,28 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func loadKymaVpcPeering(ctx context.Context, st composed.State) (error, context.Context) { +func loadLocalVpcPeering(ctx context.Context, st composed.State) (error, context.Context) { state := st.(*State) logger := composed.LoggerFromCtx(ctx) - if state.kymaVpcPeering != nil { + if state.localVpcPeering != nil { return nil, nil } - logger.Info("[KCP GCP VpcPeering loadKymaVpcPeering] Loading Kyma VPC Peering") - kymaVpcPeering, err := state.client.GetVpcPeering(ctx, state.getKymaVpcPeeringName(), state.LocalNetwork().Status.Network.Gcp.GcpProject, state.LocalNetwork().Status.Network.Gcp.NetworkName) if err != nil { - logger.Error(err, "Error loading Kyma Vpc Peering") + logger.Error(err, "Error loading Local Vpc Peering") state.ObjAsVpcPeering().Status.State = v1beta1.VirtualNetworkPeeringStateDisconnected meta.SetStatusCondition(state.ObjAsVpcPeering().Conditions(), metav1.Condition{ Type: v1beta1.ConditionTypeError, Status: "True", Reason: v1beta1.ReasonFailedCreatingVpcPeeringConnection, - Message: "Error loading Kyma Vpc Peering", + Message: "Error loading Local Vpc Peering", }) err = state.UpdateObjStatus(ctx) if err != nil { return composed.LogErrorAndReturn(err, - "Error updating status since it was not possible to load the Kyma Vpc Peering", + "Error updating status since it was not possible to load the Local Vpc Peering", composed.StopWithRequeueDelay(util.Timing.T10000ms()), ctx, ) @@ -40,7 +39,10 @@ func loadKymaVpcPeering(ctx context.Context, st composed.State) (error, context. return composed.StopWithRequeueDelay(util.Timing.T60000ms()), nil } - logger.Info("[KCP GCP VpcPeering loadKymaVpcPeering] Kyma VPC Peering loaded") - state.kymaVpcPeering = kymaVpcPeering + if kymaVpcPeering != nil { + state.localVpcPeering = kymaVpcPeering + logger.Info("[KCP GCP VpcPeering loadLocalVpcPeering] Local VPC Peering loaded", "localVpcPeering", state.getKymaVpcPeeringName()) + } + return nil, nil } diff --git a/pkg/kcp/provider/gcp/vpcpeering/loadRemoteVpcPeering.go b/pkg/kcp/provider/gcp/vpcpeering/loadRemoteVpcPeering.go index 9a6791361..0dd2ced72 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/loadRemoteVpcPeering.go +++ b/pkg/kcp/provider/gcp/vpcpeering/loadRemoteVpcPeering.go @@ -10,6 +10,7 @@ import ( "github.com/kyma-project/cloud-manager/pkg/util" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) func loadRemoteVpcPeering(ctx context.Context, st composed.State) (error, context.Context) { @@ -20,8 +21,6 @@ func loadRemoteVpcPeering(ctx context.Context, st composed.State) (error, contex return nil, nil } - logger.Info("[KCP GCP VpcPeering loadRemoteVpcPeering] Loading Remote VPC Peering") - remoteVpcPeering, err := state.client.GetVpcPeering(ctx, state.remotePeeringName, state.RemoteNetwork().Status.Network.Gcp.GcpProject, state.RemoteNetwork().Status.Network.Gcp.NetworkName) if err != nil { if composed.IsMarkedForDeletion(state.ObjAsVpcPeering()) { @@ -81,10 +80,13 @@ func loadRemoteVpcPeering(ctx context.Context, st composed.State) (error, contex ctx, ) } - return composed.StopWithRequeueDelay(util.Timing.T60000ms()), nil + return composed.StopWithRequeueDelay(util.Timing.T60000ms()), ctx + } + + if remoteVpcPeering != nil { + logger.Info("[KCP GCP VpcPeering loadRemoteVpcPeering] Remote VPC Peering loaded", "remoteVpcPeering", ptr.Deref(remoteVpcPeering.Name, "remoteVpcPeering.Name")) + state.remoteVpcPeering = remoteVpcPeering } - logger.Info("[KCP GCP VpcPeering createRemoteVpcPeering] Remote VPC Peering loaded") - state.remoteVpcPeering = remoteVpcPeering - return nil, nil + return nil, ctx } diff --git a/pkg/kcp/provider/gcp/vpcpeering/new.go b/pkg/kcp/provider/gcp/vpcpeering/new.go index 59b050535..2a06571b8 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/new.go +++ b/pkg/kcp/provider/gcp/vpcpeering/new.go @@ -23,8 +23,10 @@ func New(stateFactory StateFactory) composed.Action { return composed.ComposeActions( "gcpVpcPeering", actions.AddCommonFinalizer(), + checkRemoteOperation, loadRemoteVpcPeering, - loadKymaVpcPeering, + checkLocalOperation, + loadLocalVpcPeering, setPeeringStatusIds, composed.IfElse(composed.Not(composed.MarkedForDeletionPredicate), composed.ComposeActions( @@ -32,7 +34,7 @@ func New(stateFactory StateFactory) composed.Action { checkIfRemoteVpcIsTagged, createRemoteVpcPeering, waitRemoteVpcPeeringAvailable, - createKymaVpcPeering, + createLocalVpcPeering, waitVpcPeeringActive, updateStatus, ), @@ -40,7 +42,7 @@ func New(stateFactory StateFactory) composed.Action { "gcpVpcPeering-delete", removeReadyCondition, deleteVpcPeering, - waitKymaVpcPeeringDeletion, + waitLocalVpcPeeringDeletion, deleteRemoteVpcPeering, actions.RemoveCommonFinalizer(), composed.StopAndForgetAction, diff --git a/pkg/kcp/provider/gcp/vpcpeering/removeReadyCondition.go b/pkg/kcp/provider/gcp/vpcpeering/removeReadyCondition.go index 8297cb324..da85dfe50 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/removeReadyCondition.go +++ b/pkg/kcp/provider/gcp/vpcpeering/removeReadyCondition.go @@ -2,6 +2,7 @@ package vpcpeering import ( "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" @@ -27,5 +28,5 @@ func removeReadyCondition(ctx context.Context, st composed.State) (error, contex return composed.LogErrorAndReturn(err, "Error updating KCP VpcPeering status after removing Ready condition", composed.StopWithRequeue, ctx) } - return composed.StopWithRequeue, nil + return composed.StopWithRequeue, ctx } diff --git a/pkg/kcp/provider/gcp/vpcpeering/setPeeringStatusIds.go b/pkg/kcp/provider/gcp/vpcpeering/setPeeringStatusIds.go index 79bd05f44..a5d723960 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/setPeeringStatusIds.go +++ b/pkg/kcp/provider/gcp/vpcpeering/setPeeringStatusIds.go @@ -2,6 +2,7 @@ package vpcpeering import ( "context" + "github.com/kyma-project/cloud-manager/pkg/composed" "k8s.io/utils/ptr" ) @@ -12,15 +13,19 @@ func setPeeringStatusIds(ctx context.Context, st composed.State) (error, context vpcPeering := state.ObjAsVpcPeering() statusChanged := false - if state.kymaVpcPeering != nil && vpcPeering.Status.Id == "" { + if state.remoteOperation != nil && state.remoteOperation.GetError() != nil { + return nil, ctx + } + + if state.localVpcPeering != nil && vpcPeering.Status.Id == "" { statusChanged = true - logger.Info("[KCP GCP VpcPeering setPeeringStatusIds] setting kyma connection id status") - vpcPeering.Status.Id = ptr.Deref(state.kymaVpcPeering.Name, "") + logger.Info("[KCP GCP VpcPeering setPeeringStatusIds] setting Local connection id status " + vpcPeering.Status.Id) + vpcPeering.Status.Id = ptr.Deref(state.localVpcPeering.Name, "") } if state.remoteVpcPeering != nil && vpcPeering.Status.RemoteId == "" { statusChanged = true - logger.Info("[KCP GCP VpcPeering setPeeringStatusIds] setting remote connection id status") + logger.Info("[KCP GCP VpcPeering setPeeringStatusIds] setting remote connection id status " + vpcPeering.Status.RemoteId) vpcPeering.Status.RemoteId = ptr.Deref(state.remoteVpcPeering.Name, "") } if statusChanged { @@ -32,5 +37,5 @@ func setPeeringStatusIds(ctx context.Context, st composed.State) (error, context Run(ctx, state) } - return nil, nil + return nil, ctx } diff --git a/pkg/kcp/provider/gcp/vpcpeering/state.go b/pkg/kcp/provider/gcp/vpcpeering/state.go index 481ddd739..ed009059d 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/state.go +++ b/pkg/kcp/provider/gcp/vpcpeering/state.go @@ -18,7 +18,11 @@ type State struct { //Peerings on both sides remoteVpcPeering *pb.NetworkPeering - kymaVpcPeering *pb.NetworkPeering + localVpcPeering *pb.NetworkPeering + + //Operations + remoteOperation *pb.Operation + localOperation *pb.Operation } type StateFactory interface { diff --git a/pkg/kcp/provider/gcp/vpcpeering/updateStatus.go b/pkg/kcp/provider/gcp/vpcpeering/updateStatus.go index 75f9cd62f..4dd0a0aa5 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/updateStatus.go +++ b/pkg/kcp/provider/gcp/vpcpeering/updateStatus.go @@ -2,7 +2,11 @@ package vpcpeering import ( "context" + "strings" + + pb "cloud.google.com/go/compute/apiv1/computepb" cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" + "github.com/kyma-project/cloud-manager/api/cloud-resources/v1beta1" "github.com/kyma-project/cloud-manager/pkg/composed" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -13,13 +17,47 @@ func updateStatus(ctx context.Context, st composed.State) (error, context.Contex state := st.(*State) logger := composed.LoggerFromCtx(ctx) - logger.Info("GCP VPC Peering Update Status") - if composed.MarkedForDeletionPredicate(ctx, state) { logger.Info("GCP VPC Peering is marked for deletion") return nil, nil } + var op *pb.Operation + if state.remoteOperation.GetError() != nil { + op = state.remoteOperation + } + if state.localOperation.GetError() != nil { + op = state.localOperation + } + + if op != nil && op.GetError() != nil { + + state.ObjAsVpcPeering().Status.State = cloudcontrolv1beta1.VirtualNetworkPeeringStateDisconnected + if strings.Contains(state.remoteOperation.GetError().String(), "QUOTA_EXCEEDED") { + return composed.UpdateStatus(state.ObjAsVpcPeering()).SetExclusiveConditions(metav1.Condition{ + Type: v1beta1.ConditionTypeQuotaExceeded, + Status: "True", + Reason: v1beta1.ConditionTypeQuotaExceeded, + Message: "Error creating Vpc Peering due to quota limits " + op.GetDescription() + ", please check if your vpc quota limits are not exceeded.", + }). + ErrorLogMessage("Failed to update status to set quota exceeded on vpc peering"). + FailedError(composed.StopWithRequeue). + SuccessError(composed.StopAndForget). + Run(ctx, state) + } + + return composed.UpdateStatus(state.ObjAsVpcPeering()).SetExclusiveConditions(metav1.Condition{ + Type: v1beta1.ConditionTypeError, + Status: "True", + Reason: v1beta1.ConditionTypeError, + Message: "The cloud provider had an error while creating Vpc Peering" + op.GetDescription(), + }). + ErrorLogMessage("The cloud provider had an error while creating Remote Vpc Peering"+op.GetDescription()). + FailedError(composed.StopWithRequeue). + SuccessError(composed.StopAndForget). + Run(ctx, state) + } + if meta.IsStatusConditionTrue( ptr.Deref(state.ObjAsVpcPeering().Conditions(), []metav1.Condition{}), cloudcontrolv1beta1.ConditionTypeReady, diff --git a/pkg/kcp/provider/gcp/vpcpeering/waitKymaVpcPeeringDeletion.go b/pkg/kcp/provider/gcp/vpcpeering/waitLocalVpcPeeringDeletion.go similarity index 52% rename from pkg/kcp/provider/gcp/vpcpeering/waitKymaVpcPeeringDeletion.go rename to pkg/kcp/provider/gcp/vpcpeering/waitLocalVpcPeeringDeletion.go index f6bf1920a..d19b76419 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/waitKymaVpcPeeringDeletion.go +++ b/pkg/kcp/provider/gcp/vpcpeering/waitLocalVpcPeeringDeletion.go @@ -2,16 +2,17 @@ package vpcpeering import ( "context" + "github.com/kyma-project/cloud-manager/pkg/composed" "github.com/kyma-project/cloud-manager/pkg/util" ) -func waitKymaVpcPeeringDeletion(ctx context.Context, st composed.State) (error, context.Context) { +func waitLocalVpcPeeringDeletion(ctx context.Context, st composed.State) (error, context.Context) { state := st.(*State) logger := composed.LoggerFromCtx(ctx) - if state.kymaVpcPeering != nil { - logger.Info("GCP Kyma VPC Peering is not deleted yet, re-queueing with delay") + if state.localVpcPeering != nil { + logger.Info("GCP Local VPC Peering is not deleted yet, re-queueing with delay", "localVpcPeering", state.getKymaVpcPeeringName()) return composed.StopWithRequeueDelay(util.Timing.T10000ms()), nil } diff --git a/pkg/kcp/provider/gcp/vpcpeering/waitRemoteVpcPeeringAvailable.go b/pkg/kcp/provider/gcp/vpcpeering/waitRemoteVpcPeeringAvailable.go index 91727890e..f82b44517 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/waitRemoteVpcPeeringAvailable.go +++ b/pkg/kcp/provider/gcp/vpcpeering/waitRemoteVpcPeeringAvailable.go @@ -12,6 +12,10 @@ func waitRemoteVpcPeeringAvailable(ctx context.Context, st composed.State) (erro state := st.(*State) logger := composed.LoggerFromCtx(ctx) + if state.remoteOperation != nil && state.remoteOperation.GetError() != nil { + return nil, ctx + } + if state.remoteVpcPeering.GetState() != pb.NetworkPeering_INACTIVE.String() && state.remoteVpcPeering.GetState() != pb.NetworkPeering_ACTIVE.String() { logger.Info("[KCP GCP VpcPeering waitRemoteVpcPeeringActive] GCP Remote VPC Peering is not ready yet, re-queueing with delay", "currentState", state.remoteVpcPeering.GetState()) diff --git a/pkg/kcp/provider/gcp/vpcpeering/waitVpcPeeringActive.go b/pkg/kcp/provider/gcp/vpcpeering/waitVpcPeeringActive.go index abeb32348..bf5e56e60 100644 --- a/pkg/kcp/provider/gcp/vpcpeering/waitVpcPeeringActive.go +++ b/pkg/kcp/provider/gcp/vpcpeering/waitVpcPeeringActive.go @@ -12,9 +12,13 @@ func waitVpcPeeringActive(ctx context.Context, st composed.State) (error, contex state := st.(*State) logger := composed.LoggerFromCtx(ctx) - if state.kymaVpcPeering.GetState() != pb.NetworkPeering_ACTIVE.String() && state.remoteVpcPeering.GetState() != pb.NetworkPeering_ACTIVE.String() { + if (state.localOperation != nil && state.localOperation.GetError() != nil) || (state.remoteOperation != nil && state.remoteOperation.GetError() != nil) { + return nil, ctx + } + + if state.localVpcPeering.GetState() != pb.NetworkPeering_ACTIVE.String() && state.remoteVpcPeering.GetState() != pb.NetworkPeering_ACTIVE.String() { logger.Info("GCP VPC Peering is not ready yet, re-queueing with delay", "currentState", state.remoteVpcPeering.GetState()) - return composed.StopWithRequeueDelay(util.Timing.T10000ms()), nil + return composed.StopWithRequeueDelay(util.Timing.T10000ms()), ctx } return nil, nil diff --git a/pkg/skr/gcpvpcpeering/deleteKcpRemoteNetwork.go b/pkg/skr/gcpvpcpeering/deleteKcpRemoteNetwork.go index 65f33ae04..e58330b4b 100644 --- a/pkg/skr/gcpvpcpeering/deleteKcpRemoteNetwork.go +++ b/pkg/skr/gcpvpcpeering/deleteKcpRemoteNetwork.go @@ -2,6 +2,7 @@ package gcpvpcpeering import ( "context" + "github.com/kyma-project/cloud-manager/pkg/composed" "github.com/kyma-project/cloud-manager/pkg/util" ) @@ -19,12 +20,12 @@ func deleteKcpRemoteNetwork(ctx context.Context, st composed.State) (error, cont return nil, nil } - logger.Info("[SKR GCP VPCPeering deleteKcpRemoteNetwork] Deleting GCP KCP Remote Network") + logger.Info("[SKR GCP VPCPeering deleteKcpRemoteNetwork] Deleting GCP KCP Remote Network " + state.KcpRemoteNetwork.Name) err := state.KcpCluster.K8sClient().Delete(ctx, state.KcpRemoteNetwork) if err != nil { - return composed.LogErrorAndReturn(err, "[SKR GCP VPCPeering deleteKcpRemoteNetwork] Error deleting GCP KCP remote Network", composed.StopWithRequeue, ctx) + return composed.LogErrorAndReturn(err, "[SKR GCP VPCPeering deleteKcpRemoteNetwork] Error deleting GCP KCP remote Network "+state.KcpRemoteNetwork.Name, composed.StopWithRequeue, ctx) } return composed.StopWithRequeueDelay(util.Timing.T10000ms()), nil diff --git a/pkg/skr/gcpvpcpeering/deleteKcpVpcPeering.go b/pkg/skr/gcpvpcpeering/deleteKcpVpcPeering.go index dd79cef65..1e0bc1251 100644 --- a/pkg/skr/gcpvpcpeering/deleteKcpVpcPeering.go +++ b/pkg/skr/gcpvpcpeering/deleteKcpVpcPeering.go @@ -2,6 +2,7 @@ package gcpvpcpeering import ( "context" + cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" "github.com/kyma-project/cloud-manager/pkg/composed" "github.com/kyma-project/cloud-manager/pkg/util" @@ -16,16 +17,16 @@ func deleteKcpVpcPeering(ctx context.Context, st composed.State) (error, context } if composed.IsMarkedForDeletion(state.KcpVpcPeering) { - logger.Info("[SKR GCP VPCPeering deleteKcpVpcPeering] KCP VpcPeering is marked for deletion, re-queueing until it is deleted.") + logger.Info("[SKR GCP VPCPeering deleteKcpVpcPeering] KCP VpcPeering is marked for deletion, re-queueing until it is deleted.", "kcpVpcPeering", state.KcpVpcPeering.Name) return composed.StopWithRequeueDelay(util.Timing.T10000ms()), nil } - logger.Info("[SKR GCP VPCPeering deleteKcpVpcPeering] Deleting KCP VpcPeering") + logger.Info("[SKR GCP VPCPeering deleteKcpVpcPeering] Deleting KCP VpcPeering", "kcpVpcPeering", state.KcpVpcPeering.Name) err := state.KcpCluster.K8sClient().Delete(ctx, state.KcpVpcPeering) if err != nil { - return composed.LogErrorAndReturn(err, "[SKR GCP VPCPeering deleteKcpVpcPeering] Error deleting KCP VpcPeering", composed.StopWithRequeue, ctx) + return composed.LogErrorAndReturn(err, "[SKR GCP VPCPeering deleteKcpVpcPeering] Error deleting KCP VpcPeering "+state.KcpVpcPeering.Name, composed.StopWithRequeue, ctx) } state.ObjAsGcpVpcPeering().Status.State = cloudcontrolv1beta1.VirtualNetworkPeeringStateDeleting diff --git a/pkg/skr/gcpvpcpeering/loadKcpGcpVpcPeering.go b/pkg/skr/gcpvpcpeering/loadKcpGcpVpcPeering.go index d8a577b86..6cb8f485c 100644 --- a/pkg/skr/gcpvpcpeering/loadKcpGcpVpcPeering.go +++ b/pkg/skr/gcpvpcpeering/loadKcpGcpVpcPeering.go @@ -2,6 +2,7 @@ package gcpvpcpeering import ( "context" + cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" "github.com/kyma-project/cloud-manager/pkg/composed" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -20,12 +21,12 @@ func loadKcpGcpVpcPeering(ctx context.Context, st composed.State) (error, contex if apierrors.IsNotFound(err) { state.KcpVpcPeering = nil - logger.Info("KCP GcpVpcPeering does not exist") + logger.Info("KCP GcpVpcPeering does not exist ", "kcpGcpVpcPeering", "cm-"+state.ObjAsGcpVpcPeering().Status.Id) return nil, nil } if err != nil { - return composed.LogErrorAndReturn(err, "Error loading KCP GcpVpcPeering", composed.StopWithRequeue, ctx) + return composed.LogErrorAndReturn(err, "Error loading KCP GcpVpcPeering "+state.ObjAsGcpVpcPeering().Status.Id, composed.StopWithRequeue, ctx) } state.KcpVpcPeering = kcpVpcPeering diff --git a/pkg/skr/gcpvpcpeering/loadKcpRemoteNetwork.go b/pkg/skr/gcpvpcpeering/loadKcpRemoteNetwork.go index 116251e41..eb2fcb00b 100644 --- a/pkg/skr/gcpvpcpeering/loadKcpRemoteNetwork.go +++ b/pkg/skr/gcpvpcpeering/loadKcpRemoteNetwork.go @@ -2,6 +2,7 @@ package gcpvpcpeering import ( "context" + cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" "github.com/kyma-project/cloud-manager/pkg/composed" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -22,12 +23,12 @@ func loadKcpRemoteNetwork(ctx context.Context, st composed.State) (error, contex if apierrors.IsNotFound(err) { state.KcpRemoteNetwork = nil - logger.Info("[SKR GCP VPCPeering loadKcpRemoteNetwork] GCP KCP Network does not exist") + logger.Info("[SKR GCP VPCPeering loadKcpRemoteNetwork] GCP KCP Network does not exist", "kcpNetwork", obj.Status.Id) return nil, nil } if err != nil { - return composed.LogErrorAndReturn(err, "[SKR GCP VPCPeering loadKcpRemoteNetwork] Error loading GCP KCP RemoteNetwork", composed.StopWithRequeue, ctx) + return composed.LogErrorAndReturn(err, "[SKR GCP VPCPeering loadKcpRemoteNetwork] Error loading GCP KCP RemoteNetwork "+obj.Status.Id, composed.StopWithRequeue, ctx) } state.KcpRemoteNetwork = remoteNetwork diff --git a/pkg/skr/gcpvpcpeering/updateId.go b/pkg/skr/gcpvpcpeering/updateId.go index 5442a7064..e70f7e8c3 100644 --- a/pkg/skr/gcpvpcpeering/updateId.go +++ b/pkg/skr/gcpvpcpeering/updateId.go @@ -2,6 +2,7 @@ package gcpvpcpeering import ( "context" + "github.com/google/uuid" cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" "github.com/kyma-project/cloud-manager/pkg/composed" @@ -31,7 +32,7 @@ func updateId(ctx context.Context, st composed.State) (error, context.Context) { return composed.LogErrorAndReturn(err, "[SKR GCP VPCPeering updateId] Error updating status with ID "+err.Error(), composed.StopWithRequeueDelay(util.Timing.T10000ms()), ctx) } - logger.Info("[SKR GCP VPCPeering updateId] SKR GcpVpcPeering updated with ID status") + logger.Info("[SKR GCP VPCPeering updateId] SKR GcpVpcPeering updated with ID status " + id) return composed.StopWithRequeueDelay(util.Timing.T100ms()), nil } diff --git a/pkg/skr/gcpvpcpeering/updateStatus.go b/pkg/skr/gcpvpcpeering/updateStatus.go index 96ec37b76..1b882a94c 100644 --- a/pkg/skr/gcpvpcpeering/updateStatus.go +++ b/pkg/skr/gcpvpcpeering/updateStatus.go @@ -2,62 +2,29 @@ package gcpvpcpeering import ( "context" - cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" - cloudresourcesv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-resources/v1beta1" + "github.com/kyma-project/cloud-manager/pkg/composed" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func updateStatus(ctx context.Context, st composed.State) (error, context.Context) { state := st.(*State) - logger := composed.LoggerFromCtx(ctx) + //logger := composed.LoggerFromCtx(ctx) if state.KcpVpcPeering == nil { // it's deleted return nil, nil } - kcpCondErr := meta.FindStatusCondition(state.KcpVpcPeering.Status.Conditions, cloudcontrolv1beta1.ConditionTypeError) - kcpCondReady := meta.FindStatusCondition(state.KcpVpcPeering.Status.Conditions, cloudcontrolv1beta1.ConditionTypeReady) - - skrCondErr := meta.FindStatusCondition(state.ObjAsGcpVpcPeering().Status.Conditions, cloudresourcesv1beta1.ConditionTypeError) - skrCondReady := meta.FindStatusCondition(state.ObjAsGcpVpcPeering().Status.Conditions, cloudresourcesv1beta1.ConditionTypeReady) - - state.ObjAsGcpVpcPeering().Status.State = state.KcpVpcPeering.Status.State - - if kcpCondErr != nil && skrCondErr == nil { + // Initial status update when SKR status conditions are empty + if len(state.ObjAsGcpVpcPeering().Status.Conditions) == 0 { return composed.UpdateStatus(state.ObjAsGcpVpcPeering()). - SetExclusiveConditions(metav1.Condition{ - Type: cloudresourcesv1beta1.ConditionTypeError, - Status: metav1.ConditionTrue, - Reason: cloudresourcesv1beta1.ConditionReasonError, - Message: kcpCondErr.Message, - }). - RemoveConditions(cloudresourcesv1beta1.ConditionTypeReady). - ErrorLogMessage("Error updating KCP GcpVpcPeering status with not ready condition due to KCP error"). - SuccessLogMsg("Updated and forgot SKR GcpVpcPeering status with Error condition"). - SuccessError(composed.StopAndForget). - Run(ctx, state) - } - - if kcpCondReady != nil && skrCondReady == nil { - logger.Info("Updating SKR GcpVpcPeering status with Ready condition") - - return composed.UpdateStatus(state.ObjAsGcpVpcPeering()). - SetExclusiveConditions(metav1.Condition{ - Type: cloudresourcesv1beta1.ConditionTypeReady, - Status: metav1.ConditionTrue, - Reason: cloudresourcesv1beta1.ConditionTypeReady, - Message: kcpCondReady.Message, - }). - RemoveConditions(cloudresourcesv1beta1.ConditionTypeError). - ErrorLogMessage("Error updating KCP GcpVpcPeering status with ready condition"). + SetExclusiveConditions(state.KcpVpcPeering.Status.Conditions[0]). + ErrorLogMessage(state.KcpVpcPeering.Status.Conditions[0].Message). SuccessError(composed.StopWithRequeue). Run(ctx, state) } - if len(state.KcpVpcPeering.Status.Conditions) > 0 && len(state.ObjAsGcpVpcPeering().Status.Conditions) > 0 && + if len(state.KcpVpcPeering.Status.Conditions) > 0 && state.KcpVpcPeering.Status.Conditions[0].LastTransitionTime.After(state.ObjAsGcpVpcPeering().Status.Conditions[0].LastTransitionTime.Time) && state.KcpVpcPeering.Status.Conditions[0].Message != state.ObjAsGcpVpcPeering().Status.Conditions[0].Message { return composed.UpdateStatus(state.ObjAsGcpVpcPeering()). @@ -67,5 +34,44 @@ func updateStatus(ctx context.Context, st composed.State) (error, context.Contex Run(ctx, state) } + //kcpCondErr := meta.FindStatusCondition(state.KcpVpcPeering.Status.Conditions, cloudcontrolv1beta1.ConditionTypeError) + //kcpCondReady := meta.FindStatusCondition(state.KcpVpcPeering.Status.Conditions, cloudcontrolv1beta1.ConditionTypeReady) + // + //skrCondErr := meta.FindStatusCondition(state.ObjAsGcpVpcPeering().Status.Conditions, cloudresourcesv1beta1.ConditionTypeError) + //skrCondReady := meta.FindStatusCondition(state.ObjAsGcpVpcPeering().Status.Conditions, cloudresourcesv1beta1.ConditionTypeReady) + // + //state.ObjAsGcpVpcPeering().Status.State = state.KcpVpcPeering.Status.State + // + //if kcpCondErr != nil && skrCondErr == nil { + // return composed.UpdateStatus(state.ObjAsGcpVpcPeering()). + // SetExclusiveConditions(metav1.Condition{ + // Type: cloudresourcesv1beta1.ConditionTypeError, + // Status: metav1.ConditionTrue, + // Reason: cloudresourcesv1beta1.ConditionReasonError, + // Message: kcpCondErr.Message, + // }). + // RemoveConditions(cloudresourcesv1beta1.ConditionTypeReady). + // ErrorLogMessage("Error updating KCP GcpVpcPeering status with not ready condition due to KCP error"). + // SuccessLogMsg("Updated and forgot SKR GcpVpcPeering status with Error condition"). + // SuccessError(composed.StopAndForget). + // Run(ctx, state) + //} + + //if kcpCondReady != nil && skrCondReady == nil { + // logger.Info("Updating SKR GcpVpcPeering status with Ready condition") + // + // return composed.UpdateStatus(state.ObjAsGcpVpcPeering()). + // SetExclusiveConditions(metav1.Condition{ + // Type: cloudresourcesv1beta1.ConditionTypeReady, + // Status: metav1.ConditionTrue, + // Reason: cloudresourcesv1beta1.ConditionTypeReady, + // Message: kcpCondReady.Message, + // }). + // RemoveConditions(cloudresourcesv1beta1.ConditionTypeError). + // ErrorLogMessage("Error updating KCP GcpVpcPeering status with ready condition"). + // SuccessError(composed.StopWithRequeue). + // Run(ctx, state) + //} + return nil, nil } diff --git a/pkg/skr/gcpvpcpeering/waitSkrStatusReady.go b/pkg/skr/gcpvpcpeering/waitSkrStatusReady.go index edbcdb7fe..2b8eb8e28 100644 --- a/pkg/skr/gcpvpcpeering/waitSkrStatusReady.go +++ b/pkg/skr/gcpvpcpeering/waitSkrStatusReady.go @@ -2,6 +2,7 @@ package gcpvpcpeering import ( "context" + cloudresourcesv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-resources/v1beta1" "github.com/kyma-project/cloud-manager/pkg/composed" "github.com/kyma-project/cloud-manager/pkg/util" @@ -10,7 +11,7 @@ import ( func waitSkrStatusReady(ctx context.Context, st composed.State) (error, context.Context) { state := st.(*State) - if state.ObjAsGcpVpcPeering().Status.Conditions[0].Status != cloudresourcesv1beta1.StateReady { + if state.ObjAsGcpVpcPeering().Status.Conditions == nil || state.ObjAsGcpVpcPeering().Status.Conditions[0].Status != cloudresourcesv1beta1.StateReady { return composed.StopWithRequeueDelay(util.Timing.T60000ms()), nil } diff --git a/pkg/testinfra/dsl/vpcPeering.go b/pkg/testinfra/dsl/vpcPeering.go index cc11735d7..5176fa7a3 100644 --- a/pkg/testinfra/dsl/vpcPeering.go +++ b/pkg/testinfra/dsl/vpcPeering.go @@ -3,6 +3,7 @@ package dsl import ( "errors" "fmt" + cloudcontrolv1beta1 "github.com/kyma-project/cloud-manager/api/cloud-control/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -14,7 +15,7 @@ func HavingVpcPeeringStatusId() ObjAssertion { return fmt.Errorf("the object %T is not KCP VpcPeering", obj) } if x.Status.Id == "" { - return errors.New("the KCP VpcPeering .status.id not set") + return errors.New("the KCP VpcPeering .status.id is not set") } return nil }