in run.go [24:169]
func run(baseDir string, c *ec2macosinit.InitConfig) {
c.Log.Info("Fetching instance ID from IMDS...")
// An instance ID from IMDS is a prerequisite for run() to be able to check instance history
err := SetupInstanceID(c)
if err != nil {
c.Log.Fatalf(computeExitCode(c, 1), "Unable to get instance ID: %s", err)
}
c.Log.Infof("Running on instance %s", c.IMDS.InstanceID)
// Mark start time
startTime := time.Now()
// Read init config
c.Log.Info("Reading init config...")
err = c.ReadConfig(filepath.Join(baseDir, paths.InitTOML))
if err != nil {
c.Log.Fatalf(computeExitCode(c, 66), "Error while reading init config file: %s", err)
}
c.Log.Info("Successfully read init config")
// Validate init config and identify modules
c.Log.Info("Validating config...")
err = c.ValidateAndIdentify()
if err != nil {
c.Log.Fatalf(computeExitCode(c, 65), "Error found during init config validation: %s", err)
}
c.Log.Info("Successfully validated config")
// Prioritize modules
c.Log.Info("Prioritizing modules...")
err = c.PrioritizeModules()
if err != nil {
c.Log.Fatalf(computeExitCode(c, 1), "Error preparing and identifying modules: %s", err)
}
c.Log.Info("Successfully prioritized modules")
// Create instance history directories
c.Log.Info("Creating instance history directories for current instance...")
err = c.CreateDirectories()
if err != nil {
c.Log.Fatalf(computeExitCode(c, 73), "Error creating instance history directories: %s", err)
}
c.Log.Info("Successfully created directories")
// Read instance run history
c.Log.Info("Getting instance history...")
err = c.GetInstanceHistory()
if err != nil {
var herr ec2macosinit.HistoryError
// If GetInstanceHistory() returns a HistoryError, there was invalid JSON in the history file
// Catch this specific error to inform the user of the error and provide a way to remediate it.
if errors.As(err, &herr) {
c.Log.Warn("There was an error getting instance history")
c.Log.Info("The history JSON files might be invalid and need to be restored or removed.")
c.Log.Info("Run 'sudo ec2-macos-init clean' to remove all history files.")
}
c.Log.Fatalf(computeExitCode(c, 1), "Error getting instance history: %s", err)
}
c.Log.Info("Successfully gathered instance history")
// Process each module by priority level
var aggregateFatal bool
var aggFatalModuleName string
for i := 0; i < len(c.ModulesByPriority); i++ {
c.Log.Infof("Processing priority level %d (%d modules)...\n", i+1, len(c.ModulesByPriority[i]))
wg := sync.WaitGroup{}
// Start every module within the priority level group
for j := 0; j < len(c.ModulesByPriority[i]); j++ {
wg.Add(1)
go func(m *ec2macosinit.Module, h *[]ec2macosinit.History) {
// Run module if it should be run
if m.ShouldRun(c.IMDS.InstanceID, *h) {
c.Log.Infof("Running module [%s] (type: %s, group: %d)\n", m.Name, m.Type, m.PriorityGroup)
ctx := &ec2macosinit.ModuleContext{
Logger: c.Log,
IMDS: &c.IMDS,
BaseDirectory: baseDir,
}
// Run appropriate module
var message string
var err error
switch t := m.Type; t {
case "command":
message, err = m.CommandModule.Do(ctx)
case "motd":
message, err = m.MOTDModule.Do(ctx)
case "sshkeys":
message, err = m.SSHKeysModule.Do(ctx)
case "userdata":
message, err = m.UserDataModule.Do(ctx)
case "networkcheck":
message, err = m.NetworkCheckModule.Do(ctx)
case "systemconfig":
message, err = m.SystemConfigModule.Do(ctx)
case "usermanagement":
message, err = m.UserManagementModule.Do(ctx)
default:
message = "unknown module type"
err = fmt.Errorf("unknown module type")
}
if err != nil {
c.Log.Infof("Error while running module [%s] (type: %s, group: %d) with message: %s and err: %s\n", m.Name, m.Type, m.PriorityGroup, message, err)
if m.FatalOnError {
aggregateFatal = true
aggFatalModuleName = m.Name
}
} else {
// Module was successfully completed
m.Success = true
c.Log.Infof("Successfully completed module [%s] (type: %s, group: %d) with message: %s\n", m.Name, m.Type, m.PriorityGroup, message)
}
} else {
// In the case that we choose not to run a module, it is because the module has already succeeded
// in a prior run. For this reason, we need to pass through the success of the module to history.
m.Success = true
c.Log.Infof("Skipping module [%s] (type: %s, group: %d) due to Run type setting\n", m.Name, m.Type, m.PriorityGroup)
}
wg.Done()
}(&c.ModulesByPriority[i][j], &c.InstanceHistory)
}
wg.Wait()
c.Log.Infof("Successfully completed processing of priority level %d\n", i+1)
// If any module failed which had FatalOnError set, trigger an aggregate fail
if aggregateFatal {
break
}
}
// Write history file
c.Log.Infof("Writing instance history for instance %s...", c.IMDS.InstanceID)
err = c.WriteHistoryFile()
if err != nil {
c.Log.Fatalf(computeExitCode(c, 73), "Error writing instance history file: %s", err)
}
c.Log.Info("Successfully wrote instance history")
// If any module triggered an aggregate fatal, exit 1
if aggregateFatal {
c.Log.Fatalf(computeExitCode(c, 1), "Exiting after %s due to failure in module [%s] with FatalOnError set", time.Since(startTime).String(), aggFatalModuleName)
}
// Log completion and total run time
c.Log.Infof("EC2 macOS Init completed in %s", time.Since(startTime).String())
}