20
var talosVersion = flag.String("talos-version", "main", "the desired Talos version (branch or tag)")
22
func changePackageName(node *ast.File, newPackageName string) {
23
node.Name = ast.NewIdent(newPackageName)
26
func addFieldToStruct(node *ast.File, varName, fieldType, fieldName string) {
27
// Variable to track if the variable or type is found
30
// Inspect nodes to find and modify the target struct declaration or its type
31
ast.Inspect(node, func(n ast.Node) bool {
32
switch decl := n.(type) {
34
if decl.Tok == token.TYPE {
35
for _, spec := range decl.Specs {
36
ts, ok := spec.(*ast.TypeSpec)
37
if !ok || !strings.HasSuffix(ts.Name.Name, "Type") {
40
if typeName := strings.TrimSuffix(ts.Name.Name, "Type"); typeName != varName {
44
st, ok := ts.Type.(*ast.StructType)
49
// Add field to the found struct type
51
Names: []*ast.Ident{ast.NewIdent(fieldName)},
52
Type: ast.NewIdent(fieldType),
54
st.Fields.List = append(st.Fields.List, field)
56
return false // stop searching, type found and updated
58
} else if decl.Tok == token.VAR {
59
for _, spec := range decl.Specs {
60
vs, ok := spec.(*ast.ValueSpec)
61
if !ok || len(vs.Names) != 1 || vs.Names[0].Name != varName {
65
st, ok := vs.Type.(*ast.StructType)
70
// Add field to the found struct variable
72
Names: []*ast.Ident{ast.NewIdent(fieldName)},
73
Type: ast.NewIdent(fieldType),
75
st.Fields.List = append(st.Fields.List, field)
77
return false // stop searching, variable found and updated
84
// If the struct or type is not found, create a new variable struct
86
newField := &ast.Field{
87
Names: []*ast.Ident{ast.NewIdent(fieldName)},
88
Type: ast.NewIdent(fieldType),
90
newStruct := &ast.StructType{
91
Fields: &ast.FieldList{
92
List: []*ast.Field{newField},
95
newSpec := &ast.ValueSpec{
96
Names: []*ast.Ident{ast.NewIdent(varName)},
99
newDecl := &ast.GenDecl{
101
Specs: []ast.Spec{newSpec},
104
node.Decls = append(node.Decls, newDecl)
105
fmt.Println("New struct variable created:", varName)
109
func prependStmtToInit(node *ast.File, cmdName string) {
110
ast.Inspect(node, func(n ast.Node) bool {
111
fn, ok := n.(*ast.FuncDecl)
112
if ok && fn.Name.Name == "init" {
113
stmt := &ast.ExprStmt{
115
Fun: &ast.SelectorExpr{
116
X: &ast.SelectorExpr{
117
X: ast.NewIdent(cmdName + "Cmd"),
118
Sel: ast.NewIdent("Flags()"),
120
Sel: ast.NewIdent("StringSliceVarP"),
125
X: ast.NewIdent(cmdName + "CmdFlags.configFiles"),
127
ast.NewIdent(`"file"`),
130
ast.NewIdent(`"specify config files or patches in a YAML file (can specify multiple)"`),
134
fn.Body.List = append([]ast.Stmt{stmt}, fn.Body.List...)
141
func insertInitCode(node *ast.File, cmdName, initCode string) {
142
anonFuncCode := fmt.Sprintf(`func() { %s }`, initCode)
144
initCodeExpr, err := parser.ParseExpr(anonFuncCode)
146
log.Fatalf("Failed to parse init code: %v", err)
149
ast.Inspect(node, func(n ast.Node) bool {
150
switch x := n.(type) {
152
if x.Name.Name == "init" {
154
initFunc, ok := initCodeExpr.(*ast.FuncLit)
156
log.Fatalf("Failed to extract function body from init code expression")
159
x.Body.List = append(initFunc.Body.List, x.Body.List...)
167
func processFile(filename, cmdName string) {
168
content, err := ioutil.ReadFile(filename)
170
log.Fatalf("Failed to read the file: %v", err)
172
src := string(content)
174
src = strings.ReplaceAll(src, "\"f\"", "\"F\"")
175
src = strings.ReplaceAll(src, "github.com/siderolabs/talos/internal", "github.com/aenix-io/talm/internal")
177
// Create a new set of tokens and parse the source code
178
fset := token.NewFileSet()
179
node, err := parser.ParseFile(fset, filename, src, parser.ParseComments)
181
log.Fatalf("Failed to parse file: %v", err)
184
changePackageName(node, "commands")
185
addFieldToStruct(node, cmdName+"CmdFlags", "[]string", "configFiles")
187
initCode := fmt.Sprintf(`%sCmd.Flags().StringSliceVarP(&%sCmdFlags.configFiles, "file", "f", nil, "specify config files or patches in a YAML file (can specify multiple)")
188
%sCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
189
nodesFromArgs := len(GlobalArgs.Nodes) > 0
190
endpointsFromArgs := len(GlobalArgs.Endpoints) > 0
191
for _, configFile := range %sCmdFlags.configFiles {
192
if err := processModelineAndUpdateGlobals(configFile, nodesFromArgs, endpointsFromArgs, false); err != nil {
198
`, cmdName, cmdName, cmdName, cmdName)
200
if cmdName == "etcd" {
201
for _, subCmdName := range []string{"etcdAlarmCmd", "etcdDefragCmd", "etcdForfeitLeadershipCmd", "etcdLeaveCmd", "etcdMemberListCmd", "etcdMemberRemoveCmd", "etcdSnapshotCmd", "etcdStatusCmd"} {
202
initCode = fmt.Sprintf("%s\n%s", initCode, fmt.Sprintf(`
203
%s.Flags().StringSliceVarP(&etcdCmdFlags.configFiles, "file", "f", nil, "specify config files or patches in a YAML file (can specify multiple)")
204
%s.PreRunE = etcdCmd.PreRunE
205
`, subCmdName, subCmdName))
208
if cmdName == "image" {
209
for _, subCmdName := range []string{"imageDefaultCmd", "imageListCmd", "imagePullCmd"} {
210
initCode = fmt.Sprintf("%s\n%s", initCode, fmt.Sprintf(`
211
%s.Flags().StringSliceVarP(&etcdCmdFlags.configFiles, "file", "f", nil, "specify config files or patches in a YAML file (can specify multiple)")
212
%s.PreRunE = etcdCmd.PreRunE
213
`, subCmdName, subCmdName))
217
if cmdName == "etcd" {
218
for _, subCmdName := range []string{"etcdAlarmListCmd", "etcdAlarmDisarmCmd"} {
219
initCode = fmt.Sprintf("%s\n%s", initCode, fmt.Sprintf(`
220
%s.Flags().StringSliceVarP(&etcdCmdFlags.configFiles, "file", "f", nil, "specify config files or patches in a YAML file (can specify multiple)")
221
%s.PreRunE = etcdCmd.PreRunE
222
`, subCmdName, subCmdName))
226
insertInitCode(node, cmdName, initCode)
229
comment := fmt.Sprintf("// Code generated by go run tools/import_commands.go --talos-version %s %s\n// DO NOT EDIT.\n\n", *talosVersion, cmdName)
230
buf.WriteString(comment)
232
if err := format.Node(&buf, fset, node); err != nil {
233
log.Fatalf("Failed to format the AST: %v", err)
236
if err := ioutil.WriteFile(filename, buf.Bytes(), 0644); err != nil {
237
log.Fatalf("Failed to write the modified file: %v", err)
240
log.Printf("File %s updated successfully.", filename)
245
url := fmt.Sprintf("https://github.com/siderolabs/talos/raw/%s/cmd/talosctl/cmd/talos/", *talosVersion)
249
fmt.Println("Please provide commands to import")
253
for _, cmd := range args {
254
srcName := cmd + ".go"
255
dstName := "pkg/commands/imported_" + srcName
257
err := downloadFile(srcName, dstName, url)
259
log.Fatalf("Error downloading file: %v", err)
262
log.Printf("File %s succefully downloaded to %s", srcName, dstName)
264
cmdName := strings.TrimSuffix(filepath.Base(srcName), ".go")
265
if cmdName == "list" {
268
processFile(dstName, cmdName)
272
func downloadFile(srcName, dstName string, url string) error {
273
resp, err := http.Get(url + "/" + srcName)
277
defer resp.Body.Close()
279
file, err := os.Create(dstName)
285
_, err = io.Copy(file, resp.Body)