Skip to content

Commit

Permalink
add tbot configure command
Browse files Browse the repository at this point in the history
  • Loading branch information
strideynet committed May 10, 2022
1 parent bc09235 commit 497995c
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 8 deletions.
2 changes: 1 addition & 1 deletion tool/tbot/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func FromCLIConf(cf *CLIConf) (*BotConfig, error) {
}

if err := config.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err, "validing merged bot config")
return nil, trace.Wrap(err, "validating merged bot config")
}

return config, nil
Expand Down
61 changes: 54 additions & 7 deletions tool/tbot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"os/signal"
"syscall"
Expand All @@ -33,8 +35,8 @@ import (
"github.com/gravitational/teleport/tool/tbot/config"
"github.com/gravitational/teleport/tool/tbot/identity"
"github.com/gravitational/trace"
"github.com/kr/pretty"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)

var log = logrus.WithFields(logrus.Fields{
Expand All @@ -59,7 +61,7 @@ func Run(args []string) error {

app := utils.InitCLIParser("tbot", "tbot: Teleport Machine ID").Interspersed(false)
app.Flag("debug", "Verbose logging to stdout").Short('d').BoolVar(&cf.Debug)
app.Flag("config", "Path to a configuration file. Defaults to `/etc/tbot.yaml` if unspecified.").Short('c').StringVar(&cf.ConfigPath)
app.Flag("config", "Path to a configuration file.").Short('c').StringVar(&cf.ConfigPath)
app.HelpFlag.Short('h')

versionCmd := app.Command("version", "Print the version")
Expand All @@ -83,7 +85,15 @@ func Run(args []string) error {
initCmd.Flag("init-dir", "If using a config file and multiple destinations are configured, controls which destination dir to configure.").StringVar(&cf.InitDir)
initCmd.Flag("clean", "If set, remove unexpected files and directories from the destination.").BoolVar(&cf.Clean)

configCmd := app.Command("config", "Parse and dump a config file").Hidden()
configureCmd := app.Command("configure", "Creates a config file based on flags provided, and writes it to stdout or a file (-c <path>).")
configureCmd.Flag("auth-server", "Address of the Teleport Auth Server (On-Prem installs) or Proxy Server (Cloud installs).").Short('a').Envar(authServerEnvVar).StringVar(&cf.AuthServer)
configureCmd.Flag("ca-pin", "CA pin to validate the Teleport Auth Server; used on first connect.").StringsVar(&cf.CAPins)
configureCmd.Flag("certificate-ttl", "TTL of short-lived machine certificates.").Default("60m").DurationVar(&cf.CertificateTTL)
configureCmd.Flag("data-dir", "Directory to store internal bot data. Access to this directory should be limited.").StringVar(&cf.DataDir)
configureCmd.Flag("join-method", "Method to use to join the cluster, can be \"token\" or \"iam\".").Default(config.DefaultJoinMethod).EnumVar(&cf.JoinMethod, "token", "iam")
configureCmd.Flag("oneshot", "If set, quit after the first renewal.").BoolVar(&cf.Oneshot)
configureCmd.Flag("renewal-interval", "Interval at which short-lived certificates are renewed; must be less than the certificate TTL.").DurationVar(&cf.RenewalInterval)
configureCmd.Flag("token", "A bot join token, if attempting to onboard a new bot; used on first connect.").Envar(tokenEnvVar).StringVar(&cf.Token)

watchCmd := app.Command("watch", "Watch a destination directory for changes.").Hidden()

Expand All @@ -109,8 +119,8 @@ func Run(args []string) error {
err = onVersion()
case startCmd.FullCommand():
err = onStart(botConfig)
case configCmd.FullCommand():
err = onConfig(botConfig)
case configureCmd.FullCommand():
err = onConfigure(cf, os.Stdout)
case initCmd.FullCommand():
err = onInit(botConfig, &cf)
case watchCmd.FullCommand():
Expand All @@ -128,8 +138,45 @@ func onVersion() error {
return nil
}

func onConfig(botConfig *config.BotConfig) error {
pretty.Println(botConfig)
func onConfigure(
cf config.CLIConf,
stdout io.Writer,
) error {
out := stdout
outPath := cf.ConfigPath
if outPath != "" {
f, err := os.Create(outPath)
if err != nil {
return trace.Wrap(err)
}
defer f.Close()
out = f
}

// We do not want to load an existing configuration file as this will cause
// it to be merged with the provided flags and defaults.
cf.ConfigPath = ""
cfg, err := config.FromCLIConf(&cf)
if err != nil {
return nil
}

fmt.Fprintln(out, "# tbot config file generated by `configure` command")

enc := yaml.NewEncoder(out)
if err := enc.Encode(cfg); err != nil {
return trace.Wrap(err)
}

if err := enc.Close(); err != nil {
return trace.Wrap(err)
}

if outPath != "" {
log.Infof(
"Generated config file written to file: %s", outPath,
)
}

return nil
}
Expand Down
42 changes: 42 additions & 0 deletions tool/tbot/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"bytes"
"os"
"path/filepath"
"testing"

"github.com/gravitational/teleport/tool/tbot/config"
"github.com/stretchr/testify/require"
)

func Test_onConfigure(t *testing.T) {
t.Parallel()

cfg := &config.BotConfig{
AuthServer: "foo:bar",
}
// TODO: It would be nice to pull this out into a golden file
expect := "debug: false\nauth_server: foo:bar\ncertificate_ttl: 0s\nrenewal_interval: 0s\noneshot: false\n"

t.Run("file", func(t *testing.T) {
t.Parallel()

path := filepath.Join(t.TempDir(), "config.yaml")
err := onConfigure(&config.BotConfig{}, path)
require.NoError(t, err)

data, err := os.ReadFile(path)
require.NoError(t, err)
require.Equal(t, expect, string(data))
})

t.Run("stdout", func(t *testing.T) {
t.Parallel()

stdout := new(bytes.Buffer)
err := onConfigure(stdout, "")
require.NoError(t, err)
require.Equal(t, expect, stdout.String())
})
}

0 comments on commit 497995c

Please sign in to comment.