Skip to content

Commit

Permalink
Adding new defineDatasource function
Browse files Browse the repository at this point in the history
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
  • Loading branch information
hairyhenderson committed Jul 12, 2018
1 parent 37627e8 commit 7936db5
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 2 deletions.
29 changes: 28 additions & 1 deletion data/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ func addSourceReader(scheme string, readFunc func(*Source, ...string) ([]byte, e
type Data struct {
Sources map[string]*Source
cache map[string][]byte

// headers from the --datasource-header/-H option that don't reference datasources from the commandline
extraHeaders map[string]http.Header
}

// Cleanup - clean up datasources before shutting the process down - things
Expand All @@ -92,10 +95,14 @@ func NewData(datasourceArgs []string, headerArgs []string) (*Data, error) {
return nil, errors.Wrapf(err, "error parsing datasource")
}
s.Header = headers[s.Alias]
// pop the header out of the map, so we end up with only the unreferenced ones
delete(headers, s.Alias)

sources[s.Alias] = s
}
return &Data{
Sources: sources,
Sources: sources,
extraHeaders: headers,
}, nil
}

Expand Down Expand Up @@ -218,6 +225,26 @@ func absURL(value string) (*url.URL, error) {
return baseURL.ResolveReference(relURL), nil
}

// DefineDatasource -
func (d *Data) DefineDatasource(alias, value string) error {
if d.DatasourceExists(alias) {
return nil
}
if alias != "" {
alias = alias + "="
}
s, err := ParseSource(alias + value)
if err != nil {
return err
}
s.Header = d.extraHeaders[s.Alias]
if d.Sources == nil {
d.Sources = make(map[string]*Source)
}
d.Sources[s.Alias] = s
return nil
}

// DatasourceExists -
func (d *Data) DatasourceExists(alias string) bool {
_, ok := d.Sources[alias]
Expand Down
54 changes: 54 additions & 0 deletions data/datasource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,57 @@ func TestReadStdin(t *testing.T) {
_, err = readStdin(nil)
assert.Error(t, err)
}

func TestDefineDatasource(t *testing.T) {
d := &Data{}
err := d.DefineDatasource("", "foo.json")
assert.NoError(t, err)
s := d.Sources["foo"]
assert.Equal(t, "foo", s.Alias)

d = &Data{}
err = d.DefineDatasource("", "../foo.json")
assert.Error(t, err)

d = &Data{}
err = d.DefineDatasource("", "ftp://example.com/foo.yml")
assert.Error(t, err)

d = &Data{}
err = d.DefineDatasource("data", "foo.json")
assert.NoError(t, err)
s = d.Sources["data"]
assert.Equal(t, "data", s.Alias)
assert.Equal(t, "file", s.URL.Scheme)
assert.Equal(t, jsonMimetype, s.Type)
assert.True(t, s.URL.IsAbs())

d = &Data{}
err = d.DefineDatasource("data", "/otherdir/foo.json")
assert.NoError(t, err)
s = d.Sources["data"]
assert.Equal(t, "data", s.Alias)
assert.Equal(t, "file", s.URL.Scheme)
assert.True(t, s.URL.IsAbs())
assert.Equal(t, "/otherdir/foo.json", s.URL.Path)

d = &Data{}
err = d.DefineDatasource("data", "sftp://example.com/blahblah/foo.json")
assert.NoError(t, err)
s = d.Sources["data"]
assert.Equal(t, "data", s.Alias)
assert.Equal(t, "sftp", s.URL.Scheme)
assert.True(t, s.URL.IsAbs())
assert.Equal(t, "/blahblah/foo.json", s.URL.Path)

d = &Data{
Sources: map[string]*Source{
"data": &Source{Alias: "data"},
},
}
err = d.DefineDatasource("data", "/otherdir/foo.json")
assert.NoError(t, err)
s = d.Sources["data"]
assert.Equal(t, "data", s.Alias)
assert.Nil(t, s.URL)
}
3 changes: 2 additions & 1 deletion docs/content/datasources.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Datasources are an optional, but central concept in gomplate. While the basic fl

Some common use-cases include injecting sensitive material like passwords (which should not be stored in source-control with the templates), or providing simplified configuration formats that can be fed to a template to provide a much more complex output.

Datasources can be defined with the [`--datasource`/`-d`][] commandline flag, and referenced via an _alias_ inside the template, using a function such as [`datasource`][] or [`include`][].
Datasources can be defined with the [`--datasource`/`-d`][] commandline flag or the [`defineDatasource`][] function, and referenced via an _alias_ inside the template, using a function such as [`datasource`][] or [`include`][].

Since datasources are defined separately from the template, the same templates can be used with different datasources and even different datasource types. For example, gomplate could be run on a developer machine with a `file` datasource pointing to a JSON file containing test data, where the same template could be used in a production environment using a `consul` datasource with the real production data.

Expand Down Expand Up @@ -430,6 +430,7 @@ The file `/tmp/vault-aws-nonce` will be created if it didn't already exist, and

[`--datasource`/`-d`]: ../usage/#datasource-d
[`--datasource-header`/`-H`]: ../usage/#datasource-header-h
[`defineDatasource`]: ../functions/data/#definedatasource
[`datasource`]: ../functions/data/#datasource
[`include`]: ../functions/data/#include
[`data.CSV`]: ../functions/data/#data-csv
Expand Down
37 changes: 37 additions & 0 deletions docs/content/functions/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,43 @@ $ gomplate -i '{{if (datasourceReachable "test")}}{{datasource "test"}}{{else}}n
no worries
```

## `defineDatasource`

Define a datasource alias with target URL inside the template. Overridden by the [`--datasource/-d`](#--datasource-d) flag.

Note: once a datasource is defined, it can not be redefined (i.e. if this function is called twice with the same alias, only the first applies).

This function can provide a good way to set a default datasource when sharing templates.

See [Datasources](../../datasources) for (much!) more information.

### Usage

```go
defineDatasource alias url
```

### Arguments

| name | description |
|--------|-------|
| `alias` | the datasource alias |
| `url` | the datasource's URL |

### Examples

_`person.json`:_
```json
{ "name": "Dave" }
```

```console
$ gomplate -i '{{ defineDatasource "person" "person.json" }}Hello {{ (ds "person").name }}'
Hello Dave
$ FOO='{"name": "Daisy"}' gomplate -d person=env:///FOO -i '{{ defineDatasource "person" "person.json" }}Hello {{ (ds "person").name }}'
Hello Daisy
```

## `ds`

Alias to [`datasource`](#datasource)
Expand Down
1 change: 1 addition & 0 deletions funcs/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func AddDataFuncs(f map[string]interface{}, d *data.Data) {
f["ds"] = d.Datasource
f["datasourceExists"] = d.DatasourceExists
f["datasourceReachable"] = d.DatasourceReachable
f["defineDatasource"] = d.DefineDatasource
f["include"] = d.Include

f["data"] = DataNS
Expand Down
5 changes: 5 additions & 0 deletions test/integration/datasources_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ func (s *FileDatasourcesSuite) TestFileDatasources(c *C) {
)
result.Assert(c, icmd.Expected{ExitCode: 0, Out: "baz"})

result = icmd.RunCommand(GomplateBin,
"-i", `{{defineDatasource "config" "`+s.tmpDir.Join("config.json")+`"}}{{(datasource "config").foo.bar}}`,
)
result.Assert(c, icmd.Expected{ExitCode: 0, Out: "baz"})

result = icmd.RunCommand(GomplateBin,
"-d", "config="+s.tmpDir.Join("config.yml"),
"-i", `{{(datasource "config").foo.bar}}`,
Expand Down
5 changes: 5 additions & 0 deletions test/integration/datasources_http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@ func (s *DatasourcesHTTPSuite) TestReportsVersion(c *C) {
"-H", "foo=Foo:bar",
"-i", "{{ index (ds `foo`).headers.Foo 0 }}")
result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"})

result = icmd.RunCommand(GomplateBin,
"-H", "foo=Foo:bar",
"-i", "{{defineDatasource `foo` `http://"+s.l.Addr().String()+"/`}}{{ index (ds `foo`).headers.Foo 0 }}")
result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"})
}

0 comments on commit 7936db5

Please sign in to comment.