1
// This Source Code Form is subject to the terms of the Mozilla Public
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
15
"github.com/aenix-io/talm/pkg/engine"
16
"github.com/spf13/cobra"
17
"google.golang.org/protobuf/types/known/durationpb"
19
"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers"
20
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
21
"github.com/siderolabs/talos/pkg/machinery/client"
22
"github.com/siderolabs/talos/pkg/machinery/constants"
25
var applyCmdFlags struct {
27
certFingerprints []string
29
configFiles []string // -f/--files
32
kubernetesVersion string
37
configTryTimeout time.Duration
39
endpointsFromArgs bool
42
var applyCmd = &cobra.Command{
44
Short: "Apply config to a Talos node",
47
PreRunE: func(cmd *cobra.Command, args []string) error {
48
if !cmd.Flags().Changed("talos-version") {
49
applyCmdFlags.talosVersion = Config.TemplateOptions.TalosVersion
51
if !cmd.Flags().Changed("with-secrets") {
52
applyCmdFlags.withSecrets = Config.TemplateOptions.WithSecrets
54
if !cmd.Flags().Changed("kubernetes-version") {
55
applyCmdFlags.kubernetesVersion = Config.TemplateOptions.KubernetesVersion
57
if !cmd.Flags().Changed("preserve") {
58
applyCmdFlags.preserve = Config.UpgradeOptions.Preserve
60
if !cmd.Flags().Changed("stage") {
61
applyCmdFlags.stage = Config.UpgradeOptions.Stage
63
if !cmd.Flags().Changed("force") {
64
applyCmdFlags.force = Config.UpgradeOptions.Force
66
applyCmdFlags.nodesFromArgs = len(GlobalArgs.Nodes) > 0
67
applyCmdFlags.endpointsFromArgs = len(GlobalArgs.Endpoints) > 0
68
// Set dummy endpoint to avoid errors on building clinet
69
if len(GlobalArgs.Endpoints) == 0 {
70
GlobalArgs.Endpoints = append(GlobalArgs.Endpoints, "127.0.0.1")
75
RunE: func(cmd *cobra.Command, args []string) error {
76
return WithClientNoNodes(apply(args))
80
func apply(args []string) func(ctx context.Context, c *client.Client) error {
81
return func(ctx context.Context, c *client.Client) error {
82
for _, configFile := range applyCmdFlags.configFiles {
83
if err := processModelineAndUpdateGlobals(configFile, applyCmdFlags.nodesFromArgs, applyCmdFlags.endpointsFromArgs, true); err != nil {
87
opts := engine.Options{
88
TalosVersion: applyCmdFlags.talosVersion,
89
WithSecrets: applyCmdFlags.withSecrets,
90
KubernetesVersion: applyCmdFlags.kubernetesVersion,
93
patches := []string{"@" + configFile}
94
configBundle, err := engine.FullConfigProcess(ctx, opts, patches)
96
return fmt.Errorf("full config processing error: %s", err)
99
machineType := configBundle.ControlPlaneCfg.Machine().Type()
100
result, err := engine.SerializeConfiguration(configBundle, machineType)
102
return fmt.Errorf("error serializing configuration: %s", err)
105
withClient := func(f func(ctx context.Context, c *client.Client) error) error {
106
if applyCmdFlags.insecure {
107
return WithClientMaintenance(applyCmdFlags.certFingerprints, f)
110
return WithClientNoNodes(func(ctx context.Context, cli *client.Client) error {
111
if len(GlobalArgs.Nodes) < 1 {
112
configContext := cli.GetConfigContext()
113
if configContext == nil {
114
return errors.New("failed to resolve config context")
117
GlobalArgs.Nodes = configContext.Nodes
120
ctx = client.WithNodes(ctx, GlobalArgs.Nodes...)
126
err = withClient(func(ctx context.Context, c *client.Client) error {
127
fmt.Printf("- talm: file=%s, nodes=%s, endpoints=%s\n", configFile, GlobalArgs.Nodes, GlobalArgs.Endpoints)
129
resp, err := c.ApplyConfiguration(ctx, &machineapi.ApplyConfigurationRequest{
131
Mode: applyCmdFlags.Mode.Mode,
132
DryRun: applyCmdFlags.dryRun,
133
TryModeTimeout: durationpb.New(applyCmdFlags.configTryTimeout),
136
return fmt.Errorf("error applying new configuration: %s", err)
139
helpers.PrintApplyResults(resp)
148
if !applyCmdFlags.nodesFromArgs {
149
GlobalArgs.Nodes = []string{}
151
if !applyCmdFlags.endpointsFromArgs {
152
GlobalArgs.Endpoints = []string{}
159
// readFirstLine reads and returns the first line of the file specified by the filename.
160
// It returns an error if opening or reading the file fails.
161
func readFirstLine(filename string) (string, error) {
163
file, err := os.Open(filename)
165
return "", fmt.Errorf("error opening file: %v", err)
167
defer file.Close() // Ensure the file is closed after reading
169
// Create a scanner to read the file
170
scanner := bufio.NewScanner(file)
172
// Read the first line
174
return scanner.Text(), nil
177
// Check for errors during scanning
178
if err := scanner.Err(); err != nil {
179
return "", fmt.Errorf("error reading file: %v", err)
182
// If no lines in the file, return an empty string
187
applyCmd.Flags().BoolVarP(&applyCmdFlags.insecure, "insecure", "i", false, "apply using the insecure (encrypted with no auth) maintenance service")
188
applyCmd.Flags().StringSliceVarP(&applyCmdFlags.configFiles, "file", "f", nil, "specify config files or patches in a YAML file (can specify multiple)")
189
applyCmd.Flags().StringVar(&applyCmdFlags.talosVersion, "talos-version", "", "the desired Talos version to generate config for (backwards compatibility, e.g. v0.8)")
190
applyCmd.Flags().StringVar(&applyCmdFlags.withSecrets, "with-secrets", "", "use a secrets file generated using 'gen secrets'")
192
applyCmd.Flags().StringVar(&applyCmdFlags.kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run")
193
applyCmd.Flags().BoolVar(&applyCmdFlags.dryRun, "dry-run", false, "check how the config change will be applied in dry-run mode")
194
applyCmd.Flags().DurationVar(&applyCmdFlags.configTryTimeout, "timeout", constants.ConfigTryTimeout, "the config will be rolled back after specified timeout (if try mode is selected)")
195
applyCmd.Flags().StringSliceVar(&applyCmdFlags.certFingerprints, "cert-fingerprint", nil, "list of server certificate fingeprints to accept (defaults to no check)")
196
applyCmd.Flags().BoolVar(&applyCmdFlags.force, "force", false, "will overwrite existing files")
197
helpers.AddModeFlags(&applyCmdFlags.Mode, applyCmd)