-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2732 from hashicorp/f-validate-command
Deprecate configtest and add validate command
- Loading branch information
Showing
9 changed files
with
291 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package command | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/consul/command/agent" | ||
"github.com/hashicorp/consul/command/base" | ||
) | ||
|
||
// ValidateCommand is a Command implementation that is used to | ||
// verify config files | ||
type ValidateCommand struct { | ||
base.Command | ||
} | ||
|
||
func (c *ValidateCommand) Help() string { | ||
helpText := ` | ||
Usage: consul validate [options] FILE_OR_DIRECTORY... | ||
Performs a basic sanity test on Consul configuration files. For each file | ||
or directory given, the validate command will attempt to parse the | ||
contents just as the "consul agent" command would, and catch any errors. | ||
This is useful to do a test of the configuration only, without actually | ||
starting the agent. | ||
Returns 0 if the configuration is valid, or 1 if there are problems. | ||
` + c.Command.Help() | ||
|
||
return strings.TrimSpace(helpText) | ||
} | ||
|
||
func (c *ValidateCommand) Run(args []string) int { | ||
var configFiles []string | ||
var quiet bool | ||
|
||
f := c.Command.NewFlagSet(c) | ||
f.Var((*agent.AppendSliceValue)(&configFiles), "config-file", | ||
"Path to a JSON file to read configuration from. This can be specified multiple times.") | ||
f.Var((*agent.AppendSliceValue)(&configFiles), "config-dir", | ||
"Path to a directory to read configuration files from. This will read every file ending in "+ | ||
".json as configuration in this directory in alphabetical order.") | ||
f.BoolVar(&quiet, "quiet", false, | ||
"When given, a successful run will produce no output.") | ||
c.Command.HideFlags("config-file", "config-dir") | ||
|
||
if err := c.Command.Parse(args); err != nil { | ||
return 1 | ||
} | ||
|
||
if len(f.Args()) > 0 { | ||
configFiles = append(configFiles, f.Args()...) | ||
} | ||
|
||
if len(configFiles) < 1 { | ||
c.Ui.Error("Must specify at least one config file or directory") | ||
return 1 | ||
} | ||
|
||
_, err := agent.ReadConfigPaths(configFiles) | ||
if err != nil { | ||
c.Ui.Error(fmt.Sprintf("Config validation failed: %v", err.Error())) | ||
return 1 | ||
} | ||
|
||
if !quiet { | ||
c.Ui.Output("Configuration is valid!") | ||
} | ||
return 0 | ||
} | ||
|
||
func (c *ValidateCommand) Synopsis() string { | ||
return "Validate config files/directories" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package command | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/hashicorp/consul/command/base" | ||
"github.com/mitchellh/cli" | ||
) | ||
|
||
func testValidateCommand(t *testing.T) (*cli.MockUi, *ValidateCommand) { | ||
ui := new(cli.MockUi) | ||
return ui, &ValidateCommand{ | ||
Command: base.Command{ | ||
Ui: ui, | ||
Flags: base.FlagSetNone, | ||
}, | ||
} | ||
} | ||
|
||
func TestValidateCommand_implements(t *testing.T) { | ||
var _ cli.Command = &ValidateCommand{} | ||
} | ||
|
||
func TestValidateCommandFailOnEmptyFile(t *testing.T) { | ||
tmpFile, err := ioutil.TempFile("", "consul") | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
defer os.RemoveAll(tmpFile.Name()) | ||
|
||
_, cmd := testValidateCommand(t) | ||
|
||
args := []string{tmpFile.Name()} | ||
|
||
if code := cmd.Run(args); code == 0 { | ||
t.Fatalf("bad: %d", code) | ||
} | ||
} | ||
|
||
func TestValidateCommandSucceedOnEmptyDir(t *testing.T) { | ||
td, err := ioutil.TempDir("", "consul") | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
defer os.RemoveAll(td) | ||
|
||
ui, cmd := testValidateCommand(t) | ||
|
||
args := []string{td} | ||
|
||
if code := cmd.Run(args); code != 0 { | ||
t.Fatalf("bad: %d, %s", code, ui.ErrorWriter.String()) | ||
} | ||
} | ||
|
||
func TestValidateCommandSucceedOnMinimalConfigFile(t *testing.T) { | ||
td, err := ioutil.TempDir("", "consul") | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
defer os.RemoveAll(td) | ||
|
||
fp := filepath.Join(td, "config.json") | ||
err = ioutil.WriteFile(fp, []byte(`{}`), 0644) | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
|
||
_, cmd := testValidateCommand(t) | ||
|
||
args := []string{fp} | ||
|
||
if code := cmd.Run(args); code != 0 { | ||
t.Fatalf("bad: %d", code) | ||
} | ||
} | ||
|
||
func TestValidateCommandSucceedOnMinimalConfigDir(t *testing.T) { | ||
td, err := ioutil.TempDir("", "consul") | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
defer os.RemoveAll(td) | ||
|
||
err = ioutil.WriteFile(filepath.Join(td, "config.json"), []byte(`{}`), 0644) | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
|
||
_, cmd := testValidateCommand(t) | ||
|
||
args := []string{td} | ||
|
||
if code := cmd.Run(args); code != 0 { | ||
t.Fatalf("bad: %d", code) | ||
} | ||
} | ||
|
||
func TestValidateCommandSucceedOnConfigDirWithEmptyFile(t *testing.T) { | ||
td, err := ioutil.TempDir("", "consul") | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
defer os.RemoveAll(td) | ||
|
||
err = ioutil.WriteFile(filepath.Join(td, "config.json"), []byte{}, 0644) | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
|
||
_, cmd := testValidateCommand(t) | ||
|
||
args := []string{td} | ||
|
||
if code := cmd.Run(args); code != 0 { | ||
t.Fatalf("bad: %d", code) | ||
} | ||
} | ||
|
||
func TestValidateCommandQuiet(t *testing.T) { | ||
td, err := ioutil.TempDir("", "consul") | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
defer os.RemoveAll(td) | ||
|
||
ui, cmd := testValidateCommand(t) | ||
|
||
args := []string{"-quiet", td} | ||
|
||
if code := cmd.Run(args); code != 0 { | ||
t.Fatalf("bad: %d, %s", code, ui.ErrorWriter.String()) | ||
} | ||
if ui.OutputWriter.String() != "<nil>" { | ||
t.Fatalf("bad: %v", ui.OutputWriter.String()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
--- | ||
layout: "docs" | ||
page_title: "Commands: Validate" | ||
sidebar_current: "docs-commands-validate" | ||
description: > | ||
The `consul validate` command tests that config files are valid by | ||
attempting to parse them. Useful to ensure a configuration change will | ||
not cause consul to fail after a restart. | ||
--- | ||
|
||
# Consul Validate | ||
|
||
The `consul validate` command performs a basic sanity test on Consul | ||
configuration files. For each file or directory given, the validate command | ||
will attempt to parse the contents just as the "consul agent" command would, | ||
and catch any errors. This is useful to do a test of the configuration only, | ||
without actually starting the agent. | ||
|
||
For more information on the format of Consul's configuration files, read the | ||
consul agent [Configuration Files](/docs/agent/options.html#configuration_files) | ||
section. | ||
|
||
## Usage | ||
|
||
Usage: `consul validate [options] FILE_OR_DIRECTORY...` | ||
|
||
Performs a basic sanity test on Consul configuration files. For each file | ||
or directory given, the validate command will attempt to parse the | ||
contents just as the "consul agent" command would, and catch any errors. | ||
This is useful to do a test of the configuration only, without actually | ||
starting the agent. | ||
|
||
Returns 0 if the configuration is valid, or 1 if there are problems. | ||
|
||
```text | ||
$ consul validate /etc/consul.d | ||
Configuration is valid! | ||
``` | ||
|
Oops, something went wrong.