Better "sample config" implementation

Three changes:

- Sample configuration is no longer a dump of a string constant. It's
  generated using the same data structure used for configuration
  parsing. This guarantees that 'teleport configure' will always dump a
  valid sample config file.

- Added a unit test which validates sample configuration and  verifies
  its correctness

- MakeSampleConfig() does not return an error anymore. It will
  default   to 'localhost' with error logged instead of failing. It
  makes no sense to fail when generating an example. Also this makes
  code cleaner.
This commit is contained in:
Ev Kontsevoy 2016-02-23 23:35:25 -08:00
parent d5a948c5f2
commit 02f3459afc
9 changed files with 103 additions and 74 deletions

View file

@ -47,6 +47,8 @@ production: clean
#
test:
go test -v github.com/gravitational/teleport/lib/... -cover
go test -v github.com/gravitational/teleport/tool/teleport... -cover
test-with-etcd: install
${ETCD_FLAGS} go test -v -test.parallel=0 $(shell go list ./... | grep -v /vendor/) -cover

View file

@ -22,6 +22,7 @@ import (
"testing"
"time"
"github.com/gravitational/teleport/lib/defaults"
"gopkg.in/check.v1"
)
@ -71,6 +72,25 @@ func (s *ConfigTestSuite) TearDownSuite(c *check.C) {
os.RemoveAll(s.tempDir)
}
func (s *ConfigTestSuite) TestSampleConfig(c *check.C) {
// generate sample config and write it into a temp file:
sfc := MakeSampleFileConfig()
c.Assert(sfc, check.NotNil)
fn := filepath.Join(c.MkDir(), "default-config.yaml")
err := ioutil.WriteFile(fn, []byte(sfc.DebugDumpToYAML()), 0660)
c.Assert(err, check.IsNil)
// make sure it could be parsed:
fc, err := ReadFromFile(fn)
c.Assert(err, check.IsNil)
// validate a couple of values:
c.Assert(fc.Limits.MaxUsers, check.Equals, defaults.LimiterMaxConcurrentUsers)
c.Assert(fc.Global.Storage.DirName, check.Equals, defaults.DataDir)
c.Assert(fc.Logger.Severity, check.Equals, "INFO")
}
func (s *ConfigTestSuite) TestConfigReading(c *check.C) {
// invalid config file type:
conf, err := ReadFromFile("/bin/true")

View file

@ -24,6 +24,8 @@ import (
"gopkg.in/yaml.v2"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/service"
"github.com/gravitational/trace"
)
@ -117,6 +119,66 @@ func ReadFromFile(fp string) (fc *FileConfig, err error) {
return fc, nil
}
// makeSampleFileConfig returns a sample config structure populated by defaults,
// useful to generate sample configuration files
func MakeSampleFileConfig() (fc *FileConfig) {
conf := service.MakeDefaultConfig()
// sample global config:
var g Global
g.NodeName = conf.Hostname
g.AuthToken = "xxxx-token-xxxx"
g.Logger.Output = "stderr"
g.Logger.Severity = "INFO"
g.AuthServers = defaults.AuthListenAddr().Addr
g.Limits.MaxConnections = defaults.LimiterMaxConnections
g.Limits.MaxUsers = defaults.LimiterMaxConcurrentUsers
g.Storage.DirName = defaults.DataDir
g.Storage.Type = conf.Auth.RecordsBackend.Type
// sample SSH config:
var s SSH
s.EnabledFlag = "yes"
s.ListenAddress = conf.SSH.Addr.Addr
s.Commands = []CommandLabel{
{
Name: "hostname",
Command: []string{"/usr/bin/hostname"},
Period: time.Minute,
},
{
Name: "arch",
Command: []string{"/usr/bin/uname", "-p"},
Period: time.Hour,
},
}
s.Labels = map[string]string{
"db_type": "postgres",
"db_role": "master",
}
// sample Auth config:
var a Auth
a.ListenAddress = conf.Auth.SSHAddr.Addr
a.EnabledFlag = "yes"
// sample proxy config:
var p Proxy
p.EnabledFlag = "yes"
p.ListenAddress = conf.Proxy.SSHAddr.Addr
p.WebAddr = conf.Proxy.WebAddr.Addr
p.CertFile = "/etc/teleport/teleport.crt"
p.KeyFile = "/etc/teleport/teleport.key"
fc = &FileConfig{
Global: g,
Proxy: p,
SSH: s,
Auth: a,
}
return fc
}
// DebugDump allows for quick YAML dumping of the config
func (conf *FileConfig) DebugDumpToYAML() string {
bytes, err := yaml.Marshal(&conf)
@ -204,7 +266,7 @@ type SSH struct {
// `command` section of `ssh_service` in the config file
type CommandLabel struct {
Name string `yaml:"name"`
Command []string `yaml:"command"`
Command []string `yaml:"command,flow"`
Period time.Duration `yaml:"period"`
}

View file

@ -23,6 +23,7 @@ import (
"io"
log "github.com/Sirupsen/logrus"
"gopkg.in/yaml.v2"
"github.com/gravitational/teleport/lib/defaults"
@ -307,19 +308,18 @@ func (c *LocalCertificateAuthority) CA() (*services.CertAuthority, error) {
}
// MakeDefaultConfig() creates a new Config structure and populates it with defaults
func MakeDefaultConfig() (config *Config, err error) {
func MakeDefaultConfig() (config *Config) {
config = &Config{}
if err = ApplyDefaults(config); err != nil {
return nil, trace.Wrap(err)
}
return config, nil
ApplyDefaults(config)
return config
}
// ApplyDefaults applies default values to the existing config structure
func ApplyDefaults(cfg *Config) error {
func ApplyDefaults(cfg *Config) {
hostname, err := os.Hostname()
if err != nil {
return trace.Wrap(err)
hostname = "localhost"
log.Errorf("Failed to determine hostname: %v", err)
}
// defaults for the auth service:
@ -358,7 +358,6 @@ func ApplyDefaults(cfg *Config) error {
cfg.AuthServers = []utils.NetAddr{cfg.Auth.SSHAddr}
}
cfg.Console = os.Stdout
return nil
}
// Generates a string accepted by the BoltDB driver, like this:

View file

@ -36,8 +36,7 @@ func (s *ConfigSuite) SetUpSuite(c *C) {
}
func (s *ConfigSuite) TestDefaultConfig(c *C) {
config, err := MakeDefaultConfig()
c.Assert(err, IsNil)
config := MakeDefaultConfig()
c.Assert(config, NotNil)
// all 3 services should be enabled by default

View file

@ -56,10 +56,7 @@ func main() {
app := utils.InitCLIParser("tctl", GlobalHelpString)
// generate default tctl configuration:
cfg, err := service.MakeDefaultConfig()
if err != nil {
utils.FatalError(err)
}
cfg := service.MakeDefaultConfig()
cmdUsers := UserCommand{config: cfg}
cmdNodes := NodeCommand{config: cfg}

View file

@ -243,10 +243,7 @@ func applyString(src string, target *string) bool {
// with CLI commands taking precedence
func configure(clf *CommandLineFlags) (cfg *service.Config, err error) {
// create the default configuration:
cfg, err = service.MakeDefaultConfig()
if err != nil {
return cfg, trace.Wrap(err)
}
cfg = service.MakeDefaultConfig()
// load /etc/teleport.yaml and apply it's values:
fileConf, err := readConfigFile(clf.ConfigFile)

View file

@ -20,6 +20,7 @@ import (
"os"
"strings"
"github.com/gravitational/teleport/lib/config"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/service"
"github.com/gravitational/teleport/lib/utils"
@ -102,7 +103,7 @@ func run(cmdlineArgs []string, testRun bool) (executedCommand string, appliedCon
case status.FullCommand():
err = onStatus(config)
case dump.FullCommand():
err = onConfigDump()
onConfigDump()
case ver.FullCommand():
onVersion()
}
@ -134,9 +135,9 @@ func onStatus(config *service.Config) error {
}
// onConfigDump is the handler for "configure" CLI command
func onConfigDump() error {
fmt.Println(sampleConfig)
return nil
func onConfigDump() {
sfc := config.MakeSampleFileConfig()
fmt.Printf("%s\n%s\n", sampleConfComment, sfc.DebugDumpToYAML())
}
// onVersion is the handler for "version"

View file

@ -24,55 +24,7 @@ Examples:
Starts this SSH node named 'database' running in SSH server mode and
authenticating connections via the auth server running on 10.5.0.2`
sampleConfig = `##
## This is the example of a Teleport configuration file with all settings
## set to their default value. Uncomment & customize as needed.
##
#global:
# hostname:localhost
# listen_interface:0.0.0.0
# advertize_interface:auto
# proxy_addr: 127.0.0.1:3023
# proxy_token: ""
# auth_servers: ["tcp://127.0.0.1:3024"]
# connection_limits:
# max: 100
# rates:
# - period: 10s
# average: 5
# burst: 10
# storage:
# type: bolt
# params: { path: "/var/lib/teleport" }
# log:
# output: stderr
# severity: INFO
#
#auth_service:
# enabled: yes
# listen_addr: tcp://127.0.0.1:3024
#
#
#ssh_service:
# enabled: yes
# token:
# proxy_addr: tcp://127.0.0.1:3023
# labels:
# name:value
# name2:value2
# label-commands:
# os:
# period: 1m
# command: ["uname", "-r"]
#
#proxy_service:
# enabled: yes
# https_only: true
# insecure_http_addr: tcp://0.0.0.0:3080
# https_addr: tcp://0.0.0.0:3081
# https_key_file: ""
# https_crt_file: ""
# ssh_addr: tcp://0.0.0.0:3023
# auth_token: ""
`
sampleConfComment = `#
# Sample Teleport configuration file.
#`
)