mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-05 20:36:18 +02:00
feat: replace flags with --mode in apply, edit and patch commands
Fixes: https://github.com/talos-systems/talos/issues/4588 Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
This commit is contained in:
parent
b09be2a69c
commit
2f2bdb26aa
@ -73,9 +73,24 @@ service MachineService {
|
||||
// ApplyConfiguration describes a request to assert a new configuration upon a
|
||||
// node.
|
||||
message ApplyConfigurationRequest {
|
||||
enum Mode {
|
||||
REBOOT = 0;
|
||||
AUTO = 1;
|
||||
NO_REBOOT = 2;
|
||||
STAGED = 3;
|
||||
}
|
||||
bytes data = 1;
|
||||
bool on_reboot = 2;
|
||||
bool immediate = 3;
|
||||
// replaced by mode
|
||||
bool on_reboot = 2 [
|
||||
(common.remove_deprecated_field) = "v0.16",
|
||||
deprecated = true
|
||||
];
|
||||
// replaced by mode
|
||||
bool immediate = 3 [
|
||||
(common.remove_deprecated_field) = "v0.16",
|
||||
deprecated = true
|
||||
];
|
||||
Mode mode = 4;
|
||||
}
|
||||
|
||||
// ApplyConfigurationResponse describes the response to a configuration request.
|
||||
@ -83,6 +98,10 @@ message ApplyConfiguration {
|
||||
common.Metadata metadata = 1;
|
||||
// Configuration validation warnings.
|
||||
repeated string warnings = 2;
|
||||
// States which mode was actually chosen.
|
||||
ApplyConfigurationRequest.Mode mode = 3;
|
||||
// Human-readable message explaining the result of the apply configuration call.
|
||||
string mode_details = 4;
|
||||
}
|
||||
|
||||
message ApplyConfigurationResponse {
|
||||
|
||||
@ -12,19 +12,17 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/talos-systems/talos/cmd/talosctl/pkg/talos/helpers"
|
||||
"github.com/talos-systems/talos/internal/pkg/tui/installer"
|
||||
"github.com/talos-systems/talos/pkg/cli"
|
||||
machineapi "github.com/talos-systems/talos/pkg/machinery/api/machine"
|
||||
"github.com/talos-systems/talos/pkg/machinery/client"
|
||||
)
|
||||
|
||||
var applyConfigCmdFlags struct {
|
||||
helpers.Mode
|
||||
certFingerprints []string
|
||||
filename string
|
||||
insecure bool
|
||||
interactive bool
|
||||
onReboot bool
|
||||
immediate bool
|
||||
}
|
||||
|
||||
// applyConfigCmd represents the applyConfiguration command.
|
||||
@ -61,7 +59,7 @@ var applyConfigCmd = &cobra.Command{
|
||||
if len(cfgBytes) < 1 {
|
||||
return fmt.Errorf("no configuration data read")
|
||||
}
|
||||
} else if !applyConfigCmdFlags.interactive {
|
||||
} else if !applyConfigCmdFlags.Interactive {
|
||||
return fmt.Errorf("no filename supplied for configuration")
|
||||
}
|
||||
|
||||
@ -74,7 +72,7 @@ var applyConfigCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
return withClient(func(ctx context.Context, c *client.Client) error {
|
||||
if applyConfigCmdFlags.interactive {
|
||||
if applyConfigCmdFlags.Interactive {
|
||||
install := installer.NewInstaller()
|
||||
node := Nodes[0]
|
||||
|
||||
@ -111,18 +109,16 @@ var applyConfigCmd = &cobra.Command{
|
||||
|
||||
resp, err := c.ApplyConfiguration(ctx, &machineapi.ApplyConfigurationRequest{
|
||||
Data: cfgBytes,
|
||||
OnReboot: applyConfigCmdFlags.onReboot,
|
||||
Immediate: applyConfigCmdFlags.immediate,
|
||||
Mode: applyConfigCmdFlags.Mode.Mode,
|
||||
OnReboot: applyConfigCmdFlags.OnReboot,
|
||||
Immediate: applyConfigCmdFlags.Immediate,
|
||||
})
|
||||
for _, m := range resp.GetMessages() {
|
||||
for _, w := range m.GetWarnings() {
|
||||
cli.Warning("%s", w)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error applying new configuration: %s", err)
|
||||
}
|
||||
|
||||
helpers.PrintApplyResults(resp)
|
||||
|
||||
return nil
|
||||
})
|
||||
},
|
||||
@ -132,8 +128,6 @@ func init() {
|
||||
applyConfigCmd.Flags().StringVarP(&applyConfigCmdFlags.filename, "file", "f", "", "the filename of the updated configuration")
|
||||
applyConfigCmd.Flags().BoolVarP(&applyConfigCmdFlags.insecure, "insecure", "i", false, "apply the config using the insecure (encrypted with no auth) maintenance service")
|
||||
applyConfigCmd.Flags().StringSliceVar(&applyConfigCmdFlags.certFingerprints, "cert-fingerprint", nil, "list of server certificate fingeprints to accept (defaults to no check)")
|
||||
applyConfigCmd.Flags().BoolVar(&applyConfigCmdFlags.interactive, "interactive", false, "apply the config using text based interactive mode")
|
||||
applyConfigCmd.Flags().BoolVar(&applyConfigCmdFlags.onReboot, "on-reboot", false, "apply the config on reboot")
|
||||
applyConfigCmd.Flags().BoolVar(&applyConfigCmdFlags.immediate, "immediate", false, "apply the config immediately (without a reboot)")
|
||||
helpers.AddModeFlags(&applyConfigCmdFlags.Mode, applyConfigCmd)
|
||||
addCommand(applyConfigCmd)
|
||||
}
|
||||
|
||||
@ -19,16 +19,14 @@ import (
|
||||
"k8s.io/kubectl/pkg/cmd/util/editor/crlf"
|
||||
|
||||
"github.com/talos-systems/talos/cmd/talosctl/pkg/talos/helpers"
|
||||
"github.com/talos-systems/talos/pkg/cli"
|
||||
"github.com/talos-systems/talos/pkg/machinery/api/machine"
|
||||
"github.com/talos-systems/talos/pkg/machinery/client"
|
||||
"github.com/talos-systems/talos/pkg/machinery/resources/config"
|
||||
)
|
||||
|
||||
var editCmdFlags struct {
|
||||
helpers.Mode
|
||||
namespace string
|
||||
immediate bool
|
||||
onReboot bool
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
@ -117,8 +115,9 @@ func editFn(c *client.Client) func(context.Context, client.ResourceResponse) err
|
||||
|
||||
resp, err := c.ApplyConfiguration(ctx, &machine.ApplyConfigurationRequest{
|
||||
Data: edited,
|
||||
Immediate: editCmdFlags.immediate,
|
||||
OnReboot: editCmdFlags.onReboot,
|
||||
Mode: editCmdFlags.Mode.Mode,
|
||||
OnReboot: editCmdFlags.OnReboot,
|
||||
Immediate: editCmdFlags.Immediate,
|
||||
})
|
||||
if err != nil {
|
||||
lastError = err.Error()
|
||||
@ -126,11 +125,7 @@ func editFn(c *client.Client) func(context.Context, client.ResourceResponse) err
|
||||
continue
|
||||
}
|
||||
|
||||
for _, m := range resp.GetMessages() {
|
||||
for _, w := range m.GetWarnings() {
|
||||
cli.Warning("%s", w)
|
||||
}
|
||||
}
|
||||
helpers.PrintApplyResults(resp)
|
||||
|
||||
break
|
||||
}
|
||||
@ -181,7 +176,6 @@ or 'notepad' for Windows.`,
|
||||
|
||||
func init() {
|
||||
editCmd.Flags().StringVar(&editCmdFlags.namespace, "namespace", "", "resource namespace (default is to use default namespace per resource)")
|
||||
editCmd.Flags().BoolVar(&editCmdFlags.immediate, "immediate", false, "apply the change immediately (without a reboot)")
|
||||
editCmd.Flags().BoolVar(&editCmdFlags.onReboot, "on-reboot", false, "apply the change on next reboot")
|
||||
helpers.AddModeFlags(&editCmdFlags.Mode, editCmd)
|
||||
addCommand(editCmd)
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ import (
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
|
||||
"github.com/talos-systems/talos/cmd/talosctl/pkg/talos/helpers"
|
||||
"github.com/talos-systems/talos/pkg/cli"
|
||||
"github.com/talos-systems/talos/pkg/machinery/api/machine"
|
||||
"github.com/talos-systems/talos/pkg/machinery/client"
|
||||
"github.com/talos-systems/talos/pkg/machinery/config/configpatcher"
|
||||
@ -26,11 +25,10 @@ import (
|
||||
)
|
||||
|
||||
var patchCmdFlags struct {
|
||||
helpers.Mode
|
||||
namespace string
|
||||
patch string
|
||||
patchFile string
|
||||
immediate bool
|
||||
onReboot bool
|
||||
}
|
||||
|
||||
func patchFn(c *client.Client, patch jsonpatch.Patch) func(context.Context, client.ResourceResponse) error {
|
||||
@ -55,8 +53,9 @@ func patchFn(c *client.Client, patch jsonpatch.Patch) func(context.Context, clie
|
||||
|
||||
resp, err := c.ApplyConfiguration(ctx, &machine.ApplyConfigurationRequest{
|
||||
Data: patched,
|
||||
Immediate: patchCmdFlags.immediate,
|
||||
OnReboot: patchCmdFlags.onReboot,
|
||||
Mode: patchCmdFlags.Mode.Mode,
|
||||
OnReboot: patchCmdFlags.OnReboot,
|
||||
Immediate: patchCmdFlags.Immediate,
|
||||
})
|
||||
|
||||
if bytes.Equal(
|
||||
@ -74,11 +73,7 @@ func patchFn(c *client.Client, patch jsonpatch.Patch) func(context.Context, clie
|
||||
msg.Metadata.GetHostname(),
|
||||
)
|
||||
|
||||
for _, m := range resp.GetMessages() {
|
||||
for _, w := range m.GetWarnings() {
|
||||
cli.Warning("%s", w)
|
||||
}
|
||||
}
|
||||
helpers.PrintApplyResults(resp)
|
||||
|
||||
return err
|
||||
}
|
||||
@ -134,7 +129,6 @@ func init() {
|
||||
patchCmd.Flags().StringVar(&patchCmdFlags.namespace, "namespace", "", "resource namespace (default is to use default namespace per resource)")
|
||||
patchCmd.Flags().StringVar(&patchCmdFlags.patchFile, "patch-file", "", "a file containing a patch to be applied to the resource.")
|
||||
patchCmd.Flags().StringVarP(&patchCmdFlags.patch, "patch", "p", "", "the patch to be applied to the resource file.")
|
||||
patchCmd.Flags().BoolVar(&patchCmdFlags.immediate, "immediate", false, "apply the change immediately (without a reboot)")
|
||||
patchCmd.Flags().BoolVar(&patchCmdFlags.onReboot, "on-reboot", false, "apply the change on next reboot")
|
||||
helpers.AddModeFlags(&patchCmdFlags.Mode, patchCmd)
|
||||
addCommand(patchCmd)
|
||||
}
|
||||
|
||||
131
cmd/talosctl/pkg/talos/helpers/mode.go
Normal file
131
cmd/talosctl/pkg/talos/helpers/mode.go
Normal file
@ -0,0 +1,131 @@
|
||||
// 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 helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/talos-systems/talos/pkg/cli"
|
||||
"github.com/talos-systems/talos/pkg/machinery/api/machine"
|
||||
)
|
||||
|
||||
// InteractiveMode fake mode value for the interactive config mode.
|
||||
// Should be never passed to the API.
|
||||
const InteractiveMode machine.ApplyConfigurationRequest_Mode = -1
|
||||
|
||||
// Mode apply, patch, edit config config update mode.
|
||||
type Mode struct {
|
||||
options map[string]machine.ApplyConfigurationRequest_Mode
|
||||
Mode machine.ApplyConfigurationRequest_Mode
|
||||
Immediate bool
|
||||
Interactive bool
|
||||
OnReboot bool
|
||||
}
|
||||
|
||||
func (m Mode) String() string {
|
||||
switch m.Mode {
|
||||
case machine.ApplyConfigurationRequest_AUTO:
|
||||
return modeAuto
|
||||
case machine.ApplyConfigurationRequest_NO_REBOOT:
|
||||
return modeNoReboot
|
||||
case machine.ApplyConfigurationRequest_REBOOT:
|
||||
return modeReboot
|
||||
case machine.ApplyConfigurationRequest_STAGED:
|
||||
return modeStaged
|
||||
case InteractiveMode:
|
||||
return modeInteractive
|
||||
default:
|
||||
return modeAuto
|
||||
}
|
||||
}
|
||||
|
||||
// Set implements Flag interface.
|
||||
func (m *Mode) Set(value string) error {
|
||||
mode, ok := m.options[value]
|
||||
if !ok {
|
||||
return fmt.Errorf("possible options are: %s", m.Type())
|
||||
}
|
||||
|
||||
m.Mode = mode
|
||||
|
||||
//nolint:exhaustive
|
||||
switch m.Mode {
|
||||
case machine.ApplyConfigurationRequest_STAGED:
|
||||
m.OnReboot = true
|
||||
case machine.ApplyConfigurationRequest_NO_REBOOT:
|
||||
m.Immediate = true
|
||||
case InteractiveMode:
|
||||
m.Interactive = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type implements Flag interface.
|
||||
func (m *Mode) Type() string {
|
||||
options := make([]string, 0, len(m.options))
|
||||
for s := range m.options {
|
||||
options = append(options, s)
|
||||
}
|
||||
|
||||
sort.Strings(options)
|
||||
|
||||
return strings.Join(options, ", ")
|
||||
}
|
||||
|
||||
const (
|
||||
modeAuto = "auto"
|
||||
modeNoReboot = "no-reboot"
|
||||
modeReboot = "reboot"
|
||||
modeStaged = "staged"
|
||||
modeInteractive = "interactive"
|
||||
)
|
||||
|
||||
// AddModeFlags adds deprecated flags to the command and registers mode flag with it's parser.
|
||||
func AddModeFlags(mode *Mode, command *cobra.Command) {
|
||||
modes := map[string]machine.ApplyConfigurationRequest_Mode{
|
||||
modeAuto: machine.ApplyConfigurationRequest_AUTO,
|
||||
modeNoReboot: machine.ApplyConfigurationRequest_NO_REBOOT,
|
||||
modeReboot: machine.ApplyConfigurationRequest_REBOOT,
|
||||
modeStaged: machine.ApplyConfigurationRequest_STAGED,
|
||||
}
|
||||
|
||||
deprecatedFlag := func(dest *bool, flag, usage, deprecationWarning string) {
|
||||
command.Flags().BoolVar(dest, flag, false, fmt.Sprintf("%s (deprecated, replaced with --mode)", usage))
|
||||
command.Flags().MarkDeprecated(flag, deprecationWarning) //nolint:errcheck
|
||||
}
|
||||
|
||||
// TODO: remove in v0.16
|
||||
deprecatedFlag(&mode.OnReboot, "on-reboot", "apply the config on reboot", "Use --mode=staged instead")
|
||||
deprecatedFlag(&mode.Immediate, "immediate", "apply the config immediately (without a reboot)", "Use --mode=no-reboot instead")
|
||||
|
||||
if command.Use == "apply-config" {
|
||||
deprecatedFlag(&mode.Interactive, "interactive", "apply the config using text based interactive mode", "Use --mode=interactive instead")
|
||||
|
||||
modes[modeInteractive] = InteractiveMode
|
||||
}
|
||||
|
||||
mode.Mode = machine.ApplyConfigurationRequest_AUTO
|
||||
mode.options = modes
|
||||
|
||||
command.Flags().VarP(mode, "mode", "m", "apply config mode")
|
||||
}
|
||||
|
||||
// PrintApplyResults prints out all warnings and auto apply results.
|
||||
func PrintApplyResults(resp *machine.ApplyConfigurationResponse) {
|
||||
for _, m := range resp.GetMessages() {
|
||||
for _, w := range m.GetWarnings() {
|
||||
cli.Warning("%s", w)
|
||||
}
|
||||
|
||||
if m.ModeDetails != "" {
|
||||
fmt.Println(m.ModeDetails)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,19 @@ preface = """\
|
||||
|
||||
[notes]
|
||||
|
||||
[notes.applyconfig]
|
||||
title = "Apply Config Enhancements"
|
||||
description="""\
|
||||
`talosctl apply/patch/edit` cli commands got revamped.
|
||||
Separate flags `--on-reboot`, `--immediate`, `--interactive` were replaced
|
||||
with a single `--mode` flag that can take the following values:
|
||||
- `auto` new mode that automatically applies the configuration in immediate/reboot mode.
|
||||
- `no-reboot` force apply immediately, if not possible, then fail.
|
||||
- `reboot` force reboot with apply config.
|
||||
- `staged` write new machine configuration to STATE, but don't apply it (it will be applied after a reboot).
|
||||
- `interactive` starts interactive installer, only for `apply`.
|
||||
"""
|
||||
|
||||
[notes.updates]
|
||||
title = "Component Updates"
|
||||
description="""\
|
||||
|
||||
@ -137,20 +137,44 @@ func (s *Server) Register(obj *grpc.Server) {
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfigurationRequest) (*machine.ApplyConfigurationResponse, error) {
|
||||
log.Printf("apply config request: immediate %v, on reboot %v", in.Immediate, in.OnReboot)
|
||||
mode := in.Mode.String()
|
||||
modeDetails := ""
|
||||
|
||||
// TODO: remove in v0.16
|
||||
switch {
|
||||
case in.Immediate: //nolint:staticcheck
|
||||
in.Mode = machine.ApplyConfigurationRequest_NO_REBOOT
|
||||
case in.OnReboot: //nolint:staticcheck
|
||||
in.Mode = machine.ApplyConfigurationRequest_STAGED
|
||||
}
|
||||
|
||||
cfgProvider, err := s.Controller.Runtime().LoadAndValidateConfig(in.GetData())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// --immediate
|
||||
if in.Immediate {
|
||||
//nolint:exhaustive
|
||||
switch in.Mode {
|
||||
// --mode=no-reboot
|
||||
case machine.ApplyConfigurationRequest_NO_REBOOT:
|
||||
if err = s.Controller.Runtime().CanApplyImmediate(cfgProvider); err != nil {
|
||||
return nil, err
|
||||
return nil, status.Errorf(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
// --mode=auto detect actual update mode
|
||||
case machine.ApplyConfigurationRequest_AUTO:
|
||||
if err = s.Controller.Runtime().CanApplyImmediate(cfgProvider); err != nil {
|
||||
in.Mode = machine.ApplyConfigurationRequest_REBOOT
|
||||
modeDetails = fmt.Sprintf("applied configuration with a reboot: %s", err)
|
||||
} else {
|
||||
in.Mode = machine.ApplyConfigurationRequest_NO_REBOOT
|
||||
modeDetails = "applied configuration without a reboot"
|
||||
}
|
||||
|
||||
mode = fmt.Sprintf("%s(%s)", mode, in.Mode)
|
||||
}
|
||||
|
||||
log.Printf("apply config request: mode %s", strings.ToLower(mode))
|
||||
|
||||
cfg, err := cfgProvider.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -160,14 +184,17 @@ func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfig
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
// --immediate
|
||||
case in.Immediate:
|
||||
//nolint:exhaustive
|
||||
switch in.Mode {
|
||||
// --mode=no-reboot
|
||||
case machine.ApplyConfigurationRequest_NO_REBOOT:
|
||||
if err := s.Controller.Runtime().SetConfig(cfgProvider); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// default, no `--on-reboot`
|
||||
case !in.OnReboot:
|
||||
// --mode=staged
|
||||
case machine.ApplyConfigurationRequest_STAGED:
|
||||
// --mode=reboot
|
||||
case machine.ApplyConfigurationRequest_REBOOT:
|
||||
go func() {
|
||||
if err := s.Controller.Run(context.Background(), runtime.SequenceReboot, nil, runtime.WithTakeover()); err != nil {
|
||||
if !runtime.IsRebootError(err) {
|
||||
@ -179,11 +206,16 @@ func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfig
|
||||
}
|
||||
}
|
||||
}()
|
||||
default:
|
||||
return nil, fmt.Errorf("incorrect mode '%s' specified for the apply config call", in.Mode.String())
|
||||
}
|
||||
|
||||
return &machine.ApplyConfigurationResponse{
|
||||
Messages: []*machine.ApplyConfiguration{
|
||||
{},
|
||||
{
|
||||
Mode: in.Mode,
|
||||
ModeDetails: modeDetails,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ func Run(ctx context.Context, logger *log.Logger, r runtime.Runtime) ([]byte, er
|
||||
logger.Println("upload configuration using talosctl:")
|
||||
logger.Printf("\ttalosctl apply-config --insecure --nodes %s --file <config.yaml>", firstIP)
|
||||
logger.Println("or apply configuration using talosctl interactive installer:")
|
||||
logger.Printf("\ttalosctl apply-config --insecure --nodes %s --interactive", firstIP)
|
||||
logger.Printf("\ttalosctl apply-config --insecure --nodes %s --mode=interactive", firstIP)
|
||||
logger.Println("optionally with node fingerprint check:")
|
||||
logger.Printf("\ttalosctl apply-config --insecure --nodes %s --cert-fingerprint '%s' --file <config.yaml>", firstIP, certFingerprint)
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
@ -54,8 +55,14 @@ func (s *Server) Register(obj *grpc.Server) {
|
||||
|
||||
// ApplyConfiguration implements machine.MachineService.
|
||||
func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfigurationRequest) (*machine.ApplyConfigurationResponse, error) {
|
||||
if in.OnReboot {
|
||||
return nil, fmt.Errorf("apply configuration on reboot is not supported in maintenance mode")
|
||||
//nolint:exhaustive
|
||||
switch in.Mode {
|
||||
case machine.ApplyConfigurationRequest_REBOOT:
|
||||
fallthrough
|
||||
case machine.ApplyConfigurationRequest_AUTO:
|
||||
default:
|
||||
return nil, fmt.Errorf("apply configuration --mode='%s' is not supported in maintenance mode",
|
||||
strings.ReplaceAll(strings.ToLower(in.Mode.String()), "_", "-"))
|
||||
}
|
||||
|
||||
cfgProvider, err := configloader.NewFromBytes(in.GetData())
|
||||
|
||||
@ -9,11 +9,15 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/talos-systems/go-retry/retry"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/talos-systems/talos/internal/integration/base"
|
||||
machineapi "github.com/talos-systems/talos/pkg/machinery/api/machine"
|
||||
@ -108,6 +112,7 @@ func (suite *ApplyConfigSuite) TestApply() {
|
||||
suite.AssertRebooted(suite.ctx, node, func(nodeCtx context.Context) error {
|
||||
_, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
|
||||
Data: cfgDataOut,
|
||||
Mode: machineapi.ApplyConfigurationRequest_REBOOT,
|
||||
})
|
||||
if err != nil {
|
||||
// It is expected that the connection will EOF here, so just log the error
|
||||
@ -135,62 +140,67 @@ func (suite *ApplyConfigSuite) TestApply() {
|
||||
)
|
||||
}
|
||||
|
||||
// TestApplyOnReboot verifies the apply config API without reboot.
|
||||
func (suite *ApplyConfigSuite) TestApplyOnReboot() {
|
||||
suite.WaitForBootDone(suite.ctx)
|
||||
// TestApplyWithoutReboot verifies the apply config API without reboot.
|
||||
func (suite *ApplyConfigSuite) TestApplyWithoutReboot() {
|
||||
for _, mode := range []machineapi.ApplyConfigurationRequest_Mode{
|
||||
machineapi.ApplyConfigurationRequest_AUTO,
|
||||
machineapi.ApplyConfigurationRequest_STAGED,
|
||||
} {
|
||||
suite.WaitForBootDone(suite.ctx)
|
||||
|
||||
node := suite.RandomDiscoveredNode()
|
||||
suite.ClearConnectionRefused(suite.ctx, node)
|
||||
node := suite.RandomDiscoveredNode()
|
||||
suite.ClearConnectionRefused(suite.ctx, node)
|
||||
|
||||
nodeCtx := client.WithNodes(suite.ctx, node)
|
||||
nodeCtx := client.WithNodes(suite.ctx, node)
|
||||
|
||||
provider, err := suite.ReadConfigFromNode(nodeCtx)
|
||||
suite.Require().NoError(err, "failed to read existing config from node %q", node)
|
||||
provider, err := suite.ReadConfigFromNode(nodeCtx)
|
||||
suite.Require().NoError(err, "failed to read existing config from node %q", node)
|
||||
|
||||
cfg, ok := provider.Raw().(*v1alpha1.Config)
|
||||
suite.Require().True(ok)
|
||||
cfg, ok := provider.Raw().(*v1alpha1.Config)
|
||||
suite.Require().True(ok)
|
||||
|
||||
if cfg.MachineConfig.MachineSysctls == nil {
|
||||
cfg.MachineConfig.MachineSysctls = make(map[string]string)
|
||||
if cfg.MachineConfig.MachineSysctls == nil {
|
||||
cfg.MachineConfig.MachineSysctls = make(map[string]string)
|
||||
}
|
||||
|
||||
cfg.MachineConfig.MachineSysctls[applyConfigNoRebootTestSysctl] = applyConfigNoRebootTestSysctlVal
|
||||
|
||||
cfgDataOut, err := cfg.Bytes()
|
||||
suite.Require().NoError(err, "failed to marshal updated machine config data (node %q)", node)
|
||||
|
||||
_, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
|
||||
Data: cfgDataOut,
|
||||
Mode: mode,
|
||||
})
|
||||
suite.Require().NoError(err, "failed to apply deferred configuration (node %q): %w", node)
|
||||
|
||||
// Verify configuration change
|
||||
var newProvider config.Provider
|
||||
|
||||
newProvider, err = suite.ReadConfigFromNode(nodeCtx)
|
||||
|
||||
suite.Require().NoError(err, "failed to read updated configuration from node %q: %w", node)
|
||||
|
||||
suite.Assert().Equal(
|
||||
newProvider.Machine().Sysctls()[applyConfigNoRebootTestSysctl],
|
||||
applyConfigNoRebootTestSysctlVal,
|
||||
)
|
||||
|
||||
cfg, ok = newProvider.Raw().(*v1alpha1.Config)
|
||||
suite.Require().True(ok)
|
||||
|
||||
// revert back
|
||||
delete(cfg.MachineConfig.MachineSysctls, applyConfigNoRebootTestSysctl)
|
||||
|
||||
cfgDataOut, err = cfg.Bytes()
|
||||
suite.Require().NoError(err, "failed to marshal updated machine config data (node %q)", node)
|
||||
|
||||
_, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
|
||||
Data: cfgDataOut,
|
||||
Mode: mode,
|
||||
})
|
||||
suite.Require().NoError(err, "failed to apply deferred configuration (node %q): %w", node)
|
||||
}
|
||||
|
||||
cfg.MachineConfig.MachineSysctls[applyConfigNoRebootTestSysctl] = applyConfigNoRebootTestSysctlVal
|
||||
|
||||
cfgDataOut, err := cfg.Bytes()
|
||||
suite.Require().NoError(err, "failed to marshal updated machine config data (node %q)", node)
|
||||
|
||||
_, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
|
||||
OnReboot: true,
|
||||
Data: cfgDataOut,
|
||||
})
|
||||
suite.Require().NoError(err, "failed to apply deferred configuration (node %q): %w", node)
|
||||
|
||||
// Verify configuration change
|
||||
var newProvider config.Provider
|
||||
|
||||
newProvider, err = suite.ReadConfigFromNode(nodeCtx)
|
||||
|
||||
suite.Require().NoError(err, "failed to read updated configuration from node %q: %w", node)
|
||||
|
||||
suite.Assert().Equal(
|
||||
newProvider.Machine().Sysctls()[applyConfigNoRebootTestSysctl],
|
||||
applyConfigNoRebootTestSysctlVal,
|
||||
)
|
||||
|
||||
cfg, ok = newProvider.Raw().(*v1alpha1.Config)
|
||||
suite.Require().True(ok)
|
||||
|
||||
// revert back
|
||||
delete(cfg.MachineConfig.MachineSysctls, applyConfigNoRebootTestSysctl)
|
||||
|
||||
cfgDataOut, err = cfg.Bytes()
|
||||
suite.Require().NoError(err, "failed to marshal updated machine config data (node %q)", node)
|
||||
|
||||
_, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
|
||||
OnReboot: true,
|
||||
Data: cfgDataOut,
|
||||
})
|
||||
suite.Require().NoError(err, "failed to apply deferred configuration (node %q): %w", node)
|
||||
}
|
||||
|
||||
// TestApplyConfigRotateEncryptionSecrets verify key rotation by sequential apply config calls.
|
||||
@ -288,6 +298,7 @@ func (suite *ApplyConfigSuite) TestApplyConfigRotateEncryptionSecrets() {
|
||||
suite.AssertRebooted(suite.ctx, node, func(nodeCtx context.Context) error {
|
||||
_, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
|
||||
Data: data,
|
||||
Mode: machineapi.ApplyConfigurationRequest_REBOOT,
|
||||
})
|
||||
if err != nil {
|
||||
// It is expected that the connection will EOF here, so just log the error
|
||||
@ -334,6 +345,47 @@ func (suite *ApplyConfigSuite) TestApplyConfigRotateEncryptionSecrets() {
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyNoReboot verifies the apply config API fails if NoReboot mode is requested on a field that can not be applied immediately.
|
||||
func (suite *ApplyConfigSuite) TestApplyNoReboot() {
|
||||
nodes := suite.DiscoverNodes(suite.ctx).NodesByType(machine.TypeWorker)
|
||||
suite.Require().NotEmpty(nodes)
|
||||
|
||||
suite.WaitForBootDone(suite.ctx)
|
||||
|
||||
sort.Strings(nodes)
|
||||
|
||||
node := nodes[0]
|
||||
|
||||
nodeCtx := client.WithNodes(suite.ctx, node)
|
||||
|
||||
provider, err := suite.ReadConfigFromNode(nodeCtx)
|
||||
suite.Assert().Nilf(err, "failed to read existing config from node %q: %w", node, err)
|
||||
|
||||
cfg, ok := provider.Raw().(*v1alpha1.Config)
|
||||
suite.Require().True(ok)
|
||||
|
||||
// this won't be possible without a reboot
|
||||
cfg.MachineConfig.MachineType = "controlplane"
|
||||
|
||||
cfgDataOut, err := cfg.Bytes()
|
||||
suite.Assert().Nilf(err, "failed to marshal updated machine config data (node %q): %w", node, err)
|
||||
|
||||
_, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
|
||||
Data: cfgDataOut,
|
||||
Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT,
|
||||
})
|
||||
suite.Require().Error(err)
|
||||
|
||||
var (
|
||||
errs *multierror.Error
|
||||
nodeError *client.NodeError
|
||||
)
|
||||
|
||||
suite.Require().True(errors.As(err, &errs))
|
||||
suite.Require().True(errors.As(errs.Errors[0], &nodeError))
|
||||
suite.Require().Equal(codes.InvalidArgument, status.Code(nodeError.Err))
|
||||
}
|
||||
|
||||
func init() {
|
||||
allSuites = append(allSuites, new(ApplyConfigSuite))
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ func (suite *PatchSuite) TestSuccess() {
|
||||
data, err := json.Marshal(patch)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.RunCLI([]string{"patch", "--nodes", node, "--patch", string(data), "machineconfig", "--immediate"})
|
||||
suite.RunCLI([]string{"patch", "--nodes", node, "--patch", string(data), "machineconfig", "--mode=no-reboot"})
|
||||
}
|
||||
|
||||
// TestError runs comand with error.
|
||||
|
||||
@ -69,7 +69,8 @@ func patchNodeConfig(ctx context.Context, cluster UpgradeProvider, node string,
|
||||
|
||||
_, err = c.ApplyConfiguration(ctx, &machine.ApplyConfigurationRequest{
|
||||
Data: cfgBytes,
|
||||
Immediate: true,
|
||||
Mode: machine.ApplyConfigurationRequest_NO_REBOOT,
|
||||
Immediate: true, // keeping that for backward compatibility
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error applying config: %w", err)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -57,6 +57,11 @@ func (m *ApplyConfigurationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, er
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.Mode != 0 {
|
||||
i = encodeVarint(dAtA, i, uint64(m.Mode))
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
}
|
||||
if m.Immediate {
|
||||
i--
|
||||
if m.Immediate {
|
||||
@ -117,6 +122,18 @@ func (m *ApplyConfiguration) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if len(m.ModeDetails) > 0 {
|
||||
i -= len(m.ModeDetails)
|
||||
copy(dAtA[i:], m.ModeDetails)
|
||||
i = encodeVarint(dAtA, i, uint64(len(m.ModeDetails)))
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
if m.Mode != 0 {
|
||||
i = encodeVarint(dAtA, i, uint64(m.Mode))
|
||||
i--
|
||||
dAtA[i] = 0x18
|
||||
}
|
||||
if len(m.Warnings) > 0 {
|
||||
for iNdEx := len(m.Warnings) - 1; iNdEx >= 0; iNdEx-- {
|
||||
i -= len(m.Warnings[iNdEx])
|
||||
@ -7889,6 +7906,9 @@ func (m *ApplyConfigurationRequest) SizeVT() (n int) {
|
||||
if m.Immediate {
|
||||
n += 2
|
||||
}
|
||||
if m.Mode != 0 {
|
||||
n += 1 + sov(uint64(m.Mode))
|
||||
}
|
||||
if m.unknownFields != nil {
|
||||
n += len(m.unknownFields)
|
||||
}
|
||||
@ -7917,6 +7937,13 @@ func (m *ApplyConfiguration) SizeVT() (n int) {
|
||||
n += 1 + l + sov(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.Mode != 0 {
|
||||
n += 1 + sov(uint64(m.Mode))
|
||||
}
|
||||
l = len(m.ModeDetails)
|
||||
if l > 0 {
|
||||
n += 1 + l + sov(uint64(l))
|
||||
}
|
||||
if m.unknownFields != nil {
|
||||
n += len(m.unknownFields)
|
||||
}
|
||||
@ -11303,6 +11330,25 @@ func (m *ApplyConfigurationRequest) UnmarshalVT(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.Immediate = bool(v != 0)
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType)
|
||||
}
|
||||
m.Mode = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.Mode |= ApplyConfigurationRequest_Mode(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skip(dAtA[iNdEx:])
|
||||
@ -11431,6 +11477,57 @@ func (m *ApplyConfiguration) UnmarshalVT(dAtA []byte) error {
|
||||
}
|
||||
m.Warnings = append(m.Warnings, string(dAtA[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType)
|
||||
}
|
||||
m.Mode = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.Mode |= ApplyConfigurationRequest_Mode(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ModeDetails", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLength
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLength
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.ModeDetails = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skip(dAtA[iNdEx:])
|
||||
|
||||
@ -80,7 +80,7 @@ NODE NAMESPACE TYPE ID VERSION TYPE
|
||||
```
|
||||
|
||||
Nodes with `init` type are incompatible with `etcd` recovery procedure.
|
||||
`init` node can be converted to `controlplane` type with `talosctl edit mc --on-reboot` command followed
|
||||
`init` node can be converted to `controlplane` type with `talosctl edit mc --mode=staged` command followed
|
||||
by node reboot with `talosctl reboot` command.
|
||||
|
||||
### Preparing Control Plane Nodes
|
||||
|
||||
@ -135,7 +135,7 @@ There is no in-place encryption support for the partitions right now, so to avoi
|
||||
|
||||
As such, migration from unencrypted to encrypted needs some additional handling, especially around explicitly wiping partitions.
|
||||
|
||||
- `apply-config` should be called with `--on-reboot` flag.
|
||||
- `apply-config` should be called with `--mode=staged`.
|
||||
- Partition should be wiped after `apply-config`, but before the reboot.
|
||||
|
||||
Edit your machine config and add the encryption configuration:
|
||||
@ -144,10 +144,10 @@ Edit your machine config and add the encryption configuration:
|
||||
vim config.yaml
|
||||
```
|
||||
|
||||
Apply the configuration with `--on-reboot` flag:
|
||||
Apply the configuration with `--mode=staged`:
|
||||
|
||||
```bash
|
||||
talosctl apply-config -f config.yaml -n <node ip> --on-reboot
|
||||
talosctl apply-config -f config.yaml -n <node ip> --mode=staged
|
||||
```
|
||||
|
||||
Wipe the partition you're going to encrypt:
|
||||
|
||||
@ -15,14 +15,29 @@ There are three `talosctl` commands which facilitate machine configuration updat
|
||||
* `talosctl edit machineconfig` to launch an editor with existing node configuration, make changes and apply configuration back
|
||||
* `talosctl patch machineconfig` to apply automated machine configuration via JSON patch
|
||||
|
||||
Each of these commands can operate in one of three modes:
|
||||
Each of these commands can operate in one of four modes:
|
||||
|
||||
* apply change with a reboot (default): update configuration, reboot Talos node to apply configuration change
|
||||
* apply change immediately (`--immediate` flag): change is applied immediately without a reboot, only `.cluster` sub-tree of the machine configuration can be updated in Talos 0.9
|
||||
* apply change on next reboot (`--on-reboot`): change is staged to be applied after a reboot, but node is not rebooted
|
||||
* apply change in automatic mode(default): reboot if the change can't be applied without a reboot, otherwise apply the change immediately
|
||||
* apply change with a reboot (`--mode=reboot`): update configuration, reboot Talos node to apply configuration change
|
||||
* apply change immediately (`--mode=no-reboot` flag): change is applied immediately without a reboot, fails if the change contains any fields that can not be updated without a reboot
|
||||
* apply change on next reboot (`--mode=staged`): change is staged to be applied after a reboot, but node is not rebooted
|
||||
* apply change in the interactive mode (`--mode=interactive`; only for `talosctl apply-config`): launches TUI based interactive installer
|
||||
|
||||
> Note: applying change on next reboot (`--on-reboot`) doesn't modify current node configuration, so next call to
|
||||
> `talosctl edit machineconfig --on-reboot` will not see changes
|
||||
> Note: applying change on next reboot (`--mode=staged`) doesn't modify current node configuration, so next call to
|
||||
> `talosctl edit machineconfig --mode=staged` will not see changes
|
||||
|
||||
The list of config changes allowed to be applied immediately in talos v0.15:
|
||||
|
||||
* `.debug`
|
||||
* `.cluster`
|
||||
* `.machine.time`
|
||||
* `.machine.certCANs`
|
||||
* `.machine.network`
|
||||
* `.machine.sysctls`
|
||||
* `.machine.logging`
|
||||
* `.machine.controlplane`
|
||||
* `.machine.kubelet`
|
||||
* `.machine.kernel`
|
||||
|
||||
### `talosctl apply-config`
|
||||
|
||||
@ -44,9 +59,17 @@ talosctl -n <IP> apply machineconfig -f config.yaml
|
||||
Applying machine configuration immediately (without a reboot):
|
||||
|
||||
```bash
|
||||
talosctl -n IP apply machineconfig -f config.yaml --immediate
|
||||
talosctl -n IP apply machineconfig -f config.yaml --mode=no-reboot
|
||||
```
|
||||
|
||||
Starting the interactive installer:
|
||||
|
||||
```bash
|
||||
talosctl -n IP apply machineconfig --mode=interactive
|
||||
```
|
||||
|
||||
> Note: when a Talos node is running in the maintenance mode it's necessary to provide `--insecure (-i)` flag to connect to the API and apply the config.
|
||||
|
||||
### `taloctl edit machineconfig`
|
||||
|
||||
Command `talosctl edit` loads current machine configuration from the node and launches configured editor to modify the config.
|
||||
@ -70,14 +93,14 @@ talosctl -n <IP1>,<IP2>,... edit machineconfig
|
||||
Applying machine configuration change immediately (without a reboot):
|
||||
|
||||
```bash
|
||||
talosctl -n <IP> edit machineconfig --immediate
|
||||
talosctl -n <IP> edit machineconfig --mode=no-reboot
|
||||
```
|
||||
|
||||
### `talosctl patch machineconfig`
|
||||
|
||||
Command `talosctl patch` works similar to `talosctl edit` command - it loads current machine configuration, but instead of launching configured editor it applies [JSON patch](http://jsonpatch.com/) to the configuration and writes result back to the node.
|
||||
|
||||
Example, updating kubelet version (with a reboot):
|
||||
Example, updating kubelet version (in auto mode):
|
||||
|
||||
```bash
|
||||
$ talosctl -n <IP> patch machineconfig -p '[{"op": "replace", "path": "/machine/kubelet/image", "value": "ghcr.io/talos-systems/kubelet:v1.20.5"}]'
|
||||
@ -87,18 +110,18 @@ patched mc at the node <IP>
|
||||
Updating kube-apiserver version in immediate mode (without a reboot):
|
||||
|
||||
```bash
|
||||
$ talosctl -n <IP> patch machineconfig --immediate -p '[{"op": "replace", "path": "/cluster/apiServer/image", "value": "k8s.gcr.io/kube-apiserver:v1.20.5"}]'
|
||||
$ talosctl -n <IP> patch machineconfig --mode=no-reboot -p '[{"op": "replace", "path": "/cluster/apiServer/image", "value": "k8s.gcr.io/kube-apiserver:v1.20.5"}]'
|
||||
patched mc at the node <IP>
|
||||
```
|
||||
|
||||
Patch might be applied to multiple nodes when multiple IPs are specified:
|
||||
|
||||
```bash
|
||||
taloctl -n <IP1>,<IP2>,... patch machineconfig --immediate -p '[{...}]'
|
||||
taloctl -n <IP1>,<IP2>,... patch machineconfig -p '[{...}]'
|
||||
```
|
||||
|
||||
### Recovering from Node Boot Failures
|
||||
|
||||
If a Talos node fails to boot because of wrong configuration (for example, control plane endpoint is incorrect), configuration can be updated to fix the issue.
|
||||
If the boot sequence is still running, Talos might refuse applying config in default mode.
|
||||
In that case `--on-reboot` mode can be used coupled with `talosctl reboot` command to trigger a reboot and apply configuration update.
|
||||
In that case `--mode=staged` mode can be used coupled with `talosctl reboot` command to trigger a reboot and apply configuration update.
|
||||
|
||||
@ -86,7 +86,7 @@ cluster:
|
||||
secret: AbdsWjY9i797kGglghKvtGdxCsdllX9CemLq+WGVeaw=
|
||||
```
|
||||
|
||||
> Note: This can be applied in immediate mode (no reboot required) by passing `--immediate` to either the `edit machineconfig` or `apply-config` subcommands.
|
||||
> Note: This can be applied in immediate mode (no reboot required).
|
||||
|
||||
#### Talos v0.12
|
||||
|
||||
|
||||
@ -95,7 +95,7 @@ talosctl gen config my-cluster https://mycluster.local:6443 --config-patch '[{"o
|
||||
Patching an existing node
|
||||
|
||||
```bash
|
||||
talosctl patch --immediate machineconfig -n <node ip> --config-patch '[{"op": "add", "path": "/machine/sysctls", "value": {"vm.nr_hugepages": "1024"}}, {"op": "add", "path": "/machine/kubelet/extraArgs", "value": {"node-labels": "openebs.io/engine=mayastor"}}]'
|
||||
talosctl patch --mode=no-reboot machineconfig -n <node ip> --config-patch '[{"op": "add", "path": "/machine/sysctls", "value": {"vm.nr_hugepages": "1024"}}, {"op": "add", "path": "/machine/kubelet/extraArgs", "value": {"node-labels": "openebs.io/engine=mayastor"}}]'
|
||||
```
|
||||
|
||||
> Note: If you are adding/updating the `vm.nr_hugepages` on a node which already had the `openebs.io/engine=mayastor` label set, you'd need to restart kubelet so that it picks up the new value, by issuing the following command
|
||||
|
||||
@ -293,13 +293,13 @@ talosctl --nodes <master node> kubeconfig
|
||||
Patch machine configuration using `talosctl patch` command:
|
||||
|
||||
```bash
|
||||
$ talosctl -n <CONTROL_PLANE_IP_1> patch mc --immediate -p '[{"op": "replace", "path": "/cluster/apiServer/image", "value": "k8s.gcr.io/kube-apiserver:v1.20.4"}]'
|
||||
$ talosctl -n <CONTROL_PLANE_IP_1> patch mc --mode=no-reboot -p '[{"op": "replace", "path": "/cluster/apiServer/image", "value": "k8s.gcr.io/kube-apiserver:v1.20.4"}]'
|
||||
patched mc at the node 172.20.0.2
|
||||
```
|
||||
|
||||
JSON patch might need to be adjusted if current machine configuration is missing `.cluster.apiServer.image` key.
|
||||
|
||||
Also machine configuration can be edited manually with `talosctl -n <IP> edit mc --immediate`.
|
||||
Also machine configuration can be edited manually with `talosctl -n <IP> edit mc --mode=no-reboot`.
|
||||
|
||||
Capture new version of `kube-apiserver` config with:
|
||||
|
||||
@ -347,7 +347,7 @@ Repeat this process for every control plane node, verifying that state got propa
|
||||
Patch machine configuration using `talosctl patch` command:
|
||||
|
||||
```bash
|
||||
$ talosctl -n <CONTROL_PLANE_IP_1> patch mc --immediate -p '[{"op": "replace", "path": "/cluster/controllerManager/image", "value": "k8s.gcr.io/kube-controller-manager:v1.20.4"}]'
|
||||
$ talosctl -n <CONTROL_PLANE_IP_1> patch mc --mode=no-reboot -p '[{"op": "replace", "path": "/cluster/controllerManager/image", "value": "k8s.gcr.io/kube-controller-manager:v1.20.4"}]'
|
||||
patched mc at the node 172.20.0.2
|
||||
```
|
||||
|
||||
@ -396,7 +396,7 @@ Repeat this process for every control plane node, verifying that state got propa
|
||||
Patch machine configuration using `talosctl patch` command:
|
||||
|
||||
```bash
|
||||
$ talosctl -n <CONTROL_PLANE_IP_1> patch mc --immediate -p '[{"op": "replace", "path": "/cluster/scheduler/image", "value": "k8s.gcr.io/kube-scheduler:v1.20.4"}]'
|
||||
$ talosctl -n <CONTROL_PLANE_IP_1> patch mc --mode=no-reboot -p '[{"op": "replace", "path": "/cluster/scheduler/image", "value": "k8s.gcr.io/kube-scheduler:v1.20.4"}]'
|
||||
patched mc at the node 172.20.0.2
|
||||
```
|
||||
|
||||
@ -509,7 +509,7 @@ kubectl apply -f manifests.yaml
|
||||
For every node, patch machine configuration with new kubelet version, wait for the kubelet to restart with new version:
|
||||
|
||||
```bash
|
||||
$ talosctl -n <IP> patch mc --immediate -p '[{"op": "replace", "path": "/machine/kubelet/image", "value": "ghcr.io/talos-systems/kubelet:v1.23.0"}]'
|
||||
$ talosctl -n <IP> patch mc --mode=no-reboot -p '[{"op": "replace", "path": "/machine/kubelet/image", "value": "ghcr.io/talos-systems/kubelet:v1.23.0"}]'
|
||||
patched mc at the node 172.20.0.2
|
||||
```
|
||||
|
||||
|
||||
@ -291,7 +291,7 @@ Talos will print them out during the boot process:
|
||||
[ 4.614985] [talos] task loadConfig (1/1): upload configuration using talosctl:
|
||||
[ 4.616978] [talos] task loadConfig (1/1): talosctl apply-config --insecure --nodes 192.168.0.2 --file <config.yaml>
|
||||
[ 4.620168] [talos] task loadConfig (1/1): or apply configuration using talosctl interactive installer:
|
||||
[ 4.623046] [talos] task loadConfig (1/1): talosctl apply-config --insecure --nodes 192.168.0.2 --interactive
|
||||
[ 4.623046] [talos] task loadConfig (1/1): talosctl apply-config --insecure --nodes 192.168.0.2 --mode=interactive
|
||||
[ 4.626365] [talos] task loadConfig (1/1): optionally with node fingerprint check:
|
||||
[ 4.628692] [talos] task loadConfig (1/1): talosctl apply-config --insecure --nodes 192.168.0.2 --cert-fingerprint 'xA9a1t2dMxB0NJ0qH1pDzilWbA3+DK/DjVbFaJBYheE=' --file <config.yaml>
|
||||
```
|
||||
|
||||
@ -8,7 +8,7 @@ The new implementation is still using the same machine configuration file format
|
||||
in the way Talos works in 0.11.
|
||||
|
||||
The most notable change in Talos 0.11 is that all changes to machine configuration `.machine.network` can be applied now in immediate mode (without a reboot) via
|
||||
`talosctl edit mc --immediate` or `talosctl apply-config --immediate`.
|
||||
`talosctl edit mc --mode=no-reboot` or `talosctl apply-config --mode=no-reboot`.
|
||||
|
||||
## Resources
|
||||
|
||||
|
||||
@ -157,6 +157,7 @@ description: Talos gRPC API reference.
|
||||
- [VersionInfo](#machine.VersionInfo)
|
||||
- [VersionResponse](#machine.VersionResponse)
|
||||
|
||||
- [ApplyConfigurationRequest.Mode](#machine.ApplyConfigurationRequest.Mode)
|
||||
- [ListRequest.Type](#machine.ListRequest.Type)
|
||||
- [MachineConfig.MachineType](#machine.MachineConfig.MachineType)
|
||||
- [PhaseEvent.Action](#machine.PhaseEvent.Action)
|
||||
@ -483,6 +484,8 @@ ApplyConfigurationResponse describes the response to a configuration request.
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| metadata | [common.Metadata](#common.Metadata) | | |
|
||||
| warnings | [string](#string) | repeated | Configuration validation warnings. |
|
||||
| mode | [ApplyConfigurationRequest.Mode](#machine.ApplyConfigurationRequest.Mode) | | States which mode was actually chosen. |
|
||||
| mode_details | [string](#string) | | Human-readable message explaining the result of the apply configuration call. |
|
||||
|
||||
|
||||
|
||||
@ -500,8 +503,9 @@ node.
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| data | [bytes](#bytes) | | |
|
||||
| on_reboot | [bool](#bool) | | |
|
||||
| immediate | [bool](#bool) | | |
|
||||
| on_reboot | [bool](#bool) | | **Deprecated.** replaced by mode |
|
||||
| immediate | [bool](#bool) | | **Deprecated.** replaced by mode |
|
||||
| mode | [ApplyConfigurationRequest.Mode](#machine.ApplyConfigurationRequest.Mode) | | |
|
||||
|
||||
|
||||
|
||||
@ -2648,6 +2652,20 @@ rpc upgrade
|
||||
<!-- end messages -->
|
||||
|
||||
|
||||
<a name="machine.ApplyConfigurationRequest.Mode"></a>
|
||||
|
||||
### ApplyConfigurationRequest.Mode
|
||||
|
||||
|
||||
| Name | Number | Description |
|
||||
| ---- | ------ | ----------- |
|
||||
| REBOOT | 0 | |
|
||||
| AUTO | 1 | |
|
||||
| NO_REBOOT | 2 | |
|
||||
| STAGED | 3 | |
|
||||
|
||||
|
||||
|
||||
<a name="machine.ListRequest.Type"></a>
|
||||
|
||||
### ListRequest.Type
|
||||
|
||||
@ -16,13 +16,11 @@ talosctl apply-config [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--cert-fingerprint strings list of server certificate fingeprints to accept (defaults to no check)
|
||||
-f, --file string the filename of the updated configuration
|
||||
-h, --help help for apply-config
|
||||
--immediate apply the config immediately (without a reboot)
|
||||
-i, --insecure apply the config using the insecure (encrypted with no auth) maintenance service
|
||||
--interactive apply the config using text based interactive mode
|
||||
--on-reboot apply the config on reboot
|
||||
--cert-fingerprint strings list of server certificate fingeprints to accept (defaults to no check)
|
||||
-f, --file string the filename of the updated configuration
|
||||
-h, --help help for apply-config
|
||||
-i, --insecure apply the config using the insecure (encrypted with no auth) maintenance service
|
||||
-m, --mode auto, interactive, no-reboot, reboot, staged apply config mode (default auto)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@ -816,10 +814,9 @@ talosctl edit <type> [<id>] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for edit
|
||||
--immediate apply the change immediately (without a reboot)
|
||||
--namespace string resource namespace (default is to use default namespace per resource)
|
||||
--on-reboot apply the change on next reboot
|
||||
-h, --help help for edit
|
||||
-m, --mode auto, no-reboot, reboot, staged apply config mode (default auto)
|
||||
--namespace string resource namespace (default is to use default namespace per resource)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@ -1588,12 +1585,11 @@ talosctl patch <type> [<id>] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for patch
|
||||
--immediate apply the change immediately (without a reboot)
|
||||
--namespace string resource namespace (default is to use default namespace per resource)
|
||||
--on-reboot apply the change on next reboot
|
||||
-p, --patch string the patch to be applied to the resource file.
|
||||
--patch-file string a file containing a patch to be applied to the resource.
|
||||
-h, --help help for patch
|
||||
-m, --mode auto, no-reboot, reboot, staged apply config mode (default auto)
|
||||
--namespace string resource namespace (default is to use default namespace per resource)
|
||||
-p, --patch string the patch to be applied to the resource file.
|
||||
--patch-file string a file containing a patch to be applied to the resource.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@ -43,7 +43,7 @@ Insert the SD card to your board, turn it on and wait for the console to show yo
|
||||
Following the instructions in the console output to connect to the interactive installer:
|
||||
|
||||
```bash
|
||||
talosctl apply-config --insecure --interactive --nodes <node IP or DNS name>
|
||||
talosctl apply-config --insecure --mode=interactive --nodes <node IP or DNS name>
|
||||
```
|
||||
|
||||
Once the interactive installation is applied, the cluster will form and you can then use `kubectl`.
|
||||
|
||||
@ -43,7 +43,7 @@ Insert the SD card to your board, turn it on and wait for the console to show yo
|
||||
Following the instructions in the console output to connect to the interactive installer:
|
||||
|
||||
```bash
|
||||
talosctl apply-config --insecure --interactive --nodes <node IP or DNS name>
|
||||
talosctl apply-config --insecure --mode=interactive --nodes <node IP or DNS name>
|
||||
```
|
||||
|
||||
Once the interactive installation is applied, the cluster will form and you can then use `kubectl`.
|
||||
|
||||
@ -43,7 +43,7 @@ Insert the SD card to your board, turn it on and wait for the console to show yo
|
||||
Following the instructions in the console output to connect to the interactive installer:
|
||||
|
||||
```bash
|
||||
talosctl apply-config --insecure --interactive --nodes <node IP or DNS name>
|
||||
talosctl apply-config --insecure --mode=interactive --nodes <node IP or DNS name>
|
||||
```
|
||||
|
||||
Once the interactive installation is applied, the cluster will form and you can then use `kubectl`.
|
||||
|
||||
@ -43,7 +43,7 @@ Insert the SD card to your board, turn it on and wait for the console to show yo
|
||||
Following the instructions in the console output to connect to the interactive installer:
|
||||
|
||||
```bash
|
||||
talosctl apply-config --insecure --interactive --nodes <node IP or DNS name>
|
||||
talosctl apply-config --insecure --mode=interactive --nodes <node IP or DNS name>
|
||||
```
|
||||
|
||||
Once the interactive installation is applied, the cluster will form and you can then use `kubectl`.
|
||||
|
||||
@ -43,7 +43,7 @@ Insert the SD card to your board, turn it on and wait for the console to show yo
|
||||
Following the instructions in the console output to connect to the interactive installer:
|
||||
|
||||
```bash
|
||||
talosctl apply-config --insecure --interactive --nodes <node IP or DNS name>
|
||||
talosctl apply-config --insecure --mode=interactive --nodes <node IP or DNS name>
|
||||
```
|
||||
|
||||
Once the interactive installation is applied, the cluster will form and you can then use `kubectl`.
|
||||
|
||||
@ -71,7 +71,7 @@ Insert the SD card to your board, turn it on and wait for the console to show yo
|
||||
Following the instructions in the console output to connect to the interactive installer:
|
||||
|
||||
```bash
|
||||
talosctl apply-config --insecure --interactive --nodes <node IP or DNS name>
|
||||
talosctl apply-config --insecure --mode=interactive --nodes <node IP or DNS name>
|
||||
```
|
||||
|
||||
Once the interactive installation is applied, the cluster will form and you can then use `kubectl`.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user