summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/commandfuncs.go122
-rw-r--r--cmd/commands.go56
2 files changed, 154 insertions, 24 deletions
diff --git a/cmd/commandfuncs.go b/cmd/commandfuncs.go
index be9da93..715f064 100644
--- a/cmd/commandfuncs.go
+++ b/cmd/commandfuncs.go
@@ -26,6 +26,8 @@ import (
"net/http"
"os"
"os/exec"
+ "reflect"
+ "runtime/debug"
"strings"
"github.com/caddyserver/caddy/v2"
@@ -36,7 +38,7 @@ import (
func cmdStart(fl Flags) (int, error) {
startCmdConfigFlag := fl.String("config")
- startCmdConfigAdapterFlag := fl.String("config-adapter")
+ startCmdConfigAdapterFlag := fl.String("adapter")
// open a listener to which the child process will connect when
// it is ready to confirm that it has successfully started
@@ -61,7 +63,7 @@ func cmdStart(fl Flags) (int, error) {
cmd.Args = append(cmd.Args, "--config", startCmdConfigFlag)
}
if startCmdConfigAdapterFlag != "" {
- cmd.Args = append(cmd.Args, "--config-adapter", startCmdConfigAdapterFlag)
+ cmd.Args = append(cmd.Args, "--adapter", startCmdConfigAdapterFlag)
}
stdinpipe, err := cmd.StdinPipe()
if err != nil {
@@ -137,8 +139,8 @@ func cmdStart(fl Flags) (int, error) {
func cmdRun(fl Flags) (int, error) {
runCmdConfigFlag := fl.String("config")
- runCmdConfigAdapterFlag := fl.String("config-adapter")
- runCmdPrintEnvFlag := fl.Bool("print-env")
+ runCmdConfigAdapterFlag := fl.String("adapter")
+ runCmdPrintEnvFlag := fl.Bool("environ")
runCmdPingbackFlag := fl.String("pingback")
// if we are supposed to print the environment, do that first
@@ -216,7 +218,7 @@ func cmdStop(_ Flags) (int, error) {
func cmdReload(fl Flags) (int, error) {
reloadCmdConfigFlag := fl.String("config")
- reloadCmdConfigAdapterFlag := fl.String("config-adapter")
+ reloadCmdConfigAdapterFlag := fl.String("adapter")
reloadCmdAddrFlag := fl.String("address")
// a configuration is required
@@ -282,10 +284,61 @@ func cmdVersion(_ Flags) (int, error) {
return caddy.ExitCodeSuccess, nil
}
-func cmdListModules(_ Flags) (int, error) {
- for _, m := range caddy.Modules() {
- fmt.Println(m)
+func cmdListModules(fl Flags) (int, error) {
+ versions := fl.Bool("versions")
+
+ bi, ok := debug.ReadBuildInfo()
+ if !ok || !versions {
+ // if there's no build information,
+ // just print out the modules
+ for _, m := range caddy.Modules() {
+ fmt.Println(m)
+ }
+ return caddy.ExitCodeSuccess, nil
+ }
+
+ for _, modName := range caddy.Modules() {
+ modInfo, err := caddy.GetModule(modName)
+ if err != nil {
+ // that's weird
+ fmt.Println(modName)
+ continue
+ }
+
+ // to get the Caddy plugin's version info, we need to know
+ // the package that the Caddy module's value comes from; we
+ // can use reflection but we need a non-pointer value (I'm
+ // not sure why), and since New() should return a pointer
+ // value, we need to dereference it first
+ iface := interface{}(modInfo.New())
+ if rv := reflect.ValueOf(iface); rv.Kind() == reflect.Ptr {
+ iface = reflect.New(reflect.TypeOf(iface).Elem()).Elem().Interface()
+ }
+ modPkgPath := reflect.TypeOf(iface).PkgPath()
+
+ // now we find the Go module that the Caddy module's package
+ // belongs to; we assume the Caddy module package path will
+ // be prefixed by its Go module path, and we will choose the
+ // longest matching prefix in case there are nested modules
+ var matched *debug.Module
+ for _, dep := range bi.Deps {
+ if strings.HasPrefix(modPkgPath, dep.Path) {
+ if matched == nil || len(dep.Path) > len(matched.Path) {
+ matched = dep
+ }
+ }
+ }
+
+ // if we could find no matching module, just print out
+ // the module name instead
+ if matched == nil {
+ fmt.Println(modName)
+ continue
+ }
+
+ fmt.Printf("%s %s\n", modName, matched.Version)
}
+
return caddy.ExitCodeSuccess, nil
}
@@ -295,13 +348,13 @@ func cmdEnviron(_ Flags) (int, error) {
}
func cmdAdaptConfig(fl Flags) (int, error) {
+ adaptCmdInputFlag := fl.String("config")
adaptCmdAdapterFlag := fl.String("adapter")
- adaptCmdInputFlag := fl.String("input")
adaptCmdPrettyFlag := fl.Bool("pretty")
if adaptCmdAdapterFlag == "" || adaptCmdInputFlag == "" {
return caddy.ExitCodeFailedStartup,
- fmt.Errorf("usage: caddy adapt-config --adapter <name> --input <file>")
+ fmt.Errorf("usage: caddy adapt --adapter <name> --input <file>")
}
cfgAdapter := caddyconfig.GetAdapter(adaptCmdAdapterFlag)
@@ -341,6 +394,55 @@ func cmdAdaptConfig(fl Flags) (int, error) {
return caddy.ExitCodeSuccess, nil
}
+func cmdValidateConfig(fl Flags) (int, error) {
+ validateCmdConfigFlag := fl.String("config")
+ validateCmdAdapterFlag := fl.String("adapter")
+
+ input, err := ioutil.ReadFile(validateCmdConfigFlag)
+ if err != nil {
+ return caddy.ExitCodeFailedStartup,
+ fmt.Errorf("reading input file: %v", err)
+ }
+
+ if validateCmdAdapterFlag != "" {
+ cfgAdapter := caddyconfig.GetAdapter(validateCmdAdapterFlag)
+ if cfgAdapter == nil {
+ return caddy.ExitCodeFailedStartup,
+ fmt.Errorf("unrecognized config adapter: %s", validateCmdAdapterFlag)
+ }
+
+ adaptedConfig, warnings, err := cfgAdapter.Adapt(input, nil)
+ if err != nil {
+ return caddy.ExitCodeFailedStartup, err
+ }
+ // print warnings to stderr
+ for _, warn := range warnings {
+ msg := warn.Message
+ if warn.Directive != "" {
+ msg = fmt.Sprintf("%s: %s", warn.Directive, warn.Message)
+ }
+ log.Printf("[WARNING][%s] %s:%d: %s", validateCmdAdapterFlag, warn.File, warn.Line, msg)
+ }
+
+ input = adaptedConfig
+ }
+
+ var cfg *caddy.Config
+ err = json.Unmarshal(input, &cfg)
+ if err != nil {
+ return caddy.ExitCodeFailedStartup, fmt.Errorf("decoding config: %v", err)
+ }
+
+ err = caddy.Validate(cfg)
+ if err != nil {
+ return caddy.ExitCodeFailedStartup, err
+ }
+
+ fmt.Println("Valid configuration")
+
+ return caddy.ExitCodeSuccess, nil
+}
+
func cmdHelp(fl Flags) (int, error) {
const fullDocs = `Full documentation is available at:
https://github.com/caddyserver/caddy/wiki/v2:-Documentation`
diff --git a/cmd/commands.go b/cmd/commands.go
index 8a9d482..731e813 100644
--- a/cmd/commands.go
+++ b/cmd/commands.go
@@ -54,7 +54,7 @@ var commands = map[string]Command{
"start": {
Name: "start",
Func: cmdStart,
- Usage: "[--config <path>] [--config-adapter <name>]",
+ Usage: "[--config <path>] [--adapter <name>]",
Short: "Starts the Caddy process and returns after server has started.",
Long: `
Starts the Caddy process, optionally bootstrapped with an initial
@@ -65,7 +65,7 @@ details.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("start", flag.ExitOnError)
fs.String("config", "", "Configuration file")
- fs.String("config-adapter", "", "Name of config adapter to apply")
+ fs.String("adapter", "", "Name of config adapter to apply")
return fs
}(),
},
@@ -73,7 +73,7 @@ details.`,
"run": {
Name: "run",
Func: cmdRun,
- Usage: "[--config <path>] [--config-adapter <name>] [--print-env]",
+ Usage: "[--config <path>] [--adapter <name>] [--print-env]",
Short: `Starts the Caddy process and blocks indefinitely.`,
Long: `
Same as start, but blocks indefinitely; i.e. runs Caddy in "daemon" mode. On
@@ -83,25 +83,25 @@ window.
If a config file is specified, it will be applied immediately after the process
is running. If the config file is not in Caddy's native JSON format, you can
-specify an adapter with --config-adapter to adapt the given config file to
+specify an adapter with --adapter to adapt the given config file to
Caddy's native format. The config adapter must be a registered module. Any
warnings will be printed to the log, but beware that any adaptation without
errors will immediately be used. If you want to review the results of the
-adaptation first, use adapt-config.
+adaptation first, use the 'adapt' subcommand.
As a special case, if the current working directory has a file called
"Caddyfile" and the caddyfile config adapter is plugged in (default), then that
file will be loaded and used to configure Caddy, even without any command line
flags.
-If --print-env is specified, the environment as seen by the Caddy process will
+If --environ is specified, the environment as seen by the Caddy process will
be printed before starting. This is the same as the environ command but does
not quit after printing.`,
Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("run", flag.ExitOnError)
fs.String("config", "", "Configuration file")
- fs.String("config-adapter", "", "Name of config adapter to apply")
- fs.Bool("print-env", false, "Print environment")
+ fs.String("adapter", "", "Name of config adapter to apply")
+ fs.Bool("environ", false, "Print environment")
fs.String("pingback", "", "Echo confirmation bytes to this address on success")
return fs
}(),
@@ -120,7 +120,7 @@ shutdown on Windows, use Ctrl+C or the /stop endpoint.`,
"reload": {
Name: "reload",
Func: cmdReload,
- Usage: "--config <path> [--config-adapter <name>] [--address <interface>]",
+ Usage: "--config <path> [--adapter <name>] [--address <interface>]",
Short: "Gives the running Caddy instance a new configuration",
Long: `Gives the running Caddy instance a new configuration. This has the same effect
as POSTing a document to the /load endpoint, but is convenient for simple
@@ -129,9 +129,9 @@ configurable, the endpoint configuration is loaded from the --address flag if
specified; otherwise it is loaded from the given config file; otherwise the
default is assumed.`,
Flags: func() *flag.FlagSet {
- fs := flag.NewFlagSet("load", flag.ExitOnError)
+ fs := flag.NewFlagSet("reload", flag.ExitOnError)
fs.String("config", "", "Configuration file")
- fs.String("config-adapter", "", "Name of config adapter to apply")
+ fs.String("adapter", "", "Name of config adapter to apply")
fs.String("address", "", "Address of the administration listener, if different from config")
return fs
}(),
@@ -149,6 +149,11 @@ default is assumed.`,
Func: cmdListModules,
Short: "List installed Caddy modules.",
Long: `List installed Caddy modules.`,
+ Flags: func() *flag.FlagSet {
+ fs := flag.NewFlagSet("list-modules", flag.ExitOnError)
+ fs.Bool("versions", false, "Print version information")
+ return fs
+ }(),
},
"environ": {
@@ -158,15 +163,38 @@ default is assumed.`,
Long: `Prints the environment as seen by Caddy.`,
},
- "adapt-config": {
- Name: "adapt-config",
+ "adapt": {
+ Name: "adapt",
Func: cmdAdaptConfig,
- Usage: "--input <path> --adapter <name> [--pretty]",
+ Usage: "--config <path> --adapter <name> [--pretty]",
Short: "Adapts a configuration to Caddy's native JSON config structure",
Long: `
Adapts a configuration to Caddy's native JSON config structure and writes the
output to stdout, along with any warnings to stderr. If --pretty is specified,
the output will be formatted with indentation for human readability.`,
+ Flags: func() *flag.FlagSet {
+ fs := flag.NewFlagSet("adapt", flag.ExitOnError)
+ fs.String("config", "", "Configuration file to adapt")
+ fs.String("adapter", "", "Name of config adapter")
+ fs.Bool("pretty", false, "Format the output for human readability")
+ return fs
+ }(),
+ },
+
+ "validate": {
+ Name: "validate",
+ Func: cmdValidateConfig,
+ Usage: "--config <path> [--adapter <name>]",
+ Short: "Tests whether a configuration file is valid.",
+ Long: `
+Loads and provisions the provided config, but does not start
+running it.`,
+ Flags: func() *flag.FlagSet {
+ fs := flag.NewFlagSet("load", flag.ExitOnError)
+ fs.String("config", "", "Input configuration file")
+ fs.String("adapter", "", "Name of config adapter")
+ return fs
+ }(),
},
}