summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/datasource.go20
-rw-r--r--data/datasource_test.go21
-rw-r--r--docs/content/functions/data.md21
3 files changed, 61 insertions, 1 deletions
diff --git a/data/datasource.go b/data/datasource.go
index 9824e41f..ef693761 100644
--- a/data/datasource.go
+++ b/data/datasource.go
@@ -3,6 +3,7 @@ package data
import (
"errors"
"fmt"
+ "io"
"io/ioutil"
"log"
"mime"
@@ -22,6 +23,9 @@ import (
// logFatal is defined so log.Fatal calls can be overridden for testing
var logFatalf = log.Fatalf
+// stdin - for overriding in tests
+var stdin io.Reader
+
func regExtension(ext, typ string) {
err := mime.AddExtensionType(ext, typ)
if err != nil {
@@ -43,6 +47,7 @@ func init() {
addSourceReader("http", readHTTP)
addSourceReader("https", readHTTP)
addSourceReader("file", readFile)
+ addSourceReader("stdin", readStdin)
addSourceReader("vault", readVault)
addSourceReader("consul", readConsul)
addSourceReader("consul+http", readConsul)
@@ -157,6 +162,9 @@ func ParseSource(value string) (*Source, error) {
srcURL = absURL(f)
} else if len(parts) == 2 {
alias = parts[0]
+ if parts[1] == "-" {
+ parts[1] = "stdin://"
+ }
var err error
srcURL, err = url.Parse(parts[1])
if err != nil {
@@ -296,6 +304,18 @@ func readFile(source *Source, args ...string) ([]byte, error) {
return b, nil
}
+func readStdin(source *Source, args ...string) ([]byte, error) {
+ if stdin == nil {
+ stdin = os.Stdin
+ }
+ b, err := ioutil.ReadAll(stdin)
+ if err != nil {
+ log.Printf("Can't read %v: %#v", stdin, err)
+ return nil, err
+ }
+ return b, nil
+}
+
func readHTTP(source *Source, args ...string) ([]byte, error) {
if source.HC == nil {
source.HC = &http.Client{Timeout: time.Second * 5}
diff --git a/data/datasource_test.go b/data/datasource_test.go
index a3b6f31e..ac7b884a 100644
--- a/data/datasource_test.go
+++ b/data/datasource_test.go
@@ -9,6 +9,7 @@ import (
"net/http"
"net/http/httptest"
"net/url"
+ "strings"
"testing"
"github.com/blang/vfs"
@@ -307,3 +308,23 @@ func TestInclude(t *testing.T) {
actual := data.Include("foo")
assert.Equal(t, contents, actual)
}
+
+type errorReader struct{}
+
+func (e errorReader) Read(p []byte) (n int, err error) {
+ return 0, fmt.Errorf("error")
+}
+
+func TestReadStdin(t *testing.T) {
+ defer func() {
+ stdin = nil
+ }()
+ stdin = strings.NewReader("foo")
+ out, err := readStdin(nil)
+ assert.NoError(t, err)
+ assert.Equal(t, []byte("foo"), out)
+
+ stdin = errorReader{}
+ _, err = readStdin(nil)
+ assert.Error(t, err)
+}
diff --git a/docs/content/functions/data.md b/docs/content/functions/data.md
index 18bb4f7c..0243b25b 100644
--- a/docs/content/functions/data.md
+++ b/docs/content/functions/data.md
@@ -11,7 +11,7 @@ A collection of functions that retrieve, parse, and convert structured data.
Parses a given datasource (provided by the [`--datasource/-d`](#--datasource-d) argument).
-Currently, `file://`, `http://`, `https://`, and `vault://` URLs are supported.
+Currently, `file://`, `stdin://`, `http://`, `https://`, and `vault://` URLs are supported.
Currently-supported formats are JSON, YAML, TOML, and CSV.
@@ -34,6 +34,25 @@ $ gomplate -d person.json < input.tmpl
Hello Dave
```
+### Providing datasources on standard input (`stdin`)
+
+Normally `stdin` is used as the input for the template, but it can also be used
+to provide datasources. To do this, specify a URL with the `stdin:` scheme:
+
+```console
+$ echo 'foo: bar' | gomplate -i '{{(ds "data").foo}}' -d data=stdin:///foo.yaml
+bar
+```
+
+Note that the URL must have a file name with a supported extension in order for
+the input to be correctly parsed. If no parsing is required (i.e. if the data
+is being included verbatim with the include function), just `stdin:` is enough:
+
+```console
+$ echo 'foo' | gomplate -i '{{ include "data" }}' -d data=stdin:
+foo
+```
+
### Usage with HTTP data
```console