Repository: lyraproj/hiera (go)

Issue:
Lookup interpolation function with plugins
Me again :)
I can't seem to get the lookup interpolation function to work when using the terraform backend plugin, however lookups using the command line are working. This works using the pre-plugin single binary version

hiera.yaml
```yaml
version: 5
defaults:
  data_hash: yaml_data
  datadir: data
hierarchy:
  - name: tfstate
    data_hash: terraform_backend
    options:
      backend: azurerm
      config:
        storage_account_name: xxx
        container_name: tfstate
        access_key: xxx
        key: xxx
  - name: global
    path: global.yaml
```

global.yaml
```yaml
test: "%{lookup('vnet_name')}"
```

```
.\lookup.exe vnet_name --explain
Searching for "vnet_name"
  data_hash function 'terraform_backend'
    Found key: "vnet_name" value: 'testlemur'
```

```
.\lookup.exe test --explain
Searching for "test"
  Merge strategy "first found strategy"
    data_hash function 'terraform_backend'
      No such key: "test"
    data_hash function 'yaml_data'
      Path "C:\Temp\hiera\data\global.yaml"
        Original path: "global.yaml"
        Interpolation on "%{lookup('vnet_name')}"
          Searching for "vnet_name"
            Merge strategy "first found strategy"
              data_hash function 'terraform_backend'
                No such key: "vnet_name"
              data_hash function 'yaml_data'
                Path "C:\Temp\hiera\data\global.yaml"
                  Original path: "global.yaml"
                  No such key: "vnet_name"
        Found key: "test" value: 'undef'
    Merged result: 'undef'
```

Pull request description:
Ensure that provider data is cached throughout an invocation
This commit ensures that data obtained from a provider is be cached
during the lifetime of an invocation.

Closes #51

Patch:
diff --git a/hiera/render.go b/hiera/render.go
index 16ddd3b..02f8ae1 100644
--- a/hiera/render.go
+++ b/hiera/render.go
@@ -43,10 +43,6 @@ func Render(c px.Context, renderAs RenderName, value px.Value, out io.Writer) {
 		if renderAs == `yaml` {
 			bs, err = yaml.Marshal(v)
 		} else {
-			// JSON is not able to handle the result of reflecting an empty untyped map.
-			if em, ok := v.(map[interface{}]interface{}); ok && len(em) == 0 {
-				v = map[string]interface{}{}
-			}
 			bs, err = json.Marshal(v)
 		}
 		if err != nil {
diff --git a/internal/invocation.go b/internal/invocation.go
index d68ead9..dd890fc 100644
--- a/internal/invocation.go
+++ b/internal/invocation.go
@@ -32,6 +32,7 @@ type invocation struct {
 	scope      px.Keyed
 	redacted   bool
 	explainer  explain.Explainer
+	config     hieraapi.ResolvedConfig
 }
 
 // InitContext initializes the given context with the Hiera cache. The context initialized
@@ -152,10 +153,15 @@ func (ic *invocation) sharedCache() *sync.Map {
 }
 
 func (ic *invocation) Config() hieraapi.ResolvedConfig {
+	if ic.config != nil {
+		return ic.config
+	}
+
 	sc := ic.sharedCache()
 	cp := hieraConfigsPrefix + ic.configPath
 	if val, ok := sc.Load(cp); ok {
-		return val.(hieraapi.Config).Resolve(ic)
+		ic.config = val.(hieraapi.Config).Resolve(ic)
+		return ic.config
 	}
 
 	lc := hieraLockPrefix + ic.configPath
@@ -183,7 +189,8 @@ func (ic *invocation) Config() hieraapi.ResolvedConfig {
 		sc.Store(cp, conf)
 		myLock.Unlock()
 	}
-	return conf.Resolve(ic)
+	ic.config = conf.Resolve(ic)
+	return ic.config
 }
 
 func (ic *invocation) ExplainMode() bool {


Test patch:
diff --git a/lookup/lookup_test.go b/lookup/lookup_test.go
index c60a0f2..339858a 100644
--- a/lookup/lookup_test.go
+++ b/lookup/lookup_test.go
@@ -111,14 +111,6 @@ func TestLookup_nullentry(t *testing.T) {
 	})
 }
 
-func TestLookup_emptyMap(t *testing.T) {
-	inTestdata(func() {
-		result, err := cli.ExecuteLookup(`--config`, `empty_map.yaml`, `--render-as`, `json`, `empty_map`)
-		require.NoError(t, err)
-		require.Equal(t, "{}", string(result))
-	})
-}
-
 func TestLookup_explain(t *testing.T) {
 	inTestdata(func() {
 		result, err := cli.ExecuteLookup(`--explain`, `--facts`, `facts.yaml`, `interpolate_ca`)
@@ -331,6 +323,15 @@ func TestLookupKey_plugin(t *testing.T) {
 	})
 }
 
+func TestDataHash_plugin(t *testing.T) {
+	ensureTestPlugin(t)
+	inTestdata(func() {
+		result, err := cli.ExecuteLookup(`--config`, `data_hash_plugin.yaml`, `d`)
+		require.NoError(t, err)
+		require.Equal(t, "interpolate c is value c\n", string(result))
+	})
+}
+
 func TestDataHash_refuseToDie(t *testing.T) {
 	ensureTestPlugin(t)
 	inTestdata(func() {
diff --git a/lookup/testdata/data_hash_plugin.yaml b/lookup/testdata/data_hash_plugin.yaml
new file mode 100644
index 0000000..ae9fe2c
--- /dev/null
+++ b/lookup/testdata/data_hash_plugin.yaml
@@ -0,0 +1,6 @@
+version: 5
+
+hierarchy:
+  - name: Plugin
+    data_hash: test_data_hash
+    pluginfile: hieratestplugin
diff --git a/lookup/testdata/empty_map.yaml b/lookup/testdata/empty_map.yaml
deleted file mode 100644
index 5e2a7bd..0000000
--- a/lookup/testdata/empty_map.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-version: 5
-defaults:
-  datadir: hiera
-hierarchy:
-  - name: empty
-    path: empty_map.yaml
diff --git a/lookup/testdata/hiera/empty_map.yaml b/lookup/testdata/hiera/empty_map.yaml
deleted file mode 100644
index 2e81f50..0000000
--- a/lookup/testdata/hiera/empty_map.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-empty_map: {}
\ No newline at end of file
diff --git a/lookup/testdata/hieratestplugin/hieratestplugin.go b/lookup/testdata/hieratestplugin/hieratestplugin.go
index 179865b..6cfa2df 100644
--- a/lookup/testdata/hieratestplugin/hieratestplugin.go
+++ b/lookup/testdata/hieratestplugin/hieratestplugin.go
@@ -3,6 +3,8 @@ package main
 import (
 	"errors"
 
+	"github.com/lyraproj/dgo/vf"
+
 	"github.com/lyraproj/dgo/dgo"
 	"github.com/lyraproj/hierasdk/hiera"
 	"github.com/lyraproj/hierasdk/plugin"
@@ -11,6 +13,7 @@ import (
 
 func main() {
 	register.LookupKey(`test_lookup_key`, lookupOption)
+	register.DataHash(`test_data_hash`, sampleHash)
 	register.DataHash(`test_refuse_to_die`, refuseToDie)
 	register.DataHash(`test_panic`, panicAttack)
 	plugin.ServeAndExit()
@@ -21,6 +24,10 @@ func lookupOption(c hiera.ProviderContext, key string) dgo.Value {
 	return c.Option(key)
 }
 
+func sampleHash(c hiera.ProviderContext) dgo.Map {
+	return vf.Map(`c`, `value c`, `d`, `interpolate c is %{lookup("c")}`)
+}
+
 // refuseToDie hangs indefinitely
 func refuseToDie(c hiera.ProviderContext) dgo.Map {
 	x := make(chan bool)