tailsql

package
v0.0.0-...-820559f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 18, 2024 License: BSD-3-Clause Imports: 30 Imported by: 0

Documentation

Overview

Package tailsql implements an HTTP API and "playground" UI for sending SQL queries to a collection of local and/or remote databases, and rendering the results for human consumption.

API

The main UI is served from "/", static assets from "/static/". The following path and query parameters are understood:

  • The q parameter carries an SQL query. The syntax of the query depends on the src (see below). See also "Named Queries" below.

  • The src parameter names which database to query against. Its values are defined when the server is set up. If src is omitted, the first database is used as a default.

  • "/" serves output as HTML for the UI. In this format the query (q) may be empty (no output will be displayed).

  • "/json" serves output as JSON objects, one per row. In this format the query (q) must be non-empty.

  • "/csv" serves output as comma-separated values, the first line giving the column names, the remaining lines the rows. In this format the query (q) must be non-empty.

  • "/meta" serves a JSON blob of metadata about available data sources.

Calls to the /json endpoint must set the Sec-Tailsql header to "1". This prevents browser scripts from directing queries to this endpoint.

Calls to /csv must either set Sec-Tailsql to "1" or include a tailsqlQuery=1 same-site cookie.

Calls to the UI with a non-empty query must include the tailsqlQuery=1 same-site cookie, which is set when the UI first loads. This averts simple cross-site redirection tricks.

Named Queries

The query processor treats a query of the form "named:<string>" as a named query. Named queries are SQL queries pre-defined by the database, to allow users to make semantically stable queries without relying on a specific schema format.

Meta Queries

The query processor treats a query "meta:named" as a meta-query to report the names and content of all named queries, regardless of source.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func UnmarshalOptions

func UnmarshalOptions(data []byte, opts *Options) error

UnmarshalOptions unmarshals a HuJSON Config value into opts.

Types

type DBOptions

type DBOptions struct {
	// Label is a human-readable descriptive label to show to users when
	// rendering this database in a UI.
	Label string

	// NamedQueries is a map from names to SQL query text, that the service
	// should allow as pre-defined queries for this database.
	//
	// Unlike user-saved queries, named queries allow the database to change the
	// query when the underlying schema changes while preserving the semantics
	// the user observes.
	NamedQueries map[string]string
}

DBOptions are optional settings for a database. A nil *DBoptions is ready for use and provides defaults as described.

type DBSpec

type DBSpec struct {
	Source string `json:"source"`           // UI slug
	Label  string `json:"label,omitempty"`  // descriptive label
	Driver string `json:"driver,omitempty"` // e.g., "sqlite", "snowflake"

	// Named is an optional map of named SQL queries the database should expose.
	Named map[string]string `json:"named,omitempty"`

	URL     string `json:"url,omitempty"`     // path or connection URL
	KeyFile string `json:"keyFile,omitempty"` // path to key file
	Secret  string `json:"secret,omitempty"`  // name of secret
}

A DBSpec describes a database that the server should use.

type Duration

type Duration time.Duration

Duration is a wrapper for a time.Duration that allows it to marshal more legibly in JSON, using the standard Go notation.

func (Duration) Duration

func (d Duration) Duration() time.Duration

Duration converts d to a standard time.Duration.

func (Duration) MarshalText

func (d Duration) MarshalText() ([]byte, error)

func (*Duration) UnmarshalText

func (d *Duration) UnmarshalText(data []byte) error

type LocalClient

type LocalClient interface {
	WhoIs(context.Context, string) (*apitype.WhoIsResponse, error)
}

LocalClient is the subset of the tailscale.LocalClient interface required by the Server. It is defined here so that the caller can substitute a fake for testing and debugging.

type Options

type Options struct {
	// The tailnet hostname the server should run on (required).
	Hostname string `json:"hostname,omitempty"`

	// The directory for tailscale state and configurations (optional).
	// If omitted or empty, the default location is used.
	StateDir string `json:"stateDir,omitempty"`

	// If true, serve HTTPS instead of HTTP.
	ServeHTTPS bool `json:"serveHTTPS,omitempty"`

	// If non-empty, a SQLite database URL to use for local state.
	LocalState string `json:"localState,omitempty"`

	// If non-empty, and LocalState is defined, export a read-only copy of the
	// local state database as a source with this name.
	LocalSource string `json:"localSource,omitempty"`

	// Databases that the server will allow queries against (optional).
	Sources []DBSpec `json:"sources,omitempty"`

	// Additional links that should be propagated to the UI.
	UILinks []UILink `json:"links,omitempty"`

	// The maximum timeout for a database query (0 means no timeout).
	QueryTimeout Duration `json:"queryTimeout,omitempty"`

	// A connection to tailscaled for authorization checks. If nil, no
	// authorization checks are performed and all requests are permitted.
	LocalClient LocalClient `json:"-"`

	// If non-nil, the server will add metrics to this map. The caller is
	// responsible for ensuring the map is published.
	Metrics *expvar.Map `json:"-"`

	// If non-nil and a LocalClient is available, Authorize is called for each
	// request giving the requested database src and the caller's WhoIs record.
	// If it reports an error, the request is failed.
	//
	// If Authorize is nil and a LocalClient is available, the default rule is
	// to accept any logged-in user, rejecting tagged nodes.
	//
	// If no LocalClient is available, this field is ignored, no authorization
	// checks are performed, and all requests are accepted.
	Authorize func(src string, info *apitype.WhoIsResponse) error `json:"-"`

	// If non-nil, use this store to fetch secret values. This is required if
	// any of the sources specifies a named secret for its connection string.
	SecretStore *setec.Store `json:"-"`

	// Optional rules to apply when rendering text for presentation in the UI.
	// After generating the value string, each rule is matched in order, and the
	// first match (if any) is applied to rewrite the output. The value returned
	// by the rule replaces the original string.
	UIRewriteRules []UIRewriteRule `json:"-"`

	// If non-nil, send logs to this logger. If nil, use log.Printf.
	Logf logger.Logf `json:"-"`
}

Options describes settings for a Server.

func (Options) CheckSources

func (o Options) CheckSources() ([]string, error)

CheckSources validates the sources of o. If this succeeds, it also returns a slice of any secret names required by the specified sources, if any.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is a server for the tailsql API.

func NewServer

func NewServer(opts Options) (*Server, error)

NewServer constructs a new server with the given Options.

func (*Server) Close

func (s *Server) Close() error

Close closes all the database handles held by s and returns the join of their errors.

func (*Server) NewMux

func (s *Server) NewMux() *http.ServeMux

NewMux constructs an HTTP router for the service.

func (*Server) SetDB

func (s *Server) SetDB(source string, db *sql.DB, opts *DBOptions) bool

SetDB adds or replaces the database associated with the specified source in s with the given open db and options.

If a database was already open for the given source, its value is replaced, the old database handle is closed, and SetDB reports true.

If no database was already open for the given source, a new source is added and SetDB reports false.

type UILink struct {
	Anchor string `json:"anchor"`
	URL    string `json:"url"`
}

UILink carries anchor text and a target URL for a hyperlink.

type UIRewriteRule

type UIRewriteRule struct {
	Column *regexp.Regexp // pattern for the column name (nil matches all)
	Value  *regexp.Regexp // pattern for the value (nil matches all)

	// The Apply function takes the name of a column, the input value, and the
	// result of matching the value regexp (if any). Its return value replaces
	// the input when the value is rendered. If Apply == nil, the input is not
	// modified.
	//
	// As a special case, if Apply returns a nil value, the rule evaluator skips
	// the rule as if it had not matched, and goes on to the next rule.
	Apply func(column, input string, valueMatch []string) any
}

UIRewriteRule is a rewriting rule for rendering output in HTML.

A rule matches a value if:

  • Its Column regexp is empty or matches the column name, and
  • Its Value regexp is empty or matches the value string

If a rule matches, its Apply function is called.

func (UIRewriteRule) CheckApply

func (u UIRewriteRule) CheckApply(column, input string) (bool, any)

CheckApply reports whether u matches the specified column and input, and if so returns the result of applying u to it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL