mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 06:03:48 +03:00
cmd: Fix exiting with custom status code, add caddy -v
(#5874)
* Simplify variables for commands * Add --envfile support for adapt command * Carry custom status code for commands to os.Exit() * cmd: add `-v` and `--version` to root caddy command * Add `--envfile` to `caddy environ`, extract flag parsing to func --------- Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
This commit is contained in:
parent
b245ecd325
commit
9c419f1e1a
5 changed files with 167 additions and 109 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,6 +12,7 @@ Caddyfile.*
|
||||||
cmd/caddy/caddy
|
cmd/caddy/caddy
|
||||||
cmd/caddy/caddy.exe
|
cmd/caddy/caddy.exe
|
||||||
cmd/caddy/tmp/*.exe
|
cmd/caddy/tmp/*.exe
|
||||||
|
cmd/caddy/.env
|
||||||
|
|
||||||
# mac specific
|
# mac specific
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
30
cmd/cobra.go
30
cmd/cobra.go
|
@ -1,7 +1,11 @@
|
||||||
package caddycmd
|
package caddycmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
|
@ -95,15 +99,22 @@ https://caddyserver.com/docs/running
|
||||||
// kind of annoying to have all the help text printed out if
|
// kind of annoying to have all the help text printed out if
|
||||||
// caddy has an error provisioning its modules, for instance...
|
// caddy has an error provisioning its modules, for instance...
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
|
Version: onlyVersionText(),
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullDocsFooter = `Full documentation is available at:
|
const fullDocsFooter = `Full documentation is available at:
|
||||||
https://caddyserver.com/docs/command-line`
|
https://caddyserver.com/docs/command-line`
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
rootCmd.SetVersionTemplate("{{.Version}}")
|
||||||
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
|
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onlyVersionText() string {
|
||||||
|
_, f := caddy.Version()
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
func caddyCmdToCobra(caddyCmd Command) *cobra.Command {
|
func caddyCmdToCobra(caddyCmd Command) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: caddyCmd.Name,
|
Use: caddyCmd.Name,
|
||||||
|
@ -123,7 +134,24 @@ func caddyCmdToCobra(caddyCmd Command) *cobra.Command {
|
||||||
// in a cobra command's RunE field.
|
// in a cobra command's RunE field.
|
||||||
func WrapCommandFuncForCobra(f CommandFunc) func(cmd *cobra.Command, _ []string) error {
|
func WrapCommandFuncForCobra(f CommandFunc) func(cmd *cobra.Command, _ []string) error {
|
||||||
return func(cmd *cobra.Command, _ []string) error {
|
return func(cmd *cobra.Command, _ []string) error {
|
||||||
_, err := f(Flags{cmd.Flags()})
|
status, err := f(Flags{cmd.Flags()})
|
||||||
|
if status > 1 {
|
||||||
|
cmd.SilenceErrors = true
|
||||||
|
return &exitError{ExitCode: status, Err: err}
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// exitError carries the exit code from CommandFunc to Main()
|
||||||
|
type exitError struct {
|
||||||
|
ExitCode int
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *exitError) Error() string {
|
||||||
|
if e.Err == nil {
|
||||||
|
return fmt.Sprintf("exiting with status %d", e.ExitCode)
|
||||||
|
}
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
|
@ -42,14 +42,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func cmdStart(fl Flags) (int, error) {
|
func cmdStart(fl Flags) (int, error) {
|
||||||
startCmdConfigFlag := fl.String("config")
|
configFlag := fl.String("config")
|
||||||
startCmdConfigAdapterFlag := fl.String("adapter")
|
configAdapterFlag := fl.String("adapter")
|
||||||
startCmdPidfileFlag := fl.String("pidfile")
|
pidfileFlag := fl.String("pidfile")
|
||||||
startCmdWatchFlag := fl.Bool("watch")
|
watchFlag := fl.Bool("watch")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var startCmdEnvfileFlag []string
|
var envfileFlag []string
|
||||||
startCmdEnvfileFlag, err = fl.GetStringSlice("envfile")
|
envfileFlag, err = fl.GetStringSlice("envfile")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("reading envfile flag: %v", err)
|
fmt.Errorf("reading envfile flag: %v", err)
|
||||||
|
@ -74,23 +74,23 @@ func cmdStart(fl Flags) (int, error) {
|
||||||
// sure by giving it some random bytes and having it echo
|
// sure by giving it some random bytes and having it echo
|
||||||
// them back to us)
|
// them back to us)
|
||||||
cmd := exec.Command(os.Args[0], "run", "--pingback", ln.Addr().String())
|
cmd := exec.Command(os.Args[0], "run", "--pingback", ln.Addr().String())
|
||||||
if startCmdConfigFlag != "" {
|
if configFlag != "" {
|
||||||
cmd.Args = append(cmd.Args, "--config", startCmdConfigFlag)
|
cmd.Args = append(cmd.Args, "--config", configFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, envFile := range startCmdEnvfileFlag {
|
for _, envfile := range envfileFlag {
|
||||||
cmd.Args = append(cmd.Args, "--envfile", envFile)
|
cmd.Args = append(cmd.Args, "--envfile", envfile)
|
||||||
}
|
}
|
||||||
if startCmdConfigAdapterFlag != "" {
|
if configAdapterFlag != "" {
|
||||||
cmd.Args = append(cmd.Args, "--adapter", startCmdConfigAdapterFlag)
|
cmd.Args = append(cmd.Args, "--adapter", configAdapterFlag)
|
||||||
}
|
}
|
||||||
if startCmdWatchFlag {
|
if watchFlag {
|
||||||
cmd.Args = append(cmd.Args, "--watch")
|
cmd.Args = append(cmd.Args, "--watch")
|
||||||
}
|
}
|
||||||
if startCmdPidfileFlag != "" {
|
if pidfileFlag != "" {
|
||||||
cmd.Args = append(cmd.Args, "--pidfile", startCmdPidfileFlag)
|
cmd.Args = append(cmd.Args, "--pidfile", pidfileFlag)
|
||||||
}
|
}
|
||||||
stdinpipe, err := cmd.StdinPipe()
|
stdinPipe, err := cmd.StdinPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("creating stdin pipe: %v", err)
|
fmt.Errorf("creating stdin pipe: %v", err)
|
||||||
|
@ -102,7 +102,8 @@ func cmdStart(fl Flags) (int, error) {
|
||||||
expect := make([]byte, 32)
|
expect := make([]byte, 32)
|
||||||
_, err = rand.Read(expect)
|
_, err = rand.Read(expect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("generating random confirmation bytes: %v", err)
|
return caddy.ExitCodeFailedStartup,
|
||||||
|
fmt.Errorf("generating random confirmation bytes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin writing the confirmation bytes to the child's
|
// begin writing the confirmation bytes to the child's
|
||||||
|
@ -110,14 +111,15 @@ func cmdStart(fl Flags) (int, error) {
|
||||||
// started yet, and writing synchronously would result
|
// started yet, and writing synchronously would result
|
||||||
// in a deadlock
|
// in a deadlock
|
||||||
go func() {
|
go func() {
|
||||||
_, _ = stdinpipe.Write(expect)
|
_, _ = stdinPipe.Write(expect)
|
||||||
stdinpipe.Close()
|
stdinPipe.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// start the process
|
// start the process
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("starting caddy process: %v", err)
|
return caddy.ExitCodeFailedStartup,
|
||||||
|
fmt.Errorf("starting caddy process: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// there are two ways we know we're done: either
|
// there are two ways we know we're done: either
|
||||||
|
@ -165,47 +167,37 @@ func cmdStart(fl Flags) (int, error) {
|
||||||
func cmdRun(fl Flags) (int, error) {
|
func cmdRun(fl Flags) (int, error) {
|
||||||
caddy.TrapSignals()
|
caddy.TrapSignals()
|
||||||
|
|
||||||
runCmdConfigFlag := fl.String("config")
|
configFlag := fl.String("config")
|
||||||
runCmdConfigAdapterFlag := fl.String("adapter")
|
configAdapterFlag := fl.String("adapter")
|
||||||
runCmdResumeFlag := fl.Bool("resume")
|
resumeFlag := fl.Bool("resume")
|
||||||
runCmdPrintEnvFlag := fl.Bool("environ")
|
printEnvFlag := fl.Bool("environ")
|
||||||
runCmdWatchFlag := fl.Bool("watch")
|
watchFlag := fl.Bool("watch")
|
||||||
runCmdPidfileFlag := fl.String("pidfile")
|
pidfileFlag := fl.String("pidfile")
|
||||||
runCmdPingbackFlag := fl.String("pingback")
|
pingbackFlag := fl.String("pingback")
|
||||||
|
|
||||||
var err error
|
|
||||||
var runCmdLoadEnvfileFlag []string
|
|
||||||
runCmdLoadEnvfileFlag, err = fl.GetStringSlice("envfile")
|
|
||||||
if err != nil {
|
|
||||||
return caddy.ExitCodeFailedStartup,
|
|
||||||
fmt.Errorf("reading envfile flag: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// load all additional envs as soon as possible
|
// load all additional envs as soon as possible
|
||||||
for _, envFile := range runCmdLoadEnvfileFlag {
|
err := handleEnvFileFlag(fl)
|
||||||
if err := loadEnvFromFile(envFile); err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup, err
|
||||||
fmt.Errorf("loading additional environment variables: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we are supposed to print the environment, do that first
|
// if we are supposed to print the environment, do that first
|
||||||
if runCmdPrintEnvFlag {
|
if printEnvFlag {
|
||||||
printEnvironment()
|
printEnvironment()
|
||||||
}
|
}
|
||||||
|
|
||||||
// load the config, depending on flags
|
// load the config, depending on flags
|
||||||
var config []byte
|
var config []byte
|
||||||
if runCmdResumeFlag {
|
if resumeFlag {
|
||||||
config, err = os.ReadFile(caddy.ConfigAutosavePath)
|
config, err = os.ReadFile(caddy.ConfigAutosavePath)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
// not a bad error; just can't resume if autosave file doesn't exist
|
// not a bad error; just can't resume if autosave file doesn't exist
|
||||||
caddy.Log().Info("no autosave file exists", zap.String("autosave_file", caddy.ConfigAutosavePath))
|
caddy.Log().Info("no autosave file exists", zap.String("autosave_file", caddy.ConfigAutosavePath))
|
||||||
runCmdResumeFlag = false
|
resumeFlag = false
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, err
|
return caddy.ExitCodeFailedStartup, err
|
||||||
} else {
|
} else {
|
||||||
if runCmdConfigFlag == "" {
|
if configFlag == "" {
|
||||||
caddy.Log().Info("resuming from last configuration",
|
caddy.Log().Info("resuming from last configuration",
|
||||||
zap.String("autosave_file", caddy.ConfigAutosavePath))
|
zap.String("autosave_file", caddy.ConfigAutosavePath))
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,19 +210,19 @@ func cmdRun(fl Flags) (int, error) {
|
||||||
}
|
}
|
||||||
// we don't use 'else' here since this value might have been changed in 'if' block; i.e. not mutually exclusive
|
// we don't use 'else' here since this value might have been changed in 'if' block; i.e. not mutually exclusive
|
||||||
var configFile string
|
var configFile string
|
||||||
if !runCmdResumeFlag {
|
if !resumeFlag {
|
||||||
config, configFile, err = LoadConfig(runCmdConfigFlag, runCmdConfigAdapterFlag)
|
config, configFile, err = LoadConfig(configFlag, configAdapterFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, err
|
return caddy.ExitCodeFailedStartup, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create pidfile now, in case loading config takes a while (issue #5477)
|
// create pidfile now, in case loading config takes a while (issue #5477)
|
||||||
if runCmdPidfileFlag != "" {
|
if pidfileFlag != "" {
|
||||||
err := caddy.PIDFile(runCmdPidfileFlag)
|
err := caddy.PIDFile(pidfileFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
caddy.Log().Error("unable to write PID file",
|
caddy.Log().Error("unable to write PID file",
|
||||||
zap.String("pidfile", runCmdPidfileFlag),
|
zap.String("pidfile", pidfileFlag),
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,13 +236,13 @@ func cmdRun(fl Flags) (int, error) {
|
||||||
|
|
||||||
// if we are to report to another process the successful start
|
// if we are to report to another process the successful start
|
||||||
// of the server, do so now by echoing back contents of stdin
|
// of the server, do so now by echoing back contents of stdin
|
||||||
if runCmdPingbackFlag != "" {
|
if pingbackFlag != "" {
|
||||||
confirmationBytes, err := io.ReadAll(os.Stdin)
|
confirmationBytes, err := io.ReadAll(os.Stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("reading confirmation bytes from stdin: %v", err)
|
fmt.Errorf("reading confirmation bytes from stdin: %v", err)
|
||||||
}
|
}
|
||||||
conn, err := net.Dial("tcp", runCmdPingbackFlag)
|
conn, err := net.Dial("tcp", pingbackFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("dialing confirmation address: %v", err)
|
fmt.Errorf("dialing confirmation address: %v", err)
|
||||||
|
@ -259,14 +251,14 @@ func cmdRun(fl Flags) (int, error) {
|
||||||
_, err = conn.Write(confirmationBytes)
|
_, err = conn.Write(confirmationBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("writing confirmation bytes to %s: %v", runCmdPingbackFlag, err)
|
fmt.Errorf("writing confirmation bytes to %s: %v", pingbackFlag, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if enabled, reload config file automatically on changes
|
// if enabled, reload config file automatically on changes
|
||||||
// (this better only be used in dev!)
|
// (this better only be used in dev!)
|
||||||
if runCmdWatchFlag {
|
if watchFlag {
|
||||||
go watchConfigFile(configFile, runCmdConfigAdapterFlag)
|
go watchConfigFile(configFile, configAdapterFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
// warn if the environment does not provide enough information about the disk
|
// warn if the environment does not provide enough information about the disk
|
||||||
|
@ -292,11 +284,11 @@ func cmdRun(fl Flags) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdStop(fl Flags) (int, error) {
|
func cmdStop(fl Flags) (int, error) {
|
||||||
addrFlag := fl.String("address")
|
addressFlag := fl.String("address")
|
||||||
configFlag := fl.String("config")
|
configFlag := fl.String("config")
|
||||||
configAdapterFlag := fl.String("adapter")
|
configAdapterFlag := fl.String("adapter")
|
||||||
|
|
||||||
adminAddr, err := DetermineAdminAPIAddress(addrFlag, nil, configFlag, configAdapterFlag)
|
adminAddr, err := DetermineAdminAPIAddress(addressFlag, nil, configFlag, configAdapterFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("couldn't determine admin API address: %v", err)
|
return caddy.ExitCodeFailedStartup, fmt.Errorf("couldn't determine admin API address: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -314,7 +306,7 @@ func cmdStop(fl Flags) (int, error) {
|
||||||
func cmdReload(fl Flags) (int, error) {
|
func cmdReload(fl Flags) (int, error) {
|
||||||
configFlag := fl.String("config")
|
configFlag := fl.String("config")
|
||||||
configAdapterFlag := fl.String("adapter")
|
configAdapterFlag := fl.String("adapter")
|
||||||
addrFlag := fl.String("address")
|
addressFlag := fl.String("address")
|
||||||
forceFlag := fl.Bool("force")
|
forceFlag := fl.Bool("force")
|
||||||
|
|
||||||
// get the config in caddy's native format
|
// get the config in caddy's native format
|
||||||
|
@ -326,7 +318,7 @@ func cmdReload(fl Flags) (int, error) {
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("no config file to load")
|
return caddy.ExitCodeFailedStartup, fmt.Errorf("no config file to load")
|
||||||
}
|
}
|
||||||
|
|
||||||
adminAddr, err := DetermineAdminAPIAddress(addrFlag, config, configFlag, configAdapterFlag)
|
adminAddr, err := DetermineAdminAPIAddress(addressFlag, config, configFlag, configAdapterFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("couldn't determine admin API address: %v", err)
|
return caddy.ExitCodeFailedStartup, fmt.Errorf("couldn't determine admin API address: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -428,48 +420,60 @@ func cmdListModules(fl Flags) (int, error) {
|
||||||
return caddy.ExitCodeSuccess, nil
|
return caddy.ExitCodeSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdEnviron(_ Flags) (int, error) {
|
func cmdEnviron(fl Flags) (int, error) {
|
||||||
|
// load all additional envs as soon as possible
|
||||||
|
err := handleEnvFileFlag(fl)
|
||||||
|
if err != nil {
|
||||||
|
return caddy.ExitCodeFailedStartup, err
|
||||||
|
}
|
||||||
|
|
||||||
printEnvironment()
|
printEnvironment()
|
||||||
return caddy.ExitCodeSuccess, nil
|
return caddy.ExitCodeSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdAdaptConfig(fl Flags) (int, error) {
|
func cmdAdaptConfig(fl Flags) (int, error) {
|
||||||
adaptCmdInputFlag := fl.String("config")
|
inputFlag := fl.String("config")
|
||||||
adaptCmdAdapterFlag := fl.String("adapter")
|
adapterFlag := fl.String("adapter")
|
||||||
adaptCmdPrettyFlag := fl.Bool("pretty")
|
prettyFlag := fl.Bool("pretty")
|
||||||
adaptCmdValidateFlag := fl.Bool("validate")
|
validateFlag := fl.Bool("validate")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
adaptCmdInputFlag, err = configFileWithRespectToDefault(caddy.Log(), adaptCmdInputFlag)
|
inputFlag, err = configFileWithRespectToDefault(caddy.Log(), inputFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, err
|
return caddy.ExitCodeFailedStartup, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if adaptCmdAdapterFlag == "" {
|
// load all additional envs as soon as possible
|
||||||
|
err = handleEnvFileFlag(fl)
|
||||||
|
if err != nil {
|
||||||
|
return caddy.ExitCodeFailedStartup, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if adapterFlag == "" {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("adapter name is required (use --adapt flag or leave unspecified for default)")
|
fmt.Errorf("adapter name is required (use --adapt flag or leave unspecified for default)")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgAdapter := caddyconfig.GetAdapter(adaptCmdAdapterFlag)
|
cfgAdapter := caddyconfig.GetAdapter(adapterFlag)
|
||||||
if cfgAdapter == nil {
|
if cfgAdapter == nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("unrecognized config adapter: %s", adaptCmdAdapterFlag)
|
fmt.Errorf("unrecognized config adapter: %s", adapterFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
input, err := os.ReadFile(adaptCmdInputFlag)
|
input, err := os.ReadFile(inputFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("reading input file: %v", err)
|
fmt.Errorf("reading input file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := map[string]any{"filename": adaptCmdInputFlag}
|
opts := map[string]any{"filename": inputFlag}
|
||||||
|
|
||||||
adaptedConfig, warnings, err := cfgAdapter.Adapt(input, opts)
|
adaptedConfig, warnings, err := cfgAdapter.Adapt(input, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, err
|
return caddy.ExitCodeFailedStartup, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if adaptCmdPrettyFlag {
|
if prettyFlag {
|
||||||
var prettyBuf bytes.Buffer
|
var prettyBuf bytes.Buffer
|
||||||
err = json.Indent(&prettyBuf, adaptedConfig, "", "\t")
|
err = json.Indent(&prettyBuf, adaptedConfig, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -487,13 +491,13 @@ func cmdAdaptConfig(fl Flags) (int, error) {
|
||||||
if warn.Directive != "" {
|
if warn.Directive != "" {
|
||||||
msg = fmt.Sprintf("%s: %s", warn.Directive, warn.Message)
|
msg = fmt.Sprintf("%s: %s", warn.Directive, warn.Message)
|
||||||
}
|
}
|
||||||
caddy.Log().Named(adaptCmdAdapterFlag).Warn(msg,
|
caddy.Log().Named(adapterFlag).Warn(msg,
|
||||||
zap.String("file", warn.File),
|
zap.String("file", warn.File),
|
||||||
zap.Int("line", warn.Line))
|
zap.Int("line", warn.Line))
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate output if requested
|
// validate output if requested
|
||||||
if adaptCmdValidateFlag {
|
if validateFlag {
|
||||||
var cfg *caddy.Config
|
var cfg *caddy.Config
|
||||||
err = caddy.StrictUnmarshalJSON(adaptedConfig, &cfg)
|
err = caddy.StrictUnmarshalJSON(adaptedConfig, &cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -509,36 +513,26 @@ func cmdAdaptConfig(fl Flags) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdValidateConfig(fl Flags) (int, error) {
|
func cmdValidateConfig(fl Flags) (int, error) {
|
||||||
validateCmdConfigFlag := fl.String("config")
|
configFlag := fl.String("config")
|
||||||
validateCmdAdapterFlag := fl.String("adapter")
|
adapterFlag := fl.String("adapter")
|
||||||
|
|
||||||
var err error
|
|
||||||
var runCmdLoadEnvfileFlag []string
|
|
||||||
runCmdLoadEnvfileFlag, err = fl.GetStringSlice("envfile")
|
|
||||||
if err != nil {
|
|
||||||
return caddy.ExitCodeFailedStartup,
|
|
||||||
fmt.Errorf("reading envfile flag: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// load all additional envs as soon as possible
|
// load all additional envs as soon as possible
|
||||||
for _, envFile := range runCmdLoadEnvfileFlag {
|
err := handleEnvFileFlag(fl)
|
||||||
if err := loadEnvFromFile(envFile); err != nil {
|
|
||||||
return caddy.ExitCodeFailedStartup,
|
|
||||||
fmt.Errorf("loading additional environment variables: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use default config and ensure a config file is specified
|
|
||||||
validateCmdConfigFlag, err = configFileWithRespectToDefault(caddy.Log(), validateCmdConfigFlag)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, err
|
return caddy.ExitCodeFailedStartup, err
|
||||||
}
|
}
|
||||||
if validateCmdConfigFlag == "" {
|
|
||||||
|
// use default config and ensure a config file is specified
|
||||||
|
configFlag, err = configFileWithRespectToDefault(caddy.Log(), configFlag)
|
||||||
|
if err != nil {
|
||||||
|
return caddy.ExitCodeFailedStartup, err
|
||||||
|
}
|
||||||
|
if configFlag == "" {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("input file required when there is no Caddyfile in current directory (use --config flag)")
|
fmt.Errorf("input file required when there is no Caddyfile in current directory (use --config flag)")
|
||||||
}
|
}
|
||||||
|
|
||||||
input, _, err := LoadConfig(validateCmdConfigFlag, validateCmdAdapterFlag)
|
input, _, err := LoadConfig(configFlag, adapterFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, err
|
return caddy.ExitCodeFailedStartup, err
|
||||||
}
|
}
|
||||||
|
@ -561,13 +555,13 @@ func cmdValidateConfig(fl Flags) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdFmt(fl Flags) (int, error) {
|
func cmdFmt(fl Flags) (int, error) {
|
||||||
formatCmdConfigFile := fl.Arg(0)
|
configFile := fl.Arg(0)
|
||||||
if formatCmdConfigFile == "" {
|
if configFile == "" {
|
||||||
formatCmdConfigFile = "Caddyfile"
|
configFile = "Caddyfile"
|
||||||
}
|
}
|
||||||
|
|
||||||
// as a special case, read from stdin if the file name is "-"
|
// as a special case, read from stdin if the file name is "-"
|
||||||
if formatCmdConfigFile == "-" {
|
if configFile == "-" {
|
||||||
input, err := io.ReadAll(os.Stdin)
|
input, err := io.ReadAll(os.Stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
|
@ -577,7 +571,7 @@ func cmdFmt(fl Flags) (int, error) {
|
||||||
return caddy.ExitCodeSuccess, nil
|
return caddy.ExitCodeSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
input, err := os.ReadFile(formatCmdConfigFile)
|
input, err := os.ReadFile(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return caddy.ExitCodeFailedStartup,
|
return caddy.ExitCodeFailedStartup,
|
||||||
fmt.Errorf("reading input file: %v", err)
|
fmt.Errorf("reading input file: %v", err)
|
||||||
|
@ -586,7 +580,7 @@ func cmdFmt(fl Flags) (int, error) {
|
||||||
output := caddyfile.Format(input)
|
output := caddyfile.Format(input)
|
||||||
|
|
||||||
if fl.Bool("overwrite") {
|
if fl.Bool("overwrite") {
|
||||||
if err := os.WriteFile(formatCmdConfigFile, output, 0o600); err != nil {
|
if err := os.WriteFile(configFile, output, 0o600); err != nil {
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("overwriting formatted file: %v", err)
|
return caddy.ExitCodeFailedStartup, fmt.Errorf("overwriting formatted file: %v", err)
|
||||||
}
|
}
|
||||||
return caddy.ExitCodeSuccess, nil
|
return caddy.ExitCodeSuccess, nil
|
||||||
|
@ -610,7 +604,7 @@ func cmdFmt(fl Flags) (int, error) {
|
||||||
fmt.Print(string(output))
|
fmt.Print(string(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
if warning, diff := caddyfile.FormattingDifference(formatCmdConfigFile, input); diff {
|
if warning, diff := caddyfile.FormattingDifference(configFile, input); diff {
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf(`%s:%d: Caddyfile input is not formatted; Tip: use '--overwrite' to update your Caddyfile in-place instead of previewing it. Consult '--help' for more options`,
|
return caddy.ExitCodeFailedStartup, fmt.Errorf(`%s:%d: Caddyfile input is not formatted; Tip: use '--overwrite' to update your Caddyfile in-place instead of previewing it. Consult '--help' for more options`,
|
||||||
warning.File,
|
warning.File,
|
||||||
warning.Line,
|
warning.Line,
|
||||||
|
@ -620,6 +614,25 @@ func cmdFmt(fl Flags) (int, error) {
|
||||||
return caddy.ExitCodeSuccess, nil
|
return caddy.ExitCodeSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleEnvFileFlag loads the environment variables from the given --envfile
|
||||||
|
// flag if specified. This should be called as early in the command function.
|
||||||
|
func handleEnvFileFlag(fl Flags) error {
|
||||||
|
var err error
|
||||||
|
var envfileFlag []string
|
||||||
|
envfileFlag, err = fl.GetStringSlice("envfile")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("reading envfile flag: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, envfile := range envfileFlag {
|
||||||
|
if err := loadEnvFromFile(envfile); err != nil {
|
||||||
|
return fmt.Errorf("loading additional environment variables: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// AdminAPIRequest makes an API request according to the CLI flags given,
|
// AdminAPIRequest makes an API request according to the CLI flags given,
|
||||||
// with the given HTTP method and request URI. If body is non-nil, it will
|
// with the given HTTP method and request URI. If body is non-nil, it will
|
||||||
// be assumed to be Content-Type application/json. The caller should close
|
// be assumed to be Content-Type application/json. The caller should close
|
||||||
|
|
|
@ -94,8 +94,8 @@ func init() {
|
||||||
Starts the Caddy process, optionally bootstrapped with an initial config file.
|
Starts the Caddy process, optionally bootstrapped with an initial config file.
|
||||||
This command unblocks after the server starts running or fails to run.
|
This command unblocks after the server starts running or fails to run.
|
||||||
|
|
||||||
If --envfile is specified, an environment file with environment variables in
|
If --envfile is specified, an environment file with environment variables
|
||||||
the KEY=VALUE format will be loaded into the Caddy process.
|
in the KEY=VALUE format will be loaded into the Caddy process.
|
||||||
|
|
||||||
On Windows, the spawned child process will remain attached to the terminal, so
|
On Windows, the spawned child process will remain attached to the terminal, so
|
||||||
closing the window will forcefully stop Caddy; to avoid forgetting this, try
|
closing the window will forcefully stop Caddy; to avoid forgetting this, try
|
||||||
|
@ -133,8 +133,8 @@ As a special case, if the current working directory has a file called
|
||||||
that file will be loaded and used to configure Caddy, even without any command
|
that file will be loaded and used to configure Caddy, even without any command
|
||||||
line flags.
|
line flags.
|
||||||
|
|
||||||
If --envfile is specified, an environment file with environment variables in
|
If --envfile is specified, an environment file with environment variables
|
||||||
the KEY=VALUE format will be loaded into the Caddy process.
|
in the KEY=VALUE format will be loaded into the Caddy process.
|
||||||
|
|
||||||
If --environ 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
|
be printed before starting. This is the same as the environ command but does
|
||||||
|
@ -240,6 +240,7 @@ documentation: https://go.dev/doc/modules/version-numbers
|
||||||
|
|
||||||
RegisterCommand(Command{
|
RegisterCommand(Command{
|
||||||
Name: "environ",
|
Name: "environ",
|
||||||
|
Usage: "[--envfile <path>]",
|
||||||
Short: "Prints the environment",
|
Short: "Prints the environment",
|
||||||
Long: `
|
Long: `
|
||||||
Prints the environment as seen by this Caddy process.
|
Prints the environment as seen by this Caddy process.
|
||||||
|
@ -249,6 +250,9 @@ configuration uses environment variables (e.g. "{env.VARIABLE}") then
|
||||||
this command can be useful for verifying that the variables will have
|
this command can be useful for verifying that the variables will have
|
||||||
the values you expect in your config.
|
the values you expect in your config.
|
||||||
|
|
||||||
|
If --envfile is specified, an environment file with environment variables
|
||||||
|
in the KEY=VALUE format will be loaded into the Caddy process.
|
||||||
|
|
||||||
Note that environments may be different depending on how you run Caddy.
|
Note that environments may be different depending on how you run Caddy.
|
||||||
Environments for Caddy instances started by service managers such as
|
Environments for Caddy instances started by service managers such as
|
||||||
systemd are often different than the environment inherited from your
|
systemd are often different than the environment inherited from your
|
||||||
|
@ -259,12 +263,15 @@ by adding the "--environ" flag.
|
||||||
|
|
||||||
Environments may contain sensitive data.
|
Environments may contain sensitive data.
|
||||||
`,
|
`,
|
||||||
Func: cmdEnviron,
|
CobraFunc: func(cmd *cobra.Command) {
|
||||||
|
cmd.Flags().StringSliceP("envfile", "", []string{}, "Environment file(s) to load")
|
||||||
|
cmd.RunE = WrapCommandFuncForCobra(cmdEnviron)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
RegisterCommand(Command{
|
RegisterCommand(Command{
|
||||||
Name: "adapt",
|
Name: "adapt",
|
||||||
Usage: "--config <path> [--adapter <name>] [--pretty] [--validate]",
|
Usage: "--config <path> [--adapter <name>] [--pretty] [--validate] [--envfile <path>]",
|
||||||
Short: "Adapts a configuration to Caddy's native JSON",
|
Short: "Adapts a configuration to Caddy's native JSON",
|
||||||
Long: `
|
Long: `
|
||||||
Adapts a configuration to Caddy's native JSON format and writes the
|
Adapts a configuration to Caddy's native JSON format and writes the
|
||||||
|
@ -276,12 +283,16 @@ for human readability.
|
||||||
If --validate is used, the adapted config will be checked for validity.
|
If --validate is used, the adapted config will be checked for validity.
|
||||||
If the config is invalid, an error will be printed to stderr and a non-
|
If the config is invalid, an error will be printed to stderr and a non-
|
||||||
zero exit status will be returned.
|
zero exit status will be returned.
|
||||||
|
|
||||||
|
If --envfile is specified, an environment file with environment variables
|
||||||
|
in the KEY=VALUE format will be loaded into the Caddy process.
|
||||||
`,
|
`,
|
||||||
CobraFunc: func(cmd *cobra.Command) {
|
CobraFunc: func(cmd *cobra.Command) {
|
||||||
cmd.Flags().StringP("config", "c", "", "Configuration file to adapt (required)")
|
cmd.Flags().StringP("config", "c", "", "Configuration file to adapt (required)")
|
||||||
cmd.Flags().StringP("adapter", "a", "caddyfile", "Name of config adapter")
|
cmd.Flags().StringP("adapter", "a", "caddyfile", "Name of config adapter")
|
||||||
cmd.Flags().BoolP("pretty", "p", false, "Format the output for human readability")
|
cmd.Flags().BoolP("pretty", "p", false, "Format the output for human readability")
|
||||||
cmd.Flags().BoolP("validate", "", false, "Validate the output")
|
cmd.Flags().BoolP("validate", "", false, "Validate the output")
|
||||||
|
cmd.Flags().StringSliceP("envfile", "", []string{}, "Environment file(s) to load")
|
||||||
cmd.RunE = WrapCommandFuncForCobra(cmdAdaptConfig)
|
cmd.RunE = WrapCommandFuncForCobra(cmdAdaptConfig)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -295,8 +306,8 @@ Loads and provisions the provided config, but does not start running it.
|
||||||
This reveals any errors with the configuration through the loading and
|
This reveals any errors with the configuration through the loading and
|
||||||
provisioning stages.
|
provisioning stages.
|
||||||
|
|
||||||
If --envfile is specified, an environment file with environment variables in
|
If --envfile is specified, an environment file with environment variables
|
||||||
the KEY=VALUE format will be loaded into the Caddy process.
|
in the KEY=VALUE format will be loaded into the Caddy process.
|
||||||
`,
|
`,
|
||||||
CobraFunc: func(cmd *cobra.Command) {
|
CobraFunc: func(cmd *cobra.Command) {
|
||||||
cmd.Flags().StringP("config", "c", "", "Input configuration file")
|
cmd.Flags().StringP("config", "c", "", "Input configuration file")
|
||||||
|
|
|
@ -17,6 +17,7 @@ package caddycmd
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -63,6 +64,10 @@ func Main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
var exitError *exitError
|
||||||
|
if errors.As(err, &exitError) {
|
||||||
|
os.Exit(exitError.ExitCode)
|
||||||
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue