github.com/lyraproj/hiera@v1.0.0-rc4/lookup/lookup_test.go (about)

     1  package main_test
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  	"sync"
    12  	"testing"
    13  
    14  	"github.com/lyraproj/dgo/dgo"
    15  	"github.com/lyraproj/dgo/vf"
    16  	"github.com/lyraproj/hiera/api"
    17  	"github.com/lyraproj/hiera/cli"
    18  	"github.com/lyraproj/hiera/hiera"
    19  	"github.com/lyraproj/hiera/provider"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestLookup_defaultInt(t *testing.T) {
    25  	result, err := cli.ExecuteLookup(`--default`, `23`, `--dialect`, `dgo`, `--type`, `int`, `foo`)
    26  	require.NoError(t, err)
    27  	require.Equal(t, "23\n", string(result))
    28  }
    29  
    30  func TestLookup_defaultString(t *testing.T) {
    31  	result, err := cli.ExecuteLookup(`--default`, `23`, `--type`, `String`, `foo`)
    32  	require.NoError(t, err)
    33  	require.Equal(t, "\"23\"\n", string(result))
    34  }
    35  
    36  func TestLookup_notFound(t *testing.T) {
    37  	result, err := cli.ExecuteLookup(`foo`)
    38  	require.NoError(t, err)
    39  	require.Equal(t, "", string(result))
    40  }
    41  
    42  func TestLookup_defaultEmptyString(t *testing.T) {
    43  	result, err := cli.ExecuteLookup(`--default`, ``, `foo`)
    44  	require.NoError(t, err)
    45  	require.Equal(t, "\"\"\n", string(result))
    46  }
    47  
    48  func TestLookup_defaultHash(t *testing.T) {
    49  	result, err := cli.ExecuteLookup(`--default`, `{ x: "a", y: 9 }`, `--dialect`, `dgo`, `--type`, `map[string](string|int)`, `foo`)
    50  	require.NoError(t, err)
    51  	require.Equal(t, "x: a\ny: 9\n", string(result))
    52  }
    53  
    54  func TestLookup_defaultHash_json(t *testing.T) {
    55  	result, err := cli.ExecuteLookup(`--default`, `{ x: "a", y: 9 }`, `--dialect`, `dgo`, `--type`, `map[string](string|int)`, `--render-as`, `json`, `foo`)
    56  	require.NoError(t, err)
    57  	require.Equal(t, "{\"x\":\"a\",\"y\":9}\n", string(result))
    58  }
    59  
    60  func TestLookup_defaultString_s(t *testing.T) {
    61  	result, err := cli.ExecuteLookup(`--default`, `xyz`, `--render-as`, `s`, `foo`)
    62  	require.NoError(t, err)
    63  	require.Equal(t, "xyz\n", string(result))
    64  }
    65  
    66  func TestLookup_defaultString_binary(t *testing.T) {
    67  	result, err := cli.ExecuteLookup(`--default`, `YWJjMTIzIT8kKiYoKSctPUB+`, `--render-as`, `binary`, `foo`)
    68  	require.NoError(t, err)
    69  	require.Equal(t, "abc123!?$*&()'-=@~", string(result))
    70  }
    71  
    72  func TestLookup_defaultArray_binary(t *testing.T) {
    73  	result, err := cli.ExecuteLookup(`--default`, `{12, 28, 37, 15}`, `--dialect`, `dgo`, `--type`, `[]int`, `--render-as`, `binary`, `foo`)
    74  	require.NoError(t, err)
    75  	require.Equal(t, []byte{12, 28, 37, 15}, result)
    76  }
    77  
    78  func TestLookup_facts(t *testing.T) {
    79  	inTestdata(func() {
    80  		result, err := cli.ExecuteLookup(`--facts`, `facts.yaml`, `interpolate_a`)
    81  		require.NoError(t, err)
    82  		require.Equal(t, "This is value of a\n", string(result))
    83  	})
    84  }
    85  
    86  func TestLookup_fact_interpolated_config(t *testing.T) {
    87  	inTestdata(func() {
    88  		result, err := cli.ExecuteLookup(`--facts`, `facts.yaml`, `interpolate_ca`)
    89  		require.NoError(t, err)
    90  		require.Equal(t, "This is value of c.a\n", string(result))
    91  	})
    92  }
    93  
    94  func TestLookup_vars_interpolated_config(t *testing.T) {
    95  	inTestdata(func() {
    96  		result, err := cli.ExecuteLookup(`--vars`, `facts.yaml`, `interpolate_ca`)
    97  		require.NoError(t, err)
    98  		require.Equal(t, "This is value of c.a\n", string(result))
    99  	})
   100  }
   101  
   102  func TestLookup_var_interpolated_config(t *testing.T) {
   103  	inTestdata(func() {
   104  		result, err := cli.ExecuteLookup(`--dialect`, `dgo`, `--var`, `c={a:"the option value"}`, `--var`, `data_file: by_fact`, `interpolate_ca`)
   105  		require.NoError(t, err)
   106  		require.Equal(t, "This is the option value\n", string(result))
   107  	})
   108  }
   109  
   110  func TestLookup_fact_directly(t *testing.T) {
   111  	inTestdata(func() {
   112  		result, err := cli.ExecuteLookup(`--facts`, `facts.yaml`, `--config`, `fact_directly_hiera.yaml`, `the_fact`)
   113  		require.NoError(t, err)
   114  		require.Equal(t, "value of the_fact\n", string(result))
   115  	})
   116  }
   117  
   118  func TestLookup_nullentry(t *testing.T) {
   119  	inTestdata(func() {
   120  		result, err := cli.ExecuteLookup(`nullentry`)
   121  		require.NoError(t, err)
   122  		require.Equal(t, "nv: null\n", string(result))
   123  	})
   124  }
   125  
   126  func TestLookup_emptyMap(t *testing.T) {
   127  	inTestdata(func() {
   128  		result, err := cli.ExecuteLookup(`--config`, `empty_map_hiera.yaml`, `--render-as`, `json`, `empty_map`)
   129  		require.NoError(t, err)
   130  		require.Equal(t, "{}\n", string(result))
   131  	})
   132  }
   133  
   134  func TestLookup_emptySubMap(t *testing.T) {
   135  	inTestdata(func() {
   136  		result, err := cli.ExecuteLookup(`--config`, `empty_map_hiera.yaml`, `--render-as`, `json`, `empty_sub_map`)
   137  		require.NoError(t, err)
   138  		require.Equal(t, "{\"x\":\"the x\",\"empty\":{}}\n", string(result))
   139  	})
   140  }
   141  
   142  func TestLookup_emptySubMapInArray(t *testing.T) {
   143  	inTestdata(func() {
   144  		result, err := cli.ExecuteLookup(`--config`, `empty_map_hiera.yaml`, `--render-as`, `json`, `empty_sub_map_in_array`)
   145  		require.NoError(t, err)
   146  		require.Equal(t, "[{}]\n", string(result))
   147  	})
   148  }
   149  
   150  func TestLookup_sensitive(t *testing.T) {
   151  	inTestdata(func() {
   152  		result, err := cli.ExecuteLookup(`sense`, `--render-as`, `s`)
   153  		require.NoError(t, err)
   154  		require.Equal(t, "sensitive [value redacted]\n", string(result))
   155  
   156  		// Default rendering is yaml and the output is rich data.
   157  		result, err = cli.ExecuteLookup(`sense`)
   158  		require.NoError(t, err)
   159  		require.Equal(t, "__type: sensitive\n__value: Don't reveal this\n", string(result))
   160  	})
   161  }
   162  
   163  func TestLookup_renderJSON_NoDedup(t *testing.T) {
   164  	inTestdata(func() {
   165  		result, err := cli.ExecuteLookup(`non-existent`, `--dialect`, `dgo`, `--default`,
   166  			`{ x: "a string longer than 20 characters in length", y: "a string longer than 20 characters in length" }`,
   167  			`--render-as`, `json`)
   168  
   169  		require.NoError(t, err)
   170  		require.Equal(t,
   171  			`{"x":"a string longer than 20 characters in length","y":"a string longer than 20 characters in length"}
   172  `,
   173  			string(result))
   174  	})
   175  }
   176  
   177  func TestLookup_renderYAML_NoDedup(t *testing.T) {
   178  	inTestdata(func() {
   179  		result, err := cli.ExecuteLookup(`non-existent`, `--dialect`, `dgo`, `--default`,
   180  			`{ x: "a string longer than 20 characters in length", y: "a string longer than 20 characters in length" }`,
   181  			`--render-as`, `yaml`)
   182  
   183  		require.NoError(t, err)
   184  		require.Equal(t, `x: a string longer than 20 characters in length
   185  y: a string longer than 20 characters in length
   186  `,
   187  			string(result))
   188  	})
   189  }
   190  
   191  func TestLookup_lookup(t *testing.T) {
   192  	inTestdata(func() {
   193  		result, err := cli.ExecuteLookup(`lookup_array`)
   194  		require.NoError(t, err)
   195  		require.Equal(t, "'{\"one\",\"two\",\"three\"}'\n", string(result))
   196  	})
   197  }
   198  
   199  func TestLookup_alias(t *testing.T) {
   200  	inTestdata(func() {
   201  		result, err := cli.ExecuteLookup(`alias_array`)
   202  		require.NoError(t, err)
   203  		require.Equal(t, "- one\n- two\n- three\n", string(result))
   204  	})
   205  }
   206  
   207  func TestLookup_strictAlias(t *testing.T) {
   208  	inTestdata(func() {
   209  		result, err := cli.ExecuteLookup(`strict_alias_array`)
   210  		require.NoError(t, err)
   211  		require.Equal(t, "- one\n- two\n- three\n", string(result))
   212  	})
   213  }
   214  
   215  func TestLookup_lookupNothing(t *testing.T) {
   216  	inTestdata(func() {
   217  		result, err := cli.ExecuteLookup(`lookup_nothing`)
   218  		require.NoError(t, err)
   219  		require.Equal(t, "\"\"\n", string(result))
   220  	})
   221  }
   222  
   223  func TestLookup_aliasNothing(t *testing.T) {
   224  	inTestdata(func() {
   225  		result, err := cli.ExecuteLookup(`alias_nothing`)
   226  		require.NoError(t, err)
   227  		require.Equal(t, "\"\"\n", string(result))
   228  	})
   229  }
   230  
   231  func cmdLookup(cmdOpts *hiera.CommandOptions, opts dgo.Keyed, args []string, out io.Writer) error {
   232  	return hiera.TryWithParent(context.Background(), provider.ConfigLookupKey, opts, func(c api.Session) error {
   233  		hiera.LookupAndRender(c, cmdOpts, args, out)
   234  		return nil
   235  	})
   236  }
   237  
   238  // TestLookup_aliasNewInvocation tests that an alias uses a new invocation where the
   239  // lookup_options map is not inherited
   240  func TestLookup_aliasNewInvocation(t *testing.T) {
   241  	cmdOpts := &hiera.CommandOptions{}
   242  	cfgOpts := vf.MutableMap()
   243  	cfgOpts.Put(api.HieraConfig, "interpolate_fresh.yaml")
   244  	inTestdata(func() {
   245  		bs := &strings.Builder{}
   246  		err := cmdLookup(cmdOpts, cfgOpts, []string{"hash_with_alias"}, bs)
   247  		require.NoError(t, err)
   248  		require.Equal(t, "y: Y\neh: {}\nea: []\nx: X\n", bs.String())
   249  	})
   250  }
   251  
   252  func TestLookup_strictAliasNothing(t *testing.T) {
   253  	inTestdata(func() {
   254  		result, err := cli.ExecuteLookup(`strict_alias_nothing`)
   255  		require.NoError(t, err)
   256  		require.Equal(t, ``, string(result))
   257  	})
   258  }
   259  
   260  func TestLookup_explain(t *testing.T) {
   261  	inTestdata(func() {
   262  		result, err := cli.ExecuteLookup(`--explain`, `--facts`, `facts.yaml`, `interpolate_ca`)
   263  		require.NoError(t, err)
   264  		require.Regexp(t,
   265  			`\ASearching for "interpolate_ca"
   266    data_hash function 'yaml_data'
   267      Path "[^"]*/testdata/hiera/common\.yaml"
   268        Original path: "common\.yaml"
   269        No such key: "interpolate_ca"
   270    data_hash function 'yaml_data'
   271      Path "[^"]*/testdata/hiera/named_by_fact\.yaml"
   272        Original path: "named_%\{data_file\}.yaml"
   273        Interpolation on "This is %\{c\.a\}"
   274          Sub key: "a"
   275            Found key: "a" value: "value of c.a"
   276        Found key: "interpolate_ca" value: "This is value of c\.a"
   277  \z`, filepath.ToSlash(string(result)))
   278  	})
   279  }
   280  
   281  func TestLookup_explain_yaml(t *testing.T) {
   282  	inTestdata(func() {
   283  		result, err := cli.ExecuteLookup(`--explain`, `--facts`, `facts.yaml`, `--render-as`, `yaml`, `interpolate_ca`)
   284  		require.NoError(t, err)
   285  		require.Regexp(t,
   286  			`\A__type: hiera\.explainer
   287  branches:
   288    - __type: hiera\.explainLookup
   289      branches:
   290        - __type: hiera\.explainDataProvider
   291          branches:
   292            - __type: hiera\.explainLocation
   293              event: not_found
   294              key: interpolate_ca
   295              location:
   296                  __type: hiera\.path
   297                  original: common\.yaml
   298                  resolved: .*/testdata/hiera/common\.yaml
   299                  exists: true
   300          providerName: data_hash function 'yaml_data'
   301        - __type: hiera\.explainDataProvider
   302          branches:
   303            - __type: hiera\.explainLocation
   304              branches:
   305                - __type: hiera\.explainInterpolate
   306                  branches:
   307                    - __type: hiera\.explainSubLookup
   308                      branches:
   309                        - __type: hiera\.explainKeySegment
   310                          event: found
   311                          key: a
   312                          value: value of c\.a
   313                          segment: a
   314                      subKey: c\.a
   315                  expression: This is %\{c\.a\}
   316              event: found
   317              key: interpolate_ca
   318              value: This is value of c\.a
   319              location:
   320                  __type: hiera\.path
   321                  original: named_%\{data_file\}\.yaml
   322                  resolved: .*/testdata/hiera/named_by_fact\.yaml
   323                  exists: true
   324          providerName: data_hash function 'yaml_data'
   325      event: result
   326      key: interpolate_ca
   327      value: This is value of c\.a
   328  \z`, filepath.ToSlash(string(result)))
   329  	})
   330  }
   331  
   332  func TestLookup_explain_options(t *testing.T) {
   333  	inTestdata(func() {
   334  		result, err := cli.ExecuteLookup(`--explain-options`, `--facts`, `facts.yaml`, `hash`)
   335  		require.NoError(t, err)
   336  		require.Regexp(t,
   337  			`\ASearching for "lookup_options"
   338    Merge strategy "deep merge strategy"
   339      data_hash function 'yaml_data'
   340        Path "[^"]*/testdata/hiera/common\.yaml"
   341          Original path: "common\.yaml"
   342          Found key: "lookup_options" value: \{
   343            "hash": \{
   344              "merge": "deep"
   345            \},
   346            "sense": \{
   347              "convert_to": "Sensitive"
   348            \}
   349          \}
   350      data_hash function 'yaml_data'
   351        Path "[^"]*/testdata/hiera/named_by_fact\.yaml"
   352          Original path: "named_%\{data_file\}\.yaml"
   353          No such key: "lookup_options"
   354      Merged result: \{
   355        "hash": \{
   356          "merge": "deep"
   357        \},
   358        "sense": \{
   359          "convert_to": "Sensitive"
   360        \}
   361      \}
   362  \z`, filepath.ToSlash(string(result)))
   363  	})
   364  }
   365  
   366  func TestLookup_explain_explain_options(t *testing.T) {
   367  	inTestdata(func() {
   368  		result, err := cli.ExecuteLookup(`--explain`, `--explain-options`, `--facts`, `facts.yaml`, `hash`)
   369  		require.NoError(t, err)
   370  		require.Regexp(t,
   371  			`\ASearching for "lookup_options"
   372    Merge strategy "deep merge strategy"
   373      data_hash function 'yaml_data'
   374        Path "[^"]*/testdata/hiera/common\.yaml"
   375          Original path: "common\.yaml"
   376          Found key: "lookup_options" value: \{
   377            "hash": \{
   378              "merge": "deep"
   379            \},
   380            "sense": \{
   381              "convert_to": "Sensitive"
   382            \}
   383          \}
   384      data_hash function 'yaml_data'
   385        Path "[^"]*/testdata/hiera/named_by_fact\.yaml"
   386          Original path: "named_%\{data_file\}\.yaml"
   387          No such key: "lookup_options"
   388      Merged result: \{
   389        "hash": \{
   390          "merge": "deep"
   391        \},
   392        "sense": \{
   393          "convert_to": "Sensitive"
   394        \}
   395      \}
   396  Searching for "hash"
   397    Using merge options from "lookup_options" hash
   398    Merge strategy "deep merge strategy"
   399      data_hash function 'yaml_data'
   400        Path "[^"]*/testdata/hiera/common\.yaml"
   401          Original path: "common\.yaml"
   402          Found key: "hash" value: \{
   403            "one": 1,
   404            "two": "two",
   405            "three": \{
   406              "a": "A",
   407              "c": "C"
   408            \}
   409          \}
   410      data_hash function 'yaml_data'
   411        Path "[^"]*/testdata/hiera/named_by_fact\.yaml"
   412          Original path: "named_%\{data_file\}\.yaml"
   413          Found key: "hash" value: \{
   414            "one": "overwritten one",
   415            "three": \{
   416              "a": "overwritten A",
   417              "b": "B",
   418              "c": "overwritten C"
   419            \}
   420          \}
   421      Merged result: \{
   422        "one": 1,
   423        "two": "two",
   424        "three": \{
   425          "a": "A",
   426          "c": "C",
   427          "b": "B"
   428        \}
   429      \}
   430  \z`, filepath.ToSlash(string(result)))
   431  	})
   432  }
   433  
   434  func TestLookupKey_plugin(t *testing.T) {
   435  	ensureTestPlugin(t)
   436  	inTestdata(func() {
   437  		result, err := cli.ExecuteLookup(`--config`, `lookup_key_plugin_hiera.yaml`, `a`)
   438  		require.NoError(t, err)
   439  		require.Equal(t, "option a\n", string(result))
   440  	})
   441  }
   442  
   443  func TestDataHash_plugin(t *testing.T) {
   444  	ensureTestPlugin(t)
   445  	inTestdata(func() {
   446  		result, err := cli.ExecuteLookup(`--config`, `data_hash_plugin_hiera.yaml`, `d`)
   447  		require.NoError(t, err)
   448  		require.Equal(t, "interpolate c is value c\n", string(result))
   449  	})
   450  }
   451  
   452  func TestLookup_issue75(t *testing.T) {
   453  	ensureTestPlugin(t)
   454  	for i := 0; i < 100; i++ {
   455  		inTestdata(func() {
   456  			result, err := cli.ExecuteLookup(`dns_resource_group_name`, `--config`, `dedup_hiera.yaml`, `--dialect`, `dgo`,
   457  				`--render-as`, `yaml`)
   458  
   459  			require.NoError(t, err)
   460  			require.Equal(t, `cbuk-shared-sharedproduction-dns-uksouth
   461  `,
   462  				string(result))
   463  		})
   464  	}
   465  }
   466  
   467  /*
   468  func TestDataHash_refuseToDie(t *testing.T) {
   469  	ensureTestPlugin(t)
   470  	inTestdata(func() {
   471  		_, err := cli.ExecuteLookup(`--config`, `refuse_to_die_plugin_hiera.yaml`, `a`)
   472  		if assert.Error(t, err) {
   473  			require.Regexp(t, `net/http: request canceled`, err.Error())
   474  		}
   475  	})
   476  }
   477  */
   478  
   479  func TestDataHash_panic(t *testing.T) {
   480  	ensureTestPlugin(t)
   481  	inTestdata(func() {
   482  		_, err := cli.ExecuteLookup(`--config`, `panic_plugin_hiera.yaml`, `a`)
   483  		if assert.Error(t, err) {
   484  			require.Regexp(t, `500 Internal Server Error: dit dit dit daah daah daah dit dit dit`, err.Error())
   485  		}
   486  	})
   487  }
   488  
   489  // Mimics:
   490  // docker run --rm --hostname puppet -v $(pwd)/testdata/:/etc/puppetlabs/puppet/ -v $(pwd)/testdata/glob_hiera.yaml:/etc/puppetlabs/puppet/hiera.yaml --entrypoint puppet puppet/puppetserver lookup --facts /etc/puppetlabs/puppet/glob_facts.yaml a --explain
   491  func TestLookupKey_globExpansionExistant(t *testing.T) {
   492  	inTestdata(func() {
   493  		result, err := cli.ExecuteLookup(`--config`, `glob_hiera.yaml`, `--facts`, `glob_facts.yaml`, `a`)
   494  		require.NoError(t, err)
   495  		require.Equal(t, "fragment a\n", string(result))
   496  	})
   497  }
   498  
   499  // Mimics:
   500  // docker run --rm --hostname puppet -v $(pwd)/testdata/:/etc/puppetlabs/puppet/ -v $(pwd)/testdata/glob_hiera.yaml:/etc/puppetlabs/puppet/hiera.yaml --entrypoint puppet puppet/puppetserver lookup a --explain
   501  func TestLookupKey_globExpansionNonExistant(t *testing.T) {
   502  	inTestdata(func() {
   503  		result, err := cli.ExecuteLookup(`--config`, `glob_hiera.yaml`, `a`)
   504  		require.NoError(t, err)
   505  		require.Equal(t, "common a\n", string(result))
   506  	})
   507  }
   508  
   509  func TestLookup_fourElementSlice(t *testing.T) {
   510  	inTestdata(func() {
   511  		result, err := cli.ExecuteLookup(`--config`, `hiera.yaml`, `--render-as`, `json`, `myList`)
   512  		require.NoError(t, err)
   513  		require.Equal(t, `[{"typeA":"a"},{"typeB":"b"},{"typeC":"c"},{"tupeD":"d"}]`, strings.TrimSpace(string(result)))
   514  	})
   515  }
   516  
   517  var once = sync.Once{}
   518  
   519  func ensureTestPlugin(t *testing.T) {
   520  	once.Do(func() {
   521  		t.Helper()
   522  		cw, err := os.Getwd()
   523  		if err != nil {
   524  			t.Fatal(err)
   525  		}
   526  
   527  		if err = os.Chdir(filepath.Join(`testdata`, `hieratestplugin`)); err != nil {
   528  			t.Fatal(err)
   529  		}
   530  
   531  		defer func() {
   532  			_ = os.Chdir(cw)
   533  		}()
   534  
   535  		pe := `hieratestplugin`
   536  		ps := pe + `.go`
   537  		if runtime.GOOS == `windows` {
   538  			pe += `.exe`
   539  		}
   540  
   541  		cmd := exec.Command(`go`, `build`, `-o`, filepath.Join(`..`, `plugin`, pe), ps)
   542  		cmd.Stderr = os.Stderr
   543  		cmd.Stdout = os.Stdout
   544  		if err = cmd.Run(); err != nil {
   545  			t.Fatal(err)
   546  		}
   547  	})
   548  }
   549  
   550  func inTestdata(f func()) {
   551  	cw, err := os.Getwd()
   552  	if err == nil {
   553  		err = os.Chdir(`testdata`)
   554  		if err == nil {
   555  			defer func() {
   556  				_ = os.Chdir(cw)
   557  			}()
   558  			f()
   559  		}
   560  	}
   561  	if err != nil {
   562  		panic(err)
   563  	}
   564  }