From d13f46c41a048ba095a458e745a990a2d87c299c Mon Sep 17 00:00:00 2001 From: Vedant Durgam Date: Thu, 4 Jun 2026 12:19:43 +0530 Subject: [PATCH] nutanix: fix ccoctl to accept directory for --credentials-source-filepath When a directory is passed to --credentials-source-filepath, the code was failing with an unhelpful 'is a directory' OS error. The flag now also accepts a directory path and automatically looks for a file named 'credentials' inside it, consistent with the default ~/.nutanix/credentials path and the official OCP documentation. Also updated the flag description in the binary help output to clearly state that both a file path and a directory are accepted. Assisted-by: Claude Sonnet 4.6 Co-authored-by: Cursor --- .../nutanix/create_shared_secrets.go | 11 +++- .../nutanix/create_shared_secrets_test.go | 52 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/provisioning/nutanix/create_shared_secrets.go b/pkg/cmd/provisioning/nutanix/create_shared_secrets.go index d675ca00df..f9f1b8b6f6 100644 --- a/pkg/cmd/provisioning/nutanix/create_shared_secrets.go +++ b/pkg/cmd/provisioning/nutanix/create_shared_secrets.go @@ -53,7 +53,7 @@ func createSharedSecretsCmd() *cobra.Command { cmd.PersistentFlags().StringVar(&CreateSharedSecretsOpts.CredRequestDir, "credentials-requests-dir", "", "Directory containing files of CredentialsRequests (can be created by running 'oc adm release extract --credentials-requests --cloud=nutanix' against an OpenShift release image)") cmd.MarkPersistentFlagRequired("credentials-requests-dir") - cmd.PersistentFlags().StringVar(&CreateSharedSecretsOpts.CredentialsSourceFilePath, "credentials-source-filepath", "", "The filepath of the nutanix credentials data. If not specified, will use the default path ~/.nutanix/credentials") + cmd.PersistentFlags().StringVar(&CreateSharedSecretsOpts.CredentialsSourceFilePath, "credentials-source-filepath", "", "The path to the nutanix credentials data file, or the directory containing a file named 'credentials'. If not specified, will use the default path ~/.nutanix/credentials") cmd.PersistentFlags().StringVar(&CreateSharedSecretsOpts.TargetDir, "output-dir", "", "Directory to place generated files (defaults to current directory)") cmd.PersistentFlags().BoolVar(&CreateSharedSecretsOpts.EnableTechPreview, "enable-tech-preview", false, "Opt into processing CredentialsRequests marked as tech-preview") @@ -87,9 +87,16 @@ func createSecretsCmd(cmd *cobra.Command, args []string) error { // Retrieve the credentials data func getCredentialsFromFile(filePath string) (*kubernetes.NutanixCredentials, error) { - if _, err := os.Stat(filePath); err != nil { + fileInfo, err := os.Stat(filePath) + if err != nil { return nil, errors.Wrapf(err, "source credentials file %s does not exist", filePath) } + if fileInfo.IsDir() { + filePath = filepath.Join(filePath, "credentials") + if _, err := os.Stat(filePath); err != nil { + return nil, errors.Wrapf(err, "credentials file not found in directory; expected at %s", filePath) + } + } bytes, err := os.ReadFile(filePath) if err != nil { diff --git a/pkg/cmd/provisioning/nutanix/create_shared_secrets_test.go b/pkg/cmd/provisioning/nutanix/create_shared_secrets_test.go index c3c956fefe..4ef285b073 100644 --- a/pkg/cmd/provisioning/nutanix/create_shared_secrets_test.go +++ b/pkg/cmd/provisioning/nutanix/create_shared_secrets_test.go @@ -181,6 +181,58 @@ func TestCreateSharedSecrets(t *testing.T) { }, expectedErr: "source credentials file does/not/exist does not exist", }, + { + name: "Directory provided for credentials-source-filepath with credentials file inside", + setup: func(t *testing.T) (credReqDir, targetDir, credentialsSourceFilepath string) { + credReqDir, err := os.MkdirTemp(os.TempDir(), testCredReqDirPrefix) + require.NoError(t, err, "Failed to create temp directory for credentials requests") + testCredentialsRequest(t, "credreq-test", "NutanixProviderSpec", "secret-ns", "secret-name", credReqDir) + + targetDir, err = os.MkdirTemp(os.TempDir(), testTargetDirPrefix) + require.NoError(t, err, "Failed to create temp directory for credentials requests") + + // Create a directory with a "credentials" file inside it (as the docs describe) + credentialsDir, err := os.MkdirTemp(os.TempDir(), testCredentialsDirPrefix) + require.NoError(t, err, "Failed to create temp directory for credentials") + credentialsFilePath := filepath.Join(credentialsDir, "credentials") + err = os.WriteFile(credentialsFilePath, []byte(getBasicAuthCredentials("username", "password")), 0600) + require.NoError(t, err, "Failed to write credentials file") + + // Pass the directory, not the file + credentialsSourceFilepath = credentialsDir + return + }, + verify: func(t *testing.T, manifestsDir string) { + files, err := os.ReadDir(manifestsDir) + require.NoError(t, err, "unexpected error listing files in manifestsDir") + assert.Len(t, files, 1, "Should be exactly one file in manifestsDir when directory with credentials file is provided") + contents := getSecretFromFileContents(t, filepath.Join(manifestsDir, files[0].Name())) + assert.Equal(t, "username", contents.PrismCentral.Username) + assert.Equal(t, "password", contents.PrismCentral.Password) + }, + expectedErr: "", + }, + { + name: "Directory provided for credentials-source-filepath without credentials file inside", + setup: func(t *testing.T) (credReqDir, targetDir, credentialsSourceFilepath string) { + credReqDir, err := os.MkdirTemp(os.TempDir(), testCredReqDirPrefix) + require.NoError(t, err, "Failed to create temp directory for credentials requests") + + targetDir, err = os.MkdirTemp(os.TempDir(), testTargetDirPrefix) + require.NoError(t, err, "Failed to create temp directory for credentials requests") + + // Pass a directory with no credentials file inside + credentialsSourceFilepath, err = os.MkdirTemp(os.TempDir(), testCredentialsDirPrefix) + require.NoError(t, err, "Failed to create temp directory for credentials") + return + }, + verify: func(t *testing.T, manifestsDir string) { + files, err := os.ReadDir(manifestsDir) + require.NoError(t, err, "unexpected error listing files in manifestsDir") + assert.Zero(t, len(files), "Should be no files in manifestsDir when no credentials file in directory") + }, + expectedErr: "credentials file not found in directory", + }, { name: "Non-existent credentials requests directory", setup: func(t *testing.T) (credReqDir, targetDir, credentialsSourceFilepath string) {