Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GCP NFS Instance Reconcilation #53

Merged
merged 1 commit into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions components/kcp/api/cloud-control/v1beta1/iprange_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ type IpRangeStatus struct {
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty"`

// Operation Identifier to track the Hyperscaler Operation
// +optional
OpIdentifier string `json:"opIdentifier,omitempty"`
}

type IpRangeSubnets []IpRangeSubnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ type NfsInstanceStatus struct {
//List of NFS Hosts (DNS Names or IP Addresses) that clients can use to connect
// +optional
Hosts []string `json:"hosts,omitempty"`

// Operation Identifier to track the Hyperscaler Operation
// +optional
OpIdentifier string `json:"opIdentifier,omitempty"`
}

//+kubebuilder:object:root=true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ spec:
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
opIdentifier:
description: Operation Identifier to track the Hyperscaler Operation
type: string
ranges:
items:
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ spec:
type: array
id:
type: string
opIdentifier:
description: Operation Identifier to track the Hyperscaler Operation
type: string
state:
type: string
type: object
Expand Down
4 changes: 4 additions & 0 deletions components/kcp/pkg/provider/gcp/client/gcpConstants.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package client

import (
"fmt"
"time"

"github.com/kyma-project/cloud-manager/components/kcp/api/cloud-control/v1beta1"
)
Expand All @@ -14,6 +15,9 @@ const PsaPeeringName = "servicenetworking-googleapis-com"
const filestoreInstancePattern = "projects/%s/locations/%s/instances/%s"
const filestoreParentPattern = "projects/%s/locations/%s"

const GcpRetryWaitTime = time.Second * 3
const GcpOperationWaitTime = time.Second * 5

func GetVPCPath(projectId, vpcId string) string {
return fmt.Sprintf(vPCPathPattern, projectId, vpcId)
}
Expand Down
75 changes: 75 additions & 0 deletions components/kcp/pkg/provider/gcp/iprange/checkGcpOperation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package iprange

import (
"context"
"errors"

"github.com/kyma-project/cloud-manager/components/kcp/api/cloud-control/v1beta1"
"github.com/kyma-project/cloud-manager/components/kcp/pkg/provider/gcp/client"
"github.com/kyma-project/cloud-manager/components/lib/composed"
)

func checkGcpOperation(ctx context.Context, st composed.State) (error, context.Context) {
state := st.(*State)
logger := composed.LoggerFromCtx(ctx)

ipRange := state.ObjAsIpRange()
opName := ipRange.Status.OpIdentifier
logger.WithValues("ipRange :", ipRange.Name).Info("Checking Operation")

//If no OpIdentifier, then continue to next action.
if opName == "" {
return nil, nil
}

//Check SyncPsa Operation..
if ipRange.Status.State == client.SyncPsaConnection ||
ipRange.Status.State == client.DeletePsaConnection {
op, err := state.serviceNetworkingClient.GetOperation(ctx, opName)
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error getting operation from GCP", composed.StopWithRequeue, nil)
}

//Operation not completed yet.. requeue again.
if op != nil && !op.Done {
return composed.StopWithRequeueDelay(client.GcpRetryWaitTime), nil
}

//If not able to find the operation or it is completed, reset OpIdentifier.
if op == nil || op.Done {
ipRange.Status.OpIdentifier = ""
}

//If the operation failed, update the error status on the object.
if op != nil && op.Error != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, errors.New(op.Error.Message))
}
} else if ipRange.Status.State == client.SyncAddress ||
ipRange.Status.State == client.DeleteAddress {
project := state.Scope().Spec.Scope.Gcp.Project
op, err := state.computeClient.GetGlobalOperation(ctx, project, opName)
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error getting operation from GCP", composed.StopWithRequeue, nil)
}

//Operation not completed yet.. requeue again.
if op != nil && op.Status != "DONE" {
return composed.StopWithRequeueDelay(client.GcpRetryWaitTime), nil
}

//If not able to find the operation or it is completed, reset OpIdentifier.
if op == nil || op.Status == "DONE" {
ipRange.Status.OpIdentifier = ""
}

//If the operation failed, update the error status on the object.
if op != nil && op.Error != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, errors.New(op.StatusMessage))
}

}

return nil, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package client

import (
"context"
"github.com/kyma-project/cloud-manager/components/lib/composed"
"net/http"
"strconv"

"github.com/kyma-project/cloud-manager/components/lib/composed"

gcpclient "github.com/kyma-project/cloud-manager/components/kcp/pkg/provider/gcp/client"
"google.golang.org/api/option"
"google.golang.org/api/servicenetworking/v1"
Expand All @@ -18,6 +19,7 @@ type ServiceNetworkingClient interface {
// projectNumber: Project number which is different from project id. Get it by calling client.GetProjectNumber(ctx, projectId)
DeleteServiceConnection(ctx context.Context, projectNumber int64, vpcId string) (*servicenetworking.Operation, error)
PatchServiceConnection(ctx context.Context, projectId, vpcId string, reservedIpRanges []string) (*servicenetworking.Operation, error)
GetOperation(ctx context.Context, operationName string) (*servicenetworking.Operation, error)
}

func NewServiceNetworkingClient() gcpclient.ClientProvider[ServiceNetworkingClient] {
Expand Down
1 change: 1 addition & 0 deletions components/kcp/pkg/provider/gcp/iprange/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func New(stateFactory StateFactory) composed.Action {
"gcpIpRange",
validateCidr,
focal.AddFinalizer,
checkGcpOperation,
loadAddress,
loadPsaConnection,
compareStates,
Expand Down
28 changes: 16 additions & 12 deletions components/kcp/pkg/provider/gcp/iprange/syncAddress.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (

"github.com/kyma-project/cloud-manager/components/kcp/api/cloud-control/v1beta1"
"github.com/kyma-project/cloud-manager/components/kcp/pkg/common/actions/focal"
"github.com/kyma-project/cloud-manager/components/kcp/pkg/provider/gcp/client"
"github.com/kyma-project/cloud-manager/components/lib/composed"
"google.golang.org/api/compute/v1"
)

func syncAddress(ctx context.Context, st composed.State) (error, context.Context) {
Expand All @@ -20,24 +22,26 @@ func syncAddress(ctx context.Context, st composed.State) (error, context.Context
project := gcpScope.Project
vpc := gcpScope.VpcNetwork

var operation *compute.Operation
var err error
switch state.addressOp {
case focal.ADD:
_, err := state.computeClient.CreatePscIpRange(ctx, project, vpc, ipRange.Name, ipRange.Name, state.ipAddress, int64(state.prefix))
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error creating Address object in GCP", composed.StopWithRequeue, nil)
}
operation, err = state.computeClient.CreatePscIpRange(ctx, project, vpc, ipRange.Name, ipRange.Name, state.ipAddress, int64(state.prefix))
case focal.MODIFY:
err := errors.New("IpRange update not supported.")
err = errors.New("IpRange update not supported.")
state.AddErrorCondition(ctx, v1beta1.ReasonNotSupported, err)
return composed.LogErrorAndReturn(err, "IpRange update not supported.", composed.StopAndForget, nil)
case focal.DELETE:
_, err := state.computeClient.DeleteIpRange(ctx, project, ipRange.Name)
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error deleting address object in GCP", composed.StopWithRequeue, nil)
}
operation, err = state.computeClient.DeleteIpRange(ctx, project, ipRange.Name)
}

return composed.StopWithRequeue, nil
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error synchronizing Address object in GCP", composed.StopWithRequeueDelay(client.GcpRetryWaitTime), nil)
}
if operation != nil {
ipRange.Status.OpIdentifier = operation.Name
state.UpdateObjStatus(ctx)
}
return composed.StopWithRequeueDelay(client.GcpOperationWaitTime), nil
}
33 changes: 17 additions & 16 deletions components/kcp/pkg/provider/gcp/iprange/syncPsaConnection.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (

"github.com/kyma-project/cloud-manager/components/kcp/api/cloud-control/v1beta1"
"github.com/kyma-project/cloud-manager/components/kcp/pkg/common/actions/focal"
"github.com/kyma-project/cloud-manager/components/kcp/pkg/provider/gcp/client"
"github.com/kyma-project/cloud-manager/components/lib/composed"
"google.golang.org/api/servicenetworking/v1"
)

func syncPsaConnection(ctx context.Context, st composed.State) (error, context.Context) {
Expand All @@ -19,26 +21,25 @@ func syncPsaConnection(ctx context.Context, st composed.State) (error, context.C
project := gcpScope.Project
vpc := gcpScope.VpcNetwork

var operation *servicenetworking.Operation
var err error

switch state.connectionOp {
case focal.ADD:
_, err := state.serviceNetworkingClient.CreateServiceConnection(ctx, project, vpc, state.ipRanges)
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error creating Service Connections in GCP", composed.StopWithRequeue, nil)
}
operation, err = state.serviceNetworkingClient.CreateServiceConnection(ctx, project, vpc, state.ipRanges)
case focal.MODIFY:
_, err := state.serviceNetworkingClient.PatchServiceConnection(ctx, project, vpc, state.ipRanges)
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error patching Service Connections in GCP", composed.StopWithRequeue, nil)
}
operation, err = state.serviceNetworkingClient.PatchServiceConnection(ctx, project, vpc, state.ipRanges)
case focal.DELETE:
_, err := state.serviceNetworkingClient.DeleteServiceConnection(ctx, state.projectNumber, vpc)
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error deleting Service Connections in GCP", composed.StopWithRequeue, nil)
}
operation, err = state.serviceNetworkingClient.DeleteServiceConnection(ctx, state.projectNumber, vpc)
}

return composed.StopWithRequeue, nil
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error syncronizing Service Connections in GCP", composed.StopWithRequeueDelay(client.GcpRetryWaitTime), nil)
}
if operation != nil {
ipRange.Status.OpIdentifier = operation.Name
state.UpdateObjStatus(ctx)
}
return composed.StopWithRequeueDelay(client.GcpOperationWaitTime), nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package nfsinstance

import (
"context"
"errors"

"github.com/kyma-project/cloud-manager/components/kcp/api/cloud-control/v1beta1"
"github.com/kyma-project/cloud-manager/components/kcp/pkg/provider/gcp/client"
"github.com/kyma-project/cloud-manager/components/lib/composed"
)

func checkGcpOperation(ctx context.Context, st composed.State) (error, context.Context) {
state := st.(*State)
logger := composed.LoggerFromCtx(ctx)

nfsInstance := state.ObjAsNfsInstance()
opName := nfsInstance.Status.OpIdentifier
logger.WithValues("NfsInstance :", nfsInstance.Name).Info("Checking GCP Operation Status")

//If no OpIdentifier, then continue to next action.
if opName == "" {
return nil, nil
}

project := state.Scope().Spec.Scope.Gcp.Project
op, err := state.filestoreClient.GetOperation(ctx, project, opName)
if err != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error getting operation from GCP", composed.StopWithRequeue, nil)
}

//Operation not completed yet.. requeue again.
if op != nil && !op.Done {
return composed.StopWithRequeueDelay(client.GcpRetryWaitTime), nil
}

//If not able to find the operation or it is completed, reset OpIdentifier.
if op == nil || op.Done {
nfsInstance.Status.OpIdentifier = ""
}

//If the operation failed, update the error status on the object.
if op != nil && op.Error != nil {
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, errors.New(op.Error.Message))
}

return nil, nil
}
27 changes: 27 additions & 0 deletions components/kcp/pkg/provider/gcp/nfsinstance/checkUpdateMask.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package nfsinstance

import (
"context"

"github.com/kyma-project/cloud-manager/components/kcp/pkg/common/actions/focal"
"github.com/kyma-project/cloud-manager/components/lib/composed"
)

func checkUpdateMask(ctx context.Context, st composed.State) (error, context.Context) {
state := st.(*State)
logger := composed.LoggerFromCtx(ctx)

nfsInstance := state.ObjAsNfsInstance()
logger.WithValues("NfsInstance :", nfsInstance.Name).Info("Checking for Update Mask")

//If the operation is not modify, continue.
if state.operation != focal.MODIFY {
return nil, nil
}

//If capacity is increased, add it to updateMask
if nfsInstance.Spec.Instance.Gcp.CapacityGb > int(state.fsInstance.FileShares[0].CapacityGb) {
state.updateMask = append(state.updateMask, "FileShares")
}
return nil, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"

"github.com/kyma-project/cloud-manager/components/kcp/api/cloud-control/v1beta1"
"github.com/kyma-project/cloud-manager/components/kcp/pkg/provider/gcp/client"
"github.com/kyma-project/cloud-manager/components/lib/composed"
"google.golang.org/api/googleapi"
)
Expand Down Expand Up @@ -33,7 +34,7 @@ func loadNfsInstance(ctx context.Context, st composed.State) (error, context.Con
}
}
state.AddErrorCondition(ctx, v1beta1.ReasonGcpError, err)
return composed.LogErrorAndReturn(err, "Error getting Filestore Instance from GCP", composed.StopWithRequeue, nil)
return composed.LogErrorAndReturn(err, "Error getting Filestore Instance from GCP", composed.StopWithRequeueDelay(client.GcpRetryWaitTime), nil)
}

//Store the fsInstance in state
Expand Down
1 change: 1 addition & 0 deletions components/kcp/pkg/provider/gcp/nfsinstance/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func New(stateFactory StateFactory) composed.Action {
return composed.ComposeActions(
"gcsNfsInstance",
focal.AddFinalizer,
checkGcpOperation,
loadNfsInstance,
checkNUpdateState,
syncNfsInstance,
Expand Down
1 change: 1 addition & 0 deletions components/kcp/pkg/provider/gcp/nfsinstance/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type State struct {
types.State
curState v1beta1.StatusState
operation focal.OperationType
updateMask []string
fsInstance *file.Instance
filestoreClient client.FilestoreClient
}
Expand Down
Loading
Loading