@@ -2,11 +2,11 @@ package app
22
33import (
44 "context"
5+ "gopkg.in/yaml.v3"
56 "strconv"
67 "strings"
78
89 mconfig "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
9- cast "github.com/openebs/maya/pkg/castemplate/v1alpha1"
1010 hostpath "github.com/openebs/maya/pkg/hostpath/v1alpha1"
1111 "github.com/openebs/maya/pkg/util"
1212 "github.com/pkg/errors"
@@ -113,6 +113,17 @@ const (
113113
114114 KeyQuotaSoftLimit = "softLimitGrace"
115115 KeyQuotaHardLimit = "hardLimitGrace"
116+
117+ // FilePermissions allows to define the default directory mode
118+ // Exemple StorageClass snippet:
119+ // - name: FilePermissions
120+ // data:
121+ // mode: g+s
122+ // This is the cas-template key for all file permission 'data' keys
123+ KeyFilePermissions = "FilePermissions"
124+
125+ // FSMode defines the file permission mode of the shared directory
126+ KeyFsMode = "mode"
116127)
117128
118129const (
@@ -131,7 +142,7 @@ const (
131142// default configuration of the provisioner.
132143func (p * Provisioner ) GetVolumeConfig (ctx context.Context , pvName string , pvc * corev1.PersistentVolumeClaim ) (* VolumeConfig , error ) {
133144
134- pvConfig := p . defaultConfig
145+ var pvConfig [] Config
135146
136147 //Fetch the SC
137148 scName := GetStorageClassName (pvc )
@@ -142,35 +153,36 @@ func (p *Provisioner) GetVolumeConfig(ctx context.Context, pvName string, pvc *c
142153
143154 // extract and merge the cas config from storageclass
144155 scCASConfigStr := sc .ObjectMeta .Annotations [string (mconfig .CASConfigKey )]
156+ var scConfig []Config
145157 klog .V (4 ).Infof ("SC %v has config:%v" , * scName , scCASConfigStr )
146158 if len (strings .TrimSpace (scCASConfigStr )) != 0 {
147- scCASConfig , err := cast . UnMarshallToConfig ( scCASConfigStr )
159+ err = yaml . Unmarshal ([] byte ( scCASConfigStr ), & scConfig )
148160 if err == nil {
149- pvConfig = cast . MergeConfig ( scCASConfig , pvConfig )
161+ pvConfig = MergeConfigs ( scConfig , pvConfig )
150162 } else {
151163 return nil , errors .Wrapf (err , "failed to get config: invalid sc config {%v}" , scCASConfigStr )
152164 }
153165 }
154166
155- // Extract and merge the cas config from persistentvolumeclaim.
156- // TODO: Validation checks for what all cas-config options can be
157- // set on the PVC.
158- pvcCASConfigStr := pvc .Annotations [string (mconfig .CASConfigKey )]
167+ //TODO : extract and merge the cas volume config from pvc
168+ // This block can be added once validation checks are added
169+ // as to the type of config that can be passed via PVC
170+ var pvcConfig []Config
171+ pvcCASConfigStr := pvc .ObjectMeta .Annotations [string (mconfig .CASConfigKey )]
159172 klog .V (4 ).Infof ("PVC %v has config:%v" , pvc .Name , pvcCASConfigStr )
160173 if len (strings .TrimSpace (pvcCASConfigStr )) != 0 {
161- pvcCASConfig , err := cast . UnMarshallToConfig ( pvcCASConfigStr )
174+ err = yaml . Unmarshal ([] byte ( pvcCASConfigStr ), & pvcConfig )
162175 if err == nil {
163- // Config keys which already exist (SC config),
164- // will be skipped
165- // i.e. SC config will have precedence over PVC config,
166- // if both have the same keys
167- pvConfig = cast .MergeConfig (pvConfig , pvcCASConfig )
176+ pvConfig = MergeConfigs (pvConfig , pvcConfig )
168177 } else {
169- return nil , errors .Wrapf (err , "failed to get config: invalid pvc config {%v}" , pvcCASConfigStr )
178+ return nil , errors .Wrapf (err , "failed to get config: invalid config {%v}" +
179+ " in pvc {%v} in namespace {%v}" ,
180+ pvcCASConfigStr , pvc .Name , pvc .Namespace ,
181+ )
170182 }
171183 }
172184
173- pvConfigMap , err := cast . ConfigToMap (pvConfig )
185+ pvConfigMap , err := ConfigToMap (pvConfig )
174186 if err != nil {
175187 return nil , errors .Wrapf (err , "unable to read volume config: pvc {%v}" , pvc .ObjectMeta .Name )
176188 }
@@ -332,6 +344,15 @@ func (c *VolumeConfig) IsExt4QuotaEnabled() bool {
332344 return enableExt4QuotaBool
333345}
334346
347+ // GetFsMode fetches the file mode from PVC
348+ // or StorageClass annotation, if specified
349+ func (c * VolumeConfig ) GetFsMode () string {
350+ configData := c .getDataField (KeyFilePermissions , KeyFsMode )
351+
352+ //Keep the original default mode
353+ return configData
354+ }
355+
335356// getValue is a utility function to extract the value
336357// of the `key` from the ConfigMap object - which is
337358// map[string]interface{map[string][string]}
@@ -371,9 +392,9 @@ func (c *VolumeConfig) getEnabled(key string) string {
371392// This gets the value for a specific
372393// 'Data' parameter key-value pair.
373394func (c * VolumeConfig ) getDataField (key string , dataKey string ) string {
374- if configData , ok := util .GetNestedField (c .configData , key ).(map [string ]string ); ok {
395+ if configData , ok := util .GetNestedField (c .configData , key ).(map [string ]RawLiteral ); ok {
375396 if val , p := configData [dataKey ]; p {
376- return val
397+ return string ( val )
377398 }
378399 }
379400 //Default case
@@ -464,7 +485,7 @@ func GetImagePullSecrets(s string) []corev1.LocalObjectReference {
464485 return list
465486}
466487
467- func dataConfigToMap (pvConfig []mconfig. Config ) (map [string ]interface {}, error ) {
488+ func dataConfigToMap (pvConfig []Config ) (map [string ]interface {}, error ) {
468489 m := map [string ]interface {}{}
469490
470491 for _ , configObj := range pvConfig {
@@ -486,7 +507,7 @@ func dataConfigToMap(pvConfig []mconfig.Config) (map[string]interface{}, error)
486507 return m , nil
487508}
488509
489- func listConfigToMap (pvConfig []mconfig. Config ) (map [string ]interface {}, error ) {
510+ func listConfigToMap (pvConfig []Config ) (map [string ]interface {}, error ) {
490511 m := map [string ]interface {}{}
491512
492513 for _ , configObj := range pvConfig {
@@ -507,3 +528,74 @@ func listConfigToMap(pvConfig []mconfig.Config) (map[string]interface{}, error)
507528
508529 return m , nil
509530}
531+
532+ // A RawLiteral interprets characters as they are without assuming they are octal and translating them to ints.
533+ // This works when we want to pick up a yaml value as it is, whether it's wearing ” or "" or neither.
534+ type RawLiteral string
535+
536+ func (r * RawLiteral ) UnmarshalYAML (node * yaml.Node ) error {
537+ * r = RawLiteral (node .Value )
538+ return nil
539+ }
540+
541+ // Config holds a configuration element
542+ type Config struct {
543+ // Name of the config
544+ Name string `yaml:"name"`
545+ // Enabled flags if this config is enabled or disabled;
546+ // true indicates enabled while false indicates disabled
547+ Enabled string `yaml:"enabled"`
548+ // Value represents any specific value that is applicable
549+ // to this config
550+ Value string `yaml:"value"`
551+ // Data represents an arbitrary map of key value pairs
552+ Data map [string ]RawLiteral `yaml:"data"`
553+ // List represents a JSON(YAML) array
554+ List []string `yaml:"list"`
555+ }
556+
557+ // MergeConfig will merge configuration fields
558+ // from lowPriority that are not present in
559+ // highPriority configuration and return the
560+ // resulting config
561+ func MergeConfigs (highPriority , lowPriority []Config ) (final []Config ) {
562+ var book []string
563+ for _ , h := range highPriority {
564+ final = append (final , h )
565+ book = append (book , strings .TrimSpace (h .Name ))
566+ }
567+ for _ , l := range lowPriority {
568+ // include only if the config was not present
569+ // earlier in high priority configuration
570+ if ! util .ContainsString (book , strings .TrimSpace (l .Name )) {
571+ final = append (final , l )
572+ }
573+ }
574+ return
575+ }
576+
577+ // ConfigToMap transforms CAS template config type
578+ // to a nested map
579+ func ConfigToMap (all []Config ) (m map [string ]interface {}, err error ) {
580+ var configName string
581+ m = map [string ]interface {}{}
582+ for _ , config := range all {
583+ configName = strings .TrimSpace (config .Name )
584+ if len (configName ) == 0 {
585+ err = errors .Errorf ("failed to transform cas config to map: missing config name: %s" , config )
586+ return nil , err
587+ }
588+ confHierarchy := map [string ]interface {}{
589+ configName : map [string ]string {
590+ "enabled" : config .Enabled ,
591+ "value" : config .Value ,
592+ },
593+ }
594+ isMerged := util .MergeMapOfObjects (m , confHierarchy )
595+ if ! isMerged {
596+ err = errors .Errorf ("failed to transform cas config to map: failed to merge: %s" , config )
597+ return nil , err
598+ }
599+ }
600+ return
601+ }
0 commit comments