diff --git a/examples/postgres/.gitignore b/examples/postgres/.gitignore new file mode 100644 index 000000000..ceff812c8 --- /dev/null +++ b/examples/postgres/.gitignore @@ -0,0 +1,4 @@ +/func +vendor + +func.yaml \ No newline at end of file diff --git a/examples/postgres/README.md b/examples/postgres/README.md new file mode 100644 index 000000000..4b8c2d105 --- /dev/null +++ b/examples/postgres/README.md @@ -0,0 +1,25 @@ +# Postgres INSERT/SELECT Function Image + +This function executes an INSERT or SELECT against a table in a given postgres server. + +``` +# Create your func.yaml file +fn init /func-postgres +# Build the function +fn build +# Test it +./test.sh +# Push it to Docker Hub +fn push +# Create routes to this function on IronFunctions +fn apps create --config SERVER= +fn routes create --config TABLE= --config COMMAND=INSERT //insert +fn routes create --config TABLE= --config COMMAND=SELECT //select +``` + +Now you can call your function on IronFunctions: + +``` +echo | fn call ///insert +echo | fn call ///select +``` \ No newline at end of file diff --git a/examples/postgres/func.go b/examples/postgres/func.go new file mode 100644 index 000000000..b95d18a03 --- /dev/null +++ b/examples/postgres/func.go @@ -0,0 +1,103 @@ +package main + +import ( + "bytes" + "database/sql" + "encoding/json" + "io/ioutil" + "log" + "os" + "strconv" + + "github.com/pkg/errors" + + _ "github.com/lib/pq" +) + +var ( + // command to execute, 'SELECT' or 'INSERT' + command = os.Getenv("HEADER_COMMAND") + // postgres host:port, e.g. 'postgres:5432' + server = os.Getenv("HEADER_SERVER") + // postgres table name + table = os.Getenv("HEADER_TABLE") +) + +func main() { + req, err := ioutil.ReadAll(os.Stdin) + if err != nil { + log.Fatal(errors.Wrap(err, "failed to read stdin")) + } + + db, err := sql.Open("postgres", "postgres://postgres@" + server + "?sslmode=disable") + if err != nil { + log.Println("Failed to connect to postgres server") + log.Fatal(err) + return + } + + switch command { + case "SELECT": + if resp, err := selectCommand(req, db); err != nil { + log.Fatal(errors.Wrap(err, "select command failed")) + } else { + log.Println(resp) + } + case "INSERT": + if err := insertCommand(req, db); err != nil { + log.Fatal(errors.Wrap(err, "insert command failed")) + } + default: + log.Fatalf("invalid command: %q", command) + } +} + +func selectCommand(req []byte, db *sql.DB) (string, error) { + // Parse request JSON + var params map[string]interface{} + if err := json.Unmarshal(req, ¶ms); err != nil { + return "", errors.Wrap(err, "failed to parse json") + } + + // Build query and gather arguments + var query bytes.Buffer + var args []interface{} + + query.WriteString("SELECT json_agg(t) FROM (SELECT * FROM ") + query.WriteString(table) + query.WriteString(" WHERE") + first := true + arg := 1 + for k, v := range params { + args = append(args, v) + + if !first { + query.WriteString(" AND") + } + query.WriteString(" ") + query.WriteString(k) + query.WriteString("=$") + query.WriteString(strconv.Itoa(arg)) + arg += 1 + first = false + } + query.WriteString(") AS t") + + // Execute query + r := db.QueryRow(query.String(), args...) + var resp string + if err := r.Scan(&resp); err != nil { + return "", errors.Wrap(err, "failed to execute select query") + } + + return resp, nil +} + +func insertCommand(req []byte, db *sql.DB) error { + q := "INSERT INTO " + table + " SELECT * FROM json_populate_record(null::" + table + ", $1)" + _, err := db.Exec(q, req) + if err != nil { + return errors.Wrap(err, "Failed to execute insert query") + } + return nil +} diff --git a/examples/postgres/glide.lock b/examples/postgres/glide.lock new file mode 100644 index 000000000..1ce0b098e --- /dev/null +++ b/examples/postgres/glide.lock @@ -0,0 +1,10 @@ +hash: 12ce0fe3e599891c7aeb6d5a99ff107b4f979e88ff5da7a6d5b1cf5cac8b1ad7 +updated: 2017-01-20T22:48:30.561728713Z +imports: +- name: github.com/lib/pq + version: 67c3f2a8884c9b1aac5503c8d42ae4f73a93511c + subpackages: + - oid +- name: github.com/pkg/errors + version: 248dadf4e9068a0b3e79f02ed0a610d935de5302 +testImports: [] diff --git a/examples/postgres/glide.yaml b/examples/postgres/glide.yaml new file mode 100644 index 000000000..812a5b200 --- /dev/null +++ b/examples/postgres/glide.yaml @@ -0,0 +1,4 @@ +package: github.com/iron-io/functions/examples/postgres +import: +- package: github.com/lib/pq +- package: github.com/pkg/errors diff --git a/examples/postgres/test.sh b/examples/postgres/test.sh new file mode 100755 index 000000000..912242de2 --- /dev/null +++ b/examples/postgres/test.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -x + +docker stop test-postgres-func +docker rm test-postgres-func + +docker run -p 5432:5432 --name test-postgres-func -d postgres +sleep 5s + +docker run --rm -i --link test-postgres-func:postgres postgres psql -h postgres -U postgres -c 'CREATE TABLE people (first TEXT, last TEXT, age INTEGER);' + +RECORD1='{ + "first": "John", + "last": "Smith", + "age": 30 +}' +echo $RECORD1 | fn run -e SERVER=postgres:5432 -e COMMAND=INSERT -e TABLE=people --link test-postgres-func:postgres +QUERY1='{ + "last": "Smith" +}' +echo $QUERY1 | fn run -e SERVER=postgres:5432 -e COMMAND=SELECT -e TABLE=people --link test-postgres-func:postgres + +RECORD2='{ + "first": "Bob", + "last": "Smith", + "age": 43 +}' +echo $RECORD2 | fn run -e SERVER=postgres:5432 -e COMMAND=INSERT -e TABLE=people --link test-postgres-func:postgres + +echo $QUERY1 | fn run -e SERVER=postgres:5432 -e COMMAND=SELECT -e TABLE=people --link test-postgres-func:postgres + +QUERY2='{ + "first": "John", + "last": "Smith" +}' +echo $QUERY2 | fn run -e SERVER=postgres:5432 -e COMMAND=SELECT -e TABLE=people --link test-postgres-func:postgres + +docker stop test-postgres-func +docker rm test-postgres-func \ No newline at end of file