Skip to content

Commit

Permalink
Refactors WriteSystemdAgents for clarity and cleans up its usage in
Browse files Browse the repository at this point in the history
upgrade steps and series upgrade.
  • Loading branch information
manadart committed Jan 24, 2020
1 parent 4db2f6f commit de990d9
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 145 deletions.
127 changes: 70 additions & 57 deletions service/agentconf.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ type SystemdServiceManager interface {
StartAllAgents(machineAgent string, unitAgents []string, dataDir string) (string, []string, error)

// WriteServiceFiles writes the service files for machine and unit agents
// in the '/lib/systemd/system' path.
// in the /etc/systemd/system path.
WriteServiceFiles() error
}

Expand Down Expand Up @@ -93,7 +93,7 @@ func NewServiceManager(
}

// WriteServiceFiles writes service files to the standard
// "/lib/systemd/system" path.
// /etc/systemd/system path.
func (s *systemdServiceManager) WriteServiceFiles() error {
machineAgent, unitAgents, _, err := s.FindAgents(paths.NixDataDir)
if err != nil {
Expand Down Expand Up @@ -136,7 +136,7 @@ func (s *systemdServiceManager) FindAgents(dataDir string) (string, []string, []
if err != nil {
return "", nil, nil, errors.Annotate(err, "opening agents dir")
}
defer dir.Close()
defer func() { _ = dir.Close() }()

entries, err := dir.Readdir(-1)
if err != nil {
Expand Down Expand Up @@ -164,79 +164,92 @@ func (s *systemdServiceManager) FindAgents(dataDir string) (string, []string, []
// WriteSystemdAgents creates systemd files and symlinks for the input machine
// and unit agents, in the standard filepath '/var/lib/juju'.
func (s *systemdServiceManager) WriteSystemdAgents(
machineAgent string, unitAgents []string, dataDir, symLinkSystemdMultiUserDir string,
machineAgent string, unitAgents []string, dataDir, systemdMultiUserDir string,
) ([]string, []string, []string, error) {
var (
startedSysServiceNames []string
startedSymServiceNames []string
errAgentNames []string
lastError error
autoLinkedServiceNames []string
manualLinkedServiceNames []string
errAgentNames []string
lastError error
)

for _, agentName := range append(unitAgents, machineAgent) {
conf, err := s.CreateAgentConf(agentName, dataDir)
systemdLinked, err := s.writeSystemdAgent(agentName, dataDir, systemdMultiUserDir)
if err != nil {
logger.Infof("%s", err)
logger.Errorf("service creation failed for %s: %s", agentName, err.Error())
errAgentNames = append(errAgentNames, agentName)
lastError = err
continue
}

svcName := serviceName(agentName)
svc, err := s.newService(svcName, conf)
if err != nil {
logger.Infof("Failed to create new service %s: ", err)
if systemdLinked {
autoLinkedServiceNames = append(autoLinkedServiceNames, serviceName(agentName))
continue
}
manualLinkedServiceNames = append(manualLinkedServiceNames, serviceName(agentName))
}
return autoLinkedServiceNames, manualLinkedServiceNames, errAgentNames, lastError
}

uSvc, ok := svc.(UpgradableService)
if !ok {
return nil, nil, nil, errors.Errorf("%s service not of type UpgradableService", svcName)
}
// WriteSystemdAgents creates systemd files and symlinks for the input
// agentName.
// The boolean return indicates whether systemd automatically linked the file
// into the multi-user-target directory.
func (s *systemdServiceManager) writeSystemdAgent(agentName, dataDir, systemdMultiUserDir string) (bool, error) {
conf, err := s.CreateAgentConf(agentName, dataDir)
if err != nil {
return false, errors.Trace(err)
}

dbusMethodFound := true
if err = uSvc.WriteService(); err != nil {
// Note that this error is already logged by the systemd package.

// This is not ideal, but it is possible on an Upstart-based OS
// (such as Trusty) for run/systemd/system to exist, which is used
// for detection of systemd as the running init system.
// If this happens, then D-Bus will error with the message below.
// We need to detect this condition and fall through to linking the
// service files manually.
if strings.Contains(strings.ToLower(err.Error()), "no such method") {
dbusMethodFound = false
logger.Infof("attempting to manually link service file for %s", agentName)
} else {
errAgentNames = append(errAgentNames, agentName)
lastError = err
continue
}
} else {
logger.Infof("successfully wrote service for %s:", agentName)
}
svcName := serviceName(agentName)
svc, err := s.newService(svcName, conf)
if err != nil {
return false, errors.Annotate(err, "creating new service")
}

// If systemd is the running init system on this host, *and* if the
// call to DBusAPI.LinkUnitFiles in WriteService above returned no
// error, it will have resulted in updated sym-links for the file.
// We are done.
if s.isRunning() && dbusMethodFound {
startedSysServiceNames = append(startedSysServiceNames, svcName)
logger.Infof("wrote %s agent, enabled and linked by systemd", svcName)
continue
}
uSvc, ok := svc.(UpgradableService)
if !ok {
return false, errors.New("service not of type UpgradableService")
}

// Otherwise we need to manually ensure the service unit links.
svcFileName := svcName + ".service"
if err = os.Symlink(path.Join(systemd.EtcSystemdDir, svcFileName),
path.Join(symLinkSystemdMultiUserDir, svcFileName)); err != nil && !os.IsExist(err) {
return nil, nil, nil, errors.Errorf(
"failed to link service file (%s) in multi-user.target.wants dir: %s\n", svcFileName, err)
dbusMethodFound := true
if err = uSvc.WriteService(); err != nil {
// Note that this error is already logged by the systemd package.

// This is not ideal, but it is possible on an Upstart-based OS
// (such as Trusty) for run/systemd/system to exist, which is used
// for detection of systemd as the running init system.
// If this happens, then D-Bus will error with the message below.
// We need to detect this condition and fall through to linking the
// service files manually.
if !strings.Contains(strings.ToLower(err.Error()), "no such method") {
return false, errors.Trace(err)
} else {
dbusMethodFound = false
logger.Infof("attempting to manually link service file for %s", agentName)
}
} else {
logger.Infof("successfully wrote service for %s:", agentName)
}

// If systemd is the running init system on this host, *and* if the
// call to DBusAPI.LinkUnitFiles in WriteService above returned no
// error, it will have resulted in updated sym-links for the file.
// We are done.
if s.isRunning() && dbusMethodFound {
logger.Infof("wrote %s agent, enabled and linked by systemd", svcName)
return true, nil
}

startedSymServiceNames = append(startedSymServiceNames, svcName)
logger.Infof("wrote %s agent, enabled and linked by symlink", svcName)
// Otherwise we need to manually ensure the service unit links.
svcFileName := svcName + ".service"
if err = os.Symlink(path.Join(systemd.EtcSystemdDir, svcFileName),
path.Join(systemdMultiUserDir, svcFileName)); err != nil && !os.IsExist(err) {
return false, errors.Annotatef(err, "linking service file (%s) in multi-user.target.wants dir", svcFileName)
}
return startedSysServiceNames, startedSymServiceNames, errAgentNames, lastError

logger.Infof("wrote %s agent, enabled and linked by symlink", svcName)
return false, nil
}

// CreateAgentConf creates the configfile for specified agent running on a host with specified series.
Expand Down
48 changes: 24 additions & 24 deletions upgrades/steps_24.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,38 +65,38 @@ func stepsFor24() []Step {
&upgradeStep{
description: "Install the service file in Standard location '/lib/systemd'",
targets: []Target{AllMachines},
run: installServiceFile,
run: writeServiceFiles(true),
},
}
}

// install the service files in Standard location - '/lib/systemd/system path.
func installServiceFile(context Context) error {
hostSeries, err := series.HostSeries()
if err == nil {
// writeServiceFiles writes service files into the default systemd search path.
// The supplied boolean indicates whether the old
// /var/lib/init files should be removed.
func writeServiceFiles(cleanupOld bool) func(Context) error {
return func(ctx Context) error {
hostSeries, err := series.HostSeries()
if err != nil {
return errors.Trace(err)
}

initName, err := service.VersionInitSystem(hostSeries)
if err != nil {
logger.Errorf("unsuccessful writing the service files in /lib/systemd/system path")
return err
} else {
if initName == service.InitSystemSystemd {
oldDataDir := context.AgentConfig().DataDir()
oldInitDataDir := filepath.Join(oldDataDir, "init")
return errors.Annotate(err, "writing systemd service files")
}

sysdManager := service.NewServiceManagerWithDefaults()
err = sysdManager.WriteServiceFiles()
if err != nil {
logger.Errorf("unsuccessful writing the service files in /lib/systemd/system path")
return err
}
// Cleanup the old dir - /var/lib/init
return os.RemoveAll(oldInitDataDir)
} else {
logger.Infof("upgrade to systemd possible only for 'xenial' and above")
return nil
if initName == service.InitSystemSystemd {
if err := service.NewServiceManagerWithDefaults().WriteServiceFiles(); err != nil {
return errors.Annotate(err, "writing systemd service files")
}

if cleanupOld {
return errors.Trace(os.RemoveAll(filepath.Join(ctx.AgentConfig().DataDir(), "init")))
}
return nil
}
} else {
return errors.Trace(err)

logger.Infof("skipping upgrade for non systemd series %s", hostSeries)
return nil
}
}
32 changes: 1 addition & 31 deletions upgrades/steps_245.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,13 @@

package upgrades

import (
"github.com/juju/utils/series"

"github.com/juju/juju/service"
)

// stepsFor245 returns upgrade steps for Juju 2.4.5
func stepsFor245() []Step {
return []Step{
&upgradeStep{
description: "update exec.start.sh log path if incorrect",
targets: []Target{AllMachines},
run: correctServiceFileLogPath,
run: writeServiceFiles(false),
},
}
}

// install the service files in Standard location - '/lib/systemd/system path.
func correctServiceFileLogPath(context Context) error {
hostSeries, err := series.HostSeries()
if err != nil {
logger.Errorf("getting host series: %e", err)
}
initName, err := service.VersionInitSystem(hostSeries)
if err != nil {
logger.Errorf("unsuccessful checking init script for correct log path: %e", err)
return err
}
if initName != service.InitSystemSystemd {
return nil
}
// rewrite files to correct errors in previous upgrade step
sysdManager := service.NewServiceManagerWithDefaults()
err = sysdManager.WriteServiceFiles()
if err != nil {
logger.Errorf("rewriting service file: %e", err)
return err
}
return nil
}
36 changes: 3 additions & 33 deletions upgrades/steps_27.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ import (
"path/filepath"

"github.com/juju/errors"
"github.com/juju/utils/series"
"gopkg.in/juju/names.v3"

"github.com/juju/juju/agent"
k8sprovider "github.com/juju/juju/caas/kubernetes/provider"
"github.com/juju/juju/core/paths"
"github.com/juju/juju/service"
)

// stateStepsFor27 returns upgrade steps for Juju 2.7.0.
Expand Down Expand Up @@ -150,38 +148,10 @@ func resetLogPermissions(context Context) error {
logger.Infof("skipping agent %q, is CAAS", k8sprovider.CAASProviderType)
return nil
}
isSystemd, err := getCurrentInit()
if err != nil {
return errors.Trace(err)
}
if !isSystemd {
logger.Infof("skipping update of log file ownership as host not using systemd")
return nil
}
sysdManager := service.NewServiceManagerWithDefaults()
if err = sysdManager.WriteServiceFiles(); err != nil {
return errors.Trace(err)
}
logDir := context.AgentConfig().LogDir()
if err = setJujuFolderPermissionsToAdm(logDir); err != nil {

if err := writeServiceFiles(false)(context); err != nil {
return errors.Trace(err)
}
logger.Infof("Successfully wrote service files in /lib/systemd/system path")
return nil
}

func getCurrentInit() (bool, error) {
hostSeries, err := series.HostSeries()
if err != nil {
return false, errors.Trace(err)
}
initName, err := service.VersionInitSystem(hostSeries)
if err != nil {
return false, errors.Trace(err)
}
if initName == service.InitSystemSystemd {
return true, nil
} else {
return false, nil
}
return errors.Trace(setJujuFolderPermissionsToAdm(context.AgentConfig().LogDir()))
}

0 comments on commit de990d9

Please sign in to comment.