diff --git a/internal/exec/stages/files/units.go b/internal/exec/stages/files/units.go index d984c1c3f4..f66856f1a9 100644 --- a/internal/exec/stages/files/units.go +++ b/internal/exec/stages/files/units.go @@ -17,37 +17,66 @@ package files import ( "fmt" "path/filepath" + "strconv" "strings" + "github.com/coreos/go-systemd/dbus" "github.com/coreos/ignition/v2/config/v3_1_experimental/types" "github.com/coreos/ignition/v2/internal/distro" "github.com/coreos/ignition/v2/internal/exec/util" ) +var ( + enabledUnits []types.Unit + disabledUnits []types.Unit + isEnableUnit bool = false + isDisableUnit bool = false +) + // createUnits creates the units listed under systemd.units. func (s *stage) createUnits(config types.Config) error { - enabledOneUnit := false + var systemdVersion string + conn, err := dbus.New() + if err != nil { + return err + } + version, err := conn.GetManagerProperty("Version") + if err != nil { + return err + } + //for FCOS, the version of systemd is printed like 'v243.5-1.fc31' + if strings.HasPrefix(version, "v") { + arr := strings.Split(version, ".") + firsthalf := arr[0] + val := firsthalf[2:] + systemdVersion = val + } else { + //for RHCOS, the version of systemd is printed like '239' + systemdVersion = version[1 : len(version)-1] + } + if value, err := strconv.Atoi(systemdVersion); err == nil { + if value < 240 { + if err := s.Logger.Warning("The version of systemd is less than 240, Enabling/disabling instantiated units may not work. See https://github.com/coreos/ignition/issues/586 for more information."); err != nil { + return err + } + } + } else { + return err + } + for _, unit := range config.Systemd.Units { if err := s.writeSystemdUnit(unit, false); err != nil { return err } if unit.Enabled != nil { if *unit.Enabled { - if err := s.Logger.LogOp( - func() error { return s.EnableUnit(unit) }, - "enabling unit %q", unit.Name, - ); err != nil { - return err - } + enabledUnits = append(enabledUnits, unit) + isEnableUnit = true + } else { - if err := s.Logger.LogOp( - func() error { return s.DisableUnit(unit) }, - "disabling unit %q", unit.Name, - ); err != nil { - return err - } + disabledUnits = append(disabledUnits, unit) + isDisableUnit = true } - enabledOneUnit = true } if unit.Mask != nil && *unit.Mask { relabelpath := "" @@ -64,10 +93,79 @@ func (s *stage) createUnits(config types.Config) error { s.relabel(relabelpath) } } - // and relabel the preset file itself if we enabled/disabled something - if enabledOneUnit { - s.relabel(util.PresetPath) + if err := s.createSystemdUnits(config); err != nil { + return err + } + return nil +} + +// createSystemdUnits creates the enabled and disabled units listed under +// systemd units. This function is written to support instantiated +// units to be enabled via presets.For more information: https://github.com/systemd/systemd/pull/9901/files +func (s *stage) createSystemdUnits(config types.Config) error { + var instantiatedEnableServices []string + if isEnableUnit { + var enabledServices = make(map[string]string) + for _, enableUnit := range enabledUnits { + if strings.Contains(enableUnit.Name, "@") { + var enableInstanceAppend []string + at := strings.Index(enableUnit.Name, "@") + dot := strings.Index(enableUnit.Name, ".") + instance := enableUnit.Name[at+1 : dot] + serviceInstance := enableUnit.Name[0:at+1] + enableUnit.Name[dot:len(enableUnit.Name)] + if val, ok := enabledServices[serviceInstance]; ok { + enableInstanceAppend = append(enableInstanceAppend, val, instance) + enabledServices[serviceInstance] = strings.Join(enableInstanceAppend, " ") + } else { + enableInstanceAppend = append(enableInstanceAppend, instance) + enabledServices[serviceInstance] = strings.Join(enableInstanceAppend, " ") + } + + } else { + if err := s.Logger.LogOp( + func() error { return s.EnableUnit(enableUnit.Name) }, + "enabling unit %q", enableUnit.Name); err != nil { + return err + } + + } + } + + if len(enabledServices) > 0 { + for key, value := range enabledServices { + instantiatedEnableServices = append(instantiatedEnableServices, key+" "+value) + } + if err := s.Logger.LogOp( + func() error { return s.EnableUnit(strings.Join(instantiatedEnableServices, " ")) }, + "enabling instantiated units %q", strings.Join(instantiatedEnableServices, " "), + ); err != nil { + return err + } + } + } + + if isDisableUnit { + for _, disableUnit := range disabledUnits { + var disableSystemdUnit string + if strings.Contains(disableUnit.Name, "@") { + at := strings.Index(disableUnit.Name, "@") + dot := strings.Index(disableUnit.Name, ".") + instance := disableUnit.Name[at+1 : dot] + serviceInstance := disableUnit.Name[0:at+1] + disableUnit.Name[dot:len(disableUnit.Name)] + disableSystemdUnit = serviceInstance + " " + instance + } else { + disableSystemdUnit = disableUnit.Name + } + if err := s.Logger.LogOp( + func() error { return s.DisableUnit(disableSystemdUnit) }, + "disabling unit %q", disableSystemdUnit, + ); err != nil { + return err + } + + } } + s.relabel(util.PresetPath) return nil } diff --git a/internal/exec/util/unit.go b/internal/exec/util/unit.go index e9c7bc73dc..a59b81661e 100644 --- a/internal/exec/util/unit.go +++ b/internal/exec/util/unit.go @@ -110,8 +110,8 @@ func (ut Util) MaskUnit(unit types.Unit) (string, error) { return filepath.Join("/", SystemdUnitsPath(), unit.Name), nil } -func (ut Util) EnableUnit(unit types.Unit) error { - return ut.appendLineToPreset(fmt.Sprintf("enable %s", unit.Name)) +func (ut Util) EnableUnit(enabledUnits string) error { + return ut.appendLineToPreset(fmt.Sprintf("enable %s", enabledUnits)) } // presets link in /etc, which doesn't make sense for runtime units @@ -145,8 +145,8 @@ func (ut Util) EnableRuntimeUnit(unit types.Unit, target string) error { return ut.WriteLink(link) } -func (ut Util) DisableUnit(unit types.Unit) error { - return ut.appendLineToPreset(fmt.Sprintf("disable %s", unit.Name)) +func (ut Util) DisableUnit(disabledUnit string) error { + return ut.appendLineToPreset(fmt.Sprintf("disable %s", disabledUnit)) } func (ut Util) appendLineToPreset(data string) error {