mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-05-22 15:26:42 +00:00
Many reports are about closing or removing files. In some cases it is possible to report an error in the logs, in other cases the error can be ignored without potential issues. Test cases have been modified to not remove the temporary files. The temporary directory that is provided by the testing package, is removed once the tests are done. Signed-off-by: Niels de Vos <ndevos@ibm.com>
181 lines
5.6 KiB
Go
181 lines
5.6 KiB
Go
/*
|
|
Copyright 2018 The Ceph-CSI Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package util
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/ceph/ceph-csi/internal/util/log"
|
|
)
|
|
|
|
const (
|
|
credUserID = "userID"
|
|
credUserKey = "userKey"
|
|
credAdminID = "adminID"
|
|
credAdminKey = "adminKey"
|
|
credMonitors = "monitors"
|
|
tmpKeyFileLocation = "/tmp/csi/keys"
|
|
tmpKeyFileNamePrefix = "keyfile-"
|
|
migUserName = "admin"
|
|
migUserID = "adminId"
|
|
migUserKey = "key"
|
|
)
|
|
|
|
// Credentials struct represents credentials to access the ceph cluster.
|
|
type Credentials struct {
|
|
ID string
|
|
KeyFile string
|
|
}
|
|
|
|
// NewUserCredentials creates new user credentials from secret.
|
|
func NewUserCredentials(secrets map[string]string) (*Credentials, error) {
|
|
return newCredentialsFromSecret(credUserID, credUserKey, secrets)
|
|
}
|
|
|
|
// NewAdminCredentials creates new admin credentials from secret.
|
|
func NewAdminCredentials(secrets map[string]string) (*Credentials, error) {
|
|
// Use userID and userKey if found else fallback to adminID and adminKey
|
|
if cred, err := newCredentialsFromSecret(credUserID, credUserKey, secrets); err == nil {
|
|
return cred, nil
|
|
}
|
|
log.WarningLogMsg("adminID and adminKey are deprecated, please use userID and userKey instead")
|
|
|
|
return newCredentialsFromSecret(credAdminID, credAdminKey, secrets)
|
|
}
|
|
|
|
// NewUserCredentialsWithMigration takes secret map from the request and validate it is
|
|
// a migration secret, if yes, it continues to create CR from it after parsing the migration
|
|
// secret. If it is not a migration it will continue the attempt to create credentials from it
|
|
// without parsing the secret. This function returns credentials and error.
|
|
func NewUserCredentialsWithMigration(secrets map[string]string) (*Credentials, error) {
|
|
if isMigrationSecret(secrets) {
|
|
migSecret, err := ParseAndSetSecretMapFromMigSecret(secrets)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secrets = migSecret
|
|
}
|
|
cr, cErr := NewUserCredentials(secrets)
|
|
if cErr != nil {
|
|
return nil, cErr
|
|
}
|
|
|
|
return cr, nil
|
|
}
|
|
|
|
// DeleteCredentials removes the KeyFile.
|
|
func (cr *Credentials) DeleteCredentials() {
|
|
os.Remove(cr.KeyFile) //nolint:errcheck,gosec // no way to return/report the issue here
|
|
}
|
|
|
|
func storeKey(key string) (string, error) {
|
|
tmpfile, err := os.CreateTemp(tmpKeyFileLocation, tmpKeyFileNamePrefix)
|
|
if err != nil {
|
|
return "", fmt.Errorf("error creating a temporary keyfile: %w", err)
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
os.Remove(tmpfile.Name()) //nolint:errcheck,gosec // no way to return/report the issue here
|
|
}
|
|
}()
|
|
|
|
if _, err = tmpfile.WriteString(key); err != nil {
|
|
return "", fmt.Errorf("error writing key to temporary keyfile: %w", err)
|
|
}
|
|
|
|
keyFile := tmpfile.Name()
|
|
if keyFile == "" {
|
|
err = fmt.Errorf("error reading temporary filename for key: %w", err)
|
|
|
|
return "", err
|
|
}
|
|
|
|
if err = tmpfile.Close(); err != nil {
|
|
return "", fmt.Errorf("error closing temporary filename: %w", err)
|
|
}
|
|
|
|
return keyFile, nil
|
|
}
|
|
|
|
func newCredentialsFromSecret(idField, keyField string, secrets map[string]string) (*Credentials, error) {
|
|
var (
|
|
c = &Credentials{}
|
|
ok bool
|
|
)
|
|
|
|
if len(secrets) == 0 {
|
|
return nil, errors.New("provided secret is empty")
|
|
}
|
|
if c.ID, ok = secrets[idField]; !ok {
|
|
return nil, fmt.Errorf("missing ID field '%s' in secrets", idField)
|
|
}
|
|
|
|
key := secrets[keyField]
|
|
if key == "" {
|
|
return nil, fmt.Errorf("missing key field '%s' in secrets", keyField)
|
|
}
|
|
|
|
keyFile, err := storeKey(key)
|
|
if err == nil {
|
|
c.KeyFile = keyFile
|
|
}
|
|
|
|
return c, err
|
|
}
|
|
|
|
// GetMonValFromSecret returns monitors from secret.
|
|
func GetMonValFromSecret(secrets map[string]string) (string, error) {
|
|
if mons, ok := secrets[credMonitors]; ok {
|
|
return mons, nil
|
|
}
|
|
|
|
return "", fmt.Errorf("missing %q", credMonitors)
|
|
}
|
|
|
|
// ParseAndSetSecretMapFromMigSecret parse the secretmap from the migration request and return
|
|
// newsecretmap with the userID and userKey fields set.
|
|
func ParseAndSetSecretMapFromMigSecret(secretmap map[string]string) (map[string]string, error) {
|
|
newSecretMap := make(map[string]string)
|
|
// parse and set userKey
|
|
if !isMigrationSecret(secretmap) {
|
|
return nil, errors.New("passed secret map does not contain user key or it is nil")
|
|
}
|
|
newSecretMap[credUserKey] = secretmap[migUserKey]
|
|
// parse and set the userID
|
|
newSecretMap[credUserID] = migUserName
|
|
if secretmap[migUserID] != "" {
|
|
newSecretMap[credUserID] = secretmap[migUserID]
|
|
}
|
|
|
|
return newSecretMap, nil
|
|
}
|
|
|
|
// isMigrationSecret validates if the passed in secretmap is a secret
|
|
// of a migration volume request. The migration secret carry a field
|
|
// called `key` which is the equivalent of `userKey` which is what we
|
|
// check here for identifying the secret.
|
|
func isMigrationSecret(secrets map[string]string) bool {
|
|
// the below 'nil' check is an extra measure as the request validators like
|
|
// ValidateNodeStageVolumeRequest() already does the nil check, however considering
|
|
// this function can be called independently with a map of secret values
|
|
// it is good to have this check in place, also it gives clear error about this
|
|
// was hit on migration request compared to general one.
|
|
return len(secrets) != 0 && secrets[migUserKey] != ""
|
|
}
|