Skip to content

Commit

Permalink
update upgrade logic
Browse files Browse the repository at this point in the history
  • Loading branch information
kvaps committed May 5, 2024
1 parent c567135 commit 0968634
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 116 deletions.
70 changes: 26 additions & 44 deletions pkg/commands/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/durationpb"

"github.com/aenix-io/talm/pkg/modeline"
"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers"
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
"github.com/siderolabs/talos/pkg/machinery/client"
Expand Down Expand Up @@ -54,25 +53,11 @@ var applyCmd = &cobra.Command{

func apply(args []string) func(ctx context.Context, c *client.Client) error {
return func(ctx context.Context, c *client.Client) error {
nodesFromModeline := false
endpointsFromModeline := false
nodesFromArgs := len(GlobalArgs.Nodes) > 0
endpointsFromArgs := len(GlobalArgs.Endpoints) > 0
for _, configFile := range applyCmdFlags.configFiles {
// Use the new function to handle modeline
modelineConfig, err := modeline.ReadAndParseModeline(configFile)
if err != nil {
fmt.Printf("Warning: modeline parsing failed: %v\n", err)
}

// Update global settings if modeline was successfully parsed
if modelineConfig != nil {
if nodesFromModeline || (len(GlobalArgs.Nodes) == 0 && len(modelineConfig.Nodes) > 0) {
GlobalArgs.Nodes = modelineConfig.Nodes
nodesFromModeline = true
}
if endpointsFromModeline || (len(GlobalArgs.Endpoints) == 0 && len(modelineConfig.Endpoints) > 0) {
GlobalArgs.Endpoints = modelineConfig.Endpoints
endpointsFromModeline = true
}
if err := processModelineAndUpdateGlobals(configFile, nodesFromArgs, endpointsFromArgs); err != nil {
return err
}

opts := engine.Options{
Expand All @@ -93,7 +78,29 @@ func apply(args []string) func(ctx context.Context, c *client.Client) error {
return fmt.Errorf("error serializing configuration: %s", err)
}

withClient := func(f func(ctx context.Context, c *client.Client) error) error {
if applyCmdFlags.insecure {
return WithClientMaintenance(applyCmdFlags.certFingerprints, f)
}

return WithClientNoNodes(func(ctx context.Context, cli *client.Client) error {
if len(GlobalArgs.Nodes) < 1 {
configContext := cli.GetConfigContext()
if configContext == nil {
return errors.New("failed to resolve config context")
}

GlobalArgs.Nodes = configContext.Nodes
}

ctx = client.WithNodes(ctx, GlobalArgs.Nodes...)

return f(ctx, cli)
})
}

err = withClient(func(ctx context.Context, c *client.Client) error {
fmt.Printf("Nodes: %s\n", GlobalArgs.Nodes)
resp, err := c.ApplyConfiguration(ctx, &machineapi.ApplyConfigurationRequest{
Data: result,
Mode: applyCmdFlags.Mode.Mode,
Expand All @@ -116,31 +123,6 @@ func apply(args []string) func(ctx context.Context, c *client.Client) error {
}
}

func withClient(f func(ctx context.Context, c *client.Client) error) error {
if applyCmdFlags.insecure {
return WithClientMaintenance(applyCmdFlags.certFingerprints, f)
}

return WithClientNoNodes(func(ctx context.Context, cli *client.Client) error {
if len(GlobalArgs.Nodes) < 1 {
configContext := cli.GetConfigContext()
if configContext == nil {
return errors.New("failed to resolve config context")
}

GlobalArgs.Nodes = configContext.Nodes
}

if len(GlobalArgs.Nodes) < 1 {
return errors.New("nodes are not set for the command: please use `--nodes` flag or configuration file to set the nodes to run the command against")
}

ctx = client.WithNodes(ctx, GlobalArgs.Nodes...)

return f(ctx, cli)
})
}

// readFirstLine reads and returns the first line of the file specified by the filename.
// It returns an error if opening or reading the file fails.
func readFirstLine(filename string) (string, error) {
Expand Down
47 changes: 46 additions & 1 deletion pkg/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ package commands

import (
"context"
"errors"
"fmt"
"time"

"github.com/aenix-io/talm/pkg/modeline"
"github.com/spf13/cobra"
"google.golang.org/grpc"

Expand Down Expand Up @@ -66,7 +69,24 @@ func WithClientNoNodes(action func(context.Context, *client.Client) error, dialO

// WithClient builds upon WithClientNoNodes to provide set of nodes on request context based on config & flags.
func WithClient(action func(context.Context, *client.Client) error, dialOptions ...grpc.DialOption) error {
return GlobalArgs.WithClient(action, dialOptions...)
return WithClientNoNodes(
func(ctx context.Context, cli *client.Client) error {
if len(GlobalArgs.Nodes) < 1 {
configContext := cli.GetConfigContext()
if configContext == nil {
return errors.New("failed to resolve config context")
}

GlobalArgs.Nodes = configContext.Nodes
}

ctx = client.WithNodes(ctx, GlobalArgs.Nodes...)

return action(ctx, cli)
},
dialOptions...,
)

}

// WithClientMaintenance wraps common code to initialize Talos client in maintenance (insecure mode).
Expand All @@ -80,3 +100,28 @@ var Commands []*cobra.Command
func addCommand(cmd *cobra.Command) {
Commands = append(Commands, cmd)
}

func processModelineAndUpdateGlobals(configFile string, nodesFromArgs bool, endpointsFromArgs bool) error {
// Use the new function to handle modeline
modelineConfig, err := modeline.ReadAndParseModeline(configFile)
if err != nil {
fmt.Printf("Warning: modeline parsing failed: %v\n", err)
return err
}

// Update global settings if modeline was successfully parsed
if modelineConfig != nil {
if !nodesFromArgs && len(modelineConfig.Nodes) > 0 {
GlobalArgs.Nodes = modelineConfig.Nodes
}
if !endpointsFromArgs && len(modelineConfig.Endpoints) > 0 {
GlobalArgs.Endpoints = modelineConfig.Endpoints
}
}

if len(GlobalArgs.Nodes) < 1 {
return errors.New("nodes are not set for the command: please use `--nodes` flag or configuration file to set the nodes to run the command against")
}

return nil
}
135 changes: 64 additions & 71 deletions pkg/commands/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,7 @@ var upgradeCmdFlags struct {
stage bool
force bool
insecure bool
valueFiles []string // -f/--values
stringValues []string // --set-string
values []string // --set
fileValues []string // --set-file
jsonValues []string // --set-json
literalValues []string // --set-literal
configFiles []string // -f/--files
talosVersion string
withSecrets string
kubernetesVersion string
Expand All @@ -52,7 +47,7 @@ var upgradeCmd = &cobra.Command{
Use: "upgrade",
Short: "Upgrade Talos on the target node",
Long: ``,
Args: cobra.MinimumNArgs(1),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if upgradeCmdFlags.debug {
upgradeCmdFlags.wait = true
Expand All @@ -61,7 +56,6 @@ var upgradeCmd = &cobra.Command{
if upgradeCmdFlags.wait && upgradeCmdFlags.insecure {
return fmt.Errorf("cannot use --wait and --insecure together")
}

if upgradeCmdFlags.insecure {
return WithClientMaintenance(nil, upgrade(args))
}
Expand All @@ -72,69 +66,79 @@ var upgradeCmd = &cobra.Command{

func upgrade(args []string) func(ctx context.Context, c *client.Client) error {
return func(ctx context.Context, c *client.Client) error {
nodesFromArgs := len(GlobalArgs.Nodes) > 0
endpointsFromArgs := len(GlobalArgs.Endpoints) > 0
rebootModeStr := strings.ToUpper(upgradeCmdFlags.rebootMode)

rebootMode, rebootModeOk := machine.UpgradeRequest_RebootMode_value[rebootModeStr]
if !rebootModeOk {
return fmt.Errorf("invalid reboot mode: %s", upgradeCmdFlags.rebootMode)
}

// Gather image from config
templateOpts := engine.Options{
Insecure: upgradeCmdFlags.insecure,
ValueFiles: upgradeCmdFlags.valueFiles,
StringValues: upgradeCmdFlags.stringValues,
Values: upgradeCmdFlags.values,
FileValues: upgradeCmdFlags.fileValues,
JsonValues: upgradeCmdFlags.jsonValues,
LiteralValues: upgradeCmdFlags.literalValues,
TalosVersion: upgradeCmdFlags.talosVersion,
WithSecrets: upgradeCmdFlags.withSecrets,
Full: true,
Root: Config.RootDir,
Offline: false,
KubernetesVersion: upgradeCmdFlags.kubernetesVersion,
TemplateFiles: args,
}
result, err := engine.Render(ctx, c, templateOpts)
if err != nil {
return fmt.Errorf("failed to render templates: %w", err)
}
config, err := configloader.NewFromBytes(result)
if err != nil {
return err
}
for _, configFile := range upgradeCmdFlags.configFiles {
if err := processModelineAndUpdateGlobals(configFile, nodesFromArgs, endpointsFromArgs); err != nil {
return err
}

image := config.Machine().Install().Image()
if image == "" {
return fmt.Errorf("error getting image from config")
}
eopts := engine.Options{
TalosVersion: upgradeCmdFlags.talosVersion,
WithSecrets: upgradeCmdFlags.withSecrets,
KubernetesVersion: upgradeCmdFlags.kubernetesVersion,
}

opts := []client.UpgradeOption{
client.WithUpgradeImage(image),
client.WithUpgradeRebootMode(machine.UpgradeRequest_RebootMode(rebootMode)),
client.WithUpgradePreserve(upgradeCmdFlags.preserve),
client.WithUpgradeStage(upgradeCmdFlags.stage),
client.WithUpgradeForce(upgradeCmdFlags.force),
}
patches := []string{"@" + configFile}
configBundle, err := engine.FullConfigProcess(ctx, eopts, patches)
if err != nil {
return fmt.Errorf("full config processing error: %s", err)
}

if !upgradeCmdFlags.wait {
return runUpgradeNoWait(opts)
}
machineType := configBundle.ControlPlaneCfg.Machine().Type()
result, err := engine.SerializeConfiguration(configBundle, machineType)
if err != nil {
return fmt.Errorf("error serializing configuration: %s", err)
}

config, err := configloader.NewFromBytes(result)
if err != nil {
return err
}

image := config.Machine().Install().Image()
if image == "" {
return fmt.Errorf("error getting image from config")
}

opts := []client.UpgradeOption{
client.WithUpgradeImage(image),
client.WithUpgradeRebootMode(machine.UpgradeRequest_RebootMode(rebootMode)),
client.WithUpgradePreserve(upgradeCmdFlags.preserve),
client.WithUpgradeStage(upgradeCmdFlags.stage),
client.WithUpgradeForce(upgradeCmdFlags.force),
}

if !upgradeCmdFlags.wait {
return runUpgradeNoWait(opts)
}

common.SuppressErrors = true

return action.NewTracker(
&GlobalArgs,
action.MachineReadyEventFn,
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),
).Run()
common.SuppressErrors = true

err = action.NewTracker(
&GlobalArgs,
action.MachineReadyEventFn,
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),
).Run()
if err != nil {
return err
}
}
return nil
}
return nil
}

func runUpgradeNoWait(opts []client.UpgradeOption) error {
Expand Down Expand Up @@ -213,23 +217,12 @@ func init() {
upgradeCmdFlags.addTrackActionFlags(upgradeCmd)

upgradeCmd.Flags().BoolVarP(&upgradeCmdFlags.insecure, "insecure", "i", false, "apply using the insecure (encrypted with no auth) maintenance service")
upgradeCmd.Flags().StringSliceVarP(&upgradeCmdFlags.valueFiles, "values", "f", []string{}, "specify values in a YAML file (can specify multiple)")
upgradeCmd.Flags().StringArrayVar(&upgradeCmdFlags.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
upgradeCmd.Flags().StringArrayVar(&upgradeCmdFlags.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
upgradeCmd.Flags().StringArrayVar(&upgradeCmdFlags.fileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
upgradeCmd.Flags().StringArrayVar(&upgradeCmdFlags.jsonValues, "set-json", []string{}, "set JSON values on the command line (can specify multiple or separate values with commas: key1=jsonval1,key2=jsonval2)")
upgradeCmd.Flags().StringArrayVar(&upgradeCmdFlags.literalValues, "set-literal", []string{}, "set a literal STRING value on the command line")
upgradeCmd.Flags().StringSliceVarP(&upgradeCmdFlags.configFiles, "file", "f", nil, "specify config files or patches in a YAML file (can specify multiple)")
upgradeCmd.Flags().StringVar(&upgradeCmdFlags.talosVersion, "talos-version", "", "the desired Talos version to generate config for (backwards compatibility, e.g. v0.8)")
upgradeCmd.Flags().StringVar(&upgradeCmdFlags.withSecrets, "with-secrets", "", "use a secrets file generated using 'gen secrets'")
upgradeCmd.Flags().StringVar(&upgradeCmdFlags.kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run")

upgradeCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
upgradeCmdFlags.valueFiles = append(Config.TemplateOptions.ValueFiles, upgradeCmdFlags.valueFiles...)
upgradeCmdFlags.values = append(Config.TemplateOptions.Values, upgradeCmdFlags.values...)
upgradeCmdFlags.stringValues = append(Config.TemplateOptions.StringValues, upgradeCmdFlags.stringValues...)
upgradeCmdFlags.fileValues = append(Config.TemplateOptions.FileValues, upgradeCmdFlags.fileValues...)
upgradeCmdFlags.jsonValues = append(Config.TemplateOptions.JsonValues, upgradeCmdFlags.jsonValues...)
upgradeCmdFlags.literalValues = append(Config.TemplateOptions.LiteralValues, upgradeCmdFlags.literalValues...)
if !cmd.Flags().Changed("talos-version") {
upgradeCmdFlags.talosVersion = Config.TemplateOptions.TalosVersion
}
Expand Down

0 comments on commit 0968634

Please sign in to comment.