Skip to content

Commit 1eebbce

Browse files
frezbosmira
authored andcommitted
chore: add output flag for talosctl config info
Add output flag for `talosctl config info`. This allows to programatically gather endpoints for CI tests. Eg: ```bash _out/talosctl-linux-amd64 config info --output json | jq '.Contexts[].Endpoints[0]' ``` Signed-off-by: Noel Georgi <git@frezbo.dev>
1 parent 3fbed80 commit 1eebbce

File tree

3 files changed

+122
-37
lines changed

3 files changed

+122
-37
lines changed

cmd/talosctl/cmd/talos/config.go

Lines changed: 73 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"context"
1010
"crypto/x509"
1111
"encoding/base64"
12+
"encoding/json"
1213
"encoding/pem"
1314
"errors"
1415
"fmt"
@@ -24,6 +25,7 @@ import (
2425
"github.com/siderolabs/gen/maps"
2526
"github.com/spf13/cobra"
2627
"google.golang.org/protobuf/types/known/durationpb"
28+
"gopkg.in/yaml.v3"
2729

2830
"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers"
2931
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
@@ -444,47 +446,58 @@ var configNewCmd = &cobra.Command{
444446
}
445447

446448
// configNewCmd represents the `config info` command output template.
447-
var configInfoCmdTemplate = template.Must(template.New("configInfoCmdTemplate").Option("missingkey=error").Parse(strings.TrimSpace(`
449+
var configInfoCmdTemplate = template.Must(template.New("configInfoCmdTemplate").
450+
Funcs(template.FuncMap{"join": strings.Join}).
451+
Option("missingkey=error").
452+
Parse(strings.TrimSpace(`
448453
Current context: {{ .Context }}
449-
Nodes: {{ .Nodes }}
450-
Endpoints: {{ .Endpoints }}
454+
Nodes: {{ if .Nodes }}{{ join .Nodes ", " }}{{ else }}not defined{{ end }}
455+
Endpoints: {{ if .Endpoints }}{{ join .Endpoints ", " }}{{ else }}not defined{{ end }}
451456
{{- if .Roles }}
452-
Roles: {{ .Roles }}{{ end }}
457+
Roles: {{ join .Roles ", " }}{{ end }}
453458
{{- if .CertTTL }}
454459
Certificate expires: {{ .CertTTL }} ({{ .CertNotAfter }}){{ end }}
455460
`)))
456461

457-
// configInfoCommand implements `config info` command logic.
458-
func configInfoCommand(config *clientconfig.Config, now time.Time) (string, error) {
462+
type talosconfigInfo struct {
463+
Context string `json:"context" yaml:"context"`
464+
Nodes []string `json:"nodes" yaml:"nodes"`
465+
Endpoints []string `json:"endpoints" yaml:"endpoints"`
466+
Roles []string `json:"roles" yaml:"roles"`
467+
CertTTL string `json:"certTTL" yaml:"certTTL"`
468+
CertNotAfter string `json:"certNotAfter" yaml:"certNotAfter"`
469+
}
470+
471+
// configInfo returns talosct config info.
472+
func configInfo(config *clientconfig.Config, now time.Time) (talosconfigInfo, error) {
459473
cfgContext, err := getContextData(config)
460474
if err != nil {
461-
return "", err
475+
return talosconfigInfo{}, err
462476
}
463477

464478
var (
465479
certTTL, certNotAfter string
466480
roles role.Set
467-
rolesS string
468481
)
469482

470483
if cfgContext.Crt != "" {
471484
var b []byte
472485

473486
b, err = base64.StdEncoding.DecodeString(cfgContext.Crt)
474487
if err != nil {
475-
return "", err
488+
return talosconfigInfo{}, err
476489
}
477490

478491
block, _ := pem.Decode(b)
479492
if block == nil {
480-
return "", fmt.Errorf("error decoding PEM")
493+
return talosconfigInfo{}, fmt.Errorf("error decoding PEM")
481494
}
482495

483496
var crt *x509.Certificate
484497

485498
crt, err = x509.ParseCertificate(block.Bytes)
486499
if err != nil {
487-
return "", err
500+
return talosconfigInfo{}, err
488501
}
489502

490503
roles, _ = role.Parse(crt.Subject.Organization)
@@ -493,33 +506,33 @@ func configInfoCommand(config *clientconfig.Config, now time.Time) (string, erro
493506
certNotAfter = crt.NotAfter.UTC().Format("2006-01-02")
494507
}
495508

496-
nodesS := "not defined"
497-
if len(cfgContext.Nodes) > 0 {
498-
nodesS = strings.Join(cfgContext.Nodes, ", ")
499-
}
500-
501-
endpointsS := "not defined"
502-
if len(cfgContext.Endpoints) > 0 {
503-
endpointsS = strings.Join(cfgContext.Endpoints, ", ")
504-
}
509+
return talosconfigInfo{
510+
Context: config.Context,
511+
Nodes: cfgContext.Nodes,
512+
Endpoints: cfgContext.Endpoints,
513+
Roles: roles.Strings(),
514+
CertTTL: certTTL,
515+
CertNotAfter: certNotAfter,
516+
}, nil
517+
}
505518

506-
if s := roles.Strings(); len(s) > 0 {
507-
rolesS = strings.Join(s, ", ")
519+
// configInfoCommand implements `config info` command logic.
520+
func configInfoCommand(config *clientconfig.Config, now time.Time) (string, error) {
521+
info, err := configInfo(config, now)
522+
if err != nil {
523+
return "", err
508524
}
509525

510526
var res bytes.Buffer
511-
err = configInfoCmdTemplate.Execute(&res, map[string]string{
512-
"Context": config.Context,
513-
"Nodes": nodesS,
514-
"Endpoints": endpointsS,
515-
"Roles": rolesS,
516-
"CertTTL": certTTL,
517-
"CertNotAfter": certNotAfter,
518-
})
527+
err = configInfoCmdTemplate.Execute(&res, info)
519528

520529
return res.String() + "\n", err
521530
}
522531

532+
var configInfoCmdFlags struct {
533+
output string
534+
}
535+
523536
// configInfoCmd represents the `config info` command.
524537
var configInfoCmd = &cobra.Command{
525538
Use: "info",
@@ -531,14 +544,36 @@ var configInfoCmd = &cobra.Command{
531544
return err
532545
}
533546

534-
res, err := configInfoCommand(c, time.Now())
535-
if err != nil {
536-
return err
537-
}
547+
switch configInfoCmdFlags.output {
548+
case "text":
549+
res, err := configInfoCommand(c, time.Now())
550+
if err != nil {
551+
return err
552+
}
538553

539-
fmt.Print(res)
554+
fmt.Print(res)
540555

541-
return nil
556+
return nil
557+
case "json":
558+
info, err := configInfo(c, time.Now())
559+
if err != nil {
560+
return err
561+
}
562+
563+
enc := json.NewEncoder(os.Stdout)
564+
enc.SetIndent("", " ")
565+
566+
return enc.Encode(&info)
567+
case "yaml":
568+
info, err := configInfo(c, time.Now())
569+
if err != nil {
570+
return err
571+
}
572+
573+
return yaml.NewEncoder(os.Stdout).Encode(&info)
574+
default:
575+
return fmt.Errorf("unknown output format: %q", configInfoCmdFlags.output)
576+
}
542577
},
543578
}
544579

@@ -584,6 +619,8 @@ func init() {
584619
configNewCmd.Flags().StringSliceVar(&configNewCmdFlags.roles, "roles", role.MakeSet(role.Admin).Strings(), "roles")
585620
configNewCmd.Flags().DurationVar(&configNewCmdFlags.crtTTL, "crt-ttl", 87600*time.Hour, "certificate TTL")
586621

622+
configInfoCmd.Flags().StringVarP(&configInfoCmdFlags.output, "output", "o", "text", "output format (json|yaml|text). Default text.")
623+
587624
addCommand(configCmd)
588625
}
589626

internal/integration/cli/config.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
package cli
88

99
import (
10+
"encoding/json"
1011
"path/filepath"
1112
"regexp"
1213
"strings"
1314

1415
"google.golang.org/protobuf/encoding/protojson"
16+
"gopkg.in/yaml.v3"
1517

1618
"github.com/siderolabs/talos/internal/integration/base"
1719
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
@@ -39,6 +41,51 @@ func (suite *TalosconfigSuite) TestList() {
3941
func (suite *TalosconfigSuite) TestInfo() {
4042
suite.RunCLI([]string{"config", "info"}, // TODO: remove 10 years once the CABPT & TF providers are updated to 1.5.2+
4143
base.StdoutShouldMatch(regexp.MustCompile(`(1 year|10 years) from now`)))
44+
45+
suite.RunCLI([]string{"config", "info", "--output", "text"}, // TODO: remove 10 years once the CABPT & TF providers are updated to 1.5.2+
46+
base.StdoutShouldMatch(regexp.MustCompile(`(1 year|10 years) from now`)))
47+
48+
suite.RunCLI([]string{"config", "info", "--output", "yaml"},
49+
base.StdoutMatchFunc(func(stdout string) error {
50+
var out map[string]any
51+
52+
err := yaml.Unmarshal([]byte(stdout), &out)
53+
if err != nil {
54+
return err
55+
}
56+
57+
suite.Assert().Contains(out, "endpoints")
58+
suite.Assert().Contains(out, "nodes")
59+
suite.Assert().Contains(out, "roles")
60+
suite.Assert().Contains(out, "context")
61+
62+
return nil
63+
}),
64+
)
65+
66+
suite.RunCLI([]string{"config", "info", "--output", "json"},
67+
base.StdoutMatchFunc(func(stdout string) error {
68+
var out map[string]any
69+
70+
err := json.Unmarshal([]byte(stdout), &out)
71+
if err != nil {
72+
return err
73+
}
74+
75+
suite.Assert().Contains(out, "endpoints")
76+
suite.Assert().Contains(out, "nodes")
77+
suite.Assert().Contains(out, "roles")
78+
suite.Assert().Contains(out, "context")
79+
80+
return nil
81+
}),
82+
)
83+
84+
suite.RunCLI([]string{"config", "info", "--output", "table"},
85+
base.StdoutEmpty(),
86+
base.ShouldFail(),
87+
base.StderrShouldMatch(regexp.MustCompile(`unknown output format: "table"`)),
88+
)
4289
}
4390

4491
// TestMerge checks `talosctl config merge`.

website/content/v1.6/reference/cli.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,8 @@ talosctl config info [flags]
475475
### Options
476476

477477
```
478-
-h, --help help for info
478+
-h, --help help for info
479+
-o, --output string output format (json|yaml|text). Default text. (default "text")
479480
```
480481

481482
### Options inherited from parent commands

0 commit comments

Comments
 (0)