diff --git a/deploy/image/irods_csi_driver_image.dockerfile b/deploy/image/irods_csi_driver_image.dockerfile index a00e5c9..29f42ce 100644 --- a/deploy/image/irods_csi_driver_image.dockerfile +++ b/deploy/image/irods_csi_driver_image.dockerfile @@ -16,7 +16,7 @@ ARG CSI_DRIVER_SRC_DIR="/go/src/github.com/cyverse/irods-csi-driver" ARG IRODS_FUSE_DIR="/opt/irodsfs" ARG FUSE_NFS_DIR="/opt/fuse-nfs" ARG DEBIAN_FRONTEND=noninteractive -ARG IRODSFS_VER=v0.9.7 +ARG IRODSFS_VER=v0.10.2 ### Install dumb-init ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 \ diff --git a/deploy/image/irods_csi_driver_pool_image.dockerfile b/deploy/image/irods_csi_driver_pool_image.dockerfile index 0b83e98..98427fd 100644 --- a/deploy/image/irods_csi_driver_pool_image.dockerfile +++ b/deploy/image/irods_csi_driver_pool_image.dockerfile @@ -12,7 +12,7 @@ LABEL version="0.1" LABEL description="iRODS CSI Driver Pool Image" ARG DEBIAN_FRONTEND=noninteractive -ARG IRODSFS_POOL_VER=v0.6.26 +ARG IRODSFS_POOL_VER=v0.8.2 ### Install dumb-init ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 \ diff --git a/go.mod b/go.mod index 91f3ca3..40d9e73 100644 --- a/go.mod +++ b/go.mod @@ -2,12 +2,11 @@ module github.com/cyverse/irods-csi-driver go 1.21 -toolchain go1.21.5 - require ( github.com/container-storage-interface/spec v1.2.0 - github.com/cyverse/go-irodsclient v0.14.11 - github.com/cyverse/irodsfs-common v0.0.0-20240716191935-69006aedefdc + github.com/cyverse/go-irodsclient v0.15.6 + github.com/cyverse/irodsfs v0.10.2 + github.com/cyverse/irodsfs-common v0.0.0-20241025231356-4e650ac30aa6 github.com/pkg/xattr v0.4.9 github.com/prometheus/client_golang v1.13.0 github.com/rs/xid v1.3.0 @@ -25,6 +24,7 @@ require ( github.com/go-logr/logr v0.2.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/kelseyhightower/envconfig v1.4.0 // indirect github.com/kr/pretty v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -38,4 +38,5 @@ require ( golang.org/x/text v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index da87d7b..0e457ca 100644 --- a/go.sum +++ b/go.sum @@ -54,10 +54,12 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/container-storage-interface/spec v1.2.0 h1:bD9KIVgaVKKkQ/UbVUY9kCaH/CJbhNxe0eeB4JeJV2s= github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= -github.com/cyverse/go-irodsclient v0.14.11 h1:pPAo3UOncFon/TsIjAAUT+rDTfJ6NRux6tr1fmz7Fx0= -github.com/cyverse/go-irodsclient v0.14.11/go.mod h1:eBXha3cwfrM0p1ijYVqsrLJQHpRwTfpA4c5dKCQsQFc= -github.com/cyverse/irodsfs-common v0.0.0-20240716191935-69006aedefdc h1:o75XZvO0FQx2Vy8FwUmL9XffqqLoittvyXGv/5b4UEw= -github.com/cyverse/irodsfs-common v0.0.0-20240716191935-69006aedefdc/go.mod h1:cEywU4X9x1v1UfZC2o7T9Du7yQyJaxrkLlHrn7Wxxgs= +github.com/cyverse/go-irodsclient v0.15.6 h1:9pbyNCKqEGWEcnkFMK3ygelc1rNIGMrFh2PWbRFDxPI= +github.com/cyverse/go-irodsclient v0.15.6/go.mod h1:NN+PxHfLDUmsqfqSY84JfmqXS4EYiuiNW6ti6oPGCgk= +github.com/cyverse/irodsfs v0.10.2 h1:iQJiT3toGGbTF/yCV4q2Rbsb6/OnSF5ChsjX+OERHYs= +github.com/cyverse/irodsfs v0.10.2/go.mod h1:RetQ3DaEwuMr0p3dGZ2q/Qjui4XjNSzGb8T3Jr+o6ZM= +github.com/cyverse/irodsfs-common v0.0.0-20241025231356-4e650ac30aa6 h1:/T/JcR11r1dGzExKjrFwLe68zWdtp+HMlib+4cbz3ck= +github.com/cyverse/irodsfs-common v0.0.0-20241025231356-4e650ac30aa6/go.mod h1:+eZ+/tYP54StOJCchIBUKEmzBip0n32CYOzYr+KXhPM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -150,6 +152,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/pkg/client/common/common.go b/pkg/client/common/common.go index 43b6607..8dededc 100644 --- a/pkg/client/common/common.go +++ b/pkg/client/common/common.go @@ -5,6 +5,8 @@ import ( "path/filepath" "strconv" "strings" + + "github.com/cyverse/irods-csi-driver/pkg/common" ) // ClientType is a mount client type @@ -22,7 +24,7 @@ const ( // GetClientType returns iRODS Client value from param map func GetClientType(params map[string]string) ClientType { - return GetValidClientType(params["client"]) + return GetValidClientType(params[common.NormalizeConfigKey("client")]) } // IsValidClientType checks if given client string is valid @@ -55,14 +57,14 @@ func GetValidClientType(client string) ClientType { // GetConfigEnforceProxyAccess checks if proxy access is enforced via driver config func GetConfigEnforceProxyAccess(configs map[string]string) bool { - enforce := configs["enforceproxyaccess"] + enforce := configs[common.NormalizeConfigKey("enforce_proxy_access")] bEnforce, _ := strconv.ParseBool(enforce) return bEnforce } // GetConfigMountPathWhitelist returns a whitelist of collections that users can mount func GetConfigMountPathWhitelist(configs map[string]string) []string { - if whitelist, ok := configs["mountpathwhitelist"]; ok { + if whitelist, ok := configs[common.NormalizeConfigKey("mount_path_whitelist")]; ok { if len(whitelist) > 0 { whitelistItems := strings.Split(whitelist, ",") for idx := range whitelistItems { @@ -83,26 +85,26 @@ func GetConfigMountPathWhitelist(configs map[string]string) []string { // GetConfigDataRootPath returns a data root path func GetConfigDataRootPath(configs map[string]string, volID string) string { irodsClientType := GetClientType(configs) - return filepath.Join(configs["storagepath"], string(irodsClientType), volID) + return filepath.Join(common.NormalizeConfigKey(configs["storage_path"]), string(irodsClientType), volID) } // GetConfigOverlayFSLowerPath returns a lower path for overlayfs func GetConfigOverlayFSLowerPath(configs map[string]string, volID string) string { irodsClientType := GetClientType(configs) name := fmt.Sprintf("%s-overlayfs-lower", volID) - return filepath.Join(configs["storagepath"], string(irodsClientType), name) + return filepath.Join(common.NormalizeConfigKey(configs["storage_path"]), string(irodsClientType), name) } // GetConfigOverlayFSUpperPath returns a upper path for overlayfs func GetConfigOverlayFSUpperPath(configs map[string]string, volID string) string { irodsClientType := GetClientType(configs) name := fmt.Sprintf("%s-overlayfs-upper", volID) - return filepath.Join(configs["storagepath"], string(irodsClientType), name) + return filepath.Join(common.NormalizeConfigKey(configs["storage_path"]), string(irodsClientType), name) } // GetConfigOverlayFSWorkDirPath returns a work dir path for overlayfs func GetConfigOverlayFSWorkDirPath(configs map[string]string, volID string) string { irodsClientType := GetClientType(configs) name := fmt.Sprintf("%s-overlayfs-workdir", volID) - return filepath.Join(configs["storagepath"], string(irodsClientType), name) + return filepath.Join(common.NormalizeConfigKey(configs["storage_path"]), string(irodsClientType), name) } diff --git a/pkg/client/irods/connection_info.go b/pkg/client/irods/connection_info.go index 1c90b02..5691bad 100644 --- a/pkg/client/irods/connection_info.go +++ b/pkg/client/irods/connection_info.go @@ -2,13 +2,13 @@ package irods import ( "encoding/json" - "net/url" "path/filepath" "strconv" - irodsclient_types "github.com/cyverse/go-irodsclient/irods/types" + irodsclient_fs "github.com/cyverse/go-irodsclient/fs" client_common "github.com/cyverse/irods-csi-driver/pkg/client/common" irodsfs_common_vpath "github.com/cyverse/irodsfs-common/vpath" + irodsfs_commons "github.com/cyverse/irodsfs/commons" "github.com/cyverse/irods-csi-driver/pkg/common" "github.com/cyverse/irods-csi-driver/pkg/mounter" @@ -17,82 +17,116 @@ import ( ) const ( - irodsfsAnonymousUser string = "anonymous" + irodsfsAnonymousUser string = "anonymous" + irodsfsDefaultMountTimeout int = 60 ) // IRODSFSConnectionInfo class type IRODSFSConnectionInfo struct { - Hostname string - Port int - Zone string - User string - Password string - ClientUser string // if this field has a value, user and password fields have proxy user info - Resource string - PoolEndpoint string - MonitorURL string - PathMappings []irodsfs_common_vpath.VPathMapping - NoPermissionCheck bool - NoSetXattr bool - UID int - GID int - SystemUser string - - AuthScheme string - ClientServerNegotiation bool - CSNegotiationPolicy string - CACertificateFile string - CACertificatePath string - EncryptionKeySize int - EncryptionAlgorithm string - SaltSize int - HashRounds int + irodsfs_commons.Config MountTimeout int - Profile bool - ProfilePort int OverlayFS bool OverlayFSDriver OverlayFSDriverType } +// NewIRODSFSConnectionInfo creates a new IRODSFSConnectionInfo with default +func NewIRODSFSConnectionInfo() IRODSFSConnectionInfo { + connInfo := IRODSFSConnectionInfo{} + connInfo.Config = *irodsfs_commons.NewDefaultConfig() + + connInfo.MountTimeout = irodsfsDefaultMountTimeout + connInfo.OverlayFS = false + connInfo.OverlayFSDriver = OverlayDriverType + + return connInfo +} + // SetAnonymousUser sets anonymous user func (connInfo *IRODSFSConnectionInfo) SetAnonymousUser() { - connInfo.User = irodsfsAnonymousUser + connInfo.Username = irodsfsAnonymousUser } // IsAnonymousUser checks if the user is anonymous func (connInfo *IRODSFSConnectionInfo) IsAnonymousUser() bool { - return connInfo.User == irodsfsAnonymousUser + return connInfo.Username == irodsfsAnonymousUser } -// IsAnonymousClientUser checks if the client user is anonymous -func (connInfo *IRODSFSConnectionInfo) IsAnonymousClientUser() bool { - return connInfo.ClientUser == irodsfsAnonymousUser +// IsValidClientUser checks if the client user is valid +func (connInfo *IRODSFSConnectionInfo) IsValidClientUser() bool { + if len(connInfo.Username) > 0 && (connInfo.Username != irodsfsAnonymousUser) { + // proxy is on + return connInfo.ClientUsername == irodsfsAnonymousUser + } + return false } func getConnectionInfoFromMap(params map[string]string, connInfo *IRODSFSConnectionInfo) error { for k, v := range params { - switch k { - case "user": - connInfo.User = v - case "password": - connInfo.Password = v - case "clientuser": - // for proxy - connInfo.ClientUser = v - case "host": - connInfo.Hostname = v - case "port": + switch common.NormalizeConfigKey(k) { + case common.NormalizeConfigKey("irods_authentication_scheme"), common.NormalizeConfigKey("authentication_scheme"), common.NormalizeConfigKey("auth_scheme"): + connInfo.AuthenticationScheme = v + case common.NormalizeConfigKey("irods_authentication_file"): + connInfo.AuthenticationFile = v + case common.NormalizeConfigKey("irods_client_server_negotiation"), common.NormalizeConfigKey("client_server_negotiation"): + connInfo.ClientServerNegotiation = v + case common.NormalizeConfigKey("irods_client_server_policy"), common.NormalizeConfigKey("client_server_negotiation_policy"), common.NormalizeConfigKey("cs_negotiation_policy"): + connInfo.ClientServerPolicy = v + case common.NormalizeConfigKey("irods_host"), common.NormalizeConfigKey("hostname"), common.NormalizeConfigKey("host"): + connInfo.Host = v + case common.NormalizeConfigKey("irods_port"), common.NormalizeConfigKey("port"): p, err := strconv.Atoi(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid port number - %v", k, err) } connInfo.Port = p - case "zone": - connInfo.Zone = v - case "resource": - connInfo.Resource = v - case "path": + case common.NormalizeConfigKey("irods_zone_name"), common.NormalizeConfigKey("zone_name"), common.NormalizeConfigKey("zone"): + connInfo.ZoneName = v + case common.NormalizeConfigKey("irods_client_zone_name"), common.NormalizeConfigKey("client_zone_name"), common.NormalizeConfigKey("client_zone"): + connInfo.ClientZoneName = v + case common.NormalizeConfigKey("irods_user_name"), common.NormalizeConfigKey("user"), common.NormalizeConfigKey("user_name"): + connInfo.Username = v + case common.NormalizeConfigKey("irods_client_user_name"), common.NormalizeConfigKey("client_user"), common.NormalizeConfigKey("client_user_name"): + connInfo.ClientUsername = v + case common.NormalizeConfigKey("irods_default_resource"), common.NormalizeConfigKey("default_resource"), common.NormalizeConfigKey("resource"): + connInfo.DefaultResource = v + case common.NormalizeConfigKey("irods_encryption_algorithm"), common.NormalizeConfigKey("encryption_algorithm"): + connInfo.EncryptionAlgorithm = v + case common.NormalizeConfigKey("irods_encryption_key_size"), common.NormalizeConfigKey("encryption_key_size"): + s, err := strconv.Atoi(v) + if err != nil { + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid number - %v", k, err) + } + connInfo.EncryptionKeySize = s + case common.NormalizeConfigKey("irods_encryption_salt_size"), common.NormalizeConfigKey("encryption_salt_size"): + s, err := strconv.Atoi(v) + if err != nil { + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid number - %v", k, err) + } + connInfo.EncryptionSaltSize = s + case common.NormalizeConfigKey("irods_encryption_num_hash_rounds"), common.NormalizeConfigKey("encryption_num_hash_rounds"), common.NormalizeConfigKey("hash_rounds"): + s, err := strconv.Atoi(v) + if err != nil { + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid number - %v", k, err) + } + connInfo.EncryptionNumHashRounds = s + case common.NormalizeConfigKey("irods_ssl_ca_certificate_file"), common.NormalizeConfigKey("ca_certificate_file"): + connInfo.SSLCACertificateFile = v + case common.NormalizeConfigKey("irods_ssl_ca_certificate_path"), common.NormalizeConfigKey("ca_certificate_path"): + connInfo.SSLCACertificatePath = v + case common.NormalizeConfigKey("irods_ssl_verify_server"), common.NormalizeConfigKey("verify_server"): + connInfo.SSLVerifyServer = v + case common.NormalizeConfigKey("irods_user_password"), common.NormalizeConfigKey("user_password"): + connInfo.Password = v + case common.NormalizeConfigKey("irods_ssl_server_name"), common.NormalizeConfigKey("ssl_server_name"): + connInfo.SSLServerName = v + case common.NormalizeConfigKey("path_mappings"), common.NormalizeConfigKey("path_mapping_json"): + connInfo.PathMappings = []irodsfs_common_vpath.VPathMapping{} + err := json.Unmarshal([]byte(v), &connInfo.PathMappings) + if err != nil { + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid json string - %v", k, err) + } + case common.NormalizeConfigKey("path"): if !filepath.IsAbs(v) { return status.Errorf(codes.InvalidArgument, "Argument %q must be an absolute path", k) } @@ -105,102 +139,96 @@ func getConnectionInfoFromMap(params map[string]string, connInfo *IRODSFSConnect ResourceType: "dir", }, } - case "poolendpoint": - connInfo.PoolEndpoint = v - case "profile": - pb, err := strconv.ParseBool(v) + case common.NormalizeConfigKey("read_ahead_max"): + ram, err := strconv.Atoi(v) if err != nil { - return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid boolean string - %v", k, err) - } - connInfo.Profile = pb - case "profileport": - p, err := strconv.Atoi(v) - if err != nil { - return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid port number - %v", k, err) - } - connInfo.ProfilePort = p - case "overlayfs": - ob, err := strconv.ParseBool(v) - if err != nil { - return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid boolean string - %v", k, err) - } - connInfo.OverlayFS = ob - case "overlayfsdriver": - connInfo.OverlayFSDriver = GetOverlayFSDriverType(v) - case "monitorurl": - connInfo.MonitorURL = v - case "pathmappingjson": - connInfo.PathMappings = []irodsfs_common_vpath.VPathMapping{} - err := json.Unmarshal([]byte(v), &connInfo.PathMappings) - if err != nil { - return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid json string - %v", k, err) + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid number - %v", k, err) } - case "nopermissioncheck": + connInfo.ReadAheadMax = ram + case common.NormalizeConfigKey("no_permission_check"): npc, err := strconv.ParseBool(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid boolean string - %v", k, err) } connInfo.NoPermissionCheck = npc - case "nosetxattr": + case common.NormalizeConfigKey("no_set_xattr"): nsx, err := strconv.ParseBool(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid boolean string - %v", k, err) } connInfo.NoSetXattr = nsx - case "uid": + case common.NormalizeConfigKey("uid"), common.NormalizeConfigKey("user_id"): u, err := strconv.Atoi(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid uid number - %v", k, err) } connInfo.UID = u - case "gid": + case common.NormalizeConfigKey("gid"), common.NormalizeConfigKey("group_id"): g, err := strconv.Atoi(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid gid number - %v", k, err) } connInfo.GID = g - case "systemuser": + case common.NormalizeConfigKey("system_user"): connInfo.SystemUser = v - case "mounttimeout": - t, err := strconv.Atoi(v) + case common.NormalizeConfigKey("metadata_connection"), common.NormalizeConfigKey("metadata_connection_json"): + connInfo.MetadataConnection = irodsclient_fs.NewDefaultMetadataConnectionConfig() + err := json.Unmarshal([]byte(v), &connInfo.MetadataConnection) if err != nil { - return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid number - %v", k, err) + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid json string - %v", k, err) } - connInfo.MountTimeout = t - case "authscheme": - connInfo.AuthScheme = v - case "clientservernegotiation": - csn, err := strconv.ParseBool(v) + case common.NormalizeConfigKey("io_connection"), common.NormalizeConfigKey("io_connection_json"): + connInfo.IOConnection = irodsclient_fs.NewDefaultIOConnectionConfig() + err := json.Unmarshal([]byte(v), &connInfo.IOConnection) + if err != nil { + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid json string - %v", k, err) + } + case common.NormalizeConfigKey("cache"), common.NormalizeConfigKey("cache_json"): + connInfo.Cache = irodsclient_fs.NewDefaultCacheConfig() + err := json.Unmarshal([]byte(v), &connInfo.Cache) + if err != nil { + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid json string - %v", k, err) + } + case common.NormalizeConfigKey("pool_endpoint"): + connInfo.PoolEndpoint = v + case common.NormalizeConfigKey("enable_profile"), common.NormalizeConfigKey("profile"): + pb, err := strconv.ParseBool(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid boolean string - %v", k, err) } - connInfo.ClientServerNegotiation = csn - case "csnegotiationpolicy": - connInfo.CSNegotiationPolicy = v - case "cacertificatefile": - connInfo.CACertificateFile = v - case "cacertificatepath": - connInfo.CACertificatePath = v - case "encryptionkeysize": - s, err := strconv.Atoi(v) + connInfo.Profile = pb + case common.NormalizeConfigKey("profile_service_port"), common.NormalizeConfigKey("profile_port"): + p, err := strconv.Atoi(v) if err != nil { - return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid number - %v", k, err) + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid port number - %v", k, err) } - connInfo.EncryptionKeySize = s - case "encryptionalgorithm": - connInfo.EncryptionAlgorithm = v - case "saltsize": - s, err := strconv.Atoi(v) + connInfo.ProfileServicePort = p + case common.NormalizeConfigKey("enable_overlayfs"), common.NormalizeConfigKey("overlayfs"): + ob, err := strconv.ParseBool(v) if err != nil { - return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid number - %v", k, err) + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid boolean string - %v", k, err) } - connInfo.SaltSize = s - case "hashrounds": - s, err := strconv.Atoi(v) + connInfo.OverlayFS = ob + case common.NormalizeConfigKey("overlayfs_driver"): + connInfo.OverlayFSDriver = GetOverlayFSDriverType(v) + case common.NormalizeConfigKey("mount_timeout"): + t, err := strconv.Atoi(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid number - %v", k, err) } - connInfo.HashRounds = s + connInfo.MountTimeout = t + case common.NormalizeConfigKey("debug"): + debug, err := strconv.ParseBool(v) + if err != nil { + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid boolean string - %v", k, err) + } + connInfo.Debug = debug + case common.NormalizeConfigKey("read_only"): + ro, err := strconv.ParseBool(v) + if err != nil { + return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid boolean string - %v", k, err) + } + connInfo.Readonly = ro default: // ignore } @@ -211,30 +239,36 @@ func getConnectionInfoFromMap(params map[string]string, connInfo *IRODSFSConnect // GetConnectionInfo extracts IRODSFSConnectionInfo value from param map func GetConnectionInfo(configs map[string]string) (*IRODSFSConnectionInfo, error) { - connInfo := IRODSFSConnectionInfo{} - connInfo.UID = -1 - connInfo.GID = -1 + connInfo := NewIRODSFSConnectionInfo() err := getConnectionInfoFromMap(configs, &connInfo) if err != nil { return nil, err } + // correct + connInfo.FixAuthConfiguration() + err = connInfo.FixSystemSystemUserConfiguration() + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Argument systemuser/uid/gid are not valid - %q", err.Error()) + } + + // validate if len(connInfo.OverlayFSDriver) == 0 { connInfo.OverlayFSDriver = OverlayDriverType } - if len(connInfo.User) == 0 { + if len(connInfo.Username) == 0 && len(connInfo.ClientUsername) == 0 { connInfo.SetAnonymousUser() } // password can be empty for anonymous access if len(connInfo.Password) == 0 && !connInfo.IsAnonymousUser() { - return nil, status.Error(codes.InvalidArgument, "Argument password is empty") + return nil, status.Error(codes.InvalidArgument, "Argument password must be given") } - if connInfo.IsAnonymousClientUser() { - return nil, status.Error(codes.InvalidArgument, "Argument clientUser must be a non-anonymous user") + if connInfo.IsValidClientUser() { + return nil, status.Error(codes.InvalidArgument, "Argument client username must be a non-anonymous user") } if client_common.GetConfigEnforceProxyAccess(configs) { @@ -243,73 +277,54 @@ func GetConnectionInfo(configs map[string]string) (*IRODSFSConnectionInfo, error return nil, status.Error(codes.InvalidArgument, "Argument user must be a non-anonymous user") } - if len(connInfo.ClientUser) == 0 { - return nil, status.Error(codes.InvalidArgument, "Argument clientUser must be given") + if len(connInfo.ClientUsername) == 0 { + return nil, status.Error(codes.InvalidArgument, "Argument client username must be given") } - if connInfo.User == connInfo.ClientUser { - return nil, status.Errorf(codes.InvalidArgument, "Argument clientUser cannot be the same as user - user %q, clientUser %q", connInfo.User, connInfo.ClientUser) - } - } else { - if len(connInfo.ClientUser) == 0 { - connInfo.ClientUser = connInfo.User + if connInfo.Username == connInfo.ClientUsername { + return nil, status.Errorf(codes.InvalidArgument, "Argument client username cannot be the same as user - user %q, client user %q", connInfo.Username, connInfo.ClientUsername) } } - if len(connInfo.Hostname) == 0 { - return nil, status.Error(codes.InvalidArgument, "Argument host is empty") + if len(connInfo.Host) == 0 { + return nil, status.Error(codes.InvalidArgument, "Argument host must be given") } - if len(connInfo.Zone) == 0 { - return nil, status.Error(codes.InvalidArgument, "Argument zone is empty") + if connInfo.Port <= 0 { + return nil, status.Error(codes.InvalidArgument, "Argument port must be given") } - // port is optional - if connInfo.Port <= 0 { - // default - connInfo.Port = 1247 + if len(connInfo.Username) == 0 && len(connInfo.ClientUsername) == 0 { + return nil, status.Error(codes.InvalidArgument, "Argument username or client username must be given") } - // profile port is optional - if connInfo.ProfilePort <= 0 { - // default - connInfo.ProfilePort = 11021 + if len(connInfo.ZoneName) == 0 && len(connInfo.ClientZoneName) == 0 { + return nil, status.Error(codes.InvalidArgument, "Argument zone name or client zone name must be given") } - if connInfo.ClientServerNegotiation { - if len(connInfo.CSNegotiationPolicy) == 0 { - return nil, status.Errorf(codes.InvalidArgument, "CS negotiation policy must be given") - } + if connInfo.Profile && connInfo.ProfileServicePort <= 0 { + return nil, status.Error(codes.InvalidArgument, "Argument profile service port must be given") } - authScheme := irodsclient_types.GetAuthScheme(connInfo.AuthScheme) - if authScheme == irodsclient_types.AuthSchemeUnknown { - connInfo.AuthScheme = string(irodsclient_types.AuthSchemeNative) - } else if authScheme != irodsclient_types.AuthSchemePAM && authScheme != irodsclient_types.AuthSchemeNative { - return nil, status.Errorf(codes.InvalidArgument, "Invalid auth scheme %q", connInfo.AuthScheme) + if len(connInfo.PathMappings) == 0 { + return nil, status.Error(codes.InvalidArgument, "Argument path or path mappings must be given") } - csNegotiation, err := irodsclient_types.GetCSNegotiationRequire(connInfo.CSNegotiationPolicy) + err = irodsfs_common_vpath.ValidateVPathMappings(connInfo.PathMappings) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "Invalid cs negotiation policy %q", connInfo.CSNegotiationPolicy) + return nil, status.Errorf(codes.InvalidArgument, "Path mapping is invalid - %q", err.Error()) } - if authScheme == irodsclient_types.AuthSchemePAM || csNegotiation == irodsclient_types.CSNegotiationRequireSSL || connInfo.ClientServerNegotiation { - if connInfo.EncryptionKeySize <= 0 { - return nil, status.Errorf(codes.InvalidArgument, "SSL encryption key size must be given") - } - - if len(connInfo.EncryptionAlgorithm) == 0 { - return nil, status.Errorf(codes.InvalidArgument, "SSL encryption algorithm must be given") - } + if connInfo.UID < 0 { + return nil, status.Error(codes.InvalidArgument, "Argument uid must not be a negative value") + } - if connInfo.SaltSize <= 0 { - return nil, status.Errorf(codes.InvalidArgument, "SSL salt size must be given") - } + if connInfo.GID < 0 { + return nil, status.Error(codes.InvalidArgument, "Argument gid must not be a negative value") + } - if connInfo.HashRounds <= 0 { - return nil, status.Errorf(codes.InvalidArgument, "SSL hash rounds must be given") - } + if connInfo.ReadAheadMax < 0 { + return nil, status.Error(codes.InvalidArgument, "Argument read_ahead must not be a negative value") } if len(connInfo.PoolEndpoint) > 0 { @@ -319,27 +334,15 @@ func GetConnectionInfo(configs map[string]string) (*IRODSFSConnectionInfo, error } } - if len(connInfo.MonitorURL) > 0 { - // check - _, err := url.ParseRequestURI(connInfo.MonitorURL) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "Invalid monitor URL %q", connInfo.MonitorURL) - } - } - - if len(connInfo.PathMappings) < 1 { - return nil, status.Error(codes.InvalidArgument, "Argument path and path_mappings are empty, one must be given") - } - whitelist := client_common.GetConfigMountPathWhitelist(configs) for _, mapping := range connInfo.PathMappings { if !mounter.IsMountPathAllowed(whitelist, mapping.IRODSPath) { - return nil, status.Errorf(codes.InvalidArgument, "Argument path %q is not allowed to mount", mapping.IRODSPath) + return nil, status.Errorf(codes.InvalidArgument, "Argument path %q is not allowed to mount by the whitelist", mapping.IRODSPath) } } if connInfo.MountTimeout <= 0 { - connInfo.MountTimeout = 300 + connInfo.MountTimeout = 60 } return &connInfo, nil diff --git a/pkg/client/irods/irodsfs_config.go b/pkg/client/irods/irodsfs_config.go deleted file mode 100644 index b4d2fbe..0000000 --- a/pkg/client/irods/irodsfs_config.go +++ /dev/null @@ -1,128 +0,0 @@ -package irods - -import ( - "time" - - irodsclient_types "github.com/cyverse/go-irodsclient/irods/types" - irodsfs_common_vpath "github.com/cyverse/irodsfs-common/vpath" -) - -// this structures must match to iRODS FUSE Lite Config -// https://github.com/cyverse/irodsfs/blob/main/commons/config.go#L80 - -const ( - PortDefault int = 1247 - ReadAheadMaxDefault int = 1024 * 64 // 64KB - ConnectionMaxDefault int = 10 - OperationTimeoutDefault time.Duration = 5 * time.Minute - ConnectionLifespanDefault time.Duration = 1 * time.Hour - ConnectionIdleTimeoutDefault time.Duration = 5 * time.Minute - MetadataCacheTimeoutDefault time.Duration = 5 * time.Minute - MetadataCacheCleanupTimeDefault time.Duration = 5 * time.Minute - - AuthSchemeDefault string = string(irodsclient_types.AuthSchemeNative) - CSNegotiationDefault string = string(irodsclient_types.CSNegotiationRequireTCP) - EncryptionKeySizeDefault int = 32 - EncryptionAlgorithmDefault string = "AES-256-CBC" - SaltSizeDefault int = 8 - HashRoundsDefault int = 16 -) - -type IRODSFSConfig struct { - Host string `yaml:"host"` - Port int `yaml:"port"` - ProxyUser string `yaml:"proxy_user"` - ClientUser string `yaml:"client_user"` - Zone string `yaml:"zone"` - Password string `yaml:"password"` - Resource string `yaml:"resource,omitempty"` - PathMappings []irodsfs_common_vpath.VPathMapping `yaml:"path_mappings"` - NoPermissionCheck bool `yaml:"no_permission_check"` - NoSetXattr bool `yaml:"no_set_xattr"` - UID int `yaml:"uid"` - GID int `yaml:"gid"` - SystemUser string `yaml:"system_user"` - - DataRootPath string `yaml:"data_root_path,omitempty"` - - LogPath string `yaml:"log_path,omitempty"` - - PoolEndpoint string `yaml:"pool_endpoint,omitempty"` - - AuthScheme string `yaml:"auth_scheme"` - ClientServerNegotiation bool `yaml:"cs_negotiation"` - CSNegotiationPolicy string `yaml:"cs_negotiation_policy"` - CACertificateFile string `yaml:"ssl_ca_cert_file"` - CACertificatePath string `yaml:"ssl_ca_sert_path"` - EncryptionKeySize int `yaml:"ssl_encryption_key_size"` - EncryptionAlgorithm string `yaml:"ssl_encryption_algorithm"` - SaltSize int `yaml:"ssl_encryption_salt_size"` - HashRounds int `yaml:"ssl_encryption_hash_rounds"` - - ReadAheadMax int `yaml:"read_ahead_max"` - OperationTimeout time.Duration `yaml:"operation_timeout"` - ConnectionLifespan time.Duration `yaml:"connection_lifespan"` - ConnectionIdleTimeout time.Duration `yaml:"connection_idle_timeout"` - ConnectionMax int `yaml:"connection_max"` - MetadataCacheTimeout time.Duration `yaml:"metadata_cache_timeout"` - MetadataCacheCleanupTime time.Duration `yaml:"metadata_cache_cleanup_time"` - - MonitorURL string `yaml:"monitor_url,omitempty"` - - Profile bool `yaml:"profile,omitempty"` - ProfileServicePort int `yaml:"profile_service_port,omitempty"` - - AllowOther bool `yaml:"allow_other,omitempty"` - InstanceID string `yaml:"instanceid,omitempty"` -} - -// NewDefaultIRODSFSConfig creates default IRODSFSConfig -func NewDefaultIRODSFSConfig() *IRODSFSConfig { - return &IRODSFSConfig{ - Host: "", - Port: PortDefault, - ProxyUser: "", - ClientUser: "", - Zone: "", - Password: "", - Resource: "", - PathMappings: []irodsfs_common_vpath.VPathMapping{}, - NoPermissionCheck: false, - NoSetXattr: false, - UID: -1, - GID: -1, - SystemUser: "", - - DataRootPath: "/storage", - - LogPath: "", // use default - - PoolEndpoint: "", - - AuthScheme: AuthSchemeDefault, - ClientServerNegotiation: false, - CSNegotiationPolicy: CSNegotiationDefault, - CACertificateFile: "", - CACertificatePath: "", - EncryptionKeySize: EncryptionKeySizeDefault, - EncryptionAlgorithm: EncryptionAlgorithmDefault, - SaltSize: SaltSizeDefault, - HashRounds: HashRoundsDefault, - - ReadAheadMax: ReadAheadMaxDefault, - OperationTimeout: OperationTimeoutDefault, - ConnectionLifespan: ConnectionLifespanDefault, - ConnectionIdleTimeout: ConnectionIdleTimeoutDefault, - ConnectionMax: ConnectionMaxDefault, - MetadataCacheTimeout: MetadataCacheTimeoutDefault, - MetadataCacheCleanupTime: MetadataCacheCleanupTimeDefault, - - MonitorURL: "", - - Profile: false, - ProfileServicePort: 0, - - AllowOther: true, - InstanceID: "", - } -} diff --git a/pkg/client/irods/mount.go b/pkg/client/irods/mount.go index 4082589..250d64d 100644 --- a/pkg/client/irods/mount.go +++ b/pkg/client/irods/mount.go @@ -34,8 +34,6 @@ func Mount(mounter mounter.Mounter, volID string, configs map[string]string, mnt mountSensitiveOptions := []string{} stdinArgs := []string{} - irodsFsConfig := NewDefaultIRODSFSConfig() - // create irodsfs dataroot dataRootPath := client_common.GetConfigDataRootPath(configs, volID) err = makeIrodsFuseLiteDataRootPath(dataRootPath) @@ -43,36 +41,10 @@ func Mount(mounter mounter.Mounter, volID string, configs map[string]string, mnt return status.Error(codes.Internal, err.Error()) } - irodsFsConfig.DataRootPath = dataRootPath - irodsFsConfig.Host = irodsConnectionInfo.Hostname - irodsFsConfig.Port = irodsConnectionInfo.Port - irodsFsConfig.ProxyUser = irodsConnectionInfo.User - irodsFsConfig.ClientUser = irodsConnectionInfo.ClientUser - irodsFsConfig.Zone = irodsConnectionInfo.Zone - irodsFsConfig.Password = irodsConnectionInfo.Password - irodsFsConfig.Resource = irodsConnectionInfo.Resource - irodsFsConfig.AuthScheme = irodsConnectionInfo.AuthScheme - irodsFsConfig.ClientServerNegotiation = irodsConnectionInfo.ClientServerNegotiation - irodsFsConfig.CSNegotiationPolicy = irodsConnectionInfo.CSNegotiationPolicy - irodsFsConfig.CACertificateFile = irodsConnectionInfo.CACertificateFile - irodsFsConfig.CACertificatePath = irodsConnectionInfo.CACertificatePath - irodsFsConfig.EncryptionKeySize = irodsConnectionInfo.EncryptionKeySize - irodsFsConfig.EncryptionAlgorithm = irodsConnectionInfo.EncryptionAlgorithm - irodsFsConfig.SaltSize = irodsConnectionInfo.SaltSize - irodsFsConfig.HashRounds = irodsConnectionInfo.HashRounds - irodsFsConfig.MonitorURL = irodsConnectionInfo.MonitorURL - irodsFsConfig.PathMappings = irodsConnectionInfo.PathMappings - irodsFsConfig.NoPermissionCheck = irodsConnectionInfo.NoPermissionCheck - irodsFsConfig.NoSetXattr = irodsConnectionInfo.NoSetXattr - irodsFsConfig.UID = irodsConnectionInfo.UID - irodsFsConfig.GID = irodsConnectionInfo.GID - irodsFsConfig.SystemUser = irodsConnectionInfo.SystemUser - irodsFsConfig.PoolEndpoint = irodsConnectionInfo.PoolEndpoint - irodsFsConfig.Profile = irodsConnectionInfo.Profile - irodsFsConfig.ProfileServicePort = irodsConnectionInfo.ProfilePort - irodsFsConfig.InstanceID = volID - - irodsFsConfigBytes, err := yaml.Marshal(irodsFsConfig) + irodsConnectionInfo.DataRootPath = dataRootPath + irodsConnectionInfo.InstanceID = volID + + irodsFsConfigBytes, err := yaml.Marshal(irodsConnectionInfo.Config) if err != nil { return status.Errorf(codes.Internal, "Could not serialize configuration: %v", err) } diff --git a/pkg/client/irods/operations.go b/pkg/client/irods/operations.go index ea4cdd4..fd5ce85 100644 --- a/pkg/client/irods/operations.go +++ b/pkg/client/irods/operations.go @@ -13,54 +13,18 @@ const ( ) // GetIRODSAccount creates a new account -func GetIRODSAccount(conn *IRODSFSConnectionInfo) (*irodsclient_types.IRODSAccount, error) { - authScheme := irodsclient_types.GetAuthScheme(conn.AuthScheme) - if authScheme == irodsclient_types.AuthSchemeUnknown { - authScheme = irodsclient_types.AuthSchemeNative - } - - csNegotiation, err := irodsclient_types.GetCSNegotiationRequire(conn.CSNegotiationPolicy) - if err != nil { - return nil, err - } - - account, err := irodsclient_types.CreateIRODSProxyAccount(conn.Hostname, conn.Port, - conn.ClientUser, conn.Zone, conn.User, conn.Zone, - authScheme, conn.Password, conn.Resource) - if err != nil { - return nil, err - } - - // optional for ssl, - sslConfig, err := irodsclient_types.CreateIRODSSSLConfig(conn.CACertificateFile, conn.CACertificatePath, conn.EncryptionKeySize, - conn.EncryptionAlgorithm, conn.SaltSize, conn.HashRounds) - if err != nil { - return nil, err - } - - if authScheme == irodsclient_types.AuthSchemePAM { - account.SetSSLConfiguration(sslConfig) - account.SetCSNegotiation(true, irodsclient_types.CSNegotiationRequireSSL) - } else if conn.ClientServerNegotiation { - account.SetSSLConfiguration(sslConfig) - account.SetCSNegotiation(conn.ClientServerNegotiation, csNegotiation) - } - - return account, nil +func GetIRODSAccount(conn *IRODSFSConnectionInfo) *irodsclient_types.IRODSAccount { + return conn.ToIRODSAccount() } // GetIRODSFilesystemConfig creates a new filesystem config func GetIRODSFilesystemConfig() *irodsclient_fs.FileSystemConfig { - return irodsclient_fs.NewFileSystemConfigWithDefault(applicationName) + return irodsclient_fs.NewFileSystemConfig(applicationName) } // GetIRODSFilesystem creates a new filesystem func GetIRODSFilesystem(conn *IRODSFSConnectionInfo) (*irodsclient_fs.FileSystem, error) { - account, err := GetIRODSAccount(conn) - if err != nil { - return nil, err - } - + account := GetIRODSAccount(conn) return irodsclient_fs.NewFileSystemWithDefault(account, applicationName) } @@ -90,14 +54,11 @@ func Rmdir(conn *IRODSFSConnectionInfo, path string) error { // TestConnection just test connection creation func TestConnection(conn *IRODSFSConnectionInfo) error { - account, err := GetIRODSAccount(conn) - if err != nil { - return err - } + account := GetIRODSAccount(conn) // test connect - irodsConn := irodsclient_connection.NewIRODSConnection(account, 5*time.Minute, applicationName) - err = irodsConn.Connect() + irodsConn := irodsclient_connection.NewIRODSConnection(account, time.Duration(conn.MountTimeout), applicationName) + err := irodsConn.Connect() if err != nil { return err } diff --git a/pkg/client/irods/overlay.go b/pkg/client/irods/overlay.go index b7b1bff..f423441 100644 --- a/pkg/client/irods/overlay.go +++ b/pkg/client/irods/overlay.go @@ -35,11 +35,7 @@ type OverlayFSSyncher struct { // NewOverlayFSSyncher creates a new OverlayFSSyncher func NewOverlayFSSyncher(volumeID string, irodsConnInfo *IRODSFSConnectionInfo, upper string) (*OverlayFSSyncher, error) { - irodsAccount, err := GetIRODSAccount(irodsConnInfo) - if err != nil { - return nil, xerrors.Errorf("failed to get irods account: %w", err) - } - + irodsAccount := GetIRODSAccount(irodsConnInfo) fsConfig := GetIRODSFilesystemConfig() fsClient, err := irodsfs_common_irods.NewIRODSFSClientDirect(irodsAccount, fsConfig) @@ -86,7 +82,7 @@ func (syncher *OverlayFSSyncher) GetUpperLayerPath() string { } func (syncher *OverlayFSSyncher) getStatusFilePath() string { - return fmt.Sprintf("/%s/home/%s/.%s%s", syncher.irodsConnectionInfo.Zone, syncher.irodsConnectionInfo.ClientUser, syncher.volumeID, syncStatusFileSuffix) + return fmt.Sprintf("/%s/home/%s/.%s%s", syncher.irodsConnectionInfo.ClientZoneName, syncher.irodsConnectionInfo.ClientUsername, syncher.volumeID, syncStatusFileSuffix) } // Sync syncs upper layer data to lower layer @@ -365,7 +361,7 @@ func (syncher *OverlayFSSyncher) syncFile(path string, statusFileHandle *irodscl klog.V(5).Infof("copying file %q", irodsPath) // upload the file - err = syncher.irodsFsClient.UploadFileParallelRedirectToResource(path, irodsPath, "", 0, false, false, false, nil) + _, err = syncher.irodsFsClient.UploadFileParallelRedirectToResource(path, irodsPath, "", 0, false, true, true, nil) if err != nil { if statusFileHandle != nil { msg := fmt.Sprintf("> Fail. failed to upload file %q. %s\n", irodsPath, err) diff --git a/pkg/client/nfs/connection_info.go b/pkg/client/nfs/connection_info.go index e9e4cfc..4ef5351 100644 --- a/pkg/client/nfs/connection_info.go +++ b/pkg/client/nfs/connection_info.go @@ -3,6 +3,7 @@ package nfs import ( "strconv" + "github.com/cyverse/irods-csi-driver/pkg/common" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -16,16 +17,16 @@ type NFSConnectionInfo struct { func getConnectionInfoFromMap(params map[string]string, connInfo *NFSConnectionInfo) error { for k, v := range params { - switch k { - case "host": + switch common.NormalizeConfigKey(k) { + case common.NormalizeConfigKey("host"), common.NormalizeConfigKey("hostname"): connInfo.Hostname = v - case "port": + case common.NormalizeConfigKey("port"): p, err := strconv.Atoi(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a valid port number - %v", k, err) } connInfo.Port = p - case "path": + case common.NormalizeConfigKey("path"): connInfo.Path = v default: // ignore diff --git a/pkg/client/webdav/connection_info.go b/pkg/client/webdav/connection_info.go index 555e57e..6ec5dc2 100644 --- a/pkg/client/webdav/connection_info.go +++ b/pkg/client/webdav/connection_info.go @@ -4,6 +4,7 @@ import ( "net/url" "strings" + "github.com/cyverse/irods-csi-driver/pkg/common" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -32,14 +33,14 @@ func (connInfo *WebDAVConnectionInfo) IsAnonymousUser() bool { func getConnectionInfoFromMap(params map[string]string, connInfo *WebDAVConnectionInfo) error { for k, v := range params { - switch k { - case "user": + switch common.NormalizeConfigKey(k) { + case common.NormalizeConfigKey("user"), common.NormalizeConfigKey("username"): connInfo.User = v - case "password": + case common.NormalizeConfigKey("password"), common.NormalizeConfigKey("user_password"): connInfo.Password = v - case "url": + case common.NormalizeConfigKey("url"): connInfo.URL = v - case "config": + case common.NormalizeConfigKey("config"): connInfo.Config = map[string]string{} configStrings := strings.Split(v, ",") for _, configString := range configStrings { diff --git a/pkg/common/config.go b/pkg/common/config.go index 644e601..3dd4110 100644 --- a/pkg/common/config.go +++ b/pkg/common/config.go @@ -38,6 +38,63 @@ type Config struct { StoragePath string // Path to storage dir (for saving volume info and etc) } +// NormalizeConfigKey normalizes config key +func NormalizeConfigKey(key string) string { + key = strings.ToLower(key) + + if key == "driver" { + return "client" + } + + return strings.ReplaceAll(key, "_", "") +} + +// MergeConfig merges configuration params +func MergeConfig(driverConfig *Config, driverSecrets map[string]string, volSecrets map[string]string, volParams map[string]string) map[string]string { + configs := make(map[string]string) + for k, v := range volSecrets { + if len(v) > 0 { + configs[NormalizeConfigKey(k)] = v + } + } + + for k, v := range volParams { + if len(v) > 0 { + configs[NormalizeConfigKey(k)] = v + } + } + + // driver secrets have higher priority + for k, v := range driverSecrets { + if len(v) > 0 { + configs[NormalizeConfigKey(k)] = v + } + } + + if len(driverConfig.PoolServiceEndpoint) > 0 { + configs[NormalizeConfigKey("pool_endpoint")] = driverConfig.PoolServiceEndpoint + } + + if len(driverConfig.StoragePath) > 0 { + configs[NormalizeConfigKey("storage_path")] = driverConfig.StoragePath + } + + return configs +} + +// RedactConfig redacts sensitive values +func RedactConfig(config map[string]string) map[string]string { + newConfigs := make(map[string]string) + for k, v := range config { + if k == NormalizeConfigKey("password") || k == NormalizeConfigKey("irods_user_password") { + newConfigs[k] = "**REDACTED**" + } else { + newConfigs[k] = v + } + } + return newConfigs +} + // ParseEndpoint parses endpoint string (TCP or UNIX) func ParseEndpoint(endpoint string) (string, string, error) { u, err := url.Parse(endpoint) diff --git a/pkg/driver/config.go b/pkg/driver/config.go index 09019ab..620894b 100644 --- a/pkg/driver/config.go +++ b/pkg/driver/config.go @@ -49,7 +49,7 @@ func readSecrets(secretPath string) (map[string]string, error) { func isDynamicVolumeProvisioningMode(volContext map[string]string) bool { for k, v := range volContext { - if k == "provisioningmode" { + if k == common.NormalizeConfigKey("provisioning_mode") { if strings.ToLower(v) == "dynamic" { return true } @@ -60,7 +60,7 @@ func isDynamicVolumeProvisioningMode(volContext map[string]string) bool { } func setDynamicVolumeProvisioningMode(volContext map[string]string) { - volContext["provisioningmode"] = "dynamic" + volContext[common.NormalizeConfigKey("provisioning_mode")] = "dynamic" } // ControllerConfig is a controller config struct @@ -73,8 +73,8 @@ type ControllerConfig struct { func getControllerConfigFromMap(params map[string]string, config *ControllerConfig) error { for k, v := range params { - switch k { - case "volumerootpath": + switch common.NormalizeConfigKey(k) { + case common.NormalizeConfigKey("volume_root_path"): if !filepath.IsAbs(v) { return status.Errorf(codes.InvalidArgument, "Argument %q must be an absolute path", k) } @@ -83,13 +83,13 @@ func getControllerConfigFromMap(params map[string]string, config *ControllerConf } else { config.VolumeRootPath = strings.TrimRight(v, "/") } - case "retaindata": + case common.NormalizeConfigKey("retain_data"): retain, err := strconv.ParseBool(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a boolean value - %v", k, err) } config.RetainData = retain - case "novolumedir": + case common.NormalizeConfigKey("no_volume_dir"): novolumedir, err := strconv.ParseBool(v) if err != nil { return status.Errorf(codes.InvalidArgument, "Argument %q must be a boolean value - %v", k, err) @@ -132,58 +132,3 @@ func makeControllerConfig(volName string, configs map[string]string) (*Controlle return &controllerConfig, nil } - -func normalizeConfigKey(key string) string { - key = strings.ToLower(key) - - if key == "driver" { - return "client" - } - - return strings.ReplaceAll(key, "_", "") -} - -// mergeConfig merges configuration params -func mergeConfig(driverConfig *common.Config, driverSecrets map[string]string, volSecrets map[string]string, volParams map[string]string) map[string]string { - configs := make(map[string]string) - for k, v := range volSecrets { - if len(v) > 0 { - configs[normalizeConfigKey(k)] = v - } - } - - for k, v := range volParams { - if len(v) > 0 { - configs[normalizeConfigKey(k)] = v - } - } - - // driver secrets have higher priority - for k, v := range driverSecrets { - if len(v) > 0 { - configs[normalizeConfigKey(k)] = v - } - } - - if len(driverConfig.PoolServiceEndpoint) > 0 { - configs[normalizeConfigKey("poolendpoint")] = driverConfig.PoolServiceEndpoint - } - - if len(driverConfig.StoragePath) > 0 { - configs[normalizeConfigKey("storagepath")] = driverConfig.StoragePath - } - - return configs -} - -func redactConfig(config map[string]string) map[string]string { - newConfigs := make(map[string]string) - for k, v := range config { - if k == "password" { - newConfigs[k] = "**REDACTED**" - } else { - newConfigs[k] = v - } - } - return newConfigs -} diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go index ad7e5fa..7450e60 100644 --- a/pkg/driver/controller.go +++ b/pkg/driver/controller.go @@ -28,6 +28,7 @@ import ( "github.com/container-storage-interface/spec/lib/go/csi" client_common "github.com/cyverse/irods-csi-driver/pkg/client/common" "github.com/cyverse/irods-csi-driver/pkg/client/irods" + "github.com/cyverse/irods-csi-driver/pkg/common" "github.com/cyverse/irods-csi-driver/pkg/metrics" "github.com/cyverse/irods-csi-driver/pkg/volumeinfo" "google.golang.org/grpc/codes" @@ -75,7 +76,7 @@ func (driver *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeReq // create a new volume // merge params - configs := mergeConfig(driver.config, driver.secrets, req.GetSecrets(), req.GetParameters()) + configs := common.MergeConfig(driver.config, driver.secrets, req.GetSecrets(), req.GetParameters()) /////////////////////////////////////////////////////////// // We only support irodsfs for dynamic volume provisioning @@ -94,7 +95,7 @@ func (driver *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeReq } // set path - configs["path"] = controllerConfig.VolumePath + configs[common.NormalizeConfigKey("path")] = controllerConfig.VolumePath // get iRODS connection info irodsConnectionInfo, err := irods.GetConnectionInfo(configs) @@ -125,7 +126,7 @@ func (driver *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeReq for k, v := range req.GetParameters() { volContext[k] = v } - volContext["path"] = controllerConfig.VolumePath + volContext[common.NormalizeConfigKey("path")] = controllerConfig.VolumePath // tell this volume is created via dynamic volume provisioning setDynamicVolumeProvisioningMode(volContext) diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go index 5f96f56..1c50cae 100644 --- a/pkg/driver/driver.go +++ b/pkg/driver/driver.go @@ -84,7 +84,7 @@ func NewDriver(conf *common.Config) (*Driver, error) { volumeEncryptKey := "irodscsidriver_volume_2ce02bee-74ea-4b18-a440-472d9771f778" for k, v := range driver.secrets { - if normalizeConfigKey(k) == "volumeencryptkey" { + if common.NormalizeConfigKey(k) == common.NormalizeConfigKey("volume_encrypt_key") { volumeEncryptKey = v break } diff --git a/pkg/driver/node.go b/pkg/driver/node.go index 8b15a6e..03844a0 100644 --- a/pkg/driver/node.go +++ b/pkg/driver/node.go @@ -33,6 +33,7 @@ import ( "github.com/container-storage-interface/spec/lib/go/csi" "github.com/cyverse/irods-csi-driver/pkg/client" client_common "github.com/cyverse/irods-csi-driver/pkg/client/common" + "github.com/cyverse/irods-csi-driver/pkg/common" "github.com/cyverse/irods-csi-driver/pkg/metrics" "github.com/cyverse/irods-csi-driver/pkg/mounter" "github.com/cyverse/irods-csi-driver/pkg/volumeinfo" @@ -126,7 +127,7 @@ func (driver *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol } // merge params - configs := mergeConfig(driver.config, driver.secrets, req.GetSecrets(), req.GetVolumeContext()) + configs := common.MergeConfig(driver.config, driver.secrets, req.GetSecrets(), req.GetVolumeContext()) klog.V(5).Infof("NodeStageVolume: mounting %q", targetPath) // mount @@ -256,7 +257,7 @@ func (driver *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublis } else { // static volume provisioning // merge params - configs := mergeConfig(driver.config, driver.secrets, req.GetSecrets(), req.GetVolumeContext()) + configs := common.MergeConfig(driver.config, driver.secrets, req.GetSecrets(), req.GetVolumeContext()) // mount klog.V(5).Infof("NodePublishVolume: mounting %q", targetPath)