github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/cmd/jujud/reboot/rebootshim.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package reboot
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  
    13  	"github.com/juju/juju/agent"
    14  	"github.com/juju/juju/container"
    15  	"github.com/juju/juju/container/factory"
    16  	"github.com/juju/juju/core/instance"
    17  	"github.com/juju/juju/rpc/params"
    18  	"github.com/juju/juju/service"
    19  )
    20  
    21  // rebootWaiterShim wraps the functions required by RebootWaiter
    22  // to facilitate mock testing.
    23  type rebootWaiterShim struct {
    24  }
    25  
    26  // ListServices returns a list of names of services running
    27  // on the current host.
    28  func (r rebootWaiterShim) ListServices() ([]string, error) {
    29  	return service.ListServices()
    30  }
    31  
    32  // NewServiceReference returns a new juju service object.
    33  func (r rebootWaiterShim) NewServiceReference(name string) (Service, error) {
    34  	return service.NewServiceReference(name)
    35  }
    36  
    37  // NewContainerManager return an object implementing Manager.
    38  func (r rebootWaiterShim) NewContainerManager(containerType instance.ContainerType, conf container.ManagerConfig) (Manager, error) {
    39  	return factory.NewContainerManager(containerType, conf)
    40  }
    41  
    42  // ScheduleAction schedules the reboot action based on the
    43  // current operating system.
    44  func (r rebootWaiterShim) ScheduleAction(action params.RebootAction, after int) error {
    45  	return scheduleAction(action, after)
    46  }
    47  
    48  // agentConfigShim wraps the method required by a Model in
    49  // the RebootWaiter.
    50  type agentConfigShim struct {
    51  	aCfg agent.Config
    52  }
    53  
    54  // Model return an object implementing Model.
    55  func (a *agentConfigShim) Model() Model {
    56  	return a.aCfg.Model()
    57  }
    58  
    59  // TODO (tlm): This code has been moved across in the move to 3.0 removing
    60  // Windows. However there are a number of things that can be fixed here for some
    61  // easy wins.
    62  // - Don't write out a script file. It introduces another failure point that we
    63  // don't need to take on. We can just run the commands directly from the
    64  // interpreter
    65  //
    66  // If we do decided to keep the script file:
    67  // - Don't set the executable bit as we are giving the file directly to the
    68  // interpreter.
    69  // - Align the shabang line and the interpreter we use.
    70  
    71  // scheduleAction will do a reboot or shutdown after given number of seconds
    72  // this function executes the operating system's reboot binary with appropriate
    73  // parameters to schedule the reboot
    74  // If action is params.ShouldDoNothing, it will return immediately.
    75  func scheduleAction(action params.RebootAction, after int) error {
    76  	if action == params.ShouldDoNothing {
    77  		return nil
    78  	}
    79  	args := []string{"shutdown"}
    80  	switch action {
    81  	case params.ShouldReboot:
    82  		args = append(args, "-r")
    83  	case params.ShouldShutdown:
    84  		args = append(args, "-h")
    85  	}
    86  	args = append(args, "now")
    87  
    88  	script, err := writeScript(args, after)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	// Use the "nohup" command to run the reboot script without blocking.
    93  	scheduled := []string{
    94  		"nohup",
    95  		"sh",
    96  		script,
    97  		"&",
    98  	}
    99  	return runCommand(scheduled)
   100  }
   101  
   102  func writeScript(args []string, after int) (string, error) {
   103  	tpl := `#!/bin/bash
   104  sleep %d
   105  %s`
   106  	script := fmt.Sprintf(tpl, after, strings.Join(args, " "))
   107  
   108  	f, err := tmpFile()
   109  	if err != nil {
   110  		return "", errors.Trace(err)
   111  	}
   112  	defer func() { _ = f.Close() }()
   113  
   114  	_, err = f.WriteString(script)
   115  	if err != nil {
   116  		return "", errors.Trace(err)
   117  	}
   118  	name := f.Name()
   119  	err = os.Chmod(name, 0755)
   120  	if err != nil {
   121  		return "", errors.Trace(err)
   122  	}
   123  	return name, nil
   124  }