Skip to content

Commit

Permalink
refactor code
Browse files Browse the repository at this point in the history
  • Loading branch information
kvaps committed May 5, 2024
1 parent 011c950 commit 8ae9878
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 107 deletions.
6 changes: 0 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
VERSION=$(shell git describe --tags)
TALOS_VERSION=$(shell go list -m github.com/siderolabs/talos | awk '{sub(/^v/, "", $$NF); print $$NF}')

generate:
go generate

build:
go build -ldflags="-X 'main.Version=$(VERSION)'"

update-tui:
rm -rf internal/pkg/tui
wget -O- https://github.com/siderolabs/talos/archive/refs/tags/v$(TALOS_VERSION).tar.gz | tar --strip=1 -xzf- talos-$(TALOS_VERSION)/internal/pkg/tui
sed -i 's|github.com/siderolabs/talos/internal|github.com/aenix-io/talm/internal|g' `grep -rl 'github.com/siderolabs/talos/internal' internal/pkg/tui`
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ Create new project
mkdir newcluster
cd newcluster
talm init
mkdir nodes
```

Boot Talos Linux node, let's say it has address `1.2.3.4`

Gather node information:
```bash
talm -n 1.2.3.4 -e 1.2.3.4 template templates/controlplane.yaml -i > templates/node1.yaml
talm -n 1.2.3.4 -e 1.2.3.4 template -t templates/controlplane.yaml -i > nodes/node1.yaml
```

Edit `templates/node1.yaml` file:
Expand Down Expand Up @@ -76,12 +77,7 @@ cluster:
endpoint: https://192.168.0.1:6443
```
Show resulted config:
```bash
talm -n 1.2.3.4 -e 1.2.3.4 template templates/node1.yaml -i --full
```

Apply config:
```bash
talm -n 1.2.3.4 -e 1.2.3.4 apply templates/controlplane.yaml -i
talm apply -f nodes/node1.yaml -i
```
190 changes: 101 additions & 89 deletions pkg/commands/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
package commands

import (
"bufio"
"context"
"errors"
"fmt"
"os"
"time"

"github.com/aenix-io/talm/pkg/engine"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/durationpb"

"github.com/aenix-io/talm/internal/pkg/tui/installer"
"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 All @@ -24,12 +27,7 @@ var applyCmdFlags struct {
helpers.Mode
certFingerprints []string
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 @@ -41,132 +39,146 @@ var applyCmdFlags struct {
}

var applyCmd = &cobra.Command{
Use: "apply <file ..>",
Use: "apply",
Short: "Apply config to a Talos node",
Long: ``,
Args: cobra.MinimumNArgs(1),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if applyCmdFlags.insecure {
return WithClientMaintenance(nil, apply(args))
}

return WithClient(apply(args))
return WithClientNoNodes(apply(args))
},
}

func apply(args []string) func(ctx context.Context, c *client.Client) error {
return func(ctx context.Context, c *client.Client) error {
opts := engine.Options{
Insecure: applyCmdFlags.insecure,
ValueFiles: applyCmdFlags.valueFiles,
StringValues: applyCmdFlags.stringValues,
Values: applyCmdFlags.values,
FileValues: applyCmdFlags.fileValues,
JsonValues: applyCmdFlags.jsonValues,
LiteralValues: applyCmdFlags.literalValues,
TalosVersion: applyCmdFlags.talosVersion,
WithSecrets: applyCmdFlags.withSecrets,
Full: true,
Root: Config.RootDir,
Offline: false,
KubernetesVersion: applyCmdFlags.kubernetesVersion,
TemplateFiles: args,
}
for _, configFile := range applyCmdFlags.configFiles {
opts := engine.Options{
TalosVersion: applyCmdFlags.talosVersion,
WithSecrets: applyCmdFlags.withSecrets,
KubernetesVersion: applyCmdFlags.kubernetesVersion,
}

result, err := engine.Render(ctx, c, opts)
if err != nil {
return fmt.Errorf("failed to render templates: %w", err)
}
// Use the new function to handle modeline
modelineConfig, err := modeline.ReadAndParseModeline(configFile)
if err != nil {
fmt.Printf("Warning: modeline parsing failed: %v\n", err)
}

withClient := func(f func(context.Context, *client.Client) error) error {
if applyCmdFlags.insecure {
return WithClientMaintenance(applyCmdFlags.certFingerprints, f)
// Update global settings if modeline was successfully parsed
if modelineConfig != nil {
if len(modelineConfig.Nodes) > 0 {
GlobalArgs.Nodes = modelineConfig.Nodes
}
if len(modelineConfig.Endpoints) > 0 {
GlobalArgs.Endpoints = modelineConfig.Endpoints
}
}

return WithClient(f)
}
patches := []string{"@" + configFile}
configBundle, err := engine.FullConfigProcess(ctx, opts, patches)
if err != nil {
return fmt.Errorf("full config processing error: %s", err)
}

return withClient(func(ctx context.Context, c *client.Client) error {
if applyCmdFlags.Mode.Mode == helpers.InteractiveMode {
install := installer.NewInstaller()
node := GlobalArgs.Nodes[0]

if len(GlobalArgs.Endpoints) > 0 {
return WithClientNoNodes(func(bootstrapCtx context.Context, bootstrapClient *client.Client) error {
opts := []installer.Option{}
opts = append(opts, installer.WithBootstrapNode(bootstrapCtx, bootstrapClient, GlobalArgs.Endpoints[0]), installer.WithDryRun(applyCmdFlags.dryRun))

conn, err := installer.NewConnection(
ctx,
c,
node,
opts...,
)
if err != nil {
return err
}

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

conn, err := installer.NewConnection(
ctx,
c,
node,
installer.WithDryRun(applyCmdFlags.dryRun),
)
err = withClient(func(ctx context.Context, c *client.Client) error {
resp, err := c.ApplyConfiguration(ctx, &machineapi.ApplyConfigurationRequest{
Data: result,
Mode: applyCmdFlags.Mode.Mode,
DryRun: applyCmdFlags.dryRun,
TryModeTimeout: durationpb.New(applyCmdFlags.configTryTimeout),
})
if err != nil {
return err
return fmt.Errorf("error applying new configuration: %s", err)
}

return install.Run(conn)
}
helpers.PrintApplyResults(resp)

resp, err := c.ApplyConfiguration(ctx, &machineapi.ApplyConfigurationRequest{
Data: result,
Mode: applyCmdFlags.Mode.Mode,
DryRun: applyCmdFlags.dryRun,
TryModeTimeout: durationpb.New(applyCmdFlags.configTryTimeout),
return nil
})
if err != nil {
return fmt.Errorf("error applying new configuration: %s", err)
return err
}
}
return nil
}
}

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")
}

helpers.PrintApplyResults(resp)
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 nil
})
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) {
// Open the file
file, err := os.Open(filename)
if err != nil {
return "", fmt.Errorf("error opening file: %v", err)
}
defer file.Close() // Ensure the file is closed after reading

// Create a scanner to read the file
scanner := bufio.NewScanner(file)

// Read the first line
if scanner.Scan() {
return scanner.Text(), nil
}

// Check for errors during scanning
if err := scanner.Err(); err != nil {
return "", fmt.Errorf("error reading file: %v", err)
}

// If no lines in the file, return an empty string
return "", nil
}

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

applyCmd.Flags().StringVar(&applyCmdFlags.kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run")

applyCmd.Flags().BoolVar(&applyCmdFlags.dryRun, "dry-run", false, "check how the config change will be applied in dry-run mode")
applyCmd.Flags().DurationVar(&applyCmdFlags.configTryTimeout, "timeout", constants.ConfigTryTimeout, "the config will be rolled back after specified timeout (if try mode is selected)")
applyCmd.Flags().StringSliceVar(&applyCmdFlags.certFingerprints, "cert-fingerprint", nil, "list of server certificate fingeprints to accept (defaults to no check)")
applyCmd.Flags().BoolVar(&applyCmdFlags.force, "force", false, "will overwrite existing files")
helpers.AddModeFlags(&applyCmdFlags.Mode, applyCmd)

applyCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
applyCmdFlags.valueFiles = append(Config.TemplateOptions.ValueFiles, applyCmdFlags.valueFiles...)
applyCmdFlags.values = append(Config.TemplateOptions.Values, applyCmdFlags.values...)
applyCmdFlags.stringValues = append(Config.TemplateOptions.StringValues, applyCmdFlags.stringValues...)
applyCmdFlags.fileValues = append(Config.TemplateOptions.FileValues, applyCmdFlags.fileValues...)
applyCmdFlags.jsonValues = append(Config.TemplateOptions.JsonValues, applyCmdFlags.jsonValues...)
applyCmdFlags.literalValues = append(Config.TemplateOptions.LiteralValues, applyCmdFlags.literalValues...)
if !cmd.Flags().Changed("talos-version") {
applyCmdFlags.talosVersion = Config.TemplateOptions.TalosVersion
}
Expand Down
15 changes: 10 additions & 5 deletions pkg/commands/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ package commands
import (
"context"
"encoding/json"
"errors"
"fmt"

"github.com/aenix-io/talm/pkg/engine"
"github.com/aenix-io/talm/pkg/modeline"
"github.com/spf13/cobra"

"github.com/siderolabs/talos/pkg/machinery/client"
Expand Down Expand Up @@ -50,7 +52,6 @@ var templateCmd = &cobra.Command{
}

func template(args []string) func(ctx context.Context, c *client.Client) error {

return func(ctx context.Context, c *client.Client) error {
opts := engine.Options{
Insecure: templateCmdFlags.insecure,
Expand All @@ -69,17 +70,21 @@ func template(args []string) func(ctx context.Context, c *client.Client) error {
TemplateFiles: templateCmdFlags.templateFiles,
}

if len(templateCmdFlags.templateFiles) < 1 {
return errors.New("templates are not set for the command: please use `--template` flag to set the templates to render manifest from")
}

result, err := engine.Render(ctx, c, opts)
if err != nil {
return fmt.Errorf("failed to render templates: %w", err)
}

modeline, err := generateModeline(args)
// Use the GenerateModeline from the modeline package
modeline, err := modeline.GenerateModeline(GlobalArgs.Nodes, GlobalArgs.Endpoints, templateCmdFlags.templateFiles)
if err != nil {
return fmt.Errorf("failed generate modeline: %w", err)
return fmt.Errorf("failed to generate modeline: %w", err)
}

// Print the result to the standard output
fmt.Printf("%s\n%s", modeline, string(result))

return nil
Expand All @@ -89,7 +94,7 @@ func template(args []string) func(ctx context.Context, c *client.Client) error {
func init() {
templateCmd.Flags().BoolVarP(&templateCmdFlags.insecure, "insecure", "i", false, "template using the insecure (encrypted with no auth) maintenance service")
templateCmd.Flags().StringSliceVarP(&templateCmdFlags.valueFiles, "values", "", []string{}, "specify values in a YAML file (can specify multiple)")
templateCmd.Flags().StringSliceVarP(&templateCmdFlags.templateFiles, "template", "t", []string{}, "specify templates to rendered manifest from (can specify multiple)")
templateCmd.Flags().StringSliceVarP(&templateCmdFlags.templateFiles, "template", "t", []string{}, "specify templates to render manifest from (can specify multiple)")
templateCmd.Flags().StringArrayVar(&templateCmdFlags.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
templateCmd.Flags().StringArrayVar(&templateCmdFlags.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
templateCmd.Flags().StringArrayVar(&templateCmdFlags.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)")
Expand Down
Loading

0 comments on commit 8ae9878

Please sign in to comment.