mirror of
https://github.com/siderolabs/talos.git
synced 2025-10-03 11:41:10 +02:00
feat: implement talosctl config info
command
Closes #3852. Signed-off-by: Alexey Palazhchenko <alexey.palazhchenko@gmail.com>
This commit is contained in:
parent
6d13d2cf92
commit
4708beaee5
@ -38,7 +38,7 @@ var genCrtCmd = &cobra.Command{
|
||||
|
||||
caPemBlock, _ := pem.Decode(caBytes)
|
||||
if caPemBlock == nil {
|
||||
return fmt.Errorf("error decoding cert PEM: %s", err)
|
||||
return fmt.Errorf("error decoding cert PEM")
|
||||
}
|
||||
|
||||
caCrt, err := stdlibx509.ParseCertificate(caPemBlock.Bytes)
|
||||
@ -53,7 +53,7 @@ var genCrtCmd = &cobra.Command{
|
||||
|
||||
keyPemBlock, _ := pem.Decode(keyBytes)
|
||||
if keyPemBlock == nil {
|
||||
return fmt.Errorf("error decoding key PEM: %s", err)
|
||||
return fmt.Errorf("error decoding key PEM")
|
||||
}
|
||||
|
||||
caKey, err := stdlibx509.ParsePKCS8PrivateKey(keyPemBlock.Bytes)
|
||||
@ -68,7 +68,7 @@ var genCrtCmd = &cobra.Command{
|
||||
|
||||
csrPemBlock, _ := pem.Decode(csrBytes)
|
||||
if csrPemBlock == nil {
|
||||
return fmt.Errorf("error parsing CSR PEM: %s", err)
|
||||
return fmt.Errorf("error parsing CSR PEM")
|
||||
}
|
||||
|
||||
ccsr, err := stdlibx509.ParseCertificateRequest(csrPemBlock.Bytes)
|
||||
|
@ -40,7 +40,7 @@ var genCSRCmd = &cobra.Command{
|
||||
|
||||
pemBlock, _ := pem.Decode(keyBytes)
|
||||
if pemBlock == nil {
|
||||
return fmt.Errorf("error decoding PEM: %s", err)
|
||||
return fmt.Errorf("error decoding PEM")
|
||||
}
|
||||
|
||||
keyEC, err := stdlibx509.ParsePKCS8PrivateKey(pemBlock.Bytes)
|
||||
|
@ -5,16 +5,21 @@
|
||||
package talos
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
@ -323,6 +328,88 @@ var configNewCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
// configNewCmd represents the `config info` command output template.
|
||||
var configInfoCmdTemplate = template.Must(template.New("configInfoCmdTemplate").Option("missingkey=error").Parse(strings.TrimSpace(`
|
||||
Current context: {{ .Context }}
|
||||
Nodes: {{ .Nodes }}
|
||||
Endpoints: {{ .Endpoints }}
|
||||
Roles: {{ .Roles }}
|
||||
Certificate expires: {{ .CertTTL }} ({{ .CertNotAfter }})
|
||||
`)))
|
||||
|
||||
// configInfoCommand implements `config info` command logic.
|
||||
//
|
||||
//nolint:goconst
|
||||
func configInfoCommand(config *clientconfig.Config, now time.Time) (string, error) {
|
||||
context := config.Contexts[config.Context]
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(context.Crt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(b)
|
||||
if block == nil {
|
||||
return "", fmt.Errorf("error decoding PEM")
|
||||
}
|
||||
|
||||
crt, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
roles, _ := role.Parse(crt.Subject.Organization)
|
||||
|
||||
nodesS := "not defined"
|
||||
if len(context.Nodes) > 0 {
|
||||
nodesS = strings.Join(context.Nodes, ", ")
|
||||
}
|
||||
|
||||
endpointsS := "not defined"
|
||||
if len(context.Endpoints) > 0 {
|
||||
endpointsS = strings.Join(context.Endpoints, ", ")
|
||||
}
|
||||
|
||||
rolesS := "not defined"
|
||||
if s := roles.Strings(); len(s) > 0 {
|
||||
rolesS = strings.Join(s, ", ")
|
||||
}
|
||||
|
||||
var res bytes.Buffer
|
||||
err = configInfoCmdTemplate.Execute(&res, map[string]string{
|
||||
"Context": config.Context,
|
||||
"Nodes": nodesS,
|
||||
"Endpoints": endpointsS,
|
||||
"Roles": rolesS,
|
||||
"CertTTL": humanize.RelTime(crt.NotAfter, now, "ago", "from now"),
|
||||
"CertNotAfter": crt.NotAfter.UTC().Format("2006-01-02"),
|
||||
})
|
||||
|
||||
return res.String() + "\n", err
|
||||
}
|
||||
|
||||
// configInfoCmd represents the `config info` command.
|
||||
var configInfoCmd = &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "Show information about the current context",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
c, err := openConfigAndContext("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := configInfoCommand(c, time.Now())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(res)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
configCmd.AddCommand(
|
||||
configEndpointCmd,
|
||||
@ -332,6 +419,7 @@ func init() {
|
||||
configGetContextsCmd,
|
||||
configMergeCmd,
|
||||
configNewCmd,
|
||||
configInfoCmd,
|
||||
)
|
||||
|
||||
configAddCmd.Flags().StringVar(&configAddCmdFlags.ca, "ca", "", "the path to the CA certificate")
|
||||
|
106
cmd/talosctl/cmd/talos/config_test.go
Normal file
106
cmd/talosctl/cmd/talos/config_test.go
Normal file
@ -0,0 +1,106 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package talos //nolint:testpackage // to test unexported function
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
clientconfig "github.com/talos-systems/talos/pkg/machinery/client/config"
|
||||
)
|
||||
|
||||
func TestConfigInfoCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
now := time.Date(2021, 7, 5, 22, 6, 42, 0, time.UTC)
|
||||
|
||||
//nolint:lll
|
||||
testCases := []struct {
|
||||
name string
|
||||
config string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Default",
|
||||
config: `
|
||||
context: no-roles
|
||||
contexts:
|
||||
no-roles:
|
||||
endpoints:
|
||||
- 172.20.1.2
|
||||
- 172.20.1.3
|
||||
- 172.20.1.4
|
||||
nodes:
|
||||
- 172.20.1.2
|
||||
ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBblpENTFNNW0zUmNMNUZwWXVYUkRWVEFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qRXdOekExTVRFeE9ETXpXaGNOTXpFd056QXpNVEV4T0RNeldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUFPYU93TVBaaDNHaE9kd2ZtSjYxcUhheUxDakFzdGcrWlNJCmpLbVV0WXR2bzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkhLNzFyajVOTmxad1J1VgpXcFc4bkNabit2YWxNQVVHQXl0bGNBTkJBTjlMQ2d2RzltSDdka3RSRTVQTFJJT25VcTNORnZaMTl1SHF5bWttCjJwQVR2SU02cXpMS0x0NXUyWEtBVFUzcDZDQWFGVVpOMkJhVjV0amVTeXZSWkFzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
||||
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJNakNCNWFBREFnRUNBaEFyalRDdnc3TElYZVRPTjFSTnhqeFNNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU1UQTNNRFV4TVRFNE16TmFGdzB6TVRBM01ETXhNVEU0TXpOYU1CTXhFVEFQQmdOVgpCQW9UQ0c5ek9tRmtiV2x1TUNvd0JRWURLMlZ3QXlFQTI2bDQ0eU1ZRTAvZUVUVEtsQXBTZGhlMEgzOEhGRDBnClh1Q2VFdWZ0YnZpalVqQlFNQTRHQTFVZER3RUIvd1FFQXdJSGdEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0QKQVFZSUt3WUJCUVVIQXdJd0h3WURWUjBqQkJnd0ZvQVVjcnZXdVBrMDJWbkJHNVZhbGJ5Y0ptZjY5cVV3QlFZRApLMlZ3QTBFQXhlN3Qrb2tZUURoNlZOQ0ZLenlqTmVuWkhmQ1MrRTdFdUYyVC9kcjRkU3JXcko2eXYvaE5ZalJqCnhNb0grU3dLak5kU2trNnJhWHdWb0ZwY3JraWRBdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJQWhmdzFISzBKVFgzVjh2K09wZ2J5dXQ2VzN0OWhVaGczQW5pK043WTFTNAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K
|
||||
`,
|
||||
expected: strings.TrimSpace(`
|
||||
Current context: no-roles
|
||||
Nodes: 172.20.1.2
|
||||
Endpoints: 172.20.1.2, 172.20.1.3, 172.20.1.4
|
||||
Roles: os:admin
|
||||
Certificate expires: 10 years from now (2031-07-03)
|
||||
`) + "\n",
|
||||
},
|
||||
{
|
||||
name: "NoRoles",
|
||||
config: `
|
||||
context: no-roles
|
||||
contexts:
|
||||
no-roles:
|
||||
endpoints:
|
||||
- 172.20.1.2
|
||||
ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBblpENTFNNW0zUmNMNUZwWXVYUkRWVEFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qRXdOekExTVRFeE9ETXpXaGNOTXpFd056QXpNVEV4T0RNeldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUFPYU93TVBaaDNHaE9kd2ZtSjYxcUhheUxDakFzdGcrWlNJCmpLbVV0WXR2bzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkhLNzFyajVOTmxad1J1VgpXcFc4bkNabit2YWxNQVVHQXl0bGNBTkJBTjlMQ2d2RzltSDdka3RSRTVQTFJJT25VcTNORnZaMTl1SHF5bWttCjJwQVR2SU02cXpMS0x0NXUyWEtBVFUzcDZDQWFGVVpOMkJhVjV0amVTeXZSWkFzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
||||
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJIekNCMHFBREFnRUNBaEFpcVA1MjN0NkJWNWZmNTVhUlBOWW1NQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU1UQTNNRFV4TVRNeE5UWmFGdzB6TVRBM01ETXhNVE14TlRaYU1BQXdLakFGQmdNcgpaWEFESVFCNTlmL2h0MG1tOUJqL3I4b0VsdjFUU3VVcG5kazlwang3Mm10MUpxZGEyNk5TTUZBd0RnWURWUjBQCkFRSC9CQVFEQWdlQU1CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFmQmdOVkhTTUUKR0RBV2dCUnl1OWE0K1RUWldjRWJsVnFWdkp3bVovcjJwVEFGQmdNclpYQURRUUNXUnAzcHB6YkM5ZzlmWC9RRgp0ZGg1eFY2YVYvdTVVdTFkU05TNmQ5VFFlUE00c1NXL1U5UGViNEpvK3Uzd1lPblBlb3huSWoxNzJOdTBQTm81CldPa0MKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJREJybmN1UEV2RHFPRjhqWWJMNUxvNWhSUkZ2cXBPVWZud2RMOHRPdzdFRgotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K
|
||||
`,
|
||||
expected: strings.TrimSpace(`
|
||||
Current context: no-roles
|
||||
Nodes: not defined
|
||||
Endpoints: 172.20.1.2
|
||||
Roles: not defined
|
||||
Certificate expires: 10 years from now (2031-07-03)
|
||||
`) + "\n",
|
||||
},
|
||||
{
|
||||
name: "FutureRole",
|
||||
config: `
|
||||
context: future-role
|
||||
contexts:
|
||||
future-role:
|
||||
ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBblpENTFNNW0zUmNMNUZwWXVYUkRWVEFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qRXdOekExTVRFeE9ETXpXaGNOTXpFd056QXpNVEV4T0RNeldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUFPYU93TVBaaDNHaE9kd2ZtSjYxcUhheUxDakFzdGcrWlNJCmpLbVV0WXR2bzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkhLNzFyajVOTmxad1J1VgpXcFc4bkNabit2YWxNQVVHQXl0bGNBTkJBTjlMQ2d2RzltSDdka3RSRTVQTFJJT25VcTNORnZaMTl1SHF5bWttCjJwQVR2SU02cXpMS0x0NXUyWEtBVFUzcDZDQWFGVVpOMkJhVjV0amVTeXZSWkFzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
||||
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJNekNCNXFBREFnRUNBaEFQL1MrVGx0WTBHdGk5Q1g0UDNVZStNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU1UQTNNRFV4TVRRek1qRmFGdzB6TVRBM01ETXhNVFF6TWpGYU1CUXhFakFRQmdOVgpCQW9UQ1c5ek9tWjFkSFZ5WlRBcU1BVUdBeXRsY0FNaEFLck04NmtPYm1MdGw5OVdpdzFFL29pdnl2YXVqVmNkCmlQTk82TVhQNGxEMm8xSXdVREFPQmdOVkhROEJBZjhFQkFNQ0I0QXdIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUgKQXdFR0NDc0dBUVVGQndNQ01COEdBMVVkSXdRWU1CYUFGSEs3MXJqNU5ObFp3UnVWV3BXOG5DWm4rdmFsTUFVRwpBeXRsY0FOQkFDSno3NTlGeVFJMXlIWTJNRG9vZDNrZjdZeG9HRG1Hem1nNllqRHJueXAxWGpaQ3o1Q1RQbm9jCjhxWlAyTXE5MDJnOXZSSUh1dm84N0NIZjJacTNGZ1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJS1RCRDIyZDBLVnNTek5iSkNBdjNObUVnL1VWOTk4SHZvY2NGb1lDOEJ1bAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K
|
||||
`,
|
||||
expected: strings.TrimSpace(`
|
||||
Current context: future-role
|
||||
Nodes: not defined
|
||||
Endpoints: not defined
|
||||
Roles: os:future
|
||||
Certificate expires: 10 years from now (2031-07-03)
|
||||
`) + "\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
config, err := clientconfig.FromString(tc.config)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, err := configInfoCommand(config, now)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
@ -425,6 +425,33 @@ talosctl config endpoint <endpoint>... [flags]
|
||||
|
||||
* [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig)
|
||||
|
||||
## talosctl config info
|
||||
|
||||
Show information about the current context
|
||||
|
||||
```
|
||||
talosctl config info [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for info
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--context string Context to be used in command
|
||||
-e, --endpoints strings override default endpoints in Talos configuration
|
||||
-n, --nodes strings target the specified nodes
|
||||
--talosconfig string The path to the Talos configuration file (default "/home/user/.talos/config")
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig)
|
||||
|
||||
## talosctl config merge
|
||||
|
||||
Merge additional contexts from another client configuration file
|
||||
@ -538,6 +565,7 @@ Manage the client configuration file (talosconfig)
|
||||
* [talosctl config context](#talosctl-config-context) - Set the current context
|
||||
* [talosctl config contexts](#talosctl-config-contexts) - List defined contexts
|
||||
* [talosctl config endpoint](#talosctl-config-endpoint) - Set the endpoint(s) for the current context
|
||||
* [talosctl config info](#talosctl-config-info) - Show information about the current context
|
||||
* [talosctl config merge](#talosctl-config-merge) - Merge additional contexts from another client configuration file
|
||||
* [talosctl config new](#talosctl-config-new) - Generate a new client configuration file
|
||||
* [talosctl config node](#talosctl-config-node) - Set the node(s) for the current context
|
||||
|
Loading…
x
Reference in New Issue
Block a user