mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-04 20:06:18 +02:00
feat: add reboot-mode flag to talosctl upgrade
Allow specifying the reboot mode during upgrades by introducing `--reboot-mode` flag, similar to the `--mode` flag of the reboot command. Closes siderolabs/talos#7302. Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
This commit is contained in:
parent
7ce87f20c3
commit
0d313b9733
@ -339,10 +339,15 @@ message ShutdownResponse {
|
||||
|
||||
// rpc upgrade
|
||||
message UpgradeRequest {
|
||||
enum RebootMode {
|
||||
DEFAULT = 0;
|
||||
POWERCYCLE = 1;
|
||||
}
|
||||
string image = 1;
|
||||
bool preserve = 2;
|
||||
bool stage = 3;
|
||||
bool force = 4;
|
||||
RebootMode reboot_mode = 5;
|
||||
}
|
||||
|
||||
message Upgrade {
|
||||
|
||||
@ -8,9 +8,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/siderolabs/gen/maps"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/peer"
|
||||
@ -20,6 +23,7 @@ import (
|
||||
"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers"
|
||||
"github.com/siderolabs/talos/pkg/cli"
|
||||
"github.com/siderolabs/talos/pkg/images"
|
||||
"github.com/siderolabs/talos/pkg/machinery/api/machine"
|
||||
"github.com/siderolabs/talos/pkg/machinery/client"
|
||||
"github.com/siderolabs/talos/pkg/version"
|
||||
)
|
||||
@ -27,6 +31,7 @@ import (
|
||||
var upgradeCmdFlags struct {
|
||||
trackableActionCmdFlags
|
||||
upgradeImage string
|
||||
rebootMode string
|
||||
preserve bool
|
||||
stage bool
|
||||
force bool
|
||||
@ -48,8 +53,23 @@ var upgradeCmd = &cobra.Command{
|
||||
return fmt.Errorf("cannot use --wait and --insecure together")
|
||||
}
|
||||
|
||||
rebootModeStr := strings.ToUpper(upgradeCmdFlags.rebootMode)
|
||||
|
||||
rebootMode, rebootModeOk := machine.UpgradeRequest_RebootMode_value[rebootModeStr]
|
||||
if !rebootModeOk {
|
||||
return fmt.Errorf("invalid reboot mode: %s", upgradeCmdFlags.rebootMode)
|
||||
}
|
||||
|
||||
opts := []client.UpgradeOption{
|
||||
client.WithUpgradeImage(upgradeCmdFlags.upgradeImage),
|
||||
client.WithUpgradeRebootMode(machine.UpgradeRequest_RebootMode(rebootMode)),
|
||||
client.WithUpgradePreserve(upgradeCmdFlags.preserve),
|
||||
client.WithUpgradeStage(upgradeCmdFlags.stage),
|
||||
client.WithUpgradeForce(upgradeCmdFlags.force),
|
||||
}
|
||||
|
||||
if !upgradeCmdFlags.wait {
|
||||
return runUpgradeNoWait()
|
||||
return runUpgradeNoWait(opts)
|
||||
}
|
||||
|
||||
common.SuppressErrors = true
|
||||
@ -57,7 +77,9 @@ var upgradeCmd = &cobra.Command{
|
||||
return action.NewTracker(
|
||||
&GlobalArgs,
|
||||
action.MachineReadyEventFn,
|
||||
upgradeGetActorID,
|
||||
func(ctx context.Context, c *client.Client) (string, error) {
|
||||
return upgradeGetActorID(ctx, c, opts)
|
||||
},
|
||||
action.WithPostCheck(action.BootIDChangedPostCheckFn),
|
||||
action.WithDebug(upgradeCmdFlags.debug),
|
||||
action.WithTimeout(upgradeCmdFlags.timeout),
|
||||
@ -65,7 +87,7 @@ var upgradeCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func runUpgradeNoWait() error {
|
||||
func runUpgradeNoWait(opts []client.UpgradeOption) error {
|
||||
upgradeFn := func(ctx context.Context, c *client.Client) error {
|
||||
if err := helpers.ClientVersionCheck(ctx, c); err != nil {
|
||||
return err
|
||||
@ -73,15 +95,10 @@ func runUpgradeNoWait() error {
|
||||
|
||||
var remotePeer peer.Peer
|
||||
|
||||
opts = append(opts, client.WithUpgradeGRPCCallOptions(grpc.Peer(&remotePeer)))
|
||||
|
||||
// TODO: See if we can validate version and prevent starting upgrades to an unknown version
|
||||
resp, err := c.Upgrade(
|
||||
ctx,
|
||||
upgradeCmdFlags.upgradeImage,
|
||||
upgradeCmdFlags.preserve,
|
||||
upgradeCmdFlags.stage,
|
||||
upgradeCmdFlags.force,
|
||||
grpc.Peer(&remotePeer),
|
||||
)
|
||||
resp, err := c.UpgradeWithOptions(ctx, opts...)
|
||||
if err != nil {
|
||||
if resp == nil {
|
||||
return fmt.Errorf("error performing upgrade: %s", err)
|
||||
@ -115,14 +132,8 @@ func runUpgradeNoWait() error {
|
||||
return WithClient(upgradeFn)
|
||||
}
|
||||
|
||||
func upgradeGetActorID(ctx context.Context, c *client.Client) (string, error) {
|
||||
resp, err := c.Upgrade(
|
||||
ctx,
|
||||
upgradeCmdFlags.upgradeImage,
|
||||
upgradeCmdFlags.preserve,
|
||||
upgradeCmdFlags.stage,
|
||||
upgradeCmdFlags.force,
|
||||
)
|
||||
func upgradeGetActorID(ctx context.Context, c *client.Client, opts []client.UpgradeOption) (string, error) {
|
||||
resp, err := c.UpgradeWithOptions(ctx, opts...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -135,9 +146,18 @@ func upgradeGetActorID(ctx context.Context, c *client.Client) (string, error) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
rebootModes := maps.KeysFunc(machine.UpgradeRequest_RebootMode_value, strings.ToLower)
|
||||
sort.Slice(rebootModes, func(i, j int) bool {
|
||||
return machine.UpgradeRequest_RebootMode_value[rebootModes[i]] > machine.UpgradeRequest_RebootMode_value[rebootModes[j]]
|
||||
})
|
||||
|
||||
upgradeCmd.Flags().StringVarP(&upgradeCmdFlags.upgradeImage, "image", "i",
|
||||
fmt.Sprintf("%s/%s/installer:%s", images.Registry, images.Username, version.Trim(version.Tag)),
|
||||
"the container image to use for performing the install")
|
||||
upgradeCmd.Flags().StringVarP(&upgradeCmdFlags.rebootMode, "reboot-mode", "m", strings.ToLower(machine.UpgradeRequest_DEFAULT.String()),
|
||||
fmt.Sprintf("select the reboot mode during upgrade. Mode %q bypasses kexec. Valid values are: %q.",
|
||||
strings.ToLower(machine.UpgradeRequest_POWERCYCLE.String()),
|
||||
rebootModes))
|
||||
upgradeCmd.Flags().BoolVarP(&upgradeCmdFlags.preserve, "preserve", "p", false, "preserve data")
|
||||
upgradeCmd.Flags().BoolVarP(&upgradeCmdFlags.stage, "stage", "s", false, "stage the upgrade to perform it after a reboot")
|
||||
upgradeCmd.Flags().BoolVarP(&upgradeCmdFlags.force, "force", "f", false, "force the upgrade (skip checks on etcd health and members, might lead to data loss)")
|
||||
|
||||
@ -451,7 +451,7 @@ func (s *Server) Upgrade(ctx context.Context, in *machine.UpgradeRequest) (*mach
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("upgrade request received: preserve %v, staged %v, force %v", in.GetPreserve(), in.GetStage(), in.GetForce())
|
||||
log.Printf("upgrade request received: preserve %v, staged %v, force %v, reboot mode %v", in.GetPreserve(), in.GetStage(), in.GetForce(), in.GetRebootMode().String())
|
||||
|
||||
log.Printf("validating %q", in.GetImage())
|
||||
|
||||
|
||||
@ -419,7 +419,7 @@ func (*Sequencer) StageUpgrade(r runtime.Runtime, in *machineapi.UpgradeRequest)
|
||||
"leave",
|
||||
LeaveEtcd,
|
||||
).AppendList(
|
||||
stopAllPhaselist(r, true),
|
||||
stopAllPhaselist(r, in.GetRebootMode() == machineapi.UpgradeRequest_DEFAULT),
|
||||
).Append(
|
||||
"reboot",
|
||||
Reboot,
|
||||
@ -430,7 +430,7 @@ func (*Sequencer) StageUpgrade(r runtime.Runtime, in *machineapi.UpgradeRequest)
|
||||
}
|
||||
|
||||
// MaintenanceUpgrade is the upgrade sequence in maintenance mode.
|
||||
func (*Sequencer) MaintenanceUpgrade(r runtime.Runtime, _ *machineapi.UpgradeRequest) []runtime.Phase {
|
||||
func (*Sequencer) MaintenanceUpgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) []runtime.Phase {
|
||||
phases := PhaseList{}
|
||||
|
||||
switch r.State().Platform().Mode() { //nolint:exhaustive
|
||||
@ -449,7 +449,8 @@ func (*Sequencer) MaintenanceUpgrade(r runtime.Runtime, _ *machineapi.UpgradeReq
|
||||
).Append(
|
||||
"meta",
|
||||
ReloadMeta,
|
||||
).Append(
|
||||
).AppendWhen(
|
||||
in.GetRebootMode() == machineapi.UpgradeRequest_DEFAULT,
|
||||
"kexec",
|
||||
KexecPrepare,
|
||||
).Append(
|
||||
@ -517,7 +518,8 @@ func (*Sequencer) Upgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) []ru
|
||||
).Append(
|
||||
"meta",
|
||||
ReloadMeta,
|
||||
).Append(
|
||||
).AppendWhen(
|
||||
in.GetRebootMode() == machineapi.UpgradeRequest_DEFAULT,
|
||||
"kexec",
|
||||
KexecPrepare,
|
||||
).Append(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1623,6 +1623,11 @@ func (m *UpgradeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.RebootMode != 0 {
|
||||
i = encodeVarint(dAtA, i, uint64(m.RebootMode))
|
||||
i--
|
||||
dAtA[i] = 0x28
|
||||
}
|
||||
if m.Force {
|
||||
i--
|
||||
if m.Force {
|
||||
@ -10461,6 +10466,9 @@ func (m *UpgradeRequest) SizeVT() (n int) {
|
||||
if m.Force {
|
||||
n += 2
|
||||
}
|
||||
if m.RebootMode != 0 {
|
||||
n += 1 + sov(uint64(m.RebootMode))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
@ -17117,6 +17125,25 @@ func (m *UpgradeRequest) UnmarshalVT(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.Force = bool(v != 0)
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field RebootMode", wireType)
|
||||
}
|
||||
m.RebootMode = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.RebootMode |= UpgradeRequest_RebootMode(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skip(dAtA[iNdEx:])
|
||||
|
||||
@ -543,24 +543,85 @@ func (c *Client) Copy(ctx context.Context, rootPath string) (io.ReadCloser, <-ch
|
||||
return ReadStream(stream)
|
||||
}
|
||||
|
||||
// Upgrade initiates a Talos upgrade and implements the proto.MachineServiceClient interface.
|
||||
func (c *Client) Upgrade(ctx context.Context, image string, preserve, stage, force bool, callOptions ...grpc.CallOption) (resp *machineapi.UpgradeResponse, err error) {
|
||||
resp, err = c.MachineClient.Upgrade(
|
||||
ctx,
|
||||
&machineapi.UpgradeRequest{
|
||||
Image: image,
|
||||
Preserve: preserve,
|
||||
Stage: stage,
|
||||
Force: force,
|
||||
},
|
||||
callOptions...,
|
||||
)
|
||||
// UpgradeOptions provides upgrade API options.
|
||||
type UpgradeOptions struct {
|
||||
Request machineapi.UpgradeRequest
|
||||
GRPCCallOptions []grpc.CallOption
|
||||
}
|
||||
|
||||
var filtered interface{}
|
||||
// UpgradeOption provides upgrade API options.
|
||||
type UpgradeOption func(*UpgradeOptions)
|
||||
|
||||
// WithUpgradeImage sets the image for the upgrade.
|
||||
func WithUpgradeImage(image string) UpgradeOption {
|
||||
return func(req *UpgradeOptions) {
|
||||
req.Request.Image = image
|
||||
}
|
||||
}
|
||||
|
||||
// WithUpgradeRebootMode sets the reboot mode for the upgrade.
|
||||
func WithUpgradeRebootMode(mode machineapi.UpgradeRequest_RebootMode) UpgradeOption {
|
||||
return func(req *UpgradeOptions) {
|
||||
req.Request.RebootMode = mode
|
||||
}
|
||||
}
|
||||
|
||||
// WithUpgradePreserve sets the preserve flag for the upgrade.
|
||||
func WithUpgradePreserve(preserve bool) UpgradeOption {
|
||||
return func(req *UpgradeOptions) {
|
||||
req.Request.Preserve = preserve
|
||||
}
|
||||
}
|
||||
|
||||
// WithUpgradeStage sets the stage flag for the upgrade.
|
||||
func WithUpgradeStage(stage bool) UpgradeOption {
|
||||
return func(req *UpgradeOptions) {
|
||||
req.Request.Stage = stage
|
||||
}
|
||||
}
|
||||
|
||||
// WithUpgradeForce sets the force flag for the upgrade.
|
||||
func WithUpgradeForce(force bool) UpgradeOption {
|
||||
return func(req *UpgradeOptions) {
|
||||
req.Request.Force = force
|
||||
}
|
||||
}
|
||||
|
||||
// WithUpgradeGRPCCallOptions sets the gRPC call options for the upgrade.
|
||||
func WithUpgradeGRPCCallOptions(opts ...grpc.CallOption) UpgradeOption {
|
||||
return func(req *UpgradeOptions) {
|
||||
req.GRPCCallOptions = opts
|
||||
}
|
||||
}
|
||||
|
||||
// Upgrade initiates a Talos upgrade and implements the proto.MachineServiceClient interface.
|
||||
func (c *Client) Upgrade(ctx context.Context, image string, preserve, stage, force bool, callOptions ...grpc.CallOption) (*machineapi.UpgradeResponse, error) {
|
||||
return c.UpgradeWithOptions(
|
||||
ctx,
|
||||
WithUpgradeImage(image),
|
||||
WithUpgradeRebootMode(machineapi.UpgradeRequest_DEFAULT),
|
||||
WithUpgradePreserve(preserve),
|
||||
WithUpgradeStage(stage),
|
||||
WithUpgradeForce(force),
|
||||
WithUpgradeGRPCCallOptions(callOptions...),
|
||||
)
|
||||
}
|
||||
|
||||
// UpgradeWithOptions initiates a Talos upgrade with the given options.
|
||||
func (c *Client) UpgradeWithOptions(ctx context.Context, opts ...UpgradeOption) (*machineapi.UpgradeResponse, error) {
|
||||
var options UpgradeOptions
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
resp, err := c.MachineClient.Upgrade(ctx, &options.Request, options.GRPCCallOptions...)
|
||||
|
||||
var filtered any
|
||||
filtered, err = FilterMessages(resp, err)
|
||||
resp, _ = filtered.(*machineapi.UpgradeResponse) //nolint:errcheck
|
||||
|
||||
return
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// ServiceList returns list of services with their state.
|
||||
|
||||
@ -404,6 +404,7 @@ description: Talos gRPC API reference.
|
||||
- [SequenceEvent.Action](#machine.SequenceEvent.Action)
|
||||
- [ServiceStateEvent.Action](#machine.ServiceStateEvent.Action)
|
||||
- [TaskEvent.Action](#machine.TaskEvent.Action)
|
||||
- [UpgradeRequest.RebootMode](#machine.UpgradeRequest.RebootMode)
|
||||
|
||||
- [MachineService](#machine.MachineService)
|
||||
|
||||
@ -6644,6 +6645,7 @@ rpc upgrade
|
||||
| preserve | [bool](#bool) | | |
|
||||
| stage | [bool](#bool) | | |
|
||||
| force | [bool](#bool) | | |
|
||||
| reboot_mode | [UpgradeRequest.RebootMode](#machine.UpgradeRequest.RebootMode) | | |
|
||||
|
||||
|
||||
|
||||
@ -6923,6 +6925,18 @@ File type.
|
||||
| STOP | 1 | |
|
||||
|
||||
|
||||
|
||||
<a name="machine.UpgradeRequest.RebootMode"></a>
|
||||
|
||||
### UpgradeRequest.RebootMode
|
||||
|
||||
|
||||
| Name | Number | Description |
|
||||
| ---- | ------ | ----------- |
|
||||
| DEFAULT | 0 | |
|
||||
| POWERCYCLE | 1 | |
|
||||
|
||||
|
||||
<!-- end enums -->
|
||||
|
||||
<!-- end HasExtensions -->
|
||||
|
||||
@ -2629,15 +2629,16 @@ talosctl upgrade [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--debug debug operation from kernel logs. --wait is set to true when this flag is set
|
||||
-f, --force force the upgrade (skip checks on etcd health and members, might lead to data loss)
|
||||
-h, --help help for upgrade
|
||||
-i, --image string the container image to use for performing the install (default "ghcr.io/siderolabs/installer:v1.5.0-alpha.1")
|
||||
--insecure upgrade using the insecure (encrypted with no auth) maintenance service
|
||||
-p, --preserve preserve data
|
||||
-s, --stage stage the upgrade to perform it after a reboot
|
||||
--timeout duration time to wait for the operation is complete if --debug or --wait is set (default 30m0s)
|
||||
--wait wait for the operation to complete, tracking its progress. always set to true when --debug is set (default true)
|
||||
--debug debug operation from kernel logs. --wait is set to true when this flag is set
|
||||
-f, --force force the upgrade (skip checks on etcd health and members, might lead to data loss)
|
||||
-h, --help help for upgrade
|
||||
-i, --image string the container image to use for performing the install (default "ghcr.io/siderolabs/installer:v1.5.0-alpha.1")
|
||||
--insecure upgrade using the insecure (encrypted with no auth) maintenance service
|
||||
-p, --preserve preserve data
|
||||
-m, --reboot-mode string select the reboot mode during upgrade. Mode "powercycle" bypasses kexec. Valid values are: ["default" "powercycle"]. (default "default")
|
||||
-s, --stage stage the upgrade to perform it after a reboot
|
||||
--timeout duration time to wait for the operation is complete if --debug or --wait is set (default 30m0s)
|
||||
--wait wait for the operation to complete, tracking its progress. always set to true when --debug is set (default true)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user