lib/ec2macosinit/config.go (69 lines of code) (raw):
package ec2macosinit
import (
"fmt"
"os"
"github.com/BurntSushi/toml"
)
// InitConfig contains all fields expected from an init.toml file as well as things shared by all parts
// of the application.
type InitConfig struct {
HistoryFilename string
HistoryPath string
IMDS IMDSConfig
InstanceHistory []History
Log *Logger
Modules []Module `toml:"Module"`
ModulesByPriority [][]Module
FatalCounts FatalCount
}
// Number of runs resulting in fatal exits in a single boot before giving up
const PerBootFatalLimit = 100
// ReadConfig reads the configuration file and decodes it into the InitConfig struct.
func (c *InitConfig) ReadConfig(fileLocation string) (err error) {
// Read file
rawConfig, err := os.ReadFile(fileLocation)
if err != nil {
return fmt.Errorf("ec2macosinit: error reading config file located at %s: %s\n", fileLocation, err)
}
// Decode from TOML to InitConfig struct
_, err = toml.Decode(string(rawConfig), c)
if err != nil {
return fmt.Errorf("ec2macosinit: error decoding config: %s\n", err)
}
return nil
}
// ValidateConfig validates all modules and identifies type.
func (c *InitConfig) ValidateAndIdentify() (err error) {
// Create keySet to store used keys
keySet := map[string]struct{}{}
// Loop through every module and check a few things...
for i := 0; i < len(c.Modules); i++ {
// Identify module type
err := c.Modules[i].identifyModule()
if err != nil {
return fmt.Errorf("ec2macosinit: error while identifying module: %s\n", err)
}
// Validate individual module
err = c.Modules[i].validateModule()
if err != nil {
return fmt.Errorf("ec2macosinit: error found in module (type: %s, priority: %d): %s\n", c.Modules[i].Type, c.Modules[i].PriorityGroup, err)
}
// Check that key name is unique for the current configuration
if _, ok := keySet[c.Modules[i].Name]; !ok {
// Key hasn't been used yet - add key to the set
keySet[c.Modules[i].Name] = struct{}{}
} else {
return fmt.Errorf("ec2macosinit: duplicate name found in config:%s\n", c.Modules[i].Name)
}
}
return nil
}
// PrepareModules takes all modules and sorts them according to priority into the ModulesByPriority slice.
func (c *InitConfig) PrioritizeModules() (err error) {
for _, m := range c.Modules {
// Expand capacity of ModulesByPriority, as needed
for m.PriorityGroup > cap(c.ModulesByPriority) {
c.ModulesByPriority = append(c.ModulesByPriority, []Module{})
}
// If needed, expand ModulesByPriority to needed length
if m.PriorityGroup > len(c.ModulesByPriority) {
c.ModulesByPriority = c.ModulesByPriority[:m.PriorityGroup]
}
// Append module at correct priority level
c.ModulesByPriority[m.PriorityGroup-1] = append(c.ModulesByPriority[m.PriorityGroup-1], m)
}
return nil
}
// RetriesExceeded checks if the number of previous fatal exits exceeds the limit.
func (c *InitConfig) RetriesExceeded() (exceeded bool, err error) {
// Check for the existence of the temporary file and get the current fatal count
err = c.FatalCounts.readFatalCount()
if err != nil {
return false, fmt.Errorf("ec2macosinit: unable to read fatal counts: %s", err)
}
// If there have been more than the limit of fatal exits, return true
if c.FatalCounts.Count > PerBootFatalLimit {
return true, nil
}
// Otherwise, continue
return false, nil
}