Skip to content

Commit d2208b0

Browse files
committed
refactor(talosctl): propagate command context throughout, handle interrupts
Currently, WithClientMaintenance[1] and WithClientNoNodes[2] will spawn a new context.Background on each invocation. This effectively severs the chain of context, with respect to the the root/base Cobra command context [3]. In some cases, this was known to break handling of SIGINT (Ctrl^C), as multiple such signals were required to terminate the running CLI (one SIGINT for every separate context). What I've done to solve this: * cmd/talosctl/cmd/root.go provides a root-level Ctrl^C cancelable context to all of the talosctl commands. * Every talosctl command now reuses the root context, which is propagated through the Cobra cmd.Context(). * No talosctl command creates another SIGINT handler, like they did before on an individual basis. * Termination of processes launched via *-launch is also improved. They'll attempt a graceful shutdown as soon as cmd.Context().Done() is closed. This includes dhcpd, dnsd, loadbalancer, kms, and tftpd. Signed-off-by: Maja Bojarska <maja.bojarska@siderolabs.com>
1 parent 0760b5c commit d2208b0

68 files changed

Lines changed: 556 additions & 532 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/installer/cmd/imager/root.go

Lines changed: 118 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -57,174 +57,174 @@ var rootCmd = &cobra.Command{
5757
Args: cobra.ExactArgs(1),
5858
SilenceUsage: true,
5959
RunE: func(cmd *cobra.Command, args []string) error {
60-
return cli.WithContext(context.Background(), func(ctx context.Context) error {
61-
report := reporter.New()
62-
report.Report(reporter.Update{
63-
Message: "assembling the finalized profile...",
64-
Status: reporter.StatusRunning,
65-
})
66-
67-
baseProfile := args[0]
68-
69-
var prof profile.Profile
60+
ctx := cmd.Context()
7061

71-
if baseProfile == "-" {
72-
if err := yaml.NewDecoder(os.Stdin).Decode(&prof); err != nil {
73-
return err
74-
}
75-
} else {
76-
prof = profile.Profile{
77-
BaseProfileName: baseProfile,
78-
Arch: cmdFlags.Arch,
79-
Platform: cmdFlags.Platform,
80-
Customization: profile.CustomizationProfile{
81-
ExtraKernelArgs: cmdFlags.ExtraKernelArgs,
82-
MetaContents: cmdFlags.MetaValues.GetMetaValues(),
83-
},
84-
}
85-
86-
extraOverlayOptions := overlay.ExtraOptions{}
87-
88-
for _, option := range cmdFlags.OverlayOptions {
89-
if strings.HasPrefix(option, "@") {
90-
data, err := os.ReadFile(option[1:])
91-
if err != nil {
92-
return err
93-
}
94-
95-
decoder := yaml.NewDecoder(bytes.NewReader(data))
96-
decoder.KnownFields(true)
62+
report := reporter.New()
63+
report.Report(reporter.Update{
64+
Message: "assembling the finalized profile...",
65+
Status: reporter.StatusRunning,
66+
})
9767

98-
if err := decoder.Decode(&extraOverlayOptions); err != nil {
99-
return err
100-
}
68+
baseProfile := args[0]
10169

102-
continue
103-
}
70+
var prof profile.Profile
10471

105-
k, v, _ := strings.Cut(option, "=")
72+
if baseProfile == "-" {
73+
if err := yaml.NewDecoder(os.Stdin).Decode(&prof); err != nil {
74+
return err
75+
}
76+
} else {
77+
prof = profile.Profile{
78+
BaseProfileName: baseProfile,
79+
Arch: cmdFlags.Arch,
80+
Platform: cmdFlags.Platform,
81+
Customization: profile.CustomizationProfile{
82+
ExtraKernelArgs: cmdFlags.ExtraKernelArgs,
83+
MetaContents: cmdFlags.MetaValues.GetMetaValues(),
84+
},
85+
}
10686

107-
if strings.HasPrefix(v, "@") {
108-
data, err := os.ReadFile(v[1:])
109-
if err != nil {
110-
return err
111-
}
87+
extraOverlayOptions := overlay.ExtraOptions{}
11288

113-
v = string(data)
89+
for _, option := range cmdFlags.OverlayOptions {
90+
if strings.HasPrefix(option, "@") {
91+
data, err := os.ReadFile(option[1:])
92+
if err != nil {
93+
return err
11494
}
11595

116-
extraOverlayOptions[k] = v
117-
}
96+
decoder := yaml.NewDecoder(bytes.NewReader(data))
97+
decoder.KnownFields(true)
11898

119-
if cmdFlags.OverlayName != "" || cmdFlags.OverlayImage != "" {
120-
prof.Overlay = &profile.OverlayOptions{
121-
Name: cmdFlags.OverlayName,
122-
Image: profile.ContainerAsset{
123-
ImageRef: cmdFlags.OverlayImage,
124-
},
125-
ExtraOptions: extraOverlayOptions,
99+
if err := decoder.Decode(&extraOverlayOptions); err != nil {
100+
return err
126101
}
127102

128-
prof.Input.OverlayInstaller.ImageRef = cmdFlags.OverlayImage
103+
continue
129104
}
130105

131-
prof.Input.SystemExtensions = xslices.Map(
132-
cmdFlags.SystemExtensionImages,
133-
func(imageRef string) profile.ContainerAsset {
134-
return profile.ContainerAsset{
135-
ImageRef: imageRef,
136-
ForceInsecure: cmdFlags.Insecure,
137-
}
138-
},
139-
)
106+
k, v, _ := strings.Cut(option, "=")
140107

141-
if cmdFlags.OutputKind != "" {
142-
outKind, err := profile.OutputKindString(cmdFlags.OutputKind)
108+
if strings.HasPrefix(v, "@") {
109+
data, err := os.ReadFile(v[1:])
143110
if err != nil {
144111
return err
145112
}
146113

147-
prof.Output.Kind = outKind
114+
v = string(data)
148115
}
149116

150-
if cmdFlags.BaseInstallerImage != "" {
151-
prof.Input.BaseInstaller = profile.ContainerAsset{
152-
ImageRef: cmdFlags.BaseInstallerImage,
153-
}
117+
extraOverlayOptions[k] = v
118+
}
119+
120+
if cmdFlags.OverlayName != "" || cmdFlags.OverlayImage != "" {
121+
prof.Overlay = &profile.OverlayOptions{
122+
Name: cmdFlags.OverlayName,
123+
Image: profile.ContainerAsset{
124+
ImageRef: cmdFlags.OverlayImage,
125+
},
126+
ExtraOptions: extraOverlayOptions,
154127
}
155128

156-
if cmdFlags.ImageCache != "" {
157-
parseOpts := []name.Option{name.StrictValidation}
129+
prof.Input.OverlayInstaller.ImageRef = cmdFlags.OverlayImage
130+
}
158131

159-
if cmdFlags.Insecure {
160-
parseOpts = append(parseOpts, name.Insecure)
132+
prof.Input.SystemExtensions = xslices.Map(
133+
cmdFlags.SystemExtensionImages,
134+
func(imageRef string) profile.ContainerAsset {
135+
return profile.ContainerAsset{
136+
ImageRef: imageRef,
137+
ForceInsecure: cmdFlags.Insecure,
161138
}
139+
},
140+
)
162141

163-
if _, err := name.ParseReference(cmdFlags.ImageCache, parseOpts...); err == nil {
164-
prof.Input.ImageCache = profile.ContainerAsset{
165-
ImageRef: cmdFlags.ImageCache,
166-
}
167-
} else {
168-
prof.Input.ImageCache = profile.ContainerAsset{
169-
OCIPath: cmdFlags.ImageCache,
170-
}
171-
}
142+
if cmdFlags.OutputKind != "" {
143+
outKind, err := profile.OutputKindString(cmdFlags.OutputKind)
144+
if err != nil {
145+
return err
172146
}
173147

174-
if cmdFlags.Insecure {
175-
prof.Input.BaseInstaller.ForceInsecure = cmdFlags.Insecure
176-
prof.Input.ImageCache.ForceInsecure = cmdFlags.Insecure
148+
prof.Output.Kind = outKind
149+
}
150+
151+
if cmdFlags.BaseInstallerImage != "" {
152+
prof.Input.BaseInstaller = profile.ContainerAsset{
153+
ImageRef: cmdFlags.BaseInstallerImage,
177154
}
155+
}
178156

179-
if cmdFlags.SecurebootIncludeWellKnownCerts {
180-
if prof.Input.SecureBoot == nil {
181-
prof.Input.SecureBoot = &profile.SecureBootAssets{}
182-
}
157+
if cmdFlags.ImageCache != "" {
158+
parseOpts := []name.Option{name.StrictValidation}
183159

184-
prof.Input.SecureBoot.IncludeWellKnownCerts = true
160+
if cmdFlags.Insecure {
161+
parseOpts = append(parseOpts, name.Insecure)
185162
}
186163

187-
if cmdFlags.EmbeddedConfigPath != "" {
188-
data, err := os.ReadFile(cmdFlags.EmbeddedConfigPath)
189-
if err != nil {
190-
return fmt.Errorf("error reading embedded config file: %w", err)
164+
if _, err := name.ParseReference(cmdFlags.ImageCache, parseOpts...); err == nil {
165+
prof.Input.ImageCache = profile.ContainerAsset{
166+
ImageRef: cmdFlags.ImageCache,
167+
}
168+
} else {
169+
prof.Input.ImageCache = profile.ContainerAsset{
170+
OCIPath: cmdFlags.ImageCache,
191171
}
192-
193-
prof.Customization.EmbeddedMachineConfiguration = string(data)
194172
}
195173
}
196174

197-
if err := os.MkdirAll(cmdFlags.OutputPath, 0o755); err != nil {
198-
return err
175+
if cmdFlags.Insecure {
176+
prof.Input.BaseInstaller.ForceInsecure = cmdFlags.Insecure
177+
prof.Input.ImageCache.ForceInsecure = cmdFlags.Insecure
199178
}
200179

201-
imager, err := imager.New(prof)
202-
if err != nil {
203-
return err
180+
if cmdFlags.SecurebootIncludeWellKnownCerts {
181+
if prof.Input.SecureBoot == nil {
182+
prof.Input.SecureBoot = &profile.SecureBootAssets{}
183+
}
184+
185+
prof.Input.SecureBoot.IncludeWellKnownCerts = true
204186
}
205187

206-
if _, err = imager.Execute(ctx, cmdFlags.OutputPath, report); err != nil {
207-
report.Report(reporter.Update{
208-
Message: err.Error(),
209-
Status: reporter.StatusError,
210-
})
188+
if cmdFlags.EmbeddedConfigPath != "" {
189+
data, err := os.ReadFile(cmdFlags.EmbeddedConfigPath)
190+
if err != nil {
191+
return fmt.Errorf("error reading embedded config file: %w", err)
192+
}
211193

212-
return err
194+
prof.Customization.EmbeddedMachineConfiguration = string(data)
213195
}
196+
}
214197

215-
if cmdFlags.TarToStdout {
216-
return archiver.TarGz(ctx, cmdFlags.OutputPath, os.Stdout)
217-
}
198+
if err := os.MkdirAll(cmdFlags.OutputPath, 0o755); err != nil {
199+
return err
200+
}
218201

219-
return nil
220-
})
202+
imager, err := imager.New(prof)
203+
if err != nil {
204+
return err
205+
}
206+
207+
if _, err = imager.Execute(ctx, cmdFlags.OutputPath, report); err != nil {
208+
report.Report(reporter.Update{
209+
Message: err.Error(),
210+
Status: reporter.StatusError,
211+
})
212+
213+
return err
214+
}
215+
216+
if cmdFlags.TarToStdout {
217+
return archiver.TarGz(ctx, cmdFlags.OutputPath, os.Stdout)
218+
}
219+
220+
return nil
221221
},
222222
}
223223

224224
// Execute adds all child commands to the root command and sets flags appropriately.
225225
// This is called by main.main(). It only needs to happen once to the rootCmd.
226226
func Execute() {
227-
if err := rootCmd.Execute(); err != nil {
227+
if _, err := cli.WithContextC(context.Background(), rootCmd.ExecuteContextC); err != nil {
228228
os.Exit(1)
229229
}
230230
}

cmd/talosctl/cmd/mgmt/cluster/create/cmd_dev.go

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package create
66

77
import (
8-
"context"
98
"fmt"
109
"runtime"
1110
"slices"
@@ -268,55 +267,53 @@ func getCreateCmd(cmdName string, hidden bool) *cobra.Command {
268267
Short: "Creates a local QEMU-based cluster for Talos development.",
269268
Args: cobra.NoArgs,
270269
RunE: func(cmd *cobra.Command, args []string) error {
271-
return cli.WithContext(context.Background(), func(ctx context.Context) error {
272-
if cmdName == "create" {
273-
cli.Warning("the developer oriented 'cluster create' command has been moved to 'cluster create dev'")
274-
}
270+
if cmdName == "create" {
271+
cli.Warning("the developer oriented 'cluster create' command has been moved to 'cluster create dev'")
272+
}
275273

276-
if err := validateQemuFlags(cmd.Flags(), unImplementedFlagsDarwin); err != nil {
277-
return err
278-
}
274+
if err := validateQemuFlags(cmd.Flags(), unImplementedFlagsDarwin); err != nil {
275+
return err
276+
}
279277

280-
var disks strings.Builder
281-
fmt.Fprintf(&disks, "virtio:%d", legacyOps.clusterDiskSize)
278+
var disks strings.Builder
279+
fmt.Fprintf(&disks, "virtio:%d", legacyOps.clusterDiskSize)
282280

283-
for i := range legacyOps.extraDisks {
284-
driver := "ide"
285-
tag := ""
286-
serial := ""
281+
for i := range legacyOps.extraDisks {
282+
driver := "ide"
283+
tag := ""
284+
serial := ""
287285

288-
// ide driver is not supported on arm64
289-
if qOps.TargetArch == "arm64" {
290-
driver = "virtio"
291-
}
286+
// ide driver is not supported on arm64
287+
if qOps.TargetArch == "arm64" {
288+
driver = "virtio"
289+
}
292290

293-
if i < len(legacyOps.extraDisksDrivers) {
294-
driver = legacyOps.extraDisksDrivers[i]
295-
}
291+
if i < len(legacyOps.extraDisksDrivers) {
292+
driver = legacyOps.extraDisksDrivers[i]
293+
}
296294

297-
if i < len(legacyOps.extraDisksTags) {
298-
if legacyOps.extraDisksTags[i] != "" {
299-
tag = fmt.Sprintf(":tag=%s", legacyOps.extraDisksTags[i])
300-
}
295+
if i < len(legacyOps.extraDisksTags) {
296+
if legacyOps.extraDisksTags[i] != "" {
297+
tag = fmt.Sprintf(":tag=%s", legacyOps.extraDisksTags[i])
301298
}
299+
}
302300

303-
if i < len(legacyOps.extraDisksSerials) {
304-
if legacyOps.extraDisksSerials[i] != "" {
305-
serial = fmt.Sprintf(":serial=%s", legacyOps.extraDisksSerials[i])
306-
}
301+
if i < len(legacyOps.extraDisksSerials) {
302+
if legacyOps.extraDisksSerials[i] != "" {
303+
serial = fmt.Sprintf(":serial=%s", legacyOps.extraDisksSerials[i])
307304
}
308-
309-
fmt.Fprintf(&disks, ",%s:%d%s%s", driver, legacyOps.extraDiskSize, tag, serial)
310305
}
311306

312-
qOps.Disks = flags.Disks{}
307+
fmt.Fprintf(&disks, ",%s:%d%s%s", driver, legacyOps.extraDiskSize, tag, serial)
308+
}
313309

314-
if err := qOps.Disks.Set(disks.String()); err != nil {
315-
return err
316-
}
310+
qOps.Disks = flags.Disks{}
311+
312+
if err := qOps.Disks.Set(disks.String()); err != nil {
313+
return err
314+
}
317315

318-
return createDevCluster(ctx, cOps, qOps)
319-
})
316+
return createDevCluster(cmd.Context(), cOps, qOps)
320317
},
321318
}
322319
createCmd.Flags().IntVar(&legacyOps.clusterDiskSize, clusterDiskSizeFlag, 6*1024, "default limit on disk size in MB (each VM)")

0 commit comments

Comments
 (0)