Skip to content

Commit

Permalink
Reconciler to monitor the changes in AWS FSxN resources
Browse files Browse the repository at this point in the history
  • Loading branch information
Utkarshjh authored Dec 14, 2024
1 parent c2d2372 commit 774190b
Show file tree
Hide file tree
Showing 14 changed files with 662 additions and 469 deletions.
1 change: 1 addition & 0 deletions helm/trident-operator/templates/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ rules:
- volumesnapshotclasses
verbs:
- create
- delete
- apiGroups:
- snapshot.storage.k8s.io
resources:
Expand Down

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions mocks/mock_storage_drivers/mock_ontap/mock_awsapi.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions operator/clients/trident_crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ func (tc *TridentCRDClient) PatchTridentBackendConfig(name, namespace string, pa
func (tc *TridentCRDClient) DeleteTridentBackendConfig(name, namespace string) error {
return tc.client.TridentV1().TridentBackendConfigs(namespace).Delete(ctx, name, deleteOpts)
}

func (tc *TridentCRDClient) ListTridentBackend(namespace string) (*tridentV1.TridentBackendList, error) {
return tc.client.TridentV1().TridentBackends(namespace).List(ctx, listOpts)
}
1 change: 1 addition & 0 deletions operator/clients/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type OperatorCRDClientInterface interface {
type TridentCRDClientInterface interface {
CheckTridentBackendConfigExists(name, namespace string) (bool, error)
GetTridentBackendConfig(name, namespace string) (*tridentV1.TridentBackendConfig, error)
ListTridentBackend(namespace string) (*tridentV1.TridentBackendList, error)
PatchTridentBackendConfig(name, namespace string, patchBytes []byte, patchType types.PatchType) error
DeleteTridentBackendConfig(name, namespace string) error
}
Expand Down
18 changes: 16 additions & 2 deletions operator/controllers/configurator/clients/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (c *ConfiguratorClient) CreateOrPatchObject(objType ObjectType, objName, ob
Log().Errorf("Patch failed for %s %s: %v", objName, objType, err)
}

if err := c.deleteObject(objType, objName, objNamespace); err != nil {
if err := c.DeleteObject(objType, objName, objNamespace); err != nil {
return err
}

Expand Down Expand Up @@ -166,7 +166,8 @@ func (c *ConfiguratorClient) patchObject(objType ObjectType, objName, objNamespa
return fmt.Errorf("unsupported object %s of type %s", objName, objType)
}

func (c *ConfiguratorClient) deleteObject(objType ObjectType, objName, objNamespace string) error {
// DeleteObject deletes the object of the given type and name.
func (c *ConfiguratorClient) DeleteObject(objType ObjectType, objName, objNamespace string) error {
switch objType {
case OCRD:
return c.kClient.DeleteCRD(objName)
Expand All @@ -181,6 +182,19 @@ func (c *ConfiguratorClient) deleteObject(objType ObjectType, objName, objNamesp
return fmt.Errorf("unsupported object %s of type %s", objName, objType)
}

// ListObjects lists the objects of the given type.
func (c *ConfiguratorClient) ListObjects(objType ObjectType, objNamespace string) (interface{}, error) {
switch objType {
case OCRD:
case OBackend:
return c.tClient.ListTridentBackend(objNamespace)
case OStorageClass:
case OSnapshotClass:
}

return nil, fmt.Errorf("unsupported object type %s", objType)
}

func (c *ConfiguratorClient) GetControllingTorcCR() (*operatorV1.TridentOrchestrator, error) {
return c.oClient.GetControllingTorcCR()
}
Expand Down
2 changes: 2 additions & 0 deletions operator/controllers/configurator/clients/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type ConfiguratorClientInterface interface {
GetControllingTorcCR() (*operatorV1.TridentOrchestrator, error)
GetTconfCR(name string) (*operatorV1.TridentConfigurator, error)
GetANFSecrets(secretName string) (string, string, error)
DeleteObject(objType ObjectType, objName, objNamespace string) error
ListObjects(objType ObjectType, objNamespace string) (interface{}, error)
}

type ExtendedK8sClientInterface interface {
Expand Down
8 changes: 4 additions & 4 deletions operator/controllers/configurator/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,20 +394,20 @@ func (c *Controller) reconcile(keyItem string) error {
return err
}
case config.OntapNASStorageDriverName, config.OntapSANStorageDriverName:
isAwsFsxn, err := tconfCR.IsAwsFsxnTconf()
isAwsFSxN, err := tconfCR.IsAWSFSxNTconf()
if err != nil {
Log().Error("Failed to check if tconf is for AWS FSxN: ", err)
return err
}
if isAwsFsxn {
if isAwsFSxN {
Log().Debugf("Tconf indicates auto-backend config is for AWS FSxN")
fsxn, err := storage_drivers.NewFsxnInstance(torcCR, tconfCR, c.Clients)
fsxn, err := storage_drivers.NewFSxNInstance(torcCR, tconfCR, c.Clients)
if err != nil {
Log().Info("Failed to create FsxN backend instance: ", err)
return err
}
if err := c.ProcessBackend(fsxn, tconfCR); err != nil {
Log().Error("Failed to process FsxN backend: ", err)
Log().Error("Failed to process FSxN backend: ", err)
return err
}
}
Expand Down
12 changes: 12 additions & 0 deletions operator/controllers/configurator/storage_drivers/anf.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,18 @@ func (a *ANF) GetCloudProvider() string {
return "None"
}

func (a *ANF) DeleteBackend(map[string]interface{}) error {
return nil
}

func (a *ANF) DeleteStorageClass(map[string]interface{}) error {
return nil
}

func (a *ANF) DeleteSnapshotClass() error {
return nil
}

func (a *ANF) populateAndValidateAZResources() error {
a.FilteredCapacityPoolMap = a.AZClient.FilteredCapacityPoolMap(context.TODO(), a.ResourceGroups,
a.NetappAccounts, a.CapacityPools)
Expand Down
127 changes: 109 additions & 18 deletions operator/controllers/configurator/storage_drivers/fsx.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import (
. "github.com/netapp/trident/logging"
confClients "github.com/netapp/trident/operator/controllers/configurator/clients"
operatorV1 "github.com/netapp/trident/operator/crd/apis/netapp/v1"
tridentV1 "github.com/netapp/trident/persistent_store/crd/apis/netapp/v1"
sa "github.com/netapp/trident/storage_attribute"
"github.com/netapp/trident/storage_drivers/ontap/awsapi"
"github.com/netapp/trident/utils"
"github.com/netapp/trident/utils/errors"
)

const (
Expand Down Expand Up @@ -60,8 +62,8 @@ type SVM struct {
ManagementLIF string `json:"managementLIF"`
}

// NewFsxnInstance creates a new instance of the AWS struct and populates it with the provided CRs and client
func NewFsxnInstance(
// NewFSxNInstance creates a new instance of the AWS struct and populates it with the provided CRs and client
func NewFSxNInstance(
torcCR *operatorV1.TridentOrchestrator, configuratorCR *operatorV1.TridentConfigurator,
client confClients.ConfiguratorClientInterface,
) (*AWS, error) {
Expand All @@ -70,7 +72,7 @@ func NewFsxnInstance(
}

if configuratorCR == nil {
return nil, fmt.Errorf("empty AWSFsxN configurator CR")
return nil, fmt.Errorf("empty AWS FSxN configurator CR")
}

if client == nil {
Expand Down Expand Up @@ -117,33 +119,39 @@ func (aws *AWS) Validate() error {
aws.AwsClient = api

for key, fsxnInstance := range aws.SVMs {
if err := aws.processFsxnInstance(context.Background(), key, fsxnInstance); err != nil {
if err := aws.processFSxNInstance(context.Background(), key, fsxnInstance); err != nil {
return err
}
}

return nil
}

// processFsxnInstance processes the auto-backend configuration for the FSxN instance.
// processFSxNInstance processes the auto-backend configuration for the FSxN instance.
// It creates the SVM if it does not exist and creates the secret for the SVM.
func (aws *AWS) processFsxnInstance(ctx context.Context, key int, svm SVM) error {
func (aws *AWS) processFSxNInstance(ctx context.Context, key int, svm SVM) error {
var (
svmName string
secretName string
secretARN string
svmExists bool
svmName string
secretName string
secretARN string
svmExists bool
ErrStatusCode = "StatusCode: 400"
)
svmName = svm.SvmName
if svmName == "" {
svmName = fmt.Sprintf(SvmNamePattern, svm.FsxnID)
svm.SvmName = svmName
}
_, err := aws.AwsClient.GetFilesystemByID(ctx, svm.FsxnID)
if err != nil {
if errors.IsNotFoundError(err) || (err != nil && strings.Contains(err.Error(), ErrStatusCode)) {
err = aws.cleanUpFSxNRelatedObjects(svm, svm.Protocols)
if err != nil {
Log().Error(err)
}
return fmt.Errorf("error occurred while getting fsxn id: %v : %v", svm.FsxnID, err)
}
Log().Debugf("Filesystem ID: %s exists", svm.FsxnID)
secretName = fmt.Sprintf(TridentSecretPattern, strings.TrimPrefix(svmName, "trident-"))
secretName = getAWSSecretName(svmName)
// Get the secret if it already exists or create a new one
secret, _ := aws.AwsClient.GetSecret(ctx, secretName)
if secret != nil {
Expand Down Expand Up @@ -245,7 +253,7 @@ func (aws *AWS) Create() ([]string, error) {
)
for _, svm := range aws.SVMs {
for _, protocol := range svm.Protocols {
backendName = getFsxnBackendName(svm.FsxnID, protocol)
backendName = getFSxNBackendName(svm.FsxnID, protocol)
backendYAML := getFsxnTBCYaml(svm, aws.TridentNamespace, backendName, protocol)
if err := aws.ConfClient.CreateOrPatchObject(confClients.OBackend, backendName,
aws.TridentNamespace, backendYAML); err != nil {
Expand All @@ -262,7 +270,7 @@ func (aws *AWS) CreateStorageClass() error {
var driver string
for _, svm := range aws.SVMs {
for _, protocol := range svm.Protocols {
name := getFsxnStorageClassName(svm.FsxnID, protocol)
name := getFSxNStorageClassName(svm.FsxnID, protocol)
if protocol == sa.NFS {
driver = config.OntapNASStorageDriverName
} else if protocol == sa.ISCSI {
Expand Down Expand Up @@ -290,12 +298,95 @@ func (aws *AWS) GetCloudProvider() string {
return k8sclient.CloudProviderAWS
}

// getFsxnTBCYaml returns the FsxN Trident backend config name
func getFsxnBackendName(fsxnId, protocolType string) string {
// DeleteBackend deletes the backend if the FSxN instance is deleted
func (aws *AWS) DeleteBackend(request map[string]interface{}) error {
protocols := request["protocols"].([]string)
fsxnId := request["FSxNID"].(string)
for _, protocol := range protocols {
backendName := getFSxNBackendName(fsxnId, protocol)
if err := aws.ConfClient.DeleteObject(confClients.OBackend, backendName, aws.TridentNamespace); err != nil {
return fmt.Errorf("error occurred while deleting backend: %w", err)
}
}
return nil
}

// DeleteStorageClass deletes the storage class if the FSxN instance is deleted
func (aws *AWS) DeleteStorageClass(request map[string]interface{}) error {
protocols := request["protocols"].([]string)
fsxnId := request["FSxNID"].(string)
for _, protocol := range protocols {
name := getFSxNStorageClassName(fsxnId, protocol)
if err := aws.ConfClient.DeleteObject(confClients.OStorageClass, name, aws.TridentNamespace); err != nil {
return fmt.Errorf("error occurred while deleting storage class: %w", err)
}
}
return nil
}

// DeleteSnapshotClass deletes the snapshot class if the FSxN instance is deleted
func (aws *AWS) DeleteSnapshotClass() error {
tbcList, err := aws.ConfClient.ListObjects(confClients.OBackend, "")
if err != nil {
return fmt.Errorf("error occurred while listing TBC objects: %w", err)
}
if len(tbcList.(*tridentV1.TridentBackendList).Items) == 0 {
if err := aws.ConfClient.DeleteObject(confClients.OSnapshotClass, NetAppSnapshotClassName, ""); err != nil {
return fmt.Errorf("error occurred while deleting snapshot class: %w", err)
}
}

return nil
}

// cleanUpFSxNRelatedObjects cleans up the FSxN instance related objects like storage class, backend, and AWS secret
func (aws *AWS) cleanUpFSxNRelatedObjects(svm SVM, protocols []string) (err error) {
cleanupRequest := map[string]interface{}{
"FSxNID": svm.FsxnID,
"protocols": protocols,
}

// Delete the storage class
if deleteErr := aws.DeleteStorageClass(cleanupRequest); err != nil {
err = fmt.Errorf("failed to delete VolumeSnapshotClass: %w", deleteErr)
}

// Delete the backend
if deleteErr := aws.DeleteBackend(cleanupRequest); err != nil {
err = fmt.Errorf("failed to delete backend: %w", deleteErr)
}

// Delete the AWS secret
if deleteErr := deleteSecret(context.Background(), aws, getAWSSecretName(svm.SvmName)); err != nil {
err = fmt.Errorf("failed to delete AWS secret: %w", deleteErr)
}

// Delete the VolumeSnapshotClass if no backend is present
if deleteErr := aws.DeleteSnapshotClass(); err != nil {
err = fmt.Errorf("failed to delete VolumeSnapshotClass: %w", deleteErr)
}

return
}

func deleteSecret(ctx context.Context, aws *AWS, secretName string) error {
if err := aws.AwsClient.DeleteSecret(ctx, secretName); err != nil {
return fmt.Errorf("error occurred while deleting secret: %w", err)
}
return nil
}

// getFSxNBackendName returns the FsxN Trident backend config name
func getFSxNBackendName(fsxnId, protocolType string) string {
return fmt.Sprintf(BackendNamePattern, fsxnId, protocolType)
}

// getFsxnStorageClassName returns the storage class name for the FSxN backend
func getFsxnStorageClassName(fsxnId, protocolType string) string {
// getFSxNStorageClassName returns the storage class name for the FSxN backend
func getFSxNStorageClassName(fsxnId, protocolType string) string {
return fmt.Sprintf(StorageClassNamePattern, fsxnId, protocolType)
}

// getAWSSecretName returns the AWS secret name for the FSxN backend
func getAWSSecretName(svmName string) string {
return fmt.Sprintf(TridentSecretPattern, strings.TrimPrefix(svmName, "trident-"))
}
3 changes: 3 additions & 0 deletions operator/controllers/configurator/storage_drivers/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ type Backend interface {
CreateStorageClass() error
CreateSnapshotClass() error
GetCloudProvider() string
DeleteBackend(map[string]interface{}) error
DeleteStorageClass(map[string]interface{}) error
DeleteSnapshotClass() error
}
2 changes: 1 addition & 1 deletion operator/crd/apis/netapp/v1/tridentconfigurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (tc *TridentConfigurator) Validate() error {
return nil
}

func (tc *TridentConfigurator) IsAwsFsxnTconf() (bool, error) {
func (tc *TridentConfigurator) IsAWSFSxNTconf() (bool, error) {
var tConfSpec map[string]interface{}
if err := json.Unmarshal(tc.Spec.Raw, &tConfSpec); err != nil {
return false, err
Expand Down
19 changes: 19 additions & 0 deletions storage_drivers/ontap/awsapi/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,25 @@ func (d *Client) GetSecret(ctx context.Context, secretARN string) (*Secret, erro
return secret, nil
}

// DeleteSecret deletes a secret by its ARN/name.
func (d *Client) DeleteSecret(ctx context.Context, secretARN string) error {
logFields := LogFields{
"API": "DeleteSecret",
"secretARN": secretARN,
}
deleteSecretInput := &secretsmanager.DeleteSecretInput{
SecretId: utils.Ptr(secretARN),
ForceDeleteWithoutRecovery: utils.Ptr(true),
}
if _, err := d.secretsClient.DeleteSecret(ctx, deleteSecretInput); err != nil {
logFields["requestID"] = GetRequestIDFromError(err)
Logc(ctx).WithFields(logFields).WithError(err).Error("Could not delete secret.")
return fmt.Errorf("error deleting secret; %w", err)
}
Logc(ctx).WithFields(logFields).Info("Secret deleted.")
return nil
}

// ParseVolumeARN parses the AWS-style ARN for a volume.
func ParseVolumeARN(volumeARN string) (region, accountID, filesystemID, volumeID string, err error) {
match := volumeARNRegex.FindStringSubmatch(volumeARN)
Expand Down
1 change: 1 addition & 0 deletions storage_drivers/ontap/awsapi/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
type AWSAPI interface {
CreateSecret(ctx context.Context, request *SecretCreateRequest) (*Secret, error)
GetSecret(ctx context.Context, secretARN string) (*Secret, error)
DeleteSecret(ctx context.Context, secretARN string) error

GetFilesystems(ctx context.Context) (*[]*Filesystem, error)
GetFilesystemByID(ctx context.Context, ID string) (*Filesystem, error)
Expand Down

0 comments on commit 774190b

Please sign in to comment.