Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow machines reset #489

Merged
merged 51 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
3f62b7f
Always update elemental-system-agent config
anmazzotti Jul 24, 2023
ee82914
Create reset secret on machine inventory deletion
anmazzotti Jul 24, 2023
f23c9a2
Patch plan secret with reset plan instead of recreating it
anmazzotti Jul 26, 2023
0fa6e2b
Implement elemental-register --reset
anmazzotti Jul 26, 2023
5436781
Implement elemental-register --reset
anmazzotti Jul 26, 2023
fbdeda0
Implement --reset and --install elemental-register arguments
anmazzotti Jul 27, 2023
7e2604e
Add resettable annotation on MachineInventories
anmazzotti Jul 27, 2023
c0ba8fb
Remove now redundant test
anmazzotti Jul 27, 2023
1a18379
Update reset plan
anmazzotti Jul 27, 2023
59ba3e6
Update crds
anmazzotti Jul 27, 2023
8b8798f
Use mInventory name and namespace to determine elemental-system-agent…
anmazzotti Jul 27, 2023
da296fe
Fix incorrect handling of registration state within a live environment
anmazzotti Jul 28, 2023
420f57a
Make the linter happy
anmazzotti Jul 28, 2023
40bdf1d
Fix local reset plan location and schema
anmazzotti Jul 28, 2023
48ab712
Add debug logging to --reset
anmazzotti Jul 28, 2023
571f6d7
Typo fix
anmazzotti Jul 28, 2023
1437d51
Revert "Typo fix"
anmazzotti Jul 28, 2023
6831234
Revert "Add debug logging to --reset"
anmazzotti Jul 28, 2023
0da25fe
Revert "Fix local reset plan location and schema"
anmazzotti Jul 28, 2023
65a9ca9
Fix shutdown command arguments
anmazzotti Jul 28, 2023
c1cb54b
Blank previos plan status
anmazzotti Jul 31, 2023
339445d
Apply Finalizer on MachineInventory
anmazzotti Jul 31, 2023
3cc844e
Reset mInventory plan status on new plan created
anmazzotti Jul 31, 2023
6ddf7f7
Fix reset of status plan
anmazzotti Jul 31, 2023
acf2cc5
Requeue after adding finalizer
anmazzotti Jul 31, 2023
fb4da6d
Fix incorrect yaml encoder usage
anmazzotti Jul 31, 2023
225f4ef
Upgrade yaml encoder version
anmazzotti Jul 31, 2023
bdacd9a
Explicitly cleanup local reset plan after execution
anmazzotti Jul 31, 2023
7c98c53
Actually, cleanup the plan before execution, as reboot may occur
anmazzotti Jul 31, 2023
80255d7
Revert "Actually, cleanup the plan before execution, as reboot may oc…
anmazzotti Jul 31, 2023
fddc931
Persist registration config on registration update
anmazzotti Jul 31, 2023
42606ed
Add debug logging
anmazzotti Jul 31, 2023
7d9d449
Fix status plan not being reset correctly. Add more verbosity to elem…
anmazzotti Aug 1, 2023
8c5dc9c
Register using new state when resetting
anmazzotti Aug 2, 2023
4b2e7a9
Do not update Minventory directly in inner reconcile loop
anmazzotti Aug 2, 2023
407c28f
Fix error message
anmazzotti Aug 2, 2023
df94576
Add finalizer in test MInventory
anmazzotti Aug 2, 2023
a1c4a51
Add elemental installer test coverage
anmazzotti Aug 2, 2023
a19bebb
Add remove reset plan test
anmazzotti Aug 2, 2023
216a485
Add finalizer handling tests
anmazzotti Aug 2, 2023
f66ad7c
Improve test coverage
anmazzotti Aug 2, 2023
0c85332
Remove debug printfs
anmazzotti Aug 2, 2023
b8a3920
Check for nil delete timestamp pointer
anmazzotti Aug 3, 2023
51ea23e
Do not try to recover from missing secret plan
anmazzotti Aug 3, 2023
541b918
Do not refetch plan secret twice
anmazzotti Aug 3, 2023
7e207aa
Rename reset cloud config file to more appropriate name
anmazzotti Aug 3, 2023
284a538
Use default paths on installation
anmazzotti Aug 3, 2023
ee4489b
Return after install or reset for safety
anmazzotti Aug 3, 2023
a60afd0
Drop unused constant
anmazzotti Aug 3, 2023
3bcf68b
Merge branch 'main' into allow-machines-reset
anmazzotti Aug 3, 2023
b00495b
Add verify generate and copyright to mock files
anmazzotti Aug 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions api/v1beta1/machineinventory_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ import (
)

var (
MachineInventoryFinalizer = "machineinventory.elemental.cattle.io"
PlanSecretType corev1.SecretType = "elemental.cattle.io/plan"
MachineInventoryFinalizer = "machineinventory.elemental.cattle.io"
PlanSecretType corev1.SecretType = "elemental.cattle.io/plan"
PlanTypeAnnotation = "elemental.cattle.io/plan.type"
PlanTypeEmpty = "empty"
PlanTypeBootstrap = "bootstrap"
PlanTypeReset = "reset"
MachineInventoryResettableAnnotation = "elemental.cattle.io/resettable"
)

type MachineInventorySpec struct {
Expand Down
24 changes: 24 additions & 0 deletions api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,28 @@ type Install struct {
ConfigDir string `json:"config-dir,omitempty" yaml:"config-dir,omitempty"`
}

type Reset struct {
// +optional
Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty" mapstructure:"enabled"`
// +optional
// +kubebuilder:default:=true
ResetPersistent bool `json:"reset-persistent,omitempty" yaml:"reset-persistent,omitempty" mapstructure:"reset-persistent"`
// +optional
// +kubebuilder:default:=true
ResetOEM bool `json:"reset-oem,omitempty" yaml:"reset-oem,omitempty" mapstructure:"reset-oem"`
// +optional
ConfigURLs []string `json:"config-urls,omitempty" yaml:"config-urls,omitempty" mapstructure:"config-urls"`
// +optional
SystemURI string `json:"system-uri,omitempty" yaml:"system-uri,omitempty" mapstructure:"system-uri"`
// +optional
Debug bool `json:"debug,omitempty" yaml:"debug,omitempty" mapstructure:"debug"`
// +optional
PowerOff bool `json:"poweroff,omitempty" yaml:"poweroff,omitempty" mapstructure:"poweroff"`
// +optional
// +kubebuilder:default:=true
Reboot bool `json:"reboot,omitempty" yaml:"reboot,omitempty" mapstructure:"reboot"`
}

type Registration struct {
// +optional
URL string `json:"url,omitempty" yaml:"url,omitempty" mapstructure:"url"`
Expand Down Expand Up @@ -80,6 +102,8 @@ type Elemental struct {
// +optional
Install Install `json:"install,omitempty" yaml:"install,omitempty"`
// +optional
Reset Reset `json:"reset,omitempty" yaml:"reset,omitempty"`
// +optional
Registration Registration `json:"registration,omitempty" yaml:"registration,omitempty"`
// +optional
SystemAgent SystemAgent `json:"system-agent,omitempty" yaml:"system-agent,omitempty"`
Expand Down
21 changes: 21 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions charts/crds/templates/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,30 @@ spec:
url:
type: string
type: object
reset:
properties:
config-urls:
items:
type: string
type: array
debug:
type: boolean
enabled:
type: boolean
poweroff:
type: boolean
reboot:
default: true
type: boolean
reset-oem:
default: true
type: boolean
reset-persistent:
default: true
type: boolean
system-uri:
type: string
type: object
system-agent:
properties:
secret-name:
Expand Down
97 changes: 64 additions & 33 deletions cmd/register/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,19 @@ import (

const (
defaultStatePath = "/oem/registration/state.yaml"
defaultLiveStatePath = "/tmp/registration/state.yaml"
defaultConfigPath = "/oem/registration/config.yaml"
defaultLiveConfigPath = "/run/initramfs/live/livecd-cloud-config.yaml"
registrationUpdateSuppressTimer = 24 * time.Hour
)

var (
cfg elementalv1.Config
debug bool
configPath string
statePath string
cfg elementalv1.Config
debug bool
reset bool
installation bool
configPath string
statePath string
)

var (
Expand All @@ -56,14 +59,18 @@ func main() {
fs := vfs.OSFS
installer := install.NewInstaller(fs)
stateHandler := register.NewFileStateHandler(fs)
client := register.NewClient(stateHandler)
client := register.NewClient()
cmd := newCommand(fs, client, stateHandler, installer)
if err := cmd.Execute(); err != nil {
log.Fatalf("FATAL: %s", err)
}
}

func newCommand(fs vfs.FS, client register.Client, stateHandler register.StateHandler, installer install.Installer) *cobra.Command {
// Reset config and viper cache
cfg = elementalv1.Config{}
viper.Reset()
// Define command (using closures)
cmd := &cobra.Command{
Use: "elemental-register",
Short: "Elemental register command",
Expand All @@ -78,13 +85,16 @@ func newCommand(fs vfs.FS, client register.Client, stateHandler register.StateHa
if err := initConfig(fs); err != nil {
return fmt.Errorf("initializing configuration: %w", err)
}
// Determine if registration should execute or skip a cycle
// Load Registration State
if err := stateHandler.Init(statePath); err != nil {
return fmt.Errorf("initializing state handler on path '%s': %w", statePath, err)
}
if skip, err := shouldSkipRegistration(stateHandler, installer); err != nil {
return fmt.Errorf("determining if registration should run: %w", err)
} else if skip {
registrationState, err := getRegistrationState(stateHandler, reset)
if err != nil {
return fmt.Errorf("getting registration state: %w", err)
}
// Determine if registration should execute or skip a cycle
if !installation && !reset && !registrationState.HasLastUpdateElapsed(registrationUpdateSuppressTimer) {
log.Info("Nothing to do")
return nil
}
Expand All @@ -93,25 +103,40 @@ func newCommand(fs vfs.FS, client register.Client, stateHandler register.StateHa
if err != nil {
return fmt.Errorf("validating CA: %w", err)
}
// Register
data, err := client.Register(cfg.Elemental.Registration, caCert)
// Register (and fetch the remote MachineRegistration)
data, err := client.Register(cfg.Elemental.Registration, caCert, &registrationState)
if err != nil {
return fmt.Errorf("registering machine: %w", err)
}
if err := stateHandler.Save(registrationState); err != nil {
return fmt.Errorf("saving registration state: %w", err)
}
// Validate remote config
log.Debugf("Fetched configuration from manager cluster:\n%s\n\n", string(data))
if err := yaml.Unmarshal(data, &cfg); err != nil {
return fmt.Errorf("parsing returned configuration: %w", err)
}
// Install
if !installer.IsSystemInstalled() {
log.Info("Installing Elemental")
return installer.InstallElemental(cfg)
if installation {
log.Info("Installing elemental")
if err := installer.InstallElemental(cfg, registrationState); err != nil {
return fmt.Errorf("installing elemental: %w", err)
}
return nil
}
// Reset
if reset {
log.Info("Resetting Elemental")
if err := installer.ResetElemental(cfg, registrationState); err != nil {
return fmt.Errorf("resetting elemental: %w", err)
}
return nil
}

return nil
},
}
//Define flags
//Define and bind flags
cmd.Flags().StringVar(&cfg.Elemental.Registration.URL, "registration-url", "", "Registration url to get the machine config from")
_ = viper.BindPFlag("elemental.registration.url", cmd.Flags().Lookup("registration-url"))
cmd.Flags().StringVar(&cfg.Elemental.Registration.CACert, "registration-ca-cert", "", "File with the custom CA certificate to use against he registration url")
Expand All @@ -125,23 +150,40 @@ func newCommand(fs vfs.FS, client register.Client, stateHandler register.StateHa
cmd.Flags().StringVar(&cfg.Elemental.Registration.Auth, "auth", "tpm", "Registration authentication method")
_ = viper.BindPFlag("elemental.registration.auth", cmd.Flags().Lookup("auth"))
cmd.Flags().BoolVarP(&debug, "debug", "d", false, "Enable debug logging")
if installer.IsSystemInstalled() {
cmd.Flags().StringVar(&configPath, "config-path", defaultConfigPath, "The full path of the elemental-register config")
} else {
cmd.Flags().StringVar(&configPath, "config-path", defaultLiveConfigPath, "The full path of the elemental-register config")
}
cmd.Flags().StringVar(&configPath, "config-path", defaultConfigPath, "The full path of the elemental-register config")
cmd.Flags().StringVar(&statePath, "state-path", defaultStatePath, "The full path of the elemental-register config")
cmd.PersistentFlags().BoolP("version", "v", false, "print version and exit")
_ = viper.BindPFlag("version", cmd.PersistentFlags().Lookup("version"))
cmd.Flags().BoolVar(&reset, "reset", false, "Reset the machine to its original post-installation state")
cmd.Flags().BoolVar(&installation, "install", false, "Install a new machine")
return cmd
}

func getRegistrationState(stateHandler register.StateHandler, reset bool) (register.State, error) {
// If we are resetting, we create an empty state to perform an initial registration.
if reset {
return register.State{}, nil
}
registrationState, err := stateHandler.Load()
if err != nil {
return register.State{}, fmt.Errorf("loading registration state: %w", err)
}
return registrationState, nil
}

func initConfig(fs vfs.FS) error {
log.Infof("Register version %s, commit %s, commit date %s", version.Version, version.Commit, version.CommitDate)
if installation && reset {
return errors.New("--install and --reset flags are mutually exclusive")
}
if debug {
log.EnableDebugLogging()
}
log.Infof("Register version %s, commit %s, commit date %s", version.Version, version.Commit, version.CommitDate)

// If we are installing from a live environment, the default config path must be updated
if installation {
configPath = defaultLiveConfigPath
statePath = defaultLiveStatePath
}
// Use go-vfs afero compatibility layer (required by Viper)
afs := vfsafero.NewAferoFS(fs)
viper.SetFs(afs)
Expand All @@ -158,17 +200,6 @@ func initConfig(fs vfs.FS) error {
return nil
}

func shouldSkipRegistration(stateHandler register.StateHandler, installer install.Installer) (bool, error) {
if !installer.IsSystemInstalled() {
return false, nil
}
state, err := stateHandler.Load()
if err != nil {
return false, fmt.Errorf("loading registration state")
}
return !state.HasLastUpdateElapsed(registrationUpdateSuppressTimer), nil
}

func getRegistrationCA(fs vfs.FS, config elementalv1.Config) ([]byte, error) {
registration := config.Elemental.Registration

Expand Down
Loading