Skip to content

Commit

Permalink
feat: implement talosctl config info command
Browse files Browse the repository at this point in the history
Closes #3852.

Signed-off-by: Alexey Palazhchenko <alexey.palazhchenko@gmail.com>
(cherry picked from commit 4708bea)
  • Loading branch information
AlekSi authored and smira committed Jul 7, 2021
1 parent f6892db commit 08c9a2e
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 4 deletions.
6 changes: 3 additions & 3 deletions cmd/talosctl/cmd/mgmt/gen/crt.go
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion cmd/talosctl/cmd/mgmt/gen/csr.go
Expand Up @@ -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)
Expand Down
88 changes: 88 additions & 0 deletions cmd/talosctl/cmd/talos/config.go
Expand Up @@ -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"

Expand Down Expand Up @@ -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,
Expand All @@ -332,6 +419,7 @@ func init() {
configGetContextsCmd,
configMergeCmd,
configNewCmd,
configInfoCmd,
)

configAddCmd.Flags().StringVar(&configAddCmdFlags.ca, "ca", "", "the path to the CA certificate")
Expand Down
106 changes: 106 additions & 0 deletions cmd/talosctl/cmd/talos/config_test.go
@@ -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)
})
}
}
28 changes: 28 additions & 0 deletions website/content/docs/v0.11/Reference/cli.md
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 08c9a2e

Please sign in to comment.