| 
									
										
										
										
											2022-07-18 22:02:49 +00:00
										 |  |  | package agent | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2022-09-23 17:20:23 +02:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2022-07-18 22:02:49 +00:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	"github.com/kairos-io/kairos-agent/v2/pkg/uki" | 
					
						
							|  |  |  | 	internalutils "github.com/kairos-io/kairos-agent/v2/pkg/utils" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2023-09-14 15:35:44 +03:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-10 14:39:48 +02:00
										 |  |  | 	hook "github.com/kairos-io/kairos-agent/v2/internal/agent/hooks" | 
					
						
							|  |  |  | 	"github.com/kairos-io/kairos-agent/v2/internal/bus" | 
					
						
							|  |  |  | 	"github.com/kairos-io/kairos-agent/v2/internal/cmd" | 
					
						
							|  |  |  | 	"github.com/kairos-io/kairos-agent/v2/pkg/action" | 
					
						
							|  |  |  | 	"github.com/kairos-io/kairos-agent/v2/pkg/config" | 
					
						
							| 
									
										
										
										
											2023-07-20 15:53:48 +02:00
										 |  |  | 	sdk "github.com/kairos-io/kairos-sdk/bus" | 
					
						
							|  |  |  | 	"github.com/kairos-io/kairos-sdk/collector" | 
					
						
							|  |  |  | 	"github.com/kairos-io/kairos-sdk/machine" | 
					
						
							|  |  |  | 	"github.com/kairos-io/kairos-sdk/utils" | 
					
						
							| 
									
										
										
										
											2022-09-23 17:20:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/mudler/go-pluggable" | 
					
						
							| 
									
										
										
										
											2022-07-18 22:02:49 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | func Reset(reboot, unattended, resetOem bool, dir ...string) error { | 
					
						
							|  |  |  | 	// In both cases we want | 
					
						
							|  |  |  | 	if internalutils.UkiBootMode() == internalutils.UkiHDD { | 
					
						
							|  |  |  | 		return resetUki(reboot, unattended, resetOem, dir...) | 
					
						
							|  |  |  | 	} else if internalutils.UkiBootMode() == internalutils.UkiRemovableMedia { | 
					
						
							|  |  |  | 		return fmt.Errorf("reset is not supported on removable media, please run reset from the installed system recovery entry") | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return reset(reboot, unattended, resetOem, dir...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func reset(reboot, unattended, resetOem bool, dir ...string) error { | 
					
						
							|  |  |  | 	cfg, err := sharedReset(reboot, unattended, resetOem, dir...) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-10-15 10:47:09 +02:00
										 |  |  | 	err = cfg.CheckForUsers() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	// Load the installation Config from the cloud-config data | 
					
						
							|  |  |  | 	resetSpec, err := config.ReadResetSpecFromConfig(cfg) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = resetSpec.Sanitize() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resetAction := action.NewResetAction(cfg, resetSpec) | 
					
						
							|  |  |  | 	if err = resetAction.Run(); err != nil { | 
					
						
							|  |  |  | 		cfg.Logger.Errorf("failed to reset: %s", err) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bus.Manager.Publish(sdk.EventAfterReset, sdk.EventPayload{}) //nolint:errcheck | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-24 16:05:39 +01:00
										 |  |  | 	return hook.Run(*cfg, resetSpec, hook.FinishReset...) | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func resetUki(reboot, unattended, resetOem bool, dir ...string) error { | 
					
						
							|  |  |  | 	cfg, err := sharedReset(reboot, unattended, resetOem, dir...) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-10-15 10:47:09 +02:00
										 |  |  | 	err = cfg.CheckForUsers() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	// Load the installation Config from the cloud-config data | 
					
						
							|  |  |  | 	resetSpec, err := config.ReadUkiResetSpecFromConfig(cfg) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = resetSpec.Sanitize() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resetAction := uki.NewResetAction(cfg, resetSpec) | 
					
						
							|  |  |  | 	if err = resetAction.Run(); err != nil { | 
					
						
							|  |  |  | 		cfg.Logger.Errorf("failed to reset uki: %s", err) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bus.Manager.Publish(sdk.EventAfterReset, sdk.EventPayload{}) //nolint:errcheck | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-24 16:05:39 +01:00
										 |  |  | 	return hook.Run(*cfg, resetSpec, hook.FinishReset...) | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // sharedReset is the common reset code for both uki and non-uki | 
					
						
							|  |  |  | // sets the config, runs the event handler, publish the envent and gets the config | 
					
						
							|  |  |  | func sharedReset(reboot, unattended, resetOem bool, dir ...string) (c *config.Config, err error) { | 
					
						
							| 
									
										
										
										
											2022-09-23 17:20:23 +02:00
										 |  |  | 	bus.Manager.Initialize() | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	var optionsFromEvent map[string]string | 
					
						
							| 
									
										
										
										
											2023-09-14 15:35:44 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-21 10:37:53 +02:00
										 |  |  | 	// This config is only for reset branding. | 
					
						
							| 
									
										
										
										
											2022-07-18 22:02:49 +00:00
										 |  |  | 	agentConfig, err := LoadConfig() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 		return c, err | 
					
						
							| 
									
										
										
										
											2022-07-18 22:02:49 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-04 20:39:22 +02:00
										 |  |  | 	if !unattended { | 
					
						
							|  |  |  | 		cmd.PrintBranding(DefaultBanner) | 
					
						
							|  |  |  | 		cmd.PrintText(agentConfig.Branding.Reset, "Reset") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// We don't close the lock, as none of the following actions are expected to return | 
					
						
							|  |  |  | 		lock := sync.Mutex{} | 
					
						
							|  |  |  | 		go func() { | 
					
						
							|  |  |  | 			// Wait for user input and go back to shell | 
					
						
							|  |  |  | 			utils.Prompt("") //nolint:errcheck | 
					
						
							|  |  |  | 			// give tty1 back | 
					
						
							|  |  |  | 			svc, err := machine.Getty(1) | 
					
						
							|  |  |  | 			if err == nil { | 
					
						
							|  |  |  | 				svc.Start() //nolint:errcheck | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			lock.Lock() | 
					
						
							|  |  |  | 			fmt.Println("Reset aborted") | 
					
						
							|  |  |  | 			panic(utils.Shell().Run()) | 
					
						
							|  |  |  | 		}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if !agentConfig.Fast { | 
					
						
							|  |  |  | 			time.Sleep(60 * time.Second) | 
					
						
							| 
									
										
										
										
											2022-07-18 22:02:49 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		lock.Lock() | 
					
						
							| 
									
										
										
										
											2022-09-30 17:23:10 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-09-23 17:20:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-17 17:27:37 +02:00
										 |  |  | 	ensureDataSourceReady() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-21 10:37:53 +02:00
										 |  |  | 	// This gets the options from an event that can be sent by anyone. | 
					
						
							|  |  |  | 	// This should override the default config as it's much more dynamic | 
					
						
							|  |  |  | 	bus.Manager.Response(sdk.EventBeforeReset, func(p *pluggable.Plugin, r *pluggable.EventResponse) { | 
					
						
							|  |  |  | 		err := json.Unmarshal([]byte(r.Data), &optionsFromEvent) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			fmt.Println(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-23 17:20:23 +02:00
										 |  |  | 	bus.Manager.Publish(sdk.EventBeforeReset, sdk.EventPayload{}) //nolint:errcheck | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	// Prepare a config from the cli flags | 
					
						
							|  |  |  | 	r := ExtraConfigReset{} | 
					
						
							|  |  |  | 	r.Reset.ResetOem = resetOem | 
					
						
							| 
									
										
										
										
											2022-10-24 16:43:23 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	if resetOem { | 
					
						
							|  |  |  | 		r.Reset.ResetOem = true | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-24 16:43:23 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	if reboot { | 
					
						
							|  |  |  | 		r.Reset.Reboot = true | 
					
						
							| 
									
										
										
										
											2023-05-05 18:43:21 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-07-20 15:53:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	// Override the config with the event options | 
					
						
							| 
									
										
										
										
											2023-07-21 10:37:53 +02:00
										 |  |  | 	// Go over the possible options sent via event | 
					
						
							|  |  |  | 	if len(optionsFromEvent) > 0 { | 
					
						
							|  |  |  | 		if p := optionsFromEvent["reset-persistent"]; p != "" { | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 			r.Reset.ResetPersistent = p == "true" | 
					
						
							| 
									
										
										
										
											2023-07-21 10:37:53 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if o := optionsFromEvent["reset-oem"]; o != "" { | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 			r.Reset.ResetOem = o == "true" | 
					
						
							| 
									
										
										
										
											2023-07-21 10:37:53 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-05-05 18:43:21 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	d, err := json.Marshal(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		c.Logger.Errorf("failed to marshal reset cmdline flags/event options: %s", err) | 
					
						
							|  |  |  | 		return c, err | 
					
						
							| 
									
										
										
										
											2023-08-04 20:39:22 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	cliConf := string(d) | 
					
						
							| 
									
										
										
										
											2023-08-04 20:39:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	// cliconf goes last so it can override the rest of the config files | 
					
						
							|  |  |  | 	c, err = config.Scan(collector.Directories(dir...), collector.Readers(strings.NewReader(cliConf))) | 
					
						
							| 
									
										
										
										
											2023-09-28 13:04:03 +03:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 		return c, err | 
					
						
							| 
									
										
										
										
											2023-09-28 13:04:03 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	// Set strict validation from the event | 
					
						
							|  |  |  | 	if len(optionsFromEvent) > 0 { | 
					
						
							|  |  |  | 		if s := optionsFromEvent["strict"]; s != "" { | 
					
						
							|  |  |  | 			c.Strict = s == "true" | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-07-18 22:02:49 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | 	utils.SetEnv(c.Env) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return c, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-09-23 17:20:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 13:20:06 +01:00
										 |  |  | // ExtraConfigReset is the struct that holds the reset options that come from flags and events | 
					
						
							|  |  |  | type ExtraConfigReset struct { | 
					
						
							|  |  |  | 	Reset struct { | 
					
						
							|  |  |  | 		ResetOem        bool `json:"reset-oem,omitempty"` | 
					
						
							|  |  |  | 		ResetPersistent bool `json:"reset-persistent,omitempty"` | 
					
						
							|  |  |  | 		Reboot          bool `json:"reboot,omitempty"` | 
					
						
							|  |  |  | 	} `json:"reset"` | 
					
						
							| 
									
										
										
										
											2022-07-18 22:02:49 +00:00
										 |  |  | } |