summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunYi Wu <wu65830600@163.com>2023-08-23 14:26:20 +0800
committerwujunyi <wu65830600@163.com>2023-10-27 00:31:32 +0800
commitec2758b585bd086655641318a508fdd4b367cd3c (patch)
treec7aaf85fbf60b699b002a9e30ecf79523dccf2ee
parentMerge pull request #5114 from Shelley-BaoYue/master (diff)
downloadkubeedge-ec2758b585bd086655641318a508fdd4b367cd3c.tar.gz
feat: using nssm install edgecore as windows service
Signed-off-by: JunYi Wu <wu65830600@163.com>
-rw-r--r--keadm/cmd/keadm/app/cmd/cmd_linux.go (renamed from keadm/cmd/keadm/app/cmd/cmd.go)2
-rw-r--r--keadm/cmd/keadm/app/cmd/cmd_windows.go75
-rw-r--r--keadm/cmd/keadm/app/cmd/common/constant.go3
-rw-r--r--keadm/cmd/keadm/app/cmd/common/constant_others.go24
-rw-r--r--keadm/cmd/keadm/app/cmd/common/constant_windows.go24
-rw-r--r--keadm/cmd/keadm/app/cmd/edge/join.go105
-rw-r--r--keadm/cmd/keadm/app/cmd/util/common.go17
-rw-r--r--keadm/cmd/keadm/app/cmd/util/common_others.go35
-rw-r--r--keadm/cmd/keadm/app/cmd/util/common_windows.go35
-rw-r--r--keadm/cmd/keadm/app/cmd/util/exec.go6
-rw-r--r--keadm/cmd/keadm/app/cmd/util/nssm.go99
-rw-r--r--keadm/cmd/keadm/app/cmd/util/nssm_test.go11
12 files changed, 397 insertions, 39 deletions
diff --git a/keadm/cmd/keadm/app/cmd/cmd.go b/keadm/cmd/keadm/app/cmd/cmd_linux.go
index b42c464ae..71c699d52 100644
--- a/keadm/cmd/keadm/app/cmd/cmd.go
+++ b/keadm/cmd/keadm/app/cmd/cmd_linux.go
@@ -1,3 +1,5 @@
+//go:build !windows
+
/*
Copyright 2019 The KubeEdge Authors.
diff --git a/keadm/cmd/keadm/app/cmd/cmd_windows.go b/keadm/cmd/keadm/app/cmd/cmd_windows.go
new file mode 100644
index 000000000..2f65c24c9
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/cmd_windows.go
@@ -0,0 +1,75 @@
+//go:build windows
+
+/*
+Copyright 2019 The KubeEdge Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+
+ "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/edge"
+)
+
+var (
+ keadmLongDescription = `
+ +----------------------------------------------------------+
+ | KEADM |
+ | Easily bootstrap a KubeEdge cluster |
+ | |
+ | Please give us feedback at: |
+ | https://github.com/kubeedge/kubeedge/issues |
+ +----------------------------------------------------------+
+
+ Create a cluster with cloud node
+ (which controls the edge node cluster), and edge nodes
+ (where native containerized application, in the form of
+ pods and deployments run), connects to devices.
+
+`
+ keadmExample = `
+ +-----------------------------------------------------------------+
+ | On the edge machine(current dont support cloudcore on windows): | |
+ +-----------------------------------------------------------------+
+ | worker node (at the edge)# keadm join <flags> |
+ +-----------------------------------------------------------------+
+
+ You can then repeat the second step on, as many other machines as you like.
+`
+)
+
+// NewKubeedgeCommand returns cobra.Command to run keadm commands
+func NewKubeedgeCommand() *cobra.Command {
+ cmds := &cobra.Command{
+ Use: "keadm",
+ Short: "keadm: Bootstrap KubeEdge cluster",
+ Long: keadmLongDescription,
+ Example: keadmExample,
+ }
+
+ cmds.ResetFlags()
+
+ cmds.AddCommand(NewCmdVersion())
+
+ // recommended cmds
+ cmds.AddCommand(edge.NewEdgeJoin())
+ cmds.AddCommand(NewKubeEdgeReset())
+
+ // beta cmds
+ //cmds.AddCommand(edge.NewEdgeUpgrade())
+
+ return cmds
+}
diff --git a/keadm/cmd/keadm/app/cmd/common/constant.go b/keadm/cmd/keadm/app/cmd/common/constant.go
index c1af48223..1b5bdff77 100644
--- a/keadm/cmd/keadm/app/cmd/common/constant.go
+++ b/keadm/cmd/keadm/app/cmd/common/constant.go
@@ -41,9 +41,6 @@ const (
// CertPath sets the path of the certificates generated by the KubeEdge Cloud component
CertPath = "certPath"
- // DefaultCertPath is the default certificate path in edge node
- DefaultCertPath = "/etc/kubeedge/certs"
-
// DefaultK8SMinimumVersion is the minimum version of K8S
DefaultK8SMinimumVersion = 11
diff --git a/keadm/cmd/keadm/app/cmd/common/constant_others.go b/keadm/cmd/keadm/app/cmd/common/constant_others.go
new file mode 100644
index 000000000..b575de420
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/common/constant_others.go
@@ -0,0 +1,24 @@
+//go:build !windows
+
+/*
+Copyright 2019 The KubeEdge Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package common
+
+const (
+ // DefaultCertPath is the default certificate path in edge node
+ DefaultCertPath = "/etc/kubeedge/certs"
+)
diff --git a/keadm/cmd/keadm/app/cmd/common/constant_windows.go b/keadm/cmd/keadm/app/cmd/common/constant_windows.go
new file mode 100644
index 000000000..f9abb2391
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/common/constant_windows.go
@@ -0,0 +1,24 @@
+//go:build windows
+
+/*
+Copyright 2019 The KubeEdge Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package common
+
+const (
+ // DefaultCertPath is the default certificate path in edge node
+ DefaultCertPath = "c:/etc/kubeedge/certs"
+)
diff --git a/keadm/cmd/keadm/app/cmd/edge/join.go b/keadm/cmd/keadm/app/cmd/edge/join.go
index 0f246e2eb..a575bb12e 100644
--- a/keadm/cmd/keadm/app/cmd/edge/join.go
+++ b/keadm/cmd/keadm/app/cmd/edge/join.go
@@ -22,6 +22,7 @@ import (
"net"
"os"
"path/filepath"
+ "runtime"
"strconv"
"strings"
"time"
@@ -43,6 +44,8 @@ import (
pkgutil "github.com/kubeedge/kubeedge/pkg/util"
)
+const isWindows = runtime.GOOS == "windows"
+
var (
edgeJoinDescription = `
"keadm join" command bootstraps KubeEdge's worker node (at the edge) component.
@@ -117,12 +120,15 @@ func NewEdgeJoin() *cobra.Command {
}
func AddJoinOtherFlags(cmd *cobra.Command, joinOptions *common.JoinOptions) {
+
cmd.Flags().StringVar(&joinOptions.KubeEdgeVersion, common.KubeEdgeVersion, joinOptions.KubeEdgeVersion,
"Use this key to download and use the required KubeEdge version")
cmd.Flags().Lookup(common.KubeEdgeVersion).NoOptDefVal = joinOptions.KubeEdgeVersion
- cmd.Flags().StringVar(&joinOptions.CGroupDriver, common.CGroupDriver, joinOptions.CGroupDriver,
- "CGroupDriver that uses to manipulate cgroups on the host (cgroupfs or systemd), the default value is cgroupfs")
+ if !isWindows {
+ cmd.Flags().StringVar(&joinOptions.CGroupDriver, common.CGroupDriver, joinOptions.CGroupDriver,
+ "CGroupDriver that uses to manipulate cgroups on the host (cgroupfs or systemd), the default value is cgroupfs")
+ }
cmd.Flags().StringVar(&joinOptions.CertPath, common.CertPath, joinOptions.CertPath,
fmt.Sprintf("The certPath used by edgecore, the default value is %s", common.DefaultCertPath))
@@ -149,18 +155,24 @@ func AddJoinOtherFlags(cmd *cobra.Command, joinOptions *common.JoinOptions) {
cmd.Flags().StringVarP(&joinOptions.CertPort, common.CertPort, "s", joinOptions.CertPort,
"The port where to apply for the edge certificate")
- cmd.Flags().StringVar(&joinOptions.TarballPath, common.TarballPath, joinOptions.TarballPath,
- "Use this key to set the temp directory path for KubeEdge tarball, if not exist, download it")
+ if !isWindows {
+ cmd.Flags().StringVar(&joinOptions.TarballPath, common.TarballPath, joinOptions.TarballPath,
+ "Use this key to set the temp directory path for KubeEdge tarball, if not exist, download it")
+ }
cmd.Flags().StringSliceVarP(&joinOptions.Labels, common.Labels, "l", joinOptions.Labels,
`Use this key to set the customized labels for node, you can input customized labels like key1=value1,key2=value2`)
- cmd.Flags().BoolVar(&joinOptions.WithMQTT, "with-mqtt", joinOptions.WithMQTT,
- `Use this key to set whether to install and start MQTT Broker by default`)
+ if !isWindows {
+ cmd.Flags().BoolVar(&joinOptions.WithMQTT, "with-mqtt", joinOptions.WithMQTT,
+ `Use this key to set whether to install and start MQTT Broker by default`)
+ }
- cmd.Flags().StringVar(&joinOptions.ImageRepository, common.ImageRepository, joinOptions.ImageRepository,
- `Use this key to decide which image repository to pull images from`,
- )
+ if !isWindows {
+ cmd.Flags().StringVar(&joinOptions.ImageRepository, common.ImageRepository, joinOptions.ImageRepository,
+ `Use this key to decide which image repository to pull images from`,
+ )
+ }
}
func newOption() *common.JoinOptions {
@@ -179,15 +191,40 @@ func join(opt *common.JoinOptions, step *common.Step) error {
return err
}
- // Do not create any files in the management directory,
- // you need to mount the contents of the mirror first.
- if err := request(opt, step); err != nil {
- return err
- }
+ // current dont support install binary from windows container
+ // and we cant use systemd in windows
+ if !isWindows {
+ // Do not create any files in the management directory,
+ // you need to mount the contents of the mirror first.
+ if err := request(opt, step); err != nil {
+ return err
+ }
+ step.Printf("Generate systemd service file")
+ if err := common.GenerateServiceFile(util.KubeEdgeBinaryName, filepath.Join(util.KubeEdgeUsrBinPath, util.KubeEdgeBinaryName), opt.WithMQTT); err != nil {
+ return fmt.Errorf("create systemd service file failed: %v", err)
+ }
+ } else {
+ if err := prepareWindowsNssm(step); err != nil {
+ return fmt.Errorf("prepare windows nssm service fail: %v", err)
+ }
+
+ step.Printf("Check edge bin")
+ // check if the binary download successfully manual
+ if !util.FileExists(filepath.Join(util.KubeEdgeUsrBinPath, util.KubeEdgeBinaryName)) {
+ return fmt.Errorf("cannot find edgecore binary at %s, you should download it manual from github release and put edgecore.exe under %s", filepath.Join(util.KubeEdgeUsrBinPath, util.KubeEdgeBinaryName), util.KubeEdgeUsrBinPath)
+ }
- step.Printf("Generate systemd service file")
- if err := common.GenerateServiceFile(util.KubeEdgeBinaryName, filepath.Join(util.KubeEdgeUsrBinPath, util.KubeEdgeBinaryName), opt.WithMQTT); err != nil {
- return fmt.Errorf("create systemd service file failed: %v", err)
+ step.Printf("Install service")
+ if err := util.InstallNSSMService(util.KubeEdgeBinaryName, filepath.Join(util.KubeEdgeUsrBinPath, util.KubeEdgeBinaryName), "--config", filepath.Join(util.KubeEdgePath, "config/edgecore.yaml")); err != nil {
+ return fmt.Errorf("install edgecore useing nssm fail: %v", err)
+ }
+
+ if err := util.SetNSSMServiceStdout(util.KubeEdgeBinaryName, filepath.Join(util.KubeEdgeLogPath, "out.log")); err != nil {
+ return fmt.Errorf("setting edgecore stdout log using nssm fail: %v", err)
+ }
+ if err := util.SetNSSMServiceStdout(util.KubeEdgeBinaryName, filepath.Join(util.KubeEdgeLogPath, "err.log")); err != nil {
+ return fmt.Errorf("setting edgecore stderr log using nssm fail: %v", err)
+ }
}
// write token to bootstrap configure file
@@ -251,6 +288,9 @@ func createEdgeConfigFiles(opt *common.JoinOptions) error {
return fmt.Errorf("parse kubeedge version failed, %v", err)
}
if v.Major <= 1 && v.Minor < 12 {
+ if isWindows {
+ return errors.New("edgecore for windows dont support version earlier than v1.12.0")
+ }
return createV1alpha1EdgeConfigFiles(opt)
}
if v.Major >= 1 && v.Minor >= 14 && opt.RuntimeType != constants.DefaultRuntimeType {
@@ -288,15 +328,16 @@ func createEdgeConfigFiles(opt *common.JoinOptions) error {
edgeCoreConfig.Modules.Edged.ContainerRuntime = opt.RuntimeType
}
- switch opt.CGroupDriver {
- case v1alpha2.CGroupDriverSystemd:
- edgeCoreConfig.Modules.Edged.TailoredKubeletConfig.CgroupDriver = v1alpha2.CGroupDriverSystemd
- case v1alpha2.CGroupDriverCGroupFS:
- edgeCoreConfig.Modules.Edged.TailoredKubeletConfig.CgroupDriver = v1alpha2.CGroupDriverCGroupFS
- default:
- return fmt.Errorf("unsupported CGroupDriver: %s", opt.CGroupDriver)
+ if !isWindows {
+ switch opt.CGroupDriver {
+ case v1alpha2.CGroupDriverSystemd:
+ edgeCoreConfig.Modules.Edged.TailoredKubeletConfig.CgroupDriver = v1alpha2.CGroupDriverSystemd
+ case v1alpha2.CGroupDriverCGroupFS:
+ edgeCoreConfig.Modules.Edged.TailoredKubeletConfig.CgroupDriver = v1alpha2.CGroupDriverCGroupFS
+ default:
+ return fmt.Errorf("unsupported CGroupDriver: %s", opt.CGroupDriver)
+ }
}
- edgeCoreConfig.Modules.Edged.TailoredKubeletConfig.CgroupDriver = opt.CGroupDriver
if opt.RemoteRuntimeEndpoint != "" {
edgeCoreConfig.Modules.Edged.RemoteRuntimeEndpoint = opt.RemoteRuntimeEndpoint
@@ -409,6 +450,10 @@ func setEdgedNodeLabels(opt *common.JoinOptions) map[string]string {
}
func runEdgeCore(withMqtt bool) error {
+ if runtime.GOOS == "windows" {
+ return util.StartNSSMService(util.KubeEdgeBinaryName)
+ }
+
systemdExist := util.HasSystemd()
var binExec, tip string
@@ -451,3 +496,13 @@ func createBootstrapFile(opt *common.JoinOptions) error {
token := []byte(opt.Token)
return os.WriteFile(bootstrapFile, token, 0640)
}
+
+func prepareWindowsNssm(step *common.Step) error {
+ step.Printf("Check if nssm installed")
+ if util.IsNSSMInstalled() {
+ return nil
+ }
+
+ step.Printf("Nssm not find, start install under $env:ProgramFiles\\nssm")
+ return util.InstallNSSM()
+}
diff --git a/keadm/cmd/keadm/app/cmd/util/common.go b/keadm/cmd/keadm/app/cmd/util/common.go
index 90888ef1c..f3c9c211c 100644
--- a/keadm/cmd/keadm/app/cmd/util/common.go
+++ b/keadm/cmd/keadm/app/cmd/util/common.go
@@ -55,10 +55,6 @@ const (
EdgeServiceFile = "edgecore.service"
CloudServiceFile = "cloudcore.service"
ServiceFileURLFormat = "https://raw.githubusercontent.com/kubeedge/kubeedge/release-%s/build/tools/%s"
- KubeEdgePath = "/etc/kubeedge/"
- KubeEdgeBackupPath = "/etc/kubeedge/backup/"
- KubeEdgeUpgradePath = "/etc/kubeedge/upgrade/"
- KubeEdgeUsrBinPath = "/usr/local/bin"
KubeEdgeBinaryName = "edgecore"
KeadmBinaryName = "keadm"
@@ -68,15 +64,8 @@ const (
KubeEdgeCloudCoreNewYaml = KubeEdgeConfigDir + "cloudcore.yaml"
KubeEdgeEdgeCoreNewYaml = KubeEdgeConfigDir + "edgecore.yaml"
- KubeEdgeLogPath = "/var/log/kubeedge/"
KubeEdgeCrdPath = KubeEdgePath + "crds"
- KubeEdgeSocketPath = "/var/lib/kubeedge/"
-
- EdgeRootDir = "/var/lib/edged"
-
- SystemdBootPath = "/run/systemd/system"
-
KubeEdgeCRDDownloadURL = "https://raw.githubusercontent.com/kubeedge/kubeedge/release-%s/build/crds"
latestReleaseVersionURL = "https://kubeedge.io/latestversion"
@@ -544,6 +533,12 @@ func KillKubeEdgeBinary(proc string) error {
// IsKubeEdgeProcessRunning checks if the given process is running or not
func IsKubeEdgeProcessRunning(proc string) (bool, error) {
+ if runtime.GOOS == "windows" {
+ if IsNSSMServiceExit(proc) {
+ return true, nil
+ }
+ return false, nil
+ }
procRunning := fmt.Sprintf("pidof %s 2>&1", proc)
cmd := NewCommand(procRunning)
diff --git a/keadm/cmd/keadm/app/cmd/util/common_others.go b/keadm/cmd/keadm/app/cmd/util/common_others.go
new file mode 100644
index 000000000..82e0ab4e7
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/util/common_others.go
@@ -0,0 +1,35 @@
+//go:build !windows
+
+/*
+Copyright 2019 The KubeEdge Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package util
+
+// Constants used by installers
+const (
+ KubeEdgePath = "/etc/kubeedge/"
+ KubeEdgeBackupPath = "/etc/kubeedge/backup/"
+ KubeEdgeUpgradePath = "/etc/kubeedge/upgrade/"
+ KubeEdgeUsrBinPath = "/usr/local/bin"
+
+ KubeEdgeLogPath = "/var/log/kubeedge/"
+
+ KubeEdgeSocketPath = "/var/lib/kubeedge/"
+
+ EdgeRootDir = "/var/lib/edged"
+
+ SystemdBootPath = "/run/systemd/system"
+)
diff --git a/keadm/cmd/keadm/app/cmd/util/common_windows.go b/keadm/cmd/keadm/app/cmd/util/common_windows.go
new file mode 100644
index 000000000..ec8fc711c
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/util/common_windows.go
@@ -0,0 +1,35 @@
+//go:build windows
+
+/*
+Copyright 2019 The KubeEdge Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package util
+
+// Constants used by installers
+const (
+ KubeEdgePath = "C:/etc/kubeedge/"
+ KubeEdgeBackupPath = "C:/etc/kubeedge/backup/"
+ KubeEdgeUpgradePath = "C:/etc/kubeedge/upgrade/"
+ KubeEdgeUsrBinPath = "C:/usr/local/bin"
+
+ KubeEdgeLogPath = "C:/var/log/kubeedge/"
+
+ KubeEdgeSocketPath = "C:/var/lib/kubeedge/"
+
+ EdgeRootDir = "C:/var/lib/edged"
+
+ SystemdBootPath = "C:/run/systemd/system"
+)
diff --git a/keadm/cmd/keadm/app/cmd/util/exec.go b/keadm/cmd/keadm/app/cmd/util/exec.go
index 718a2e648..820f41788 100644
--- a/keadm/cmd/keadm/app/cmd/util/exec.go
+++ b/keadm/cmd/keadm/app/cmd/util/exec.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os/exec"
+ "runtime"
"strings"
"syscall"
)
@@ -18,6 +19,11 @@ type Command struct {
}
func NewCommand(command string) *Command {
+ if runtime.GOOS == "windows" {
+ return &Command{
+ Cmd: exec.Command("powershell", "-c", command),
+ }
+ }
return &Command{
Cmd: exec.Command("bash", "-c", command),
}
diff --git a/keadm/cmd/keadm/app/cmd/util/nssm.go b/keadm/cmd/keadm/app/cmd/util/nssm.go
new file mode 100644
index 000000000..4f01d8c29
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/util/nssm.go
@@ -0,0 +1,99 @@
+package util
+
+import (
+ "fmt"
+ "strings"
+)
+
+const installNSSMScript = `
+function DownloadFile($destination, $source) {
+ Write-Host("Downloading $source to $destination")
+ curl.exe --silent --fail -Lo $destination $source
+
+ if (!$?) {
+ Write-Error "Download $source failed"
+ exit 1
+ }
+}
+
+$global:NssmInstallDirectory = "$env:ProgramFiles\nssm"
+
+[Environment]::SetEnvironmentVariable("Path", $env:Path, [System.EnvironmentVariableTarget]::Machine)
+
+Write-Host "Installing nssm"
+$arch = "win32"
+if ([Environment]::Is64BitOperatingSystem) {
+ $arch = "win64"
+}
+
+mkdir -Force $global:NssmInstallDirectory
+DownloadFile nssm.zip https://k8stestinfrabinaries.blob.core.windows.net/nssm-mirror/nssm-2.24.zip
+tar C $global:NssmInstallDirectory -xvf .\nssm.zip --strip-components 2 */$arch/*.exe
+Remove-Item -Force .\nssm.zip
+
+$env:path += ";$global:NssmInstallDirectory"
+$newPath = "$global:NssmInstallDirectory;" +
+ [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine)
+
+[Environment]::SetEnvironmentVariable("PATH", $newPath, [EnvironmentVariableTarget]::Machine)
+`
+
+func IsNSSMInstalled() bool {
+ cmd := NewCommand("nssm version")
+ err := cmd.Exec()
+ return err == nil
+}
+
+func InstallNSSM() error {
+ cmd := NewCommand(installNSSMScript)
+ err := cmd.Exec()
+ fmt.Println(cmd.GetStdOut())
+ return err
+}
+
+func InstallNSSMService(name, path string, args ...string) error {
+ cmd := NewCommand(fmt.Sprintf("nssm install '%s' %s %s", name, path, strings.Join(args, " ")))
+ return cmd.Exec()
+}
+
+func IsNSSMServiceExit(service string) bool {
+ cmd := NewCommand("nssm status " + service)
+ return cmd.Exec() == nil
+}
+
+func IsNSSMServiceRunning(service string) bool {
+ cmd := NewCommand("nssm status " + service)
+ _err := cmd.Exec()
+ if _err != nil || cmd.ExitCode > 0 {
+ return false
+ }
+ if cmd.GetStdOut() == "SERVICE_RUNNING" {
+ return true
+ }
+ return false
+}
+
+func StartNSSMService(service string) error {
+ cmd := NewCommand("nssm start " + service)
+ return cmd.Exec()
+}
+
+func StopNSSMService(service string) error {
+ cmd := NewCommand("nssm stop " + service)
+ return cmd.Exec()
+}
+
+func SetNSSMServiceStdout(service string, file string) error {
+ cmd := NewCommand(strings.Join([]string{"nssm", "set", service, "AppStdout", file}, " "))
+ return cmd.Exec()
+}
+
+func SetNSSMServiceStderr(service string, file string) error {
+ cmd := NewCommand(strings.Join([]string{"nssm", "set", service, "AppStderr", file}, " "))
+ return cmd.Exec()
+}
+
+func UninstallNSSMService(service string) error {
+ cmd := NewCommand(strings.Join([]string{"nssm", "remove", service, "confirm"}, " "))
+ return cmd.Exec()
+}
diff --git a/keadm/cmd/keadm/app/cmd/util/nssm_test.go b/keadm/cmd/keadm/app/cmd/util/nssm_test.go
new file mode 100644
index 000000000..e491f4b8d
--- /dev/null
+++ b/keadm/cmd/keadm/app/cmd/util/nssm_test.go
@@ -0,0 +1,11 @@
+package util
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestInstallNSSM(t *testing.T) {
+ err := InstallNSSM()
+ assert.NoError(t, err)
+}