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 }