Files
nomad/command/windows_service_uninstall.go
Chris Roberts c3dcdb5413 [cli] Add windows service commands (#26442)
Adds a new `windows` command which is available when running on
a Windows hosts. The command includes two new subcommands:

* `service install`
* `service uninstall`

The `service install` command will install the called binary into
the Windows program files directory, create a new Windows service,
setup configuration and data directories, and register the service
with the Window eventlog. If the service and/or binary already
exist, the service will be stopped, service and eventlog updated
if needed, binary replaced, and the service started again.

The `service uninstall` command will stop the service, remove the
Windows service, and deregister the service with the eventlog. It
will not remove the configuration/data directory nor will it remove
the installed binary.
2025-09-02 16:40:35 -07:00

122 lines
3.1 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package command
import (
"fmt"
"strings"
"github.com/hashicorp/nomad/helper/winsvc"
"github.com/posener/complete"
)
type WindowsServiceUninstallCommand struct {
Meta
serviceManagerFn func() (winsvc.WindowsServiceManager, error)
privilegedCheckFn func() bool
}
func (c *WindowsServiceUninstallCommand) AutoCompleteFlags() complete.Flags {
return c.Meta.AutocompleteFlags(FlagSetDefault)
}
func (c *WindowsServiceUninstallCommand) Synopsis() string {
return "Uninstall the nomad Windows system service"
}
func (c *WindowsServiceUninstallCommand) Name() string { return "windows service uninstall" }
func (c *WindowsServiceUninstallCommand) Help() string {
helpText := `
Usage: nomad windows service uninstall [options]
This command uninstalls nomad as a Windows system service.
General Options:
` + generalOptionsUsage(usageOptsDefault)
return strings.TrimSpace(helpText)
}
func (c *WindowsServiceUninstallCommand) Run(args []string) int {
flags := c.Meta.FlagSet(c.Name(), FlagSetDefault)
flags.Usage = func() { c.Ui.Output(c.Help()) }
if err := flags.Parse(args); err != nil {
return 1
}
if args = flags.Args(); len(args) > 0 {
c.Ui.Error("This command takes no arguments")
c.Ui.Error(commandErrorText(c))
return 1
}
// Set helper functions to default if unset
if c.serviceManagerFn == nil {
c.serviceManagerFn = winsvc.NewWindowsServiceManager
}
if c.privilegedCheckFn == nil {
c.privilegedCheckFn = winsvc.IsPrivilegedProcess
}
// Check that command is being run with elevated permissions
if !c.privilegedCheckFn() {
c.Ui.Error("Service uninstall must be run with Administator privileges")
return 1
}
c.Ui.Output("Uninstalling nomad Windows service...")
m, err := c.serviceManagerFn()
if err != nil {
c.Ui.Error(fmt.Sprintf("Could not connect to Windows service manager - %s", err))
return 1
}
defer m.Close()
if err := c.performUninstall(m); err != nil {
c.Ui.Error(fmt.Sprintf("Service uninstall failed: %s", err))
return 1
}
c.Ui.Info("Successfully uninstalled nomad Windows service")
return 0
}
func (c *WindowsServiceUninstallCommand) performUninstall(m winsvc.WindowsServiceManager) error {
// Check that the nomad service is currently registered
exists, err := m.IsServiceRegistered(winsvc.WINDOWS_SERVICE_NAME)
if err != nil {
return fmt.Errorf("unable to check for existing service - %w", err)
}
if !exists {
return nil
}
// Grab the service and ensure the service is stopped
srvc, err := m.GetService(winsvc.WINDOWS_SERVICE_NAME)
if err != nil {
return fmt.Errorf("could not get existing service - %w", err)
}
defer srvc.Close()
if err := srvc.Stop(); err != nil {
return fmt.Errorf("unable to stop service - %w", err)
}
// Remove the service from the event log
if err := srvc.DisableEventlog(); err != nil {
return fmt.Errorf("could not remove eventlog configuration - %w", err)
}
// Finally, delete the service
if err := srvc.Delete(); err != nil {
return fmt.Errorf("could not delete service - %w", err)
}
return nil
}