Skip to content

Commit

Permalink
Merge pull request #58 from ravi-shankar-sap/nfs-validation
Browse files Browse the repository at this point in the history
GCP NFS Validations
  • Loading branch information
kyma-bot authored Jan 25, 2024
2 parents 9e8e906 + 4d11d53 commit d265153
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 8 deletions.
9 changes: 5 additions & 4 deletions components/kcp/api/cloud-control/v1beta1/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ const (

ReasonScopeNotFound = "ScopeNoFound"

ReasonUnknown = "Unknown"
ReasonReady = "Ready"
ReasonGcpError = "GCPError"
ReasonNotSupported = "NotSupported"
ReasonUnknown = "Unknown"
ReasonReady = "Ready"
ReasonGcpError = "GCPError"
ReasonNotSupported = "NotSupported"
ReasonValidationFailed = "ValidationFailed"
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type NfsOptionsGcp struct {
type GcpFileTier string

const (
STANDARD = GcpFileTier("STANDARD")
PREMIUM = GcpFileTier("PREMIUM")
BASIC_HDD = GcpFileTier("BASIC_HDD")
BASIC_SSD = GcpFileTier("BASIC_SSD")
HIGH_SCALE_SSD = GcpFileTier("HIGH_SCALE_SSD")
Expand Down
3 changes: 3 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"
"regexp"
"time"

"github.com/kyma-project/cloud-manager/components/kcp/api/cloud-control/v1beta1"
Expand All @@ -18,6 +19,8 @@ const filestoreParentPattern = "projects/%s/locations/%s"
const GcpRetryWaitTime = time.Second * 3
const GcpOperationWaitTime = time.Second * 5

var FilestoreInstanceRegEx *regexp.Regexp = regexp.MustCompile(`^projects\/([^/]+)\/locations\/([^/]+)\/instances\/([^/]+)$`)

func GetVPCPath(projectId, vpcId string) string {
return fmt.Sprintf(vPCPathPattern, projectId, vpcId)
}
Expand Down
2 changes: 2 additions & 0 deletions components/kcp/pkg/provider/gcp/nfsinstance/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ func New(stateFactory StateFactory) composed.Action {
}
return composed.ComposeActions(
"gcsNfsInstance",
validateAlways,
actions.AddFinalizer,
checkGcpOperation,
loadNfsInstance,
validatePostCreate,
checkNUpdateState,
syncNfsInstance,
composed.BuildBranchingAction("RunFinalizer", StatePredicate(client.Deleted, ctx, state),
Expand Down
14 changes: 10 additions & 4 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 {
curState v1beta1.StatusState
operation gcpclient.OperationType
updateMask []string
validations []string
fsInstance *file.Instance
filestoreClient client.FilestoreClient
}
Expand Down Expand Up @@ -83,6 +84,14 @@ func (s State) toInstance() *file.Instance {
project := gcpScope.Project
vpc := gcpScope.VpcNetwork

nwConfig := &file.NetworkConfig{
Network: gcpclient.GetVPCPath(project, vpc),
ConnectMode: string(gcpOptions.ConnectMode),
}
if s.IpRange() != nil {
nwConfig.ReservedIpRange = s.IpRange().Spec.Cidr
}

return &file.Instance{
Description: nfsInstance.Name,
Tier: string(gcpOptions.Tier),
Expand All @@ -94,10 +103,7 @@ func (s State) toInstance() *file.Instance {
},
},
Networks: []*file.NetworkConfig{
{
Network: gcpclient.GetVPCPath(project, vpc),
ConnectMode: string(gcpOptions.ConnectMode),
},
nwConfig,
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ func syncNfsInstance(ctx context.Context, st composed.State) (error, context.Con
}

if operation != nil {
if state.operation == client.ADD {
nfsInstance.Status.Id = client.GetFilestoreInstancePath(project, location, name)
}
nfsInstance.Status.OpIdentifier = operation.Name
state.UpdateObjStatus(ctx)
}
Expand Down
48 changes: 48 additions & 0 deletions components/kcp/pkg/provider/gcp/nfsinstance/validateAlways.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package nfsinstance

import (
"context"
"errors"
"strings"

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

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

//If the instance already exists, continue.
if state.fsInstance != nil {
return nil, nil
}

nfsInstance := state.ObjAsNfsInstance()
logger.WithValues("NfsInstance :", nfsInstance.Name).Info("Validating Instance Details")

//Get GCP details.
gcpOptions := nfsInstance.Spec.Instance.Gcp

//Validate whether the requested capacity is a valid value.
if _, err := IsValidCapacity(gcpOptions.Tier, gcpOptions.CapacityGb); err != nil {
state.validations = append(state.validations, err.Error())
}

//Validate whether the nwMask is a valid value.
cidr := ""
if state.IpRange() != nil {
cidr = state.IpRange().Spec.Cidr
}
if _, err := IsValidNwMask(gcpOptions.Tier, cidr); err != nil {
state.validations = append(state.validations, err.Error())
}

if len(state.validations) > 0 {
err := errors.New(strings.Join(state.validations, "\n"))
state.AddErrorCondition(ctx, v1beta1.ReasonValidationFailed, err)
return composed.LogErrorAndReturn(err, "Validation Failed", composed.StopAndForget, nil)
}

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

import (
"context"
"errors"
"strings"

"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 validatePostCreate(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("Validating Instance Details")

//Get GCP details.
gcpOptions := nfsInstance.Spec.Instance.Gcp
name := nfsInstance.Spec.RemoteRef.Name

id := nfsInstance.Status.Id
if id != "" {
matches := client.FilestoreInstanceRegEx.FindStringSubmatch(id)
l := len(matches)

//Validate the location of the instance is not modified
if l > 2 && matches[2] != gcpOptions.Location {
state.validations = append(state.validations, "Location cannot be modified")
}

//Validate if the name of the instance is not modified.
if l > 3 && matches[3] != name {
state.validations = append(state.validations, "Name cannot be modified")
}
}

//Validate the Tier is not modified.
if state.fsInstance != nil && v1beta1.GcpFileTier(state.fsInstance.Tier) != gcpOptions.Tier {
state.validations = append(state.validations, "Tier cannot be modified")
}

//Validate the instance is not being scale down.
if !CanScaleDown(gcpOptions.Tier) && state.fsInstance != nil &&
state.fsInstance.FileShares[0].CapacityGb > int64(gcpOptions.CapacityGb) {
state.validations = append(state.validations, "Capacity cannot be reduced.")
}

//Add error condition
if len(state.validations) > 0 {
err := errors.New(strings.Join(state.validations, "\n"))
state.AddErrorCondition(ctx, v1beta1.ReasonValidationFailed, err)
return composed.LogErrorAndReturn(err, "Validation Failed", composed.StopAndForget, nil)
}

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

import (
"errors"
"fmt"
"strings"

"github.com/kyma-project/cloud-manager/components/kcp/api/cloud-control/v1beta1"
)

type GcpFileTierValidation interface {
IsValidCapacity(capacityGb int) (bool, error)
CanScaleDown() bool
IsValidNwMask(cidr string) (bool, error)
}

func IsValidCapacity(tier v1beta1.GcpFileTier, capacityGb int) (bool, error) {
switch tier {
case v1beta1.BASIC_HDD, v1beta1.STANDARD:
if capacityGb < 1024 {
return false, errors.New("Capacity should be > 1 TB")
} else if capacityGb > 65433 {
return false, errors.New("Capacity should be < 63.9 TB")
}
case v1beta1.BASIC_SSD, v1beta1.PREMIUM:
if capacityGb < 2560 {
return false, errors.New("Capacity should be > 2.5 TB")
} else if capacityGb > 65433 {
return false, errors.New("Capacity should be < 63.9 TB")
}
case v1beta1.ZONAL, v1beta1.ENTERPRISE, v1beta1.REGIONAL:
if capacityGb < 1024 {
return false, errors.New("Capacity should be > 1 TB")
} else if capacityGb > 10240 {
return false, errors.New("Capacity should be < 10 TB")
} else if capacityGb%256 != 0 {
return false, errors.New("Capacity should be in increments of 256 GBs")
}
case v1beta1.HIGH_SCALE_SSD:
if capacityGb < 10240 {
return false, errors.New("Capacity should be > 10 TB")
} else if capacityGb > 102400 {
return false, errors.New("Capacity should be < 100 TB")
} else if capacityGb%2560 != 0 {
return false, errors.New("Capacity should be in increments of 2560 GBs")
}
default:
return false, errors.New("Unknown Tier")
}
return true, nil
}

func CanScaleDown(tier v1beta1.GcpFileTier) bool {
return tier == v1beta1.ZONAL || tier == v1beta1.HIGH_SCALE_SSD ||
tier == v1beta1.ENTERPRISE || tier == v1beta1.REGIONAL
}

func IsValidNwMask(tier v1beta1.GcpFileTier, cidr string) (bool, error) {
if cidr == "" {
return true, nil
}
switch tier {
case v1beta1.BASIC_HDD, v1beta1.STANDARD, v1beta1.BASIC_SSD, v1beta1.PREMIUM, v1beta1.ZONAL:
return checkCidrSuffix(tier, cidr, "/29")
case v1beta1.ENTERPRISE, v1beta1.REGIONAL:
return checkCidrSuffix(tier, cidr, "/26")
case v1beta1.HIGH_SCALE_SSD:
return checkCidrSuffix(tier, cidr, "/24")
default:
return false, errors.New("Unknown Tier")
}
}

func checkCidrSuffix(tier v1beta1.GcpFileTier, cidr, suffix string) (bool, error) {
valid := strings.HasSuffix(cidr, suffix)
if valid {
return valid, nil
} else {
return valid, errors.New(fmt.Sprintf("CIDR block should be %s for Tier: %s", suffix, string(tier)))
}
}

0 comments on commit d265153

Please sign in to comment.