Skip to content

Conversation

@ankush-ntx
Copy link
Contributor

@ankush-ntx ankush-ntx commented May 5, 2025

Summary:

This PR introduces a testing framework for the Nutanix COSI driver, including both unit tests and end-to-end (E2E) tests. In addition, several minor bugs discovered during test development have been addressed and resolved.

Changes:

Unit Tests:

  • Covers core driver logic, IAM client ops, S3 client ops and provisioner methods.
  • Mocks for S3 client and IAM client to isolate functionality.

E2E Tests:

  • Tests all the workflows completely with edge cases.
  • Including: Create/Delete buckets, Grant/Revoke user access
  • Tests can be run on both a real cluster or a local cluster using Objects Triton as S3 backend objects store.

CI/CD Pipeline:

  • Updated GitHub pipeline to include running unit tests when a PR is created.

Deployment Changes:

  • PC_SECRET now only contains username and password credentials for the Prism Central in the previous format.
  • PC_ENDPOINT instead should have the full endpoint of the Prism Central.

Fixes:

How to run tests?

Unit Tests:
Execute the following to run unit tests:

go test ./...

To generate the coverage report and an HTML page to view the report:

go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html

Open the HTML file in any web browser to view the coverage of each file.

E2E Tests:
Execute the following to run E2E tests:

sh scripts/setup_test_env.sh [flags]
Options:
-o, --oss_endpoint ENDPOINT    Nutanix Object Store instance endpoint, eg. "http://10.51.142.82:80".
-i, --pc_endpoint ENDPOINT     Prism Central endpoint, eg. "https://10.51.142.82:9440".
-u, --pc_user USERNAME         Prism Central username. [default = admin]
-p, --pc_pass PASSWORD         Prism Central password.
-a, --access_key KEY           Admin IAM Access key to be used for Nutanix Objects.
-s, --secret_key KEY           Admin IAM Secret key to be used for Nutanix Objects.
-n, --namespace NAMESAPCE      Cluster namespace for the COSI deployment [default = cosi]

Check the README.md for more info.

Copy link

@anirudhbansal95 anirudhbansal95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Ankush. Reviewed the unit tests and the associated code. Yet to review the end to end tests.

return fmt.Errorf("%s", delete_resp.Status)
if delete_resp.StatusCode == 404 {
return nil
} else if delete_resp.StatusCode != 204 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: else if can be removed from line 165 since we are returning in line 164


t.Run("TestCreateUser_MissingUsername", func(t *testing.T) {
api := &admin.API{}
_, err := api.CreateUser(ctx, "", mockDisplayName)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we allow the display name to be empty, though not the user name. Is this the expected behaviour?

api := admin.API{
AccountName: "test",
}
accountName := api.AccountName

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use api.GetAccountName() here and in the test below

klog.ErrorS(err, "failed to delete user")
}

klog.InfoS("Successfully revoked access of user", "userName", req.GetAccountId(), "bucketName", req.GetBucketId())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we also need to update the bucket policy here to remove the user from it, like we updated the bucket policy to grant user access in the previous case?

klog.InfoS("Deleting user", "id", req.GetAccountId())

err := s.ntnxIamClient.RemoveUser(ctx, req.GetAccountId())
err := s.NtnxIamClient.RemoveUser(ctx, req.GetAccountId())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does ignoring the error mean this is only a best-effort API to revoke user access from bucket, since it returns nil error even if there was an IAM issue for example? Is the caller fine with this behaviour?

foundSID1 = true
assert.Equal(t, "Deny", string(ps.Effect))
case "sid2":
foundSID2 = true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also check that sid2 and sid3 have "Allow" effect

ps.Allows()
assert.Equal(t, s3client.Effect("Allow"), ps.Effect)
ps.Effect = "Other"
ps.Allows()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe add a comment that ps.Allows() doesn't modify an existing effect, if set.

s := &s3client.S3Agent{Client: mock}
content, err := s.GetObjectInBucket("missing-bucket", "missing-key")
assert.Error(t, err)
assert.Equal(t, "ERROR_ OBJECT NOT FOUND", content)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a specific format in which the object not found error should be returned? Or can it be something like: ERROR_OBJECT_NOT_FOUND or some other string?

Copy link

@anirudhbansal95 anirudhbansal95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Ankush. Took a look at the end to end tests. They look good in general with a few comments.

return err
}

if bucket.Status.BucketReady == false {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if !bucket.Status.BucketReady

return err
}

if bucket.Status.BucketReady == true {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if bucket.Status.BucketReady

Expect(err).ToNot(HaveOccurred())

// By("Checking if Bucket is created in the Objectstore backend")
err = s3Client.WaitUntilBucketExists(&s3.HeadBucketInput{Bucket: &failBucket.Name})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to call this function in other tests as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deleting a Bucket object with a missing bucket fails

2 participants