Skip to content

Commit

Permalink
cmd/tailsql: generate sample config as HuJSON
Browse files Browse the repository at this point in the history
Generate the sample config from a template instead of using the raw JSON
document, so that we can include explanatory comments.
  • Loading branch information
creachadair committed Oct 4, 2023
1 parent 930ccbe commit 4fc1afb
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 10 deletions.
43 changes: 43 additions & 0 deletions cmd/tailsql/sample.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Example tailsql configuration file.
// This file is in HuJSON (JWCC) format; plain JSON also works.
{
@{ with .Hostname }@// The tailnet hostname the server should run on.
"hostname": "@{.}@",@{ end }@
@{ with .LocalState }@
// If set, a SQLite database URL to use for the query log.
"localState": "@{.}@",@{ end }@
@{ with .LocalSource }@
// If localState is defined, export a read-only copy of the local state
// database as a source with this name.
"localSource": "@{.}@",@{ end }@
@{ if .Sources }@
// Databases that the server will allow queries against.
"sources": [@{ range .Sources }@
{
// The name that selects this database in queries (src=name).
"source": "@{ .Source }@",

// A human-readable description for the database.
"label": "@{ .Label }@",

// The name of the database/sql driver to use for this source.
"driver": "@{ .Driver }@",
@{ if .Named }@
// Named queries supported by this database (q=named:x).
"named": {@{ range $name, $query := .Named }@
"@{ $name }@": "@{ $query }@",
@{ end }@},@{ end }@

// The database/sql connection string for the database.
"url": "@{ .URL }@",
},
@{ end }@],@{ end }@
@{ if .UILinks }@
// Additional links to display in the UI.
"links": [@{ range .UILinks }@
{
"anchor": "@{ .Anchor }@",
"url": "@{ .URL }@",
},@{ end }@
],@{ end }@
}
33 changes: 23 additions & 10 deletions cmd/tailsql/tailsql.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
package main

import (
"bytes"
"context"
"encoding/json"
"errors"
"expvar"
"flag"
"fmt"
"html/template"
"log"
"net/http"
"os"
Expand All @@ -21,10 +22,13 @@ import (
"github.com/creachadair/ctrl"
"github.com/tailscale/tailsql/server/tailsql"
"github.com/tailscale/tailsql/uirules"
"tailscale.com/atomicfile"
"tailscale.com/tsnet"
"tailscale.com/tsweb"
"tailscale.com/types/logger"

_ "embed"

// If you want to support other source types with this tool, you will need
// to import other database drivers below.

Expand Down Expand Up @@ -197,14 +201,16 @@ func runTailscaleService(ctx context.Context, opts tailsql.Options) error {
return tsNode.Close()
}

//go:embed sample.tmpl
var sampleConfig string

func generateBasicConfig(path string) error {
f, err := os.Create(path)
t, err := template.New("sample").Delims("@{", "}@").Parse(sampleConfig)
if err != nil {
ctrl.Fatalf("Create config: %v", err)
ctrl.Fatalf("Parse config template: %v", err)
}
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
eerr := enc.Encode(tailsql.Options{
var buf bytes.Buffer
if err := t.Execute(&buf, tailsql.Options{
Hostname: "tailsql-dev",
LocalState: "tailsql-state.db",
LocalSource: "self",
Expand All @@ -221,10 +227,17 @@ func generateBasicConfig(path string) error {
Anchor: "source code",
URL: "https://github.com/tailscale/tailsql",
}},
})
cerr := f.Close()
if err := errors.Join(eerr, cerr); err != nil {
ctrl.Fatalf("Write config: %v", err)
}); err != nil {
ctrl.Fatalf("Generate sample config: %v", err)
}

// Verify that the generated sample is valid, in case the template got broken.
//lint:ignore S1030 We clone the data because HuJSON clobbers its input slice.
if err := tailsql.UnmarshalOptions([]byte(buf.String()), new(tailsql.Options)); err != nil {
ctrl.Fatalf("Verifying sample config: %v", err)
}
if err := atomicfile.WriteFile(path, buf.Bytes(), 0644); err != nil {
ctrl.Fatalf("Writing sample config: %v", err)
}
log.Printf("Generated sample config in %s", path)
return nil
Expand Down

0 comments on commit 4fc1afb

Please sign in to comment.