caddycfg

package module
v0.0.9 Latest Latest
Warning

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

Go to latest
Published: Nov 16, 2022 License: MIT Imports: 20 Imported by: 0

README

GoDoc

A small library that helps modify Caddy server's configuration from a web app.

Installation

go get -u github.com/zzwx/caddycfg

Motivation

Maintaining Caddy global configuration for several web apps may become cumbersome. Having each web app directly configure Caddy makes it possible to completely stop thinking about updating Caddy manually.

This is a work-in-progress project. Feel free to comment or enhance with pull requests.

Tests Notes

The tests assume that a Caddy server is running on the testing site.

Usage Example

func runCaddyConfRefresher() {
	if !cfg.CaddyCfg.Enabled {
		fmt.Printf("Skipping [caddycfg] injection due to [caddycfg].enabled = false")
		return
	}
	fmt.Printf("[caddycfg] injection enabled!")
	modification := func() {
		instance := caddycfg.NewCaddyCfg(caddycfg.DefaultConfigURL)
		err := instance.AddRoute(
			cfg.CaddyCfg.ServerKey,
			cfg.CaddyCfg.RouteId,
			caddycfg.ReverseProxyCaddyRouteConf(
				cfg.Server.Port,
				cfg.CaddyCfg.MatchHosts,
				cfg.CaddyCfg.PathMatch,
			))
		if err != nil {
			fmt.Printf("error changing Caddy configuration: %v\n", err)
		}
	}
	// This ensures that if Caddy is restarted or initiated later than the app,
	// configuration will still reach it within this max interval.
	go caddycfg.Refresher(time.Second*4, modification)
}

Documentation

Index

Examples

Constants

View Source
const CaddyConfigURL = "http://localhost:2019"

Variables

View Source
var (
	// ErrNotFoundID is a base error to what errNotFoundID leads to when unwrapped,
	// in order to check with errors.Is(err, ErrNotFoundID)
	ErrNotFoundID = errors.New("unknown object ID")
)

Functions

func BaseConfig

func BaseConfig(configURL string, serverKey string) string

BaseConfig returns a JSON string of a base configuration with :443 listen port and empty routes array:

	"admin": { "listen": <caddyCfg.configURL> { ...
	"apps"."http"."servers" { "<serverKey>": ...
	                          "listen": [":443"]
                           "routes": []

This can be passed to CaddyCfg.Upload as initial empty configuration that might be later enhanced with routes.

func EncodeAtId

func EncodeAtId(id string) string

EncodeAtId returns "@id":"<id>" encoded with proper character escaping for the id field.

Example
stripped := EncodeAtId("test")
fmt.Println(stripped)
Output:

"@id":"test"

func EncodeJSONString

func EncodeJSONString(value string) string

EncodeJSONString returns "value" properly escaped for JSON and surrounded with quotes.

func JoinURLPath

func JoinURLPath(url_ string, paths ...string) string

JoinURLPath ignores any url_ parsing errors

Example
fmt.Println(JoinURLPath("http://localhost:2019", "test"))
fmt.Println(JoinURLPath("http://localhost:2019/", "test"))
fmt.Println(JoinURLPath("http://localhost:2019/in", "test", "where", "to", "go"))
fmt.Println(JoinURLPath("http://localhost:2019/in", "test/where/to/go"))
fmt.Println(JoinURLPath("", "test"))
Output:

http://localhost:2019/test
http://localhost:2019/test
http://localhost:2019/in/test/where/to/go
http://localhost:2019/in/test/where/to/go
test

func Refresher added in v0.0.2

func Refresher(refreshDelay time.Duration, refresh func())

Refresher calls the passed refresh first immediately and then continuously after refreshDelay.

This will likely run in a separate Goroutine.

func ReverseProxyCaddyRouteConf

func ReverseProxyCaddyRouteConf(backendPort int, matchHosts []string, pathMatch string) *caddyhttp.Route

ReverseProxyCaddyRouteConf generates a "routes" (https://caddyserver.com/docs/json/apps/http/servers/routes/) element configuration structure. Returned route may be consumed as-is in the next steps or marshalled for Caddy using either json.Marshal or json.MarshalIndent:

m, err := json.MarshalIndent(route, "", "\t")

pathMatch is usually "/*" for matching any paths.

Example
r := ReverseProxyCaddyRouteConf(
	8080,
	[]string{
		"example.com",
		"www.example.com",
	}, "/*",
)
s, err := json.MarshalIndent(r, "", "\t")
if err != nil {
	panic(err)
}
fmt.Println(string(s))
Output:

{
	"match": [
		{
			"host": [
				"example.com",
				"www.example.com"
			],
			"path": [
				"/*"
			]
		}
	],
	"handle": [
		{
			"handler": "reverse_proxy",
			"transport": {
				"protocol": "http"
			},
			"upstreams": [
				{
					"dial": "localhost:8080"
				}
			]
		}
	]
}

func RouteConfigsEqual added in v0.0.3

func RouteConfigsEqual(cfg0, cfg1 string) bool

RouteConfigsEqual compares two configurations that can be decoded into RouteConfigType, allowing for shuffled named parameters.

Types

type CaddyCfg

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

func NewCaddyCfg

func NewCaddyCfg(configURL string) *CaddyCfg

NewCaddyCfg creates Caddy's configuration, with Caddy configuration url as argument.

For default "http://localhost:2019" configuration use NewCaddyCfg(CaddyConfigURL).

func (*CaddyCfg) AddRoute

func (caddyCfg *CaddyCfg) AddRoute(serverKey string, routeId string, routeConfig *caddyhttp.Route) error

AddRoute ensures that configuration marked by unique route config "@id" field specified by routeId enters Caddy's configuration. A good candidate for routeId is a domain name.

To avoid any downtime, this function first pokes Caddy for current configuration on "@id" to see if it matches routeConfig either byte-to-byte or by structure. This allows to skip unnecessary deletion/addition described below.

In case configuration is not found or doesn't match, it will be attempted to be deleted using "@id" key (ignoring errors) and then get added, to keep only one configuration for this routeId.

serverKey is an arbitrary name in the base configuration for the "apps"."http"."servers" entry. Default value is usually "myserver".

Look up base configuration for the right key.

{
	"apps": {
		"http": {
			"servers": {
				"<serverKey>":

func (*CaddyCfg) Config

func (caddyCfg *CaddyCfg) Config() (string, error)

Config returns full configuration of CaddyCfg, including root node. Trailing "\n" will be removed.

func (*CaddyCfg) ConfigById

func (caddyCfg *CaddyCfg) ConfigById(id string) (string, error)

ConfigById returns configuration section belonging to a marked by "@id" section in a JSON string format. Trailing "\n" will be removed.

If not finding the object by id error occurs, it will be converted into a errNotFoundID.

func (*CaddyCfg) DeleteById

func (caddyCfg *CaddyCfg) DeleteById(id string) error

DeleteById attempts to delete a config by specified id. In theory this should work for any section of configuration, but here it's only used to remove routes.

If not finding the object by id error occurs, it will be converted into a errNotFoundID.

func (*CaddyCfg) Upload

func (caddyCfg *CaddyCfg) Upload(configJSON string) error

Upload (in Caddy terms "load") is sending full configuration that will replace the existing one completely. It might be good for a base configuration.

func (*CaddyCfg) UploadTo

func (caddyCfg *CaddyCfg) UploadTo(configURL string, configJSON string) error

UploadTo does the same as Upload, only to a custom configURL, which usually equals CaddyConfigURL. This allows for uploading a new configuration on top of an empty `caddy run` that started with a 'null' configuration.

If configJSON contains a new "admin:listen" section, it seems to retarget Caddy's configURL to it for any next configuration manipulations.

type IDField

type IDField struct {
	Id string `json:"@id"`
}

type RouteConfigType added in v0.0.3

type RouteConfigType struct {
	// TODO: It must eventually grow to fill the gaps
	Id    string `json:"@id"`
	Match []struct {
		Host []string `json:"host"`
		Path []string `json:"path"`
	} `json:"match"`
	Handle []struct {
		Handler   string `json:"handler"`
		Transport struct {
			Protocol string `json:"protocol"`
		} `json:"transport"`
		Upstreams []struct {
			Dial string `json:"dial"`
		} `json:"upstreams"`
	} `json:"handle"`
}

RouteConfigType is used to compare route configurations.

Jump to

Keyboard shortcuts

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