github.com/lyraproj/hiera@v1.0.0-rc4/provider/modulelookupkey.go (about)

     1  package provider
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	"github.com/tada/catch"
     9  
    10  	"github.com/lyraproj/hiera/config"
    11  
    12  	"github.com/lyraproj/dgo/dgo"
    13  	"github.com/lyraproj/dgo/vf"
    14  	"github.com/lyraproj/hiera/api"
    15  	"github.com/lyraproj/hierasdk/hiera"
    16  )
    17  
    18  // ModulePath is the session option that the ModuleLookupKey function uses as the module path. The
    19  // value must be a string with paths separated with the OS-specific path separator.
    20  const ModulePath = `hiera::lookup::modulepath`
    21  
    22  // ModuleLookupKey is a lookup key function that performs a lookup in modules. The function expects
    23  // a key with multiple segments separated by a double colon '::'. The first segment is considered
    24  // the name of a module and that module must be found in the path stored as the ModulePath option.
    25  // If such a path exists and is a directory that in turn contains a hiera.yaml file, then a lookup
    26  // will be performed in that module.
    27  func ModuleLookupKey(pc hiera.ProviderContext, key string) dgo.Value {
    28  	sc := pc.(api.ServerContext)
    29  	if ci := strings.Index(key, `::`); ci > 0 {
    30  		modName := strings.ToLower(key[:ci])
    31  		var mp dgo.Function
    32  		mpm := moduleProviders(sc)
    33  		if f := mpm.Get(modName); f != nil {
    34  			mp = f.(dgo.Function)
    35  		} else {
    36  			mp = loadModuleProvider(sc.Invocation(), mpm, modName)
    37  		}
    38  		iv := sc.Invocation()
    39  		return iv.WithModule(modName, func() dgo.Value {
    40  			if mp == notFoundLookupKeyFunc {
    41  				iv.ReportModuleNotFound()
    42  				return nil
    43  			}
    44  			return mp.Call(vf.MutableValues(pc, key))[0]
    45  		})
    46  	}
    47  	return nil
    48  }
    49  
    50  func moduleProviders(sc api.ServerContext) dgo.Map {
    51  	var mp dgo.Map
    52  	if c, ok := sc.CachedValue(`hiera::moduleproviders`); ok {
    53  		mp = c.(dgo.Map)
    54  	} else {
    55  		mp = vf.MutableMap()
    56  		sc.Cache(`hiera::moduleproviders`, mp)
    57  	}
    58  	return mp
    59  }
    60  
    61  var notFoundLookupKeyFunc = vf.Value(func(pc hiera.ProviderContext, key string) dgo.Value { return nil }).(dgo.Function)
    62  
    63  func loadModuleProvider(ic api.Invocation, mpm dgo.Map, moduleName string) dgo.Function {
    64  	var mp dgo.Function = notFoundLookupKeyFunc
    65  	if modulePath, ok := ic.SessionOptions().Get(ModulePath).(dgo.String); ok {
    66  		for _, path := range filepath.SplitList(modulePath.GoString()) {
    67  			if loaded := loadModule(path, moduleName); loaded != nil {
    68  				mp = loaded
    69  				break
    70  			}
    71  		}
    72  	}
    73  	mpm.Put(moduleName, mp)
    74  	return mp
    75  }
    76  
    77  func loadModule(path, moduleName string) dgo.Function {
    78  	f, err := os.Open(path)
    79  	if err != nil {
    80  		if os.IsNotExist(err) {
    81  			return nil
    82  		}
    83  		panic(catch.Error(err))
    84  	}
    85  
    86  	// Lookup module by finding a directory that matches the moduleName using case insensitive string comparison
    87  	fileInfos, err := f.Readdir(-1)
    88  	_ = f.Close()
    89  	if err != nil {
    90  		panic(catch.Error(err))
    91  	}
    92  
    93  	for _, fi := range fileInfos {
    94  		if !strings.EqualFold(fi.Name(), moduleName) {
    95  			continue
    96  		}
    97  		if !fi.IsDir() {
    98  			break
    99  		}
   100  		configPath := filepath.Join(path, fi.Name(), config.FileName)
   101  		cf, err := os.Lstat(configPath)
   102  		if err != nil {
   103  			if os.IsNotExist(err) {
   104  				break
   105  			}
   106  			panic(catch.Error(err))
   107  		}
   108  		if cf.IsDir() {
   109  			break
   110  		}
   111  		return loadHieraConfig(configPath, moduleName)
   112  	}
   113  	return nil
   114  }
   115  
   116  func loadHieraConfig(configPath, moduleName string) dgo.Function {
   117  	return vf.Value(
   118  		func(pc hiera.ProviderContext, key string) dgo.Value {
   119  			if sc, ok := pc.(api.ServerContext); ok {
   120  				return ConfigLookupKeyAt(sc, configPath, key, moduleName)
   121  			}
   122  			return nil
   123  		}).(dgo.Function)
   124  }