forked from siderolabs/talos
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add talosctl machineconfig patch command
Add talosctl machineconfig patch command which accepts a machine config as input and a list of patches, applying the patches and writing the result to a file or to stdout. Link `talosctl machineconfig gen` to `talosctl gen config`, so they work the same way. Closes siderolabs#6562. Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
- Loading branch information
1 parent
d3cf061
commit 7ab140a
Showing
10 changed files
with
389 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// 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 machineconfig | ||
|
||
import "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/gen" | ||
|
||
func init() { | ||
// alias for `talosctl gen config` | ||
Cmd.AddCommand(gen.NewConfigCmd("gen")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// 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 machineconfig | ||
|
||
import "github.com/spf13/cobra" | ||
|
||
// Cmd represents the `machineconfig` command. | ||
var Cmd = &cobra.Command{ | ||
Use: "machineconfig", | ||
Short: "Machine config related commands", | ||
Aliases: []string{"mc"}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// 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 machineconfig | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/siderolabs/talos/pkg/machinery/config/configpatcher" | ||
) | ||
|
||
var patchCmdFlags struct { | ||
patches []string | ||
output string | ||
} | ||
|
||
// patchCmd represents the `machineconfig patch` command. | ||
var patchCmd = &cobra.Command{ | ||
Use: "patch <machineconfig-file>", | ||
Short: "Patch a machine config", | ||
Args: cobra.ExactArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
data, err := os.ReadFile(args[0]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
patches, err := configpatcher.LoadPatches(patchCmdFlags.patches) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
patched, err := configpatcher.Apply(configpatcher.WithBytes(data), patches) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
patchedData, err := patched.Bytes() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if patchCmdFlags.output == "" { // write to stdout | ||
fmt.Printf("%s\n", patchedData) | ||
|
||
return nil | ||
} | ||
|
||
// write to file | ||
|
||
parentDir := filepath.Dir(patchCmdFlags.output) | ||
|
||
// Create dir path, ignoring "already exists" messages | ||
if err := os.MkdirAll(parentDir, os.ModePerm); err != nil && !os.IsExist(err) { | ||
return fmt.Errorf("failed to create output dir: %w", err) | ||
} | ||
|
||
return os.WriteFile(patchCmdFlags.output, patchedData, 0o644) | ||
}, | ||
} | ||
|
||
func init() { | ||
// use StringArrayVarP instead of StringSliceVarP to prevent cobra from splitting the patch string on commas | ||
patchCmd.Flags().StringArrayVarP(&patchCmdFlags.patches, "patch", "p", nil, "patch generated machineconfigs (applied to all node types), use @file to read a patch from file") | ||
patchCmd.Flags().StringVarP(&patchCmdFlags.output, "output", "o", "", "output destination. if not specified, output will be printed to stdout") | ||
|
||
Cmd.AddCommand(patchCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// 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/. | ||
|
||
//go:build integration_cli | ||
|
||
package cli | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"go.uber.org/multierr" | ||
|
||
"github.com/siderolabs/talos/internal/integration/base" | ||
) | ||
|
||
// MachineConfigSuite verifies machineconfig command. | ||
type MachineConfigSuite struct { | ||
base.CLISuite | ||
|
||
savedCwd string | ||
} | ||
|
||
// SuiteName ... | ||
func (suite *MachineConfigSuite) SuiteName() string { | ||
return "cli.MachineConfigSuite" | ||
} | ||
|
||
// SetupTest ... | ||
func (suite *MachineConfigSuite) SetupTest() { | ||
var err error | ||
suite.savedCwd, err = os.Getwd() | ||
suite.Require().NoError(err) | ||
} | ||
|
||
// TearDownTest ... | ||
func (suite *MachineConfigSuite) TearDownTest() { | ||
if suite.savedCwd != "" { | ||
suite.Require().NoError(os.Chdir(suite.savedCwd)) | ||
} | ||
} | ||
|
||
// TestGen tests the gen subcommand. | ||
// Identical to GenSuite.TestGenConfigToStdoutControlPlane | ||
// The remaining functionality is checked for `talosctl gen config` in GenSuite. | ||
func (suite *MachineConfigSuite) TestGen() { | ||
suite.RunCLI([]string{ | ||
"gen", "config", | ||
"foo", "https://192.168.0.1:6443", | ||
"--output-types", "controlplane", | ||
"--output", "-", | ||
}, base.StdoutMatchFunc(func(output string) error { | ||
expected := "type: controlplane" | ||
if !strings.Contains(output, expected) { | ||
return fmt.Errorf("stdout does not contain %q: %q", expected, output) | ||
} | ||
|
||
return nil | ||
})) | ||
} | ||
|
||
// TestPatchPrintStdout tests the patch subcommand with output set to stdout | ||
// with multiple patches from the command line and from file. | ||
func (suite *MachineConfigSuite) TestPatchPrintStdout() { | ||
patch1, err := json.Marshal([]map[string]interface{}{ | ||
{ | ||
"op": "replace", | ||
"path": "/cluster/clusterName", | ||
"value": "replaced", | ||
}, | ||
}) | ||
suite.Require().NoError(err) | ||
|
||
patch2, err := json.Marshal([]map[string]interface{}{ | ||
{ | ||
"op": "replace", | ||
"path": "/cluster/controlPlane/endpoint", | ||
"value": "replaced", | ||
}, | ||
}) | ||
suite.Require().NoError(err) | ||
|
||
patch2Path := filepath.Join(suite.T().TempDir(), "patch2.json") | ||
|
||
err = os.WriteFile(patch2Path, patch2, 0o644) | ||
suite.Require().NoError(err) | ||
|
||
mc := filepath.Join(suite.T().TempDir(), "input.yaml") | ||
|
||
suite.RunCLI([]string{ | ||
"gen", "config", | ||
"foo", "https://192.168.0.1:6443", | ||
"--output-types", "controlplane", | ||
"--output", mc, | ||
}) | ||
|
||
suite.RunCLI([]string{ | ||
"machineconfig", "patch", mc, "--patch", string(patch1), "-p", "@" + patch2Path, | ||
}, base.StdoutMatchFunc(func(output string) error { | ||
var matchErr error | ||
|
||
if !strings.Contains(output, "clusterName: replaced") { | ||
matchErr = multierr.Append(matchErr, fmt.Errorf("clusterName not replaced")) | ||
} | ||
|
||
if !strings.Contains(output, "endpoint: replaced") { | ||
matchErr = multierr.Append(matchErr, fmt.Errorf("endpoint not replaced")) | ||
} | ||
|
||
return matchErr | ||
})) | ||
} | ||
|
||
// TestPatchWriteToFile tests the patch subcommand with output set to a file. | ||
func (suite *MachineConfigSuite) TestPatchWriteToFile() { | ||
patch1, err := json.Marshal([]map[string]interface{}{ | ||
{ | ||
"op": "replace", | ||
"path": "/cluster/clusterName", | ||
"value": "replaced", | ||
}, | ||
}) | ||
suite.Require().NoError(err) | ||
|
||
mc := filepath.Join(suite.T().TempDir(), "input2.yaml") | ||
|
||
suite.RunCLI([]string{ | ||
"gen", "config", | ||
"foo", "https://192.168.0.1:6443", | ||
"--output-types", "controlplane", | ||
"--output", mc, | ||
}) | ||
|
||
outputFile := filepath.Join(suite.T().TempDir(), "inner", "output.yaml") | ||
|
||
suite.RunCLI([]string{ | ||
"machineconfig", "patch", mc, "--patch", string(patch1), "--output", outputFile, | ||
}, base.StdoutEmpty()) | ||
|
||
outputBytes, err := os.ReadFile(outputFile) | ||
suite.Assert().NoError(err) | ||
|
||
suite.Assert().Contains(string(outputBytes), "clusterName: replaced") | ||
} | ||
|
||
func init() { | ||
allSuites = append(allSuites, new(MachineConfigSuite)) | ||
} |
Oops, something went wrong.