From 13089bf3ef714c49bfc8a063ad033e2ad5f438e4 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Mon, 7 Oct 2024 13:19:23 -0600 Subject: [PATCH 01/68] initial service linux file with notes --- cmd/service_linux.go | 100 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 cmd/service_linux.go diff --git a/cmd/service_linux.go b/cmd/service_linux.go new file mode 100644 index 000000000..2d846d0b2 --- /dev/null +++ b/cmd/service_linux.go @@ -0,0 +1,100 @@ +//go:build linux + +/* + Licensed under the MIT License . + + Copyright © 2023-2024 Seagate Technology LLC and/or its Affiliates + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +*/ + +package cmd + +import ( + "errors" + + "github.com/spf13/cobra" +) + +type serviceOptions struct { + ConfigFile string + MountPath string +} + +const SvcName = "cloudfuse.service" + +var servOpts serviceOptions + +// Section defining all the command that we have in secure feature +var serviceCmd = &cobra.Command{ + Use: "service", + Short: "Manage cloudfuse startup process on Linux", + Long: "Manage cloudfuse startup process on Linux", + SuggestFor: []string{"ser", "serv"}, + Example: "cloudfuse service install", + FlagErrorHandling: cobra.ExitOnError, + RunE: func(cmd *cobra.Command, args []string) error { + return errors.New("missing command options\n\nDid you mean this?\n\tcloudfuse service install\n\nRun 'cloudfuse service --help' for usage") + }, +} + +var installCmd = &cobra.Command{ + Use: "install", + Short: "Installs the startup process for Cloudfuse. Requires elevated permissions.", + Long: "Installs the startup process for Cloudfuse which remounts any active previously active mounts on startup. elevated permissions.", + SuggestFor: []string{"ins", "inst"}, + Example: "cloudfuse service install", + FlagErrorHandling: cobra.ExitOnError, + RunE: func(cmd *cobra.Command, args []string) error { + + // if err != nil { + // return fmt.Errorf("unable to determine location of cloudfuse binary [%s]", err.Error()) + // } + + // 1. get the cloudfuse.service file from the setup folder and verify its contents (the mount path and config path is valid) + // 2. retrieve the user account from cloudfuse.service file and make it if it doesn't exist + // 3. copy the cloudfuse.service file to /etc/systemd/system + // 4. run systemctl daemon-reload + + return nil + }, +} + +var uninstallCmd = &cobra.Command{ + Use: "uninstall", + Short: "Uninstall the startup process for Cloudfuse. Requires running as admin.", + Long: "Uninstall the startup process for Cloudfuse. Requires running as admin.", + SuggestFor: []string{"uninst", "uninstal"}, + Example: "cloudfuse service uninstall", + FlagErrorHandling: cobra.ExitOnError, + RunE: func(cmd *cobra.Command, args []string) error { + + // 1. find and remove cloudfuse.service from /etc/systemd/system and run systemctl daemon-reload + // 2. handle errors + + return nil + }, +} + +//--------------- command section ends + +func init() { + rootCmd.AddCommand(serviceCmd) + serviceCmd.AddCommand(installCmd) + serviceCmd.AddCommand(uninstallCmd) +} From c60d152bad6e85ab0a3cb3fa4715290279de48d9 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 8 Oct 2024 11:07:02 -0600 Subject: [PATCH 02/68] added parse and collect data from cloudfuse.service file --- cmd/service_linux.go | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 2d846d0b2..ece27a6b8 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -26,7 +26,11 @@ package cmd import ( + "bufio" "errors" + "fmt" + "os" + "strings" "github.com/spf13/cobra" ) @@ -36,8 +40,6 @@ type serviceOptions struct { MountPath string } -const SvcName = "cloudfuse.service" - var servOpts serviceOptions // Section defining all the command that we have in secure feature @@ -67,6 +69,34 @@ var installCmd = &cobra.Command{ // } // 1. get the cloudfuse.service file from the setup folder and verify its contents (the mount path and config path is valid) + serviceFile, err := os.Open("./setup/cloudfuse.service") //currently assumes we are in cloudfuse repo dir. where is this file going to be for the end user? + + if err != nil { + fmt.Println("Error opening file:", err) + return err + } + + defer serviceFile.Close() + + scanner := bufio.NewScanner(serviceFile) + serviceData := make(map[string]string) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "#") { + continue + } + + parts := strings.SplitN(line, "=", 2) + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + serviceData[key] = value + } + + if err := scanner.Err(); err != nil { + fmt.Println("Error reading file:", err) + return err + } + // 2. retrieve the user account from cloudfuse.service file and make it if it doesn't exist // 3. copy the cloudfuse.service file to /etc/systemd/system // 4. run systemctl daemon-reload From a7d254e9817d25f9a3320d03296a1f81a72ceff7 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 8 Oct 2024 14:03:04 -0600 Subject: [PATCH 03/68] added data collect and user create draft --- cmd/service_linux.go | 55 +++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index ece27a6b8..2c3f11977 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -30,6 +30,7 @@ import ( "errors" "fmt" "os" + "os/exec" "strings" "github.com/spf13/cobra" @@ -64,12 +65,8 @@ var installCmd = &cobra.Command{ FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { - // if err != nil { - // return fmt.Errorf("unable to determine location of cloudfuse binary [%s]", err.Error()) - // } - - // 1. get the cloudfuse.service file from the setup folder and verify its contents (the mount path and config path is valid) - serviceFile, err := os.Open("./setup/cloudfuse.service") //currently assumes we are in cloudfuse repo dir. where is this file going to be for the end user? + // 1. get the cloudfuse.service file from the setup folder and collect relevant data (user, mount, config) + serviceFile, err := os.Open("./setup/cloudfuse.service") if err != nil { fmt.Println("Error opening file:", err) @@ -82,21 +79,51 @@ var installCmd = &cobra.Command{ serviceData := make(map[string]string) for scanner.Scan() { line := scanner.Text() - if strings.HasPrefix(line, "#") { - continue + if strings.Contains(line, "Environment=") { + parts := strings.SplitN(line, "=", 3) + key := strings.TrimSpace(parts[1]) + value := strings.TrimSpace(parts[2]) + serviceData[key] = value + } + if strings.Contains(line, "User=") { + parts := strings.SplitN(line, "=", 2) + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + serviceData[key] = value } - - parts := strings.SplitN(line, "=", 2) - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - serviceData[key] = value } - if err := scanner.Err(); err != nil { fmt.Println("Error reading file:", err) return err } + //check the 'User' key. compare to the the /etc/passwd list for the value and create it if it doesn't exist. + + value := serviceData["User"] + usersList, err := os.Open("/etc/passwd") + if err != nil { + return fmt.Errorf("failed to open /etc/passwd due to following error: [%s]", err.Error()) + } + scanner = bufio.NewScanner(usersList) + var foundUser bool + defer usersList.Close() + + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, value) { + foundUser = true + } + } + if !foundUser { + //create the user + cmd := exec.Command("useradd", "-m", value) + err := cmd.Run() + if err != nil { + return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) + } + + } + // 2. retrieve the user account from cloudfuse.service file and make it if it doesn't exist // 3. copy the cloudfuse.service file to /etc/systemd/system // 4. run systemctl daemon-reload From 0cd2984118ebc7677cfff7da613e822cbfde85e8 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 8 Oct 2024 17:01:55 -0600 Subject: [PATCH 04/68] Added copy and systemctl commands to the installCmd --- cmd/service_linux.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 2c3f11977..67bccde36 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -97,8 +97,7 @@ var installCmd = &cobra.Command{ return err } - //check the 'User' key. compare to the the /etc/passwd list for the value and create it if it doesn't exist. - + // 2. retrieve the user account from cloudfuse.service file and make it if it doesn't exist value := serviceData["User"] usersList, err := os.Open("/etc/passwd") if err != nil { @@ -116,18 +115,28 @@ var installCmd = &cobra.Command{ } if !foundUser { //create the user - cmd := exec.Command("useradd", "-m", value) - err := cmd.Run() + userAddCmd := exec.Command("useradd", "-m", value) + err := userAddCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } - } - // 2. retrieve the user account from cloudfuse.service file and make it if it doesn't exist // 3. copy the cloudfuse.service file to /etc/systemd/system + + copyFileCmd := exec.Command("cp", "./setup/cloudfuse.service", "/etc/systemd/system") + err = copyFileCmd.Run() + if err != nil { + return fmt.Errorf("failed to copy cloudfuse.service file to /etc/systemd/system due to following error: [%s]", err.Error()) + } + // 4. run systemctl daemon-reload + systemctlCmd := exec.Command("systemctl", "daemon-reload") + err = systemctlCmd.Run() + if err != nil { + return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) + } return nil }, } From 74faea2e3b95b64f0507a6516fee8a8c4e83f5d0 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Wed, 9 Oct 2024 10:37:12 -0600 Subject: [PATCH 05/68] added wrapper for data collection from file. started composing default mount logic --- cmd/service_linux.go | 78 ++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 67bccde36..dfe84a31d 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -66,39 +66,25 @@ var installCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { // 1. get the cloudfuse.service file from the setup folder and collect relevant data (user, mount, config) - serviceFile, err := os.Open("./setup/cloudfuse.service") + serviceData, err := collectServiceData("./setup/cloudfuse.service") if err != nil { - fmt.Println("Error opening file:", err) - return err + return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: ", err.Error()) } - defer serviceFile.Close() - - scanner := bufio.NewScanner(serviceFile) - serviceData := make(map[string]string) - for scanner.Scan() { - line := scanner.Text() - if strings.Contains(line, "Environment=") { - parts := strings.SplitN(line, "=", 3) - key := strings.TrimSpace(parts[1]) - value := strings.TrimSpace(parts[2]) - serviceData[key] = value - } - if strings.Contains(line, "User=") { - parts := strings.SplitN(line, "=", 2) - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - serviceData[key] = value + //TODO: set a default mount and config path when stubbed examples are found in the file. + mountPath := serviceData["MoutingPoint"] + if mountPath == "/path/to/mounting/point" { + fmt.Printf("creating mount folder in $HOME/cloudfuseMount") + userAddCmd := exec.Command("mkdir", "$HOME/cloudfuseMount") + err := userAddCmd.Run() + if err != nil { + return fmt.Errorf("failed to create default mount folder due to following error: [%s]", err.Error()) } } - if err := scanner.Err(); err != nil { - fmt.Println("Error reading file:", err) - return err - } - // 2. retrieve the user account from cloudfuse.service file and make it if it doesn't exist - value := serviceData["User"] + // 2. retrieve the user account from cloudfuse.service file and create it if it doesn't exist + user := serviceData["User"] usersList, err := os.Open("/etc/passwd") if err != nil { return fmt.Errorf("failed to open /etc/passwd due to following error: [%s]", err.Error()) @@ -109,13 +95,13 @@ var installCmd = &cobra.Command{ for scanner.Scan() { line := scanner.Text() - if strings.HasPrefix(line, value) { + if strings.HasPrefix(line, user) { foundUser = true } } if !foundUser { //create the user - userAddCmd := exec.Command("useradd", "-m", value) + userAddCmd := exec.Command("useradd", "-m", user) err := userAddCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) @@ -159,6 +145,42 @@ var uninstallCmd = &cobra.Command{ //--------------- command section ends +func collectServiceData(serviceFilePath string) (map[string]string, error) { + serviceFile, err := os.Open("./setup/cloudfuse.service") + + if err != nil { + fmt.Println("Error opening file:", err) + return nil, err + } + + defer serviceFile.Close() + + scanner := bufio.NewScanner(serviceFile) + serviceData := make(map[string]string) + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, "Environment=") { + parts := strings.SplitN(line, "=", 3) + key := strings.TrimSpace(parts[1]) + value := strings.TrimSpace(parts[2]) + serviceData[key] = value + } + if strings.Contains(line, "User=") { + parts := strings.SplitN(line, "=", 2) + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + serviceData[key] = value + } + } + if err := scanner.Err(); err != nil { + fmt.Println("Error reading file:", err) + return nil, err + } + return serviceData, nil +} + +//TODO: add wrapper function for collecting data, creating user, setting default paths, running commands. + func init() { rootCmd.AddCommand(serviceCmd) serviceCmd.AddCommand(installCmd) From b301ade57691d58eb14f718de1cf1e7bb58bc4c1 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Wed, 9 Oct 2024 13:06:00 -0600 Subject: [PATCH 06/68] added wrapper function to edit service file for config or mount Paths --- cmd/service_linux.go | 123 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 7 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index dfe84a31d..42d983d49 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -31,6 +31,7 @@ import ( "fmt" "os" "os/exec" + "os/user" "strings" "github.com/spf13/cobra" @@ -67,19 +68,31 @@ var installCmd = &cobra.Command{ // 1. get the cloudfuse.service file from the setup folder and collect relevant data (user, mount, config) - serviceData, err := collectServiceData("./setup/cloudfuse.service") + // get current dir + dir, err := os.Getwd() if err != nil { - return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: ", err.Error()) + return fmt.Errorf("error: [%s]", err.Error()) + } + + // assumes dir is in cloudfuse repo dir + serviceData, err := collectServiceData(fmt.Sprintf("%s/setup/cloudfuse.service", dir)) + if err != nil { + return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: [%s]", err) } //TODO: set a default mount and config path when stubbed examples are found in the file. mountPath := serviceData["MoutingPoint"] + configPath := serviceData["ConfigFile"] if mountPath == "/path/to/mounting/point" { - fmt.Printf("creating mount folder in $HOME/cloudfuseMount") - userAddCmd := exec.Command("mkdir", "$HOME/cloudfuseMount") - err := userAddCmd.Run() + err = modifySericeFile(mountPath, dir) + if err != nil { + return fmt.Errorf("error when attempting to write defaults into service file: [%s]", err.Error()) + } + } + if configPath == "/path/to/config/file/config.yaml" { + err = modifySericeFile(configPath, dir) if err != nil { - return fmt.Errorf("failed to create default mount folder due to following error: [%s]", err.Error()) + return fmt.Errorf("error when attempting to write defaults into service file: [%s]", err.Error()) } } @@ -89,7 +102,7 @@ var installCmd = &cobra.Command{ if err != nil { return fmt.Errorf("failed to open /etc/passwd due to following error: [%s]", err.Error()) } - scanner = bufio.NewScanner(usersList) + scanner := bufio.NewScanner(usersList) var foundUser bool defer usersList.Close() @@ -179,6 +192,102 @@ func collectServiceData(serviceFilePath string) (map[string]string, error) { return serviceData, nil } +func modifySericeFile(path string, curDir string) error { + + var defaultMountPath string + + //get current user + usr, err := user.Current() + if err != nil { + return fmt.Errorf("Error: [%s]", err.Error()) + } + + //mountpath or config? + var oldString string + var newString string + var config bool + var mount bool + + if strings.Contains(path, "config.yaml") { + oldString = "Environment=ConfigFile=/path/to/config/file/config.yaml" + newString = fmt.Sprintf("Environment=ConfigFile=%s/config.yaml", curDir) + config = true + mount = false + } + if strings.Contains(path, "mounting") { + defaultMountPath = fmt.Sprintf("/home/%s/cloudfuseMount", strings.ToLower(usr.Name)) + fmt.Printf("creating mount folder in %s", defaultMountPath) + + //Create default mount directory + userAddCmd := exec.Command("mkdir", defaultMountPath) + err = userAddCmd.Run() + if err != nil { + return fmt.Errorf("failed to create default mount folder due to following error: [%s]", err.Error()) + } + oldString = "Environment=MoutingPoint=/path/to/mounting/point" + newString = fmt.Sprintf("Environment=MoutingPoint=%s", defaultMountPath) + config = false + mount = true + } + + // open service file for read write + file, err := os.OpenFile("./setup/cloudfuse.service", os.O_RDWR, 0644) + if err != nil { + return fmt.Errorf("Error opening file: [%s]", err.Error()) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + var lines []string + + // Read the file line by line + for scanner.Scan() { + line := scanner.Text() + + // Check if the line contains the search string + if mount && strings.Contains(line, "MoutingPoint") { + // Modify the line by replacing the old string with the new string + line = strings.ReplaceAll(line, oldString, newString) + } + if config && strings.Contains(line, "ConfigFile") { + line = strings.ReplaceAll(line, oldString, newString) + } + + // Append the (possibly modified) line to the slice + lines = append(lines, line) + } + // Check for errors during file reading + if err := scanner.Err(); err != nil { + return fmt.Errorf("Error reading file: [%s]", err.Error()) + } + + // Move the file pointer to the start for overwriting + file.Seek(0, 0) + + // Create a buffered writer to overwrite the file + writer := bufio.NewWriter(file) + + // Write the modified lines back to the file + for _, line := range lines { + _, err := writer.WriteString(line + "\n") + if err != nil { + return fmt.Errorf("Error writing to file: [%s]", err.Error()) + } + } + + // Truncate the file to the new size in case the modified content is shorter + err = file.Truncate(int64(writer.Buffered())) + if err != nil { + return fmt.Errorf("Error truncating file: [%s]", err.Error()) + + } + + // Flush the buffer to write all data to disk + writer.Flush() + + return nil +} + //TODO: add wrapper function for collecting data, creating user, setting default paths, running commands. func init() { From 3fecd43891307c03bc3e7189a20676a545089648 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Wed, 9 Oct 2024 13:25:24 -0600 Subject: [PATCH 07/68] added sudo to commands --- cmd/service_linux.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 42d983d49..49f8a93cd 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -80,7 +80,7 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: [%s]", err) } - //TODO: set a default mount and config path when stubbed examples are found in the file. + // set a default mount and config path when stubbed examples are found in the file. mountPath := serviceData["MoutingPoint"] configPath := serviceData["ConfigFile"] if mountPath == "/path/to/mounting/point" { @@ -114,7 +114,7 @@ var installCmd = &cobra.Command{ } if !foundUser { //create the user - userAddCmd := exec.Command("useradd", "-m", user) + userAddCmd := exec.Command("sudo", "useradd", "-m", user) err := userAddCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) @@ -123,7 +123,7 @@ var installCmd = &cobra.Command{ // 3. copy the cloudfuse.service file to /etc/systemd/system - copyFileCmd := exec.Command("cp", "./setup/cloudfuse.service", "/etc/systemd/system") + copyFileCmd := exec.Command("sudo", "cp", "./setup/cloudfuse.service", "/etc/systemd/system") err = copyFileCmd.Run() if err != nil { return fmt.Errorf("failed to copy cloudfuse.service file to /etc/systemd/system due to following error: [%s]", err.Error()) @@ -131,7 +131,7 @@ var installCmd = &cobra.Command{ // 4. run systemctl daemon-reload - systemctlCmd := exec.Command("systemctl", "daemon-reload") + systemctlCmd := exec.Command("sudo", "systemctl", "daemon-reload") err = systemctlCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) @@ -216,7 +216,7 @@ func modifySericeFile(path string, curDir string) error { } if strings.Contains(path, "mounting") { defaultMountPath = fmt.Sprintf("/home/%s/cloudfuseMount", strings.ToLower(usr.Name)) - fmt.Printf("creating mount folder in %s", defaultMountPath) + fmt.Printf("creating mount folder in %s \n", defaultMountPath) //Create default mount directory userAddCmd := exec.Command("mkdir", defaultMountPath) From cf992a824e8497d5b768f976b3474cf3bb1d0f44 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 10 Oct 2024 13:23:31 -0600 Subject: [PATCH 08/68] -fixed permission issues for mount --- cmd/service_linux.go | 49 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 49f8a93cd..e9199d8aa 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -96,8 +96,8 @@ var installCmd = &cobra.Command{ } } - // 2. retrieve the user account from cloudfuse.service file and create it if it doesn't exist - user := serviceData["User"] + // 2. retrieve the newUser account from cloudfuse.service file and create it if it doesn't exist + newUser := serviceData["User"] usersList, err := os.Open("/etc/passwd") if err != nil { return fmt.Errorf("failed to open /etc/passwd due to following error: [%s]", err.Error()) @@ -108,21 +108,41 @@ var installCmd = &cobra.Command{ for scanner.Scan() { line := scanner.Text() - if strings.HasPrefix(line, user) { + if strings.HasPrefix(line, newUser) { foundUser = true } } if !foundUser { //create the user - userAddCmd := exec.Command("sudo", "useradd", "-m", user) - err := userAddCmd.Run() + userAddCmd := exec.Command("sudo", "useradd", "-m", newUser) + err = userAddCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } + + // use the current user for reference on permissions + usr, err := user.Current() + if err != nil { + return fmt.Errorf("error getting looking up current user: [%s]", err.Error()) + } + + // get a list of group from reference user + groups, err := usr.GroupIds() + if err != nil { + return fmt.Errorf("error getting group id list from current user: [%s]", err.Error()) + } + + // add the list of groups to the CloudfuseUser + for _, group := range groups { + usermodCmd := exec.Command("sudo", "usermod", "-aG", group, newUser) + err = usermodCmd.Run() + if err != nil { + return fmt.Errorf("failed to add group to user due to following error: [%s]", err.Error()) + } + } } // 3. copy the cloudfuse.service file to /etc/systemd/system - copyFileCmd := exec.Command("sudo", "cp", "./setup/cloudfuse.service", "/etc/systemd/system") err = copyFileCmd.Run() if err != nil { @@ -130,9 +150,15 @@ var installCmd = &cobra.Command{ } // 4. run systemctl daemon-reload + systemctlDaemonReloadCmd := exec.Command("sudo", "systemctl", "daemon-reload") + err = systemctlDaemonReloadCmd.Run() + if err != nil { + return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) + } - systemctlCmd := exec.Command("sudo", "systemctl", "daemon-reload") - err = systemctlCmd.Run() + // 5. Enable the service to start at system boot + systemctlEnableCmd := exec.Command("sudo", "systemctl", "enable", "cloudfuse.service") + err = systemctlEnableCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) } @@ -253,6 +279,13 @@ func modifySericeFile(path string, curDir string) error { line = strings.ReplaceAll(line, oldString, newString) } + // add the -o default_permissions if not present + if strings.Contains(line, "ExecStart") && !strings.Contains(line, "-o allow_other") { + oldString = "ExecStart=/usr/bin/cloudfuse mount ${MoutingPoint} --config-file=${ConfigFile}" + newString = "ExecStart=/usr/bin/cloudfuse mount ${MoutingPoint} --config-file=${ConfigFile} -o allow_other" + line = strings.ReplaceAll(line, oldString, newString) + } + // Append the (possibly modified) line to the slice lines = append(lines, line) } From 8e8633703e9a1526621f91915254815f19d3d188 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 10 Oct 2024 13:25:33 -0600 Subject: [PATCH 09/68] added service removal automation. --- cmd/service_linux.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index e9199d8aa..a315b0f7b 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -176,7 +176,17 @@ var uninstallCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { // 1. find and remove cloudfuse.service from /etc/systemd/system and run systemctl daemon-reload - // 2. handle errors + removeFileCmd := exec.Command("sudo", "rm", "/etc/systemd/system/cloudfuse.service") + err := removeFileCmd.Run() + if err != nil { + return fmt.Errorf("failed to delete cloudfuse.service file from /etc/systemd/system due to following error: [%s]", err.Error()) + } + + systemctlDaemonReloadCmd := exec.Command("sudo", "systemctl", "daemon-reload") + err = systemctlDaemonReloadCmd.Run() + if err != nil { + return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) + } return nil }, From 2129c1964ea9a98e304932262a75a6c5a207de27 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 29 Oct 2024 16:20:18 -0600 Subject: [PATCH 10/68] Added argument support and replaced default mount and config paths with arguments --- cmd/service_linux.go | 154 +++++++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index a315b0f7b..8d8615c9e 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -34,6 +34,7 @@ import ( "os/user" "strings" + "github.com/Seagate/cloudfuse/common" "github.com/spf13/cobra" ) @@ -64,6 +65,7 @@ var installCmd = &cobra.Command{ SuggestFor: []string{"ins", "inst"}, Example: "cloudfuse service install", FlagErrorHandling: cobra.ExitOnError, + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { // 1. get the cloudfuse.service file from the setup folder and collect relevant data (user, mount, config) @@ -80,67 +82,40 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: [%s]", err) } - // set a default mount and config path when stubbed examples are found in the file. - mountPath := serviceData["MoutingPoint"] - configPath := serviceData["ConfigFile"] - if mountPath == "/path/to/mounting/point" { - err = modifySericeFile(mountPath, dir) - if err != nil { - return fmt.Errorf("error when attempting to write defaults into service file: [%s]", err.Error()) - } + // TODO: get arguments of cloudfuse service install and pass that to be values written to the service file's config file and mount point paths. + mountPath := args[0] + configPath := args[1] + + mountExists := common.DirectoryExists(mountPath) + if !mountExists { + return fmt.Errorf("error, the mount path provided does not exist") + // TODO: add useage output upon failure with input } - if configPath == "/path/to/config/file/config.yaml" { - err = modifySericeFile(configPath, dir) - if err != nil { - return fmt.Errorf("error when attempting to write defaults into service file: [%s]", err.Error()) - } + isDirEmpty := common.IsDirectoryEmpty(mountPath) + if !isDirEmpty { + return fmt.Errorf("error, the mount path provided is not empty") + // TODO: add useage output upon failure with input } - // 2. retrieve the newUser account from cloudfuse.service file and create it if it doesn't exist - newUser := serviceData["User"] - usersList, err := os.Open("/etc/passwd") - if err != nil { - return fmt.Errorf("failed to open /etc/passwd due to following error: [%s]", err.Error()) + configExists := common.DirectoryExists(configPath) + if !configExists { + return fmt.Errorf("error, the configfile path provided does not exist") // TODO: add useage output upon failure with input } - scanner := bufio.NewScanner(usersList) - var foundUser bool - defer usersList.Close() - - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, newUser) { - foundUser = true - } + + err = modifySericeFile(mountPath, dir) + if err != nil { + return fmt.Errorf("error when attempting to write defaults into service file: [%s]", err.Error()) } - if !foundUser { - //create the user - userAddCmd := exec.Command("sudo", "useradd", "-m", newUser) - err = userAddCmd.Run() - if err != nil { - return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) - } - // use the current user for reference on permissions - usr, err := user.Current() - if err != nil { - return fmt.Errorf("error getting looking up current user: [%s]", err.Error()) - } + err = modifySericeFile(configPath, dir) + if err != nil { + return fmt.Errorf("error when attempting to write defaults into service file: [%s]", err.Error()) + } - // get a list of group from reference user - groups, err := usr.GroupIds() - if err != nil { - return fmt.Errorf("error getting group id list from current user: [%s]", err.Error()) - } + // 2. retrieve the newUser account from cloudfuse.service file and create it if it doesn't exist - // add the list of groups to the CloudfuseUser - for _, group := range groups { - usermodCmd := exec.Command("sudo", "usermod", "-aG", group, newUser) - err = usermodCmd.Run() - if err != nil { - return fmt.Errorf("failed to add group to user due to following error: [%s]", err.Error()) - } - } - } + serviceUser := serviceData["User"] + setUser(serviceUser) // 3. copy the cloudfuse.service file to /etc/systemd/system copyFileCmd := exec.Command("sudo", "cp", "./setup/cloudfuse.service", "/etc/systemd/system") @@ -208,12 +183,6 @@ func collectServiceData(serviceFilePath string) (map[string]string, error) { serviceData := make(map[string]string) for scanner.Scan() { line := scanner.Text() - if strings.Contains(line, "Environment=") { - parts := strings.SplitN(line, "=", 3) - key := strings.TrimSpace(parts[1]) - value := strings.TrimSpace(parts[2]) - serviceData[key] = value - } if strings.Contains(line, "User=") { parts := strings.SplitN(line, "=", 2) key := strings.TrimSpace(parts[0]) @@ -230,14 +199,6 @@ func collectServiceData(serviceFilePath string) (map[string]string, error) { func modifySericeFile(path string, curDir string) error { - var defaultMountPath string - - //get current user - usr, err := user.Current() - if err != nil { - return fmt.Errorf("Error: [%s]", err.Error()) - } - //mountpath or config? var oldString string var newString string @@ -250,18 +211,10 @@ func modifySericeFile(path string, curDir string) error { config = true mount = false } - if strings.Contains(path, "mounting") { - defaultMountPath = fmt.Sprintf("/home/%s/cloudfuseMount", strings.ToLower(usr.Name)) - fmt.Printf("creating mount folder in %s \n", defaultMountPath) - //Create default mount directory - userAddCmd := exec.Command("mkdir", defaultMountPath) - err = userAddCmd.Run() - if err != nil { - return fmt.Errorf("failed to create default mount folder due to following error: [%s]", err.Error()) - } + if common.IsDirectoryEmpty(path) { oldString = "Environment=MoutingPoint=/path/to/mounting/point" - newString = fmt.Sprintf("Environment=MoutingPoint=%s", defaultMountPath) + newString = fmt.Sprintf("Environment=MoutingPoint=%s", path) config = false mount = true } @@ -331,6 +284,53 @@ func modifySericeFile(path string, curDir string) error { return nil } +func setUser(serviceUser string) error { + usersList, err := os.Open("/etc/passwd") + if err != nil { + return fmt.Errorf("failed to open /etc/passwd due to following error: [%s]", err.Error()) + } + scanner := bufio.NewScanner(usersList) + var foundUser bool + defer usersList.Close() + + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, serviceUser) { + foundUser = true + } + } + if !foundUser { + //create the user + userAddCmd := exec.Command("sudo", "useradd", "-m", serviceUser) + err = userAddCmd.Run() + if err != nil { + return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) + } + + // use the current user for reference on permissions + usr, err := user.Current() + if err != nil { + return fmt.Errorf("error getting looking up current user: [%s]", err.Error()) + } + + // get a list of group from reference user + groups, err := usr.GroupIds() + if err != nil { + return fmt.Errorf("error getting group id list from current user: [%s]", err.Error()) + } + + // add the list of groups to the CloudfuseUser + for _, group := range groups { + usermodCmd := exec.Command("sudo", "usermod", "-aG", group, serviceUser) + err = usermodCmd.Run() + if err != nil { + return fmt.Errorf("failed to add group to user due to following error: [%s]", err.Error()) + } + } + } + return nil +} + //TODO: add wrapper function for collecting data, creating user, setting default paths, running commands. func init() { From 3ebbe21818b05ef8a5b261c9e9b7c906aa12c92d Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Wed, 13 Nov 2024 11:32:35 -0700 Subject: [PATCH 11/68] correct Short and Long description for install --- cmd/service_linux.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 8d8615c9e..1b1677f0b 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -60,8 +60,8 @@ var serviceCmd = &cobra.Command{ var installCmd = &cobra.Command{ Use: "install", - Short: "Installs the startup process for Cloudfuse. Requires elevated permissions.", - Long: "Installs the startup process for Cloudfuse which remounts any active previously active mounts on startup. elevated permissions.", + Short: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", + Long: "Installs a service file for a single mount with Cloudfuse which remounts any active previously active mounts on startup. elevated permissions.", SuggestFor: []string{"ins", "inst"}, Example: "cloudfuse service install", FlagErrorHandling: cobra.ExitOnError, @@ -76,12 +76,6 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error: [%s]", err.Error()) } - // assumes dir is in cloudfuse repo dir - serviceData, err := collectServiceData(fmt.Sprintf("%s/setup/cloudfuse.service", dir)) - if err != nil { - return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: [%s]", err) - } - // TODO: get arguments of cloudfuse service install and pass that to be values written to the service file's config file and mount point paths. mountPath := args[0] configPath := args[1] @@ -114,6 +108,11 @@ var installCmd = &cobra.Command{ // 2. retrieve the newUser account from cloudfuse.service file and create it if it doesn't exist + // assumes dir is in cloudfuse repo dir + serviceData, err := collectServiceData(fmt.Sprintf("%s/setup/cloudfuse.service", dir)) + if err != nil { + return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: [%s]", err) + } serviceUser := serviceData["User"] setUser(serviceUser) From 497706ca402083cad8eb74ba532ab3b496f634ac Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Wed, 13 Nov 2024 11:36:07 -0700 Subject: [PATCH 12/68] corrected usage --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 1b1677f0b..abd6b097f 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -59,7 +59,7 @@ var serviceCmd = &cobra.Command{ } var installCmd = &cobra.Command{ - Use: "install", + Use: "install [mount path] [config path]", Short: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", Long: "Installs a service file for a single mount with Cloudfuse which remounts any active previously active mounts on startup. elevated permissions.", SuggestFor: []string{"ins", "inst"}, From 439ddcb702e0c08c2253eacfa30b8b58338e6bc1 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Wed, 13 Nov 2024 12:48:11 -0700 Subject: [PATCH 13/68] use os.stat to check file exists --- cmd/service_linux.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index abd6b097f..99c528cfe 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -29,6 +29,7 @@ import ( "bufio" "errors" "fmt" + "io/fs" "os" "os/exec" "os/user" @@ -91,8 +92,8 @@ var installCmd = &cobra.Command{ // TODO: add useage output upon failure with input } - configExists := common.DirectoryExists(configPath) - if !configExists { + _, err = os.Stat(configPath) + if errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error, the configfile path provided does not exist") // TODO: add useage output upon failure with input } From be9c13b1ecd87a3f011c828bc027ab0d5e54c17d Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 14 Nov 2024 12:25:08 -0700 Subject: [PATCH 14/68] changed modifyServiceFile() to createServiceFile() --- cmd/service_linux.go | 58 ++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 99c528cfe..259e22296 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -97,14 +97,9 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error, the configfile path provided does not exist") // TODO: add useage output upon failure with input } - err = modifySericeFile(mountPath, dir) + serviceFile, err := newSericeFile(mountPath, configPath) if err != nil { - return fmt.Errorf("error when attempting to write defaults into service file: [%s]", err.Error()) - } - - err = modifySericeFile(configPath, dir) - if err != nil { - return fmt.Errorf("error when attempting to write defaults into service file: [%s]", err.Error()) + return fmt.Errorf("error when attempting to create service file: [%s]", err.Error()) } // 2. retrieve the newUser account from cloudfuse.service file and create it if it doesn't exist @@ -118,7 +113,7 @@ var installCmd = &cobra.Command{ setUser(serviceUser) // 3. copy the cloudfuse.service file to /etc/systemd/system - copyFileCmd := exec.Command("sudo", "cp", "./setup/cloudfuse.service", "/etc/systemd/system") + copyFileCmd := exec.Command("sudo", "cp", serviceFile, "/etc/systemd/system") err = copyFileCmd.Run() if err != nil { return fmt.Errorf("failed to copy cloudfuse.service file to /etc/systemd/system due to following error: [%s]", err.Error()) @@ -132,7 +127,7 @@ var installCmd = &cobra.Command{ } // 5. Enable the service to start at system boot - systemctlEnableCmd := exec.Command("sudo", "systemctl", "enable", "cloudfuse.service") + systemctlEnableCmd := exec.Command("sudo", "systemctl", "enable", serviceFile) err = systemctlEnableCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) @@ -197,7 +192,7 @@ func collectServiceData(serviceFilePath string) (map[string]string, error) { return serviceData, nil } -func modifySericeFile(path string, curDir string) error { +func newSericeFile(mountPath string, configPath string) (string, error) { //mountpath or config? var oldString string @@ -205,28 +200,28 @@ func modifySericeFile(path string, curDir string) error { var config bool var mount bool - if strings.Contains(path, "config.yaml") { + if strings.Contains(configPath, "config.yaml") { oldString = "Environment=ConfigFile=/path/to/config/file/config.yaml" - newString = fmt.Sprintf("Environment=ConfigFile=%s/config.yaml", curDir) + newString = fmt.Sprintf("Environment=ConfigFile=%s", configPath) config = true mount = false } - if common.IsDirectoryEmpty(path) { + if common.IsDirectoryEmpty(mountPath) { oldString = "Environment=MoutingPoint=/path/to/mounting/point" - newString = fmt.Sprintf("Environment=MoutingPoint=%s", path) + newString = fmt.Sprintf("Environment=MoutingPoint=%s", mountPath) config = false mount = true } - // open service file for read write - file, err := os.OpenFile("./setup/cloudfuse.service", os.O_RDWR, 0644) + // open service defaultFile for read write + defaultFile, err := os.OpenFile("./setup/cloudfuse.service", os.O_RDWR, 0644) if err != nil { - return fmt.Errorf("Error opening file: [%s]", err.Error()) + return "", fmt.Errorf("error opening file: [%s]", err.Error()) } - defer file.Close() + defer defaultFile.Close() - scanner := bufio.NewScanner(file) + scanner := bufio.NewScanner(defaultFile) var lines []string // Read the file line by line @@ -254,34 +249,33 @@ func modifySericeFile(path string, curDir string) error { } // Check for errors during file reading if err := scanner.Err(); err != nil { - return fmt.Errorf("Error reading file: [%s]", err.Error()) + return "", fmt.Errorf("error reading file: [%s]", err.Error()) + } + + mountDirs := strings.Split(mountPath, "/") + serviceName := mountDirs[len(mountDirs)] + ".service" + newFile, err := os.Create(serviceName) + if err != nil { + return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } - // Move the file pointer to the start for overwriting - file.Seek(0, 0) + // Open a new file and write all lines to the new file. // Create a buffered writer to overwrite the file - writer := bufio.NewWriter(file) + writer := bufio.NewWriter(newFile) // Write the modified lines back to the file for _, line := range lines { _, err := writer.WriteString(line + "\n") if err != nil { - return fmt.Errorf("Error writing to file: [%s]", err.Error()) + return "", fmt.Errorf("error writing to file: [%s]", err.Error()) } } - // Truncate the file to the new size in case the modified content is shorter - err = file.Truncate(int64(writer.Buffered())) - if err != nil { - return fmt.Errorf("Error truncating file: [%s]", err.Error()) - - } - // Flush the buffer to write all data to disk writer.Flush() - return nil + return serviceName, nil } func setUser(serviceUser string) error { From 60a3893282c39e939b4fdda6b6be05746bff5dac Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 15 Nov 2024 15:52:43 -0700 Subject: [PATCH 15/68] adjusted newServiceFile() to have correct paths and directly write file into the /etc/systemd/system/ --- cmd/service_linux.go | 44 +++++++++++++------------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 259e22296..68aa1b9b1 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -97,7 +97,7 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error, the configfile path provided does not exist") // TODO: add useage output upon failure with input } - serviceFile, err := newSericeFile(mountPath, configPath) + serviceFile, err := newSericeFile(mountPath, configPath, dir) if err != nil { return fmt.Errorf("error when attempting to create service file: [%s]", err.Error()) } @@ -112,13 +112,6 @@ var installCmd = &cobra.Command{ serviceUser := serviceData["User"] setUser(serviceUser) - // 3. copy the cloudfuse.service file to /etc/systemd/system - copyFileCmd := exec.Command("sudo", "cp", serviceFile, "/etc/systemd/system") - err = copyFileCmd.Run() - if err != nil { - return fmt.Errorf("failed to copy cloudfuse.service file to /etc/systemd/system due to following error: [%s]", err.Error()) - } - // 4. run systemctl daemon-reload systemctlDaemonReloadCmd := exec.Command("sudo", "systemctl", "daemon-reload") err = systemctlDaemonReloadCmd.Run() @@ -192,27 +185,17 @@ func collectServiceData(serviceFilePath string) (map[string]string, error) { return serviceData, nil } -func newSericeFile(mountPath string, configPath string) (string, error) { +func newSericeFile(mountPath string, configPath string, dir string) (string, error) { //mountpath or config? var oldString string var newString string - var config bool - var mount bool - - if strings.Contains(configPath, "config.yaml") { - oldString = "Environment=ConfigFile=/path/to/config/file/config.yaml" - newString = fmt.Sprintf("Environment=ConfigFile=%s", configPath) - config = true - mount = false - } - if common.IsDirectoryEmpty(mountPath) { - oldString = "Environment=MoutingPoint=/path/to/mounting/point" - newString = fmt.Sprintf("Environment=MoutingPoint=%s", mountPath) - config = false - mount = true - } + oldConfigStr := "Environment=ConfigFile=/path/to/config/file/config.yaml" + newConfigStr := fmt.Sprintf("Environment=ConfigFile=%s", common.JoinUnixFilepath(dir, configPath)) + + oldMountStr := "Environment=MoutingPoint=/path/to/mounting/point" + newMountStr := fmt.Sprintf("Environment=MoutingPoint=%s", common.JoinUnixFilepath(dir, mountPath)) // open service defaultFile for read write defaultFile, err := os.OpenFile("./setup/cloudfuse.service", os.O_RDWR, 0644) @@ -229,12 +212,12 @@ func newSericeFile(mountPath string, configPath string) (string, error) { line := scanner.Text() // Check if the line contains the search string - if mount && strings.Contains(line, "MoutingPoint") { + if strings.Contains(line, "MoutingPoint") { // Modify the line by replacing the old string with the new string - line = strings.ReplaceAll(line, oldString, newString) + line = strings.ReplaceAll(line, oldMountStr, newMountStr) } - if config && strings.Contains(line, "ConfigFile") { - line = strings.ReplaceAll(line, oldString, newString) + if strings.Contains(line, "ConfigFile") { + line = strings.ReplaceAll(line, oldConfigStr, newConfigStr) } // add the -o default_permissions if not present @@ -252,9 +235,8 @@ func newSericeFile(mountPath string, configPath string) (string, error) { return "", fmt.Errorf("error reading file: [%s]", err.Error()) } - mountDirs := strings.Split(mountPath, "/") - serviceName := mountDirs[len(mountDirs)] + ".service" - newFile, err := os.Create(serviceName) + folderList := strings.Split(mountPath, "/") + newFile, err := os.Create("/etc/systemd/system/" + folderList[len(folderList)-1] + ".service") if err != nil { return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } From 8a2c3907a30deca57d2375567fc99bb07f224b30 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Mon, 18 Nov 2024 10:46:08 -0700 Subject: [PATCH 16/68] adjusted collectServiceData to collectServiceUser --- cmd/service_linux.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 68aa1b9b1..ced09f38b 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -97,7 +97,7 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error, the configfile path provided does not exist") // TODO: add useage output upon failure with input } - serviceFile, err := newSericeFile(mountPath, configPath, dir) + serviceFile, err := newServiceFile(mountPath, configPath, dir) if err != nil { return fmt.Errorf("error when attempting to create service file: [%s]", err.Error()) } @@ -157,7 +157,7 @@ var uninstallCmd = &cobra.Command{ //--------------- command section ends -func collectServiceData(serviceFilePath string) (map[string]string, error) { +func collectServiceUser(serviceFilePath string) (string, error) { serviceFile, err := os.Open("./setup/cloudfuse.service") if err != nil { @@ -166,26 +166,25 @@ func collectServiceData(serviceFilePath string) (map[string]string, error) { } defer serviceFile.Close() + var serviceUser string scanner := bufio.NewScanner(serviceFile) - serviceData := make(map[string]string) for scanner.Scan() { line := scanner.Text() + if strings.Contains(line, "User=") { parts := strings.SplitN(line, "=", 2) - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - serviceData[key] = value + serviceUser = strings.TrimSpace(parts[1]) } } if err := scanner.Err(); err != nil { fmt.Println("Error reading file:", err) return nil, err } - return serviceData, nil + return serviceUser, nil } -func newSericeFile(mountPath string, configPath string, dir string) (string, error) { +func newServiceFile(mountPath string, configPath string, dir string) (string, error) { //mountpath or config? var oldString string @@ -236,7 +235,8 @@ func newSericeFile(mountPath string, configPath string, dir string) (string, err } folderList := strings.Split(mountPath, "/") - newFile, err := os.Create("/etc/systemd/system/" + folderList[len(folderList)-1] + ".service") + serviceName := folderList[len(folderList)-1] + ".service" + newFile, err := os.Create("/etc/systemd/system/" + serviceName) if err != nil { return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } From 7d217efe6a6d2c6e426f20882f999c113d668f5c Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Mon, 18 Nov 2024 10:49:12 -0700 Subject: [PATCH 17/68] moved newServiceFile() --- cmd/service_linux.go | 54 ++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index ced09f38b..240359931 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -157,33 +157,6 @@ var uninstallCmd = &cobra.Command{ //--------------- command section ends -func collectServiceUser(serviceFilePath string) (string, error) { - serviceFile, err := os.Open("./setup/cloudfuse.service") - - if err != nil { - fmt.Println("Error opening file:", err) - return nil, err - } - - defer serviceFile.Close() - var serviceUser string - - scanner := bufio.NewScanner(serviceFile) - for scanner.Scan() { - line := scanner.Text() - - if strings.Contains(line, "User=") { - parts := strings.SplitN(line, "=", 2) - serviceUser = strings.TrimSpace(parts[1]) - } - } - if err := scanner.Err(); err != nil { - fmt.Println("Error reading file:", err) - return nil, err - } - return serviceUser, nil -} - func newServiceFile(mountPath string, configPath string, dir string) (string, error) { //mountpath or config? @@ -260,6 +233,33 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er return serviceName, nil } +func collectServiceUser(serviceFilePath string) (string, error) { + serviceFile, err := os.Open("./setup/cloudfuse.service") + + if err != nil { + fmt.Println("Error opening file:", err) + return nil, err + } + + defer serviceFile.Close() + var serviceUser string + + scanner := bufio.NewScanner(serviceFile) + for scanner.Scan() { + line := scanner.Text() + + if strings.Contains(line, "User=") { + parts := strings.SplitN(line, "=", 2) + serviceUser = strings.TrimSpace(parts[1]) + } + } + if err := scanner.Err(); err != nil { + fmt.Println("Error reading file:", err) + return nil, err + } + return serviceUser, nil +} + func setUser(serviceUser string) error { usersList, err := os.Open("/etc/passwd") if err != nil { From ce37b7498f30593ce2c2c0e7a021f5590ef090ba Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 19 Nov 2024 13:01:42 -0700 Subject: [PATCH 18/68] -added support for referencial path arguments -changed collectServiceData() to extractValue() that outputs value from file for provided key -changed setUser() to use SUDO_USER env variable to add to service user group. and adjusted permissions for service user. --- cmd/service_linux.go | 138 +++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 72 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 240359931..72c3d854b 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -69,18 +69,23 @@ var installCmd = &cobra.Command{ Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - // 1. get the cloudfuse.service file from the setup folder and collect relevant data (user, mount, config) - // get current dir dir, err := os.Getwd() if err != nil { return fmt.Errorf("error: [%s]", err.Error()) } - // TODO: get arguments of cloudfuse service install and pass that to be values written to the service file's config file and mount point paths. mountPath := args[0] configPath := args[1] + if strings.Contains(mountPath, ".") || strings.Contains(mountPath, "..") { + mountPath = common.JoinUnixFilepath(dir, mountPath) + } + + if strings.Contains(configPath, ".") || strings.Contains(configPath, "..") { + configPath = common.JoinUnixFilepath(dir, configPath) + } + mountExists := common.DirectoryExists(mountPath) if !mountExists { return fmt.Errorf("error, the mount path provided does not exist") @@ -97,29 +102,32 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error, the configfile path provided does not exist") // TODO: add useage output upon failure with input } - serviceFile, err := newServiceFile(mountPath, configPath, dir) + // extract the user from the service file + serviceUser, err := extractValue("User=", fmt.Sprintf("%s/setup/cloudfuse.service", dir)) if err != nil { - return fmt.Errorf("error when attempting to create service file: [%s]", err.Error()) + return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: [%s]", err) } - // 2. retrieve the newUser account from cloudfuse.service file and create it if it doesn't exist + //create the new user and set permissions + err = setUser(serviceUser, mountPath) + if err != nil { + fmt.Println("Error setting permissions for user:", err) + return err + } - // assumes dir is in cloudfuse repo dir - serviceData, err := collectServiceData(fmt.Sprintf("%s/setup/cloudfuse.service", dir)) + serviceFile, err := newServiceFile(mountPath, configPath, dir) if err != nil { - return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: [%s]", err) + return fmt.Errorf("error when attempting to create service file: [%s]", err.Error()) } - serviceUser := serviceData["User"] - setUser(serviceUser) - // 4. run systemctl daemon-reload + // run systemctl daemon-reload systemctlDaemonReloadCmd := exec.Command("sudo", "systemctl", "daemon-reload") err = systemctlDaemonReloadCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) } - // 5. Enable the service to start at system boot + // Enable the service to start at system boot systemctlEnableCmd := exec.Command("sudo", "systemctl", "enable", serviceFile) err = systemctlEnableCmd.Run() if err != nil { @@ -159,15 +167,11 @@ var uninstallCmd = &cobra.Command{ func newServiceFile(mountPath string, configPath string, dir string) (string, error) { - //mountpath or config? - var oldString string - var newString string - oldConfigStr := "Environment=ConfigFile=/path/to/config/file/config.yaml" - newConfigStr := fmt.Sprintf("Environment=ConfigFile=%s", common.JoinUnixFilepath(dir, configPath)) + newConfigStr := fmt.Sprintf("Environment=ConfigFile=%s", configPath) oldMountStr := "Environment=MoutingPoint=/path/to/mounting/point" - newMountStr := fmt.Sprintf("Environment=MoutingPoint=%s", common.JoinUnixFilepath(dir, mountPath)) + newMountStr := fmt.Sprintf("Environment=MoutingPoint=%s", mountPath) // open service defaultFile for read write defaultFile, err := os.OpenFile("./setup/cloudfuse.service", os.O_RDWR, 0644) @@ -194,8 +198,8 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er // add the -o default_permissions if not present if strings.Contains(line, "ExecStart") && !strings.Contains(line, "-o allow_other") { - oldString = "ExecStart=/usr/bin/cloudfuse mount ${MoutingPoint} --config-file=${ConfigFile}" - newString = "ExecStart=/usr/bin/cloudfuse mount ${MoutingPoint} --config-file=${ConfigFile} -o allow_other" + oldString := "ExecStart=/usr/bin/cloudfuse mount ${MoutingPoint} --config-file=${ConfigFile}" + newString := "ExecStart=/usr/bin/cloudfuse mount ${MoutingPoint} --config-file=${ConfigFile} -o allow_other" line = strings.ReplaceAll(line, oldString, newString) } @@ -207,7 +211,7 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er return "", fmt.Errorf("error reading file: [%s]", err.Error()) } - folderList := strings.Split(mountPath, "/") + folderList := strings.Split(common.JoinUnixFilepath(dir, mountPath), "/") serviceName := folderList[len(folderList)-1] + ".service" newFile, err := os.Create("/etc/systemd/system/" + serviceName) if err != nil { @@ -233,75 +237,65 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er return serviceName, nil } -func collectServiceUser(serviceFilePath string) (string, error) { - serviceFile, err := os.Open("./setup/cloudfuse.service") - +func extractValue(key string, filePath string) (string, error) { + file, err := os.Open(filePath) if err != nil { fmt.Println("Error opening file:", err) - return nil, err + return "", err } + defer file.Close() - defer serviceFile.Close() - var serviceUser string + var value string - scanner := bufio.NewScanner(serviceFile) + scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() - - if strings.Contains(line, "User=") { - parts := strings.SplitN(line, "=", 2) - serviceUser = strings.TrimSpace(parts[1]) + if strings.Contains(line, key) { + if strings.Contains(key, "=") { + parts := strings.SplitN(line, "=", 2) + value = strings.TrimSpace(parts[1]) + } + if strings.Contains(key, ":") { + parts := strings.SplitN(line, ":", 2) + value = strings.TrimSpace(parts[1]) + } } } if err := scanner.Err(); err != nil { fmt.Println("Error reading file:", err) - return nil, err + return "", err } - return serviceUser, nil + return value, nil } -func setUser(serviceUser string) error { - usersList, err := os.Open("/etc/passwd") +func setUser(serviceUser string, mountPath string) error { + _, err := user.Lookup(serviceUser) if err != nil { - return fmt.Errorf("failed to open /etc/passwd due to following error: [%s]", err.Error()) - } - scanner := bufio.NewScanner(usersList) - var foundUser bool - defer usersList.Close() - - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, serviceUser) { - foundUser = true - } - } - if !foundUser { - //create the user - userAddCmd := exec.Command("sudo", "useradd", "-m", serviceUser) - err = userAddCmd.Run() - if err != nil { - return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) - } - - // use the current user for reference on permissions - usr, err := user.Current() - if err != nil { - return fmt.Errorf("error getting looking up current user: [%s]", err.Error()) - } - - // get a list of group from reference user - groups, err := usr.GroupIds() - if err != nil { - return fmt.Errorf("error getting group id list from current user: [%s]", err.Error()) - } + if strings.Contains(err.Error(), "unknown user") { + //create the user + userAddCmd := exec.Command("sudo", "useradd", "-m", serviceUser) + err = userAddCmd.Run() + if err != nil { + return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) + } - // add the list of groups to the CloudfuseUser - for _, group := range groups { - usermodCmd := exec.Command("sudo", "usermod", "-aG", group, serviceUser) + sudoUser := os.Getenv("SUDO_USER") + //add current user to serviceUser group + usermodCmd := exec.Command("sudo", "usermod", "-aG", sudoUser, serviceUser) err = usermodCmd.Run() if err != nil { - return fmt.Errorf("failed to add group to user due to following error: [%s]", err.Error()) + return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } + + // set set folder permission on the mount path + chmodCmd := exec.Command("sudo", "chmod", "770", mountPath) + err = chmodCmd.Run() + if err != nil { + return fmt.Errorf("failed set permisions on mount path due to following error: [%s]", err.Error()) + } + + } else { + fmt.Printf("An error occurred: %v\n", err) } } return nil From 08feb84aaf7f2180119cb910308db481c170c1de Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 22 Nov 2024 10:20:46 -0700 Subject: [PATCH 19/68] -added service user getting added to current user without sudo -corrected typo of moutingpoint that came from cloudfuse.service template file --- cmd/service_linux.go | 138 ++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 95 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 72c3d854b..000add7e8 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -26,9 +26,9 @@ package cmd import ( - "bufio" "errors" "fmt" + "html/template" "io/fs" "os" "os/exec" @@ -102,14 +102,8 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error, the configfile path provided does not exist") // TODO: add useage output upon failure with input } - // extract the user from the service file - serviceUser, err := extractValue("User=", fmt.Sprintf("%s/setup/cloudfuse.service", dir)) - if err != nil { - return fmt.Errorf("error collecting data from cloudfuse.service file due to the following error: [%s]", err) - } - //create the new user and set permissions - err = setUser(serviceUser, mountPath) + err = setUser("CloudfuseUser", mountPath) if err != nil { fmt.Println("Error setting permissions for user:", err) return err @@ -167,48 +161,36 @@ var uninstallCmd = &cobra.Command{ func newServiceFile(mountPath string, configPath string, dir string) (string, error) { - oldConfigStr := "Environment=ConfigFile=/path/to/config/file/config.yaml" - newConfigStr := fmt.Sprintf("Environment=ConfigFile=%s", configPath) - - oldMountStr := "Environment=MoutingPoint=/path/to/mounting/point" - newMountStr := fmt.Sprintf("Environment=MoutingPoint=%s", mountPath) - - // open service defaultFile for read write - defaultFile, err := os.OpenFile("./setup/cloudfuse.service", os.O_RDWR, 0644) - if err != nil { - return "", fmt.Errorf("error opening file: [%s]", err.Error()) + serviceTemplate := ` [Unit] + Description=Cloudfuse is an open source project developed to provide a virtual filesystem backed by S3 or Azure storage. + After=network-online.target + Requires=network-online.target + + [Service] + # User service will run as. + User=CloudfuseUser + # Path to the location Cloudfuse will mount to. Note this folder must currently exist. + Environment=MountingPoint={{.MountPath}} + # Path to the configuration file. + Environment=ConfigFile={{.ConfigFile}} + + # Under the hood + Type=forking + ExecStart=/usr/bin/cloudfuse mount ${MountingPoint} --config-file=${ConfigFile} + ExecStop=/usr/bin/fusermount -u ${MountingPoint} -z + + [Install] + WantedBy=multi-user.target + ` + + config := serviceOptions{ + ConfigFile: configPath, + MountPath: mountPath, } - defer defaultFile.Close() - - scanner := bufio.NewScanner(defaultFile) - var lines []string - - // Read the file line by line - for scanner.Scan() { - line := scanner.Text() - - // Check if the line contains the search string - if strings.Contains(line, "MoutingPoint") { - // Modify the line by replacing the old string with the new string - line = strings.ReplaceAll(line, oldMountStr, newMountStr) - } - if strings.Contains(line, "ConfigFile") { - line = strings.ReplaceAll(line, oldConfigStr, newConfigStr) - } - // add the -o default_permissions if not present - if strings.Contains(line, "ExecStart") && !strings.Contains(line, "-o allow_other") { - oldString := "ExecStart=/usr/bin/cloudfuse mount ${MoutingPoint} --config-file=${ConfigFile}" - newString := "ExecStart=/usr/bin/cloudfuse mount ${MoutingPoint} --config-file=${ConfigFile} -o allow_other" - line = strings.ReplaceAll(line, oldString, newString) - } - - // Append the (possibly modified) line to the slice - lines = append(lines, line) - } - // Check for errors during file reading - if err := scanner.Err(); err != nil { - return "", fmt.Errorf("error reading file: [%s]", err.Error()) + tmpl, err := template.New("service").Parse(serviceTemplate) + if err != nil { + fmt.Errorf("error creating new service file: [%s]", err.Error()) } folderList := strings.Split(common.JoinUnixFilepath(dir, mountPath), "/") @@ -218,54 +200,12 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } - // Open a new file and write all lines to the new file. - - // Create a buffered writer to overwrite the file - writer := bufio.NewWriter(newFile) - - // Write the modified lines back to the file - for _, line := range lines { - _, err := writer.WriteString(line + "\n") - if err != nil { - return "", fmt.Errorf("error writing to file: [%s]", err.Error()) - } - } - - // Flush the buffer to write all data to disk - writer.Flush() - - return serviceName, nil -} - -func extractValue(key string, filePath string) (string, error) { - file, err := os.Open(filePath) + err = tmpl.Execute(newFile, config) if err != nil { - fmt.Println("Error opening file:", err) - return "", err + return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } - defer file.Close() - - var value string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - if strings.Contains(line, key) { - if strings.Contains(key, "=") { - parts := strings.SplitN(line, "=", 2) - value = strings.TrimSpace(parts[1]) - } - if strings.Contains(key, ":") { - parts := strings.SplitN(line, ":", 2) - value = strings.TrimSpace(parts[1]) - } - } - } - if err := scanner.Err(); err != nil { - fmt.Println("Error reading file:", err) - return "", err - } - return value, nil + return serviceName, nil } func setUser(serviceUser string, mountPath string) error { @@ -279,9 +219,17 @@ func setUser(serviceUser string, mountPath string) error { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } - sudoUser := os.Getenv("SUDO_USER") + curUserGrp := os.Getenv("SUDO_USER") + if curUserGrp == "" { + curUser, err := user.Current() + if err != nil { + return fmt.Errorf("failed to add service user to group due to error: [%s]", err.Error()) + } + curUserGrp = curUser.Username + } + //add current user to serviceUser group - usermodCmd := exec.Command("sudo", "usermod", "-aG", sudoUser, serviceUser) + usermodCmd := exec.Command("sudo", "usermod", "-aG", curUserGrp, serviceUser) err = usermodCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) From dbce110f5a39fd6951596265025b0218387fb857 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 22 Nov 2024 10:34:55 -0700 Subject: [PATCH 20/68] adjusted how the service user gets assigned the appropriate group for congfile access --- cmd/service_linux.go | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 000add7e8..d41ab231a 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -34,6 +34,7 @@ import ( "os/exec" "os/user" "strings" + "syscall" "github.com/Seagate/cloudfuse/common" "github.com/spf13/cobra" @@ -103,7 +104,7 @@ var installCmd = &cobra.Command{ } //create the new user and set permissions - err = setUser("CloudfuseUser", mountPath) + err = setUser("CloudfuseUser", mountPath, configPath) if err != nil { fmt.Println("Error setting permissions for user:", err) return err @@ -176,7 +177,7 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er # Under the hood Type=forking - ExecStart=/usr/bin/cloudfuse mount ${MountingPoint} --config-file=${ConfigFile} + ExecStart=/usr/bin/cloudfuse mount ${MountingPoint} --config-file=${ConfigFile} -o allow_other ExecStop=/usr/bin/fusermount -u ${MountingPoint} -z [Install] @@ -208,7 +209,7 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er return serviceName, nil } -func setUser(serviceUser string, mountPath string) error { +func setUser(serviceUser string, mountPath string, configPath string) error { _, err := user.Lookup(serviceUser) if err != nil { if strings.Contains(err.Error(), "unknown user") { @@ -219,17 +220,26 @@ func setUser(serviceUser string, mountPath string) error { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } - curUserGrp := os.Getenv("SUDO_USER") - if curUserGrp == "" { - curUser, err := user.Current() - if err != nil { - return fmt.Errorf("failed to add service user to group due to error: [%s]", err.Error()) - } - curUserGrp = curUser.Username + configFileInfo, err := os.Stat(configPath) + if err != nil { + return fmt.Errorf("Failed to stat file: %v", err) + } + + // Get file's group ID + stat, ok := configFileInfo.Sys().(*syscall.Stat_t) + if !ok { + return fmt.Errorf("Failed to get file system stats") + } + groupID := stat.Gid + + // Get group name + group, err := user.LookupGroupId(fmt.Sprint(groupID)) + if err != nil { + return fmt.Errorf("Failed to lookup group: %v", err) } - //add current user to serviceUser group - usermodCmd := exec.Command("sudo", "usermod", "-aG", curUserGrp, serviceUser) + //add group to serviceUser group + usermodCmd := exec.Command("sudo", "usermod", "-aG", group.Name, serviceUser) err = usermodCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) From 429b77d07e483f41c6c7c222e7b009f5b18d4f37 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 22 Nov 2024 11:14:04 -0700 Subject: [PATCH 21/68] added --flag string support and added user specification support --- cmd/service_linux.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index d41ab231a..76b4a3dfa 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -41,8 +41,9 @@ import ( ) type serviceOptions struct { - ConfigFile string - MountPath string + ConfigFile string + MountPath string + ServiceUser string } var servOpts serviceOptions @@ -60,14 +61,16 @@ var serviceCmd = &cobra.Command{ }, } +var mountPath string +var configPath string +var serviceUser string var installCmd = &cobra.Command{ - Use: "install [mount path] [config path]", + Use: "install --mountPath= --configPath= --user=", Short: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", Long: "Installs a service file for a single mount with Cloudfuse which remounts any active previously active mounts on startup. elevated permissions.", SuggestFor: []string{"ins", "inst"}, Example: "cloudfuse service install", FlagErrorHandling: cobra.ExitOnError, - Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { // get current dir @@ -76,9 +79,6 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error: [%s]", err.Error()) } - mountPath := args[0] - configPath := args[1] - if strings.Contains(mountPath, ".") || strings.Contains(mountPath, "..") { mountPath = common.JoinUnixFilepath(dir, mountPath) } @@ -104,13 +104,13 @@ var installCmd = &cobra.Command{ } //create the new user and set permissions - err = setUser("CloudfuseUser", mountPath, configPath) + err = setUser(serviceUser, mountPath, configPath) if err != nil { fmt.Println("Error setting permissions for user:", err) return err } - serviceFile, err := newServiceFile(mountPath, configPath, dir) + serviceFile, err := newServiceFile(mountPath, configPath, serviceUser) if err != nil { return fmt.Errorf("error when attempting to create service file: [%s]", err.Error()) } @@ -160,8 +160,7 @@ var uninstallCmd = &cobra.Command{ //--------------- command section ends -func newServiceFile(mountPath string, configPath string, dir string) (string, error) { - +func newServiceFile(mountPath string, configPath string, serviceUser string) (string, error) { serviceTemplate := ` [Unit] Description=Cloudfuse is an open source project developed to provide a virtual filesystem backed by S3 or Azure storage. After=network-online.target @@ -169,7 +168,7 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er [Service] # User service will run as. - User=CloudfuseUser + User={{.ServiceUser}} # Path to the location Cloudfuse will mount to. Note this folder must currently exist. Environment=MountingPoint={{.MountPath}} # Path to the configuration file. @@ -185,8 +184,9 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er ` config := serviceOptions{ - ConfigFile: configPath, - MountPath: mountPath, + ConfigFile: configPath, + MountPath: mountPath, + ServiceUser: serviceUser, } tmpl, err := template.New("service").Parse(serviceTemplate) @@ -194,7 +194,7 @@ func newServiceFile(mountPath string, configPath string, dir string) (string, er fmt.Errorf("error creating new service file: [%s]", err.Error()) } - folderList := strings.Split(common.JoinUnixFilepath(dir, mountPath), "/") + folderList := strings.Split(mountPath, "/") serviceName := folderList[len(folderList)-1] + ".service" newFile, err := os.Create("/etc/systemd/system/" + serviceName) if err != nil { @@ -264,5 +264,8 @@ func setUser(serviceUser string, mountPath string, configPath string) error { func init() { rootCmd.AddCommand(serviceCmd) serviceCmd.AddCommand(installCmd) + installCmd.Flags().StringVar(&mountPath, "mount-path", "", "Input mount path") + installCmd.Flags().StringVar(&configPath, "config-file", "", "Input config file") + installCmd.Flags().StringVar(&serviceUser, "user", "CloudfuseUser", "Input service user") serviceCmd.AddCommand(uninstallCmd) } From ec849dba48c81f863de2aa2c5744d36cd0f2c9e1 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 22 Nov 2024 11:14:42 -0700 Subject: [PATCH 22/68] removed servOpts var --- cmd/service_linux.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 76b4a3dfa..aa57964cc 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -46,8 +46,6 @@ type serviceOptions struct { ServiceUser string } -var servOpts serviceOptions - // Section defining all the command that we have in secure feature var serviceCmd = &cobra.Command{ Use: "service", From 4d9390a619f57625402532c5813acdcd98b39437 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 22 Nov 2024 12:10:42 -0700 Subject: [PATCH 23/68] added required flags and unsilenced usage with cobra --- cmd/service_linux.go | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index aa57964cc..60cf7033a 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -63,11 +63,11 @@ var mountPath string var configPath string var serviceUser string var installCmd = &cobra.Command{ - Use: "install --mountPath= --configPath= --user=", + Use: "install", Short: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", Long: "Installs a service file for a single mount with Cloudfuse which remounts any active previously active mounts on startup. elevated permissions.", SuggestFor: []string{"ins", "inst"}, - Example: "cloudfuse service install", + Example: "cloudfuse service install --mount-path= --config-file= --user=", FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { @@ -130,28 +130,44 @@ var installCmd = &cobra.Command{ }, } +var serviceName string var uninstallCmd = &cobra.Command{ Use: "uninstall", Short: "Uninstall the startup process for Cloudfuse. Requires running as admin.", Long: "Uninstall the startup process for Cloudfuse. Requires running as admin.", SuggestFor: []string{"uninst", "uninstal"}, - Example: "cloudfuse service uninstall", + Example: "cloudfuse service uninstall --mount-path=", FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { - // 1. find and remove cloudfuse.service from /etc/systemd/system and run systemctl daemon-reload - removeFileCmd := exec.Command("sudo", "rm", "/etc/systemd/system/cloudfuse.service") + // get absolute path of provided relative mount path + if strings.Contains(serviceName, ".") || strings.Contains(serviceName, "..") { + dir, err := os.Getwd() + if err != nil { + return fmt.Errorf("error: [%s]", err.Error()) + } + mountPath = common.JoinUnixFilepath(dir, serviceName) + + } + + // get service file name and service file path + folderList := strings.Split(serviceName, "/") + serviceName = folderList[len(folderList)-1] + ".service" + servicePath := "/etc/systemd/system/" + serviceName + + // delete service file + removeFileCmd := exec.Command("sudo", "rm", servicePath) err := removeFileCmd.Run() if err != nil { - return fmt.Errorf("failed to delete cloudfuse.service file from /etc/systemd/system due to following error: [%s]", err.Error()) + return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system due to following error: [%s]", err.Error()) } + // reload daemon systemctlDaemonReloadCmd := exec.Command("sudo", "systemctl", "daemon-reload") err = systemctlDaemonReloadCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) } - return nil }, } @@ -261,9 +277,14 @@ func setUser(serviceUser string, mountPath string, configPath string) error { func init() { rootCmd.AddCommand(serviceCmd) + rootCmd.SilenceUsage = false serviceCmd.AddCommand(installCmd) installCmd.Flags().StringVar(&mountPath, "mount-path", "", "Input mount path") installCmd.Flags().StringVar(&configPath, "config-file", "", "Input config file") installCmd.Flags().StringVar(&serviceUser, "user", "CloudfuseUser", "Input service user") + installCmd.MarkFlagRequired("mount-path") + installCmd.MarkFlagRequired("config-file") serviceCmd.AddCommand(uninstallCmd) + uninstallCmd.Flags().StringVar(&serviceName, "mount-path", "", "Input mount path") + uninstallCmd.MarkFlagRequired("mount-path") } From ec389579ebfe50933981bf57b83f15d645a3d5bb Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 22 Nov 2024 12:35:27 -0700 Subject: [PATCH 24/68] support servcie user already existing and setting permissions as needed. --- cmd/service_linux.go | 85 +++++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 60cf7033a..40388faaf 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -224,7 +224,44 @@ func newServiceFile(mountPath string, configPath string, serviceUser string) (st } func setUser(serviceUser string, mountPath string, configPath string) error { - _, err := user.Lookup(serviceUser) + + configFileInfo, err := os.Stat(configPath) + if err != nil { + return fmt.Errorf("failed to stat file: %v", err) + } + + // Get file's group ID + stat, ok := configFileInfo.Sys().(*syscall.Stat_t) + if !ok { + return fmt.Errorf("failed to get file system stats") + } + configGroupID := stat.Gid + + // Get configFileGroup name + configFileGroup, err := user.LookupGroupId(fmt.Sprint(configGroupID)) + if err != nil { + return fmt.Errorf("failed to lookup group: %v", err) + } + + mountPathInfo, err := os.Stat(mountPath) + if err != nil { + return fmt.Errorf("failed to stat file: %v", err) + } + + // Get file's group ID + stat, ok = mountPathInfo.Sys().(*syscall.Stat_t) + if !ok { + return fmt.Errorf("failed to get file system stats") + } + mountGroupID := stat.Gid + + // Get configFileGroup name + mountPathGroup, err := user.LookupGroupId(fmt.Sprint(mountGroupID)) + if err != nil { + return fmt.Errorf("failed to lookup group: %v", err) + } + + _, err = user.Lookup(serviceUser) if err != nil { if strings.Contains(err.Error(), "unknown user") { //create the user @@ -234,32 +271,19 @@ func setUser(serviceUser string, mountPath string, configPath string) error { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } - configFileInfo, err := os.Stat(configPath) - if err != nil { - return fmt.Errorf("Failed to stat file: %v", err) - } - - // Get file's group ID - stat, ok := configFileInfo.Sys().(*syscall.Stat_t) - if !ok { - return fmt.Errorf("Failed to get file system stats") - } - groupID := stat.Gid - - // Get group name - group, err := user.LookupGroupId(fmt.Sprint(groupID)) + //add group to serviceUser group + usermodCmd := exec.Command("sudo", "usermod", "-aG", configFileGroup.Name, serviceUser) + err = usermodCmd.Run() if err != nil { - return fmt.Errorf("Failed to lookup group: %v", err) + return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } - - //add group to serviceUser group - usermodCmd := exec.Command("sudo", "usermod", "-aG", group.Name, serviceUser) + usermodCmd = exec.Command("sudo", "usermod", "-aG", mountPathGroup.Name, serviceUser) err = usermodCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } - // set set folder permission on the mount path + //set set folder permission on the mount path chmodCmd := exec.Command("sudo", "chmod", "770", mountPath) err = chmodCmd.Run() if err != nil { @@ -269,7 +293,28 @@ func setUser(serviceUser string, mountPath string, configPath string) error { } else { fmt.Printf("An error occurred: %v\n", err) } + } else { + //add group to serviceUser group + usermodCmd := exec.Command("sudo", "usermod", "-aG", configFileGroup.Name, serviceUser) + err = usermodCmd.Run() + if err != nil { + return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) + } + usermodCmd = exec.Command("sudo", "usermod", "-aG", mountPathGroup.Name, serviceUser) + err = usermodCmd.Run() + if err != nil { + return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) + } + + //set set folder permission on the mount path + chmodCmd := exec.Command("sudo", "chmod", "770", mountPath) + err = chmodCmd.Run() + if err != nil { + return fmt.Errorf("failed set permisions on mount path due to following error: [%s]", err.Error()) + } + } + return nil } From bf51d9ba7fedf7b428014361af50228ca77253d6 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 19 Dec 2024 11:45:13 -0700 Subject: [PATCH 25/68] updated long install description --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 40388faaf..b4f6e6e98 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -65,7 +65,7 @@ var serviceUser string var installCmd = &cobra.Command{ Use: "install", Short: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", - Long: "Installs a service file for a single mount with Cloudfuse which remounts any active previously active mounts on startup. elevated permissions.", + Long: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", SuggestFor: []string{"ins", "inst"}, Example: "cloudfuse service install --mount-path= --config-file= --user=", FlagErrorHandling: cobra.ExitOnError, From 5ca9a397cf9872fa1b0e8f79fae1d72de5cc3221 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 19 Dec 2024 11:59:24 -0700 Subject: [PATCH 26/68] replaced relative path support with filepath.IsAbs --- cmd/service_linux.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index b4f6e6e98..3f77f840f 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -33,6 +33,7 @@ import ( "os" "os/exec" "os/user" + "path/filepath" "strings" "syscall" @@ -77,12 +78,11 @@ var installCmd = &cobra.Command{ return fmt.Errorf("error: [%s]", err.Error()) } - if strings.Contains(mountPath, ".") || strings.Contains(mountPath, "..") { - mountPath = common.JoinUnixFilepath(dir, mountPath) + if !filepath.IsAbs(mountPath) { + mountPath = filepath.Clean(mountPath) } - - if strings.Contains(configPath, ".") || strings.Contains(configPath, "..") { - configPath = common.JoinUnixFilepath(dir, configPath) + if !filepath.IsAbs(configPath) { + configPath = filepath.Clean(configPath) } mountExists := common.DirectoryExists(mountPath) From b6bdefbf3995027b56fc7783ef0cf5ed46a35044 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 19 Dec 2024 12:00:06 -0700 Subject: [PATCH 27/68] removed "error" from error msg --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 3f77f840f..8a2bd1722 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -87,7 +87,7 @@ var installCmd = &cobra.Command{ mountExists := common.DirectoryExists(mountPath) if !mountExists { - return fmt.Errorf("error, the mount path provided does not exist") + return fmt.Errorf("the mount path provided does not exist") // TODO: add useage output upon failure with input } isDirEmpty := common.IsDirectoryEmpty(mountPath) From 3f78e7ea6dc13d0940a1d8dbbf954fe9c83ffee2 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 19 Dec 2024 12:04:13 -0700 Subject: [PATCH 28/68] removed mount path empty check --- cmd/service_linux.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 8a2bd1722..cabfc48fc 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -90,11 +90,7 @@ var installCmd = &cobra.Command{ return fmt.Errorf("the mount path provided does not exist") // TODO: add useage output upon failure with input } - isDirEmpty := common.IsDirectoryEmpty(mountPath) - if !isDirEmpty { - return fmt.Errorf("error, the mount path provided is not empty") - // TODO: add useage output upon failure with input - } + // TODO: consider logging a warning if the mount path is empty _, err = os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { From 63b1be94350dd09d452159a23290da1e41720962 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 19 Dec 2024 12:05:30 -0700 Subject: [PATCH 29/68] removed old TODO usage comment --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index cabfc48fc..bb2d1e02b 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -94,7 +94,7 @@ var installCmd = &cobra.Command{ _, err = os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { - return fmt.Errorf("error, the configfile path provided does not exist") // TODO: add useage output upon failure with input + return fmt.Errorf("error, the configfile path provided does not exist") } //create the new user and set permissions From ac1a87074f3407bb924185e3e5aa49512c2f3d69 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 19 Dec 2024 12:07:15 -0700 Subject: [PATCH 30/68] updated short and long descriptions for uninstall --- cmd/service_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index bb2d1e02b..6b12bf306 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -129,8 +129,8 @@ var installCmd = &cobra.Command{ var serviceName string var uninstallCmd = &cobra.Command{ Use: "uninstall", - Short: "Uninstall the startup process for Cloudfuse. Requires running as admin.", - Long: "Uninstall the startup process for Cloudfuse. Requires running as admin.", + Short: "Uninstall a startup process for Cloudfuse.", + Long: "Uninstall a startup process for Cloudfuse.", SuggestFor: []string{"uninst", "uninstal"}, Example: "cloudfuse service uninstall --mount-path=", FlagErrorHandling: cobra.ExitOnError, From 43fd36de0079bbe9c7299524764e7ece8a10d314 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 20 Dec 2024 11:16:21 -0700 Subject: [PATCH 31/68] removed space before "[unit]" in template --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 6b12bf306..574284411 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -171,7 +171,7 @@ var uninstallCmd = &cobra.Command{ //--------------- command section ends func newServiceFile(mountPath string, configPath string, serviceUser string) (string, error) { - serviceTemplate := ` [Unit] + serviceTemplate := `[Unit] Description=Cloudfuse is an open source project developed to provide a virtual filesystem backed by S3 or Azure storage. After=network-online.target Requires=network-online.target From dac35d0eb776f9d3536f2620c2db427fd8d5a83a Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 20 Dec 2024 11:21:05 -0700 Subject: [PATCH 32/68] removed unused dir variable --- cmd/service_linux.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 574284411..aeb28ee29 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -71,13 +71,6 @@ var installCmd = &cobra.Command{ Example: "cloudfuse service install --mount-path= --config-file= --user=", FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { - - // get current dir - dir, err := os.Getwd() - if err != nil { - return fmt.Errorf("error: [%s]", err.Error()) - } - if !filepath.IsAbs(mountPath) { mountPath = filepath.Clean(mountPath) } @@ -180,9 +173,9 @@ func newServiceFile(mountPath string, configPath string, serviceUser string) (st # User service will run as. User={{.ServiceUser}} # Path to the location Cloudfuse will mount to. Note this folder must currently exist. - Environment=MountingPoint={{.MountPath}} + MountingPoint={{.MountPath}} # Path to the configuration file. - Environment=ConfigFile={{.ConfigFile}} + ConfigFile={{.ConfigFile}} # Under the hood Type=forking From 77a1eeafbb794a88693e21e169ccffaad70d5f02 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 20 Dec 2024 11:21:29 -0700 Subject: [PATCH 33/68] initiated err variable --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index aeb28ee29..ee8a50dff 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -85,7 +85,7 @@ var installCmd = &cobra.Command{ } // TODO: consider logging a warning if the mount path is empty - _, err = os.Stat(configPath) + _, err := os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error, the configfile path provided does not exist") } From ae54de514484a45cb52a9ab917c7a34860e3ab7d Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 20 Dec 2024 11:29:32 -0700 Subject: [PATCH 34/68] corrected filepath.Abs usage --- cmd/service_linux.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index ee8a50dff..9ea65e045 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -71,11 +71,18 @@ var installCmd = &cobra.Command{ Example: "cloudfuse service install --mount-path= --config-file= --user=", FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { + var err error if !filepath.IsAbs(mountPath) { - mountPath = filepath.Clean(mountPath) + mountPath, err = filepath.Abs(mountPath) + if err != nil { + return fmt.Errorf("couldn't format the mount path string") + } } if !filepath.IsAbs(configPath) { - configPath = filepath.Clean(configPath) + configPath, err = filepath.Abs(configPath) + if err != nil { + return fmt.Errorf("couldn't format the config path string") + } } mountExists := common.DirectoryExists(mountPath) @@ -85,7 +92,7 @@ var installCmd = &cobra.Command{ } // TODO: consider logging a warning if the mount path is empty - _, err := os.Stat(configPath) + _, err = os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error, the configfile path provided does not exist") } From a07dd71d2b8163c52debc9e163b1a8ee33577cc5 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Fri, 20 Dec 2024 14:36:08 -0700 Subject: [PATCH 35/68] set up wrapper function for abs path --- cmd/service_linux.go | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 9ea65e045..ab2fae957 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -71,19 +71,9 @@ var installCmd = &cobra.Command{ Example: "cloudfuse service install --mount-path= --config-file= --user=", FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { - var err error - if !filepath.IsAbs(mountPath) { - mountPath, err = filepath.Abs(mountPath) - if err != nil { - return fmt.Errorf("couldn't format the mount path string") - } - } - if !filepath.IsAbs(configPath) { - configPath, err = filepath.Abs(configPath) - if err != nil { - return fmt.Errorf("couldn't format the config path string") - } - } + + mountPath = getAbsPath(mountPath) + configPath = getAbsPath(configPath) mountExists := common.DirectoryExists(mountPath) if !mountExists { @@ -92,7 +82,7 @@ var installCmd = &cobra.Command{ } // TODO: consider logging a warning if the mount path is empty - _, err = os.Stat(configPath) + _, err := os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error, the configfile path provided does not exist") } @@ -316,6 +306,18 @@ func setUser(serviceUser string, mountPath string, configPath string) error { //TODO: add wrapper function for collecting data, creating user, setting default paths, running commands. +func getAbsPath(leaf string) string { + var absPath string + var err error + if !filepath.IsAbs(leaf) { + absPath, err = filepath.Abs(leaf) + if err != nil { + return fmt.Errorf("couldn't format the path string") + } + } + return absPath +} + func init() { rootCmd.AddCommand(serviceCmd) rootCmd.SilenceUsage = false From 2c760f7f43b2fedf35103b6f140b394b1abf0e2d Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 11:12:46 -0700 Subject: [PATCH 36/68] added error return for getAbsPath() --- cmd/service_linux.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index ab2fae957..0b704c7c5 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -306,16 +306,16 @@ func setUser(serviceUser string, mountPath string, configPath string) error { //TODO: add wrapper function for collecting data, creating user, setting default paths, running commands. -func getAbsPath(leaf string) string { +func getAbsPath(leaf string) (string, error) { var absPath string var err error if !filepath.IsAbs(leaf) { absPath, err = filepath.Abs(leaf) if err != nil { - return fmt.Errorf("couldn't format the path string") + return "", fmt.Errorf("couldn't format the path string", err.Error()) } } - return absPath + return absPath, err } func init() { From a908035c6040b158df49237d4f8f227ae795aebb Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 11:14:18 -0700 Subject: [PATCH 37/68] added err vars to calling getAbsPath() --- cmd/service_linux.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 0b704c7c5..af1aa2c54 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -72,8 +72,8 @@ var installCmd = &cobra.Command{ FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { - mountPath = getAbsPath(mountPath) - configPath = getAbsPath(configPath) + mountPath, err := getAbsPath(mountPath) + configPath, err := getAbsPath(configPath) mountExists := common.DirectoryExists(mountPath) if !mountExists { @@ -82,7 +82,7 @@ var installCmd = &cobra.Command{ } // TODO: consider logging a warning if the mount path is empty - _, err := os.Stat(configPath) + _, err = os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error, the configfile path provided does not exist") } From 72e00fd04ee3314c54df6652e70922bbacaff6c6 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 11:18:57 -0700 Subject: [PATCH 38/68] moved string variables out of global scope --- cmd/service_linux.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index af1aa2c54..fe5c0cfc7 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -60,9 +60,6 @@ var serviceCmd = &cobra.Command{ }, } -var mountPath string -var configPath string -var serviceUser string var installCmd = &cobra.Command{ Use: "install", Short: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", @@ -72,8 +69,17 @@ var installCmd = &cobra.Command{ FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { - mountPath, err := getAbsPath(mountPath) - configPath, err := getAbsPath(configPath) + var mountPath string + var configPath string + var err error + mountPath, err = getAbsPath(mountPath) + if err != nil { + return err + } + configPath, err = getAbsPath(configPath) + if err != nil { + return err + } mountExists := common.DirectoryExists(mountPath) if !mountExists { @@ -88,6 +94,7 @@ var installCmd = &cobra.Command{ } //create the new user and set permissions + var serviceUser string err = setUser(serviceUser, mountPath, configPath) if err != nil { fmt.Println("Error setting permissions for user:", err) From a8e503c7830f96c101f89aa2869540141d7fbb28 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 11:22:03 -0700 Subject: [PATCH 39/68] added a todo note for uninstall's mountPath --- cmd/service_linux.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index fe5c0cfc7..7b7d6b73e 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -135,11 +135,12 @@ var uninstallCmd = &cobra.Command{ // get absolute path of provided relative mount path if strings.Contains(serviceName, ".") || strings.Contains(serviceName, "..") { + // TODO: since the serviceName is the same as the mount folder name, we can use the getAbsPath() for our mount path dir, err := os.Getwd() if err != nil { return fmt.Errorf("error: [%s]", err.Error()) } - mountPath = common.JoinUnixFilepath(dir, serviceName) + mountPath := common.JoinUnixFilepath(dir, serviceName) } From dbca1a0f7a30f5c2bc47f732cc09a0438604453b Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 11:33:41 -0700 Subject: [PATCH 40/68] added "cloudfuse-" to service filename --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 7b7d6b73e..b9a50ea18 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -203,7 +203,7 @@ func newServiceFile(mountPath string, configPath string, serviceUser string) (st } folderList := strings.Split(mountPath, "/") - serviceName := folderList[len(folderList)-1] + ".service" + serviceName := "cloudfuse-" + folderList[len(folderList)-1] + ".service" newFile, err := os.Create("/etc/systemd/system/" + serviceName) if err != nil { return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) From 17b6d81181ca4c194a791f326c4eb6555eb4ed4c Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 11:40:26 -0700 Subject: [PATCH 41/68] moved string vars back into global scope so init() can use them --- cmd/service_linux.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index b9a50ea18..7b30f3189 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -60,6 +60,10 @@ var serviceCmd = &cobra.Command{ }, } +var mountPath string +var configPath string +var serviceUser string + var installCmd = &cobra.Command{ Use: "install", Short: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", @@ -69,8 +73,6 @@ var installCmd = &cobra.Command{ FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { - var mountPath string - var configPath string var err error mountPath, err = getAbsPath(mountPath) if err != nil { @@ -94,7 +96,6 @@ var installCmd = &cobra.Command{ } //create the new user and set permissions - var serviceUser string err = setUser(serviceUser, mountPath, configPath) if err != nil { fmt.Println("Error setting permissions for user:", err) @@ -140,7 +141,7 @@ var uninstallCmd = &cobra.Command{ if err != nil { return fmt.Errorf("error: [%s]", err.Error()) } - mountPath := common.JoinUnixFilepath(dir, serviceName) + mountPath = common.JoinUnixFilepath(dir, serviceName) } From 0a195ae33da10b62c369f7d16c9d75fb8e7d8cb2 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 12:06:34 -0700 Subject: [PATCH 42/68] used getAbsPath in uninstall and revised logic on finding and deletnig service file --- cmd/service_linux.go | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 7b30f3189..2e276903a 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -135,26 +135,23 @@ var uninstallCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { // get absolute path of provided relative mount path - if strings.Contains(serviceName, ".") || strings.Contains(serviceName, "..") { - // TODO: since the serviceName is the same as the mount folder name, we can use the getAbsPath() for our mount path - dir, err := os.Getwd() - if err != nil { - return fmt.Errorf("error: [%s]", err.Error()) - } - mountPath = common.JoinUnixFilepath(dir, serviceName) - + var err error + mountPath, err = getAbsPath(serviceName) + if err != nil { + return err } - // get service file name and service file path - folderList := strings.Split(serviceName, "/") - serviceName = folderList[len(folderList)-1] + ".service" - servicePath := "/etc/systemd/system/" + serviceName + // TODO: take the serviceName and simply find "cloudfuse-serviceName.service" in the /etc/systemd/system - // delete service file - removeFileCmd := exec.Command("sudo", "rm", servicePath) - err := removeFileCmd.Run() - if err != nil { - return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system due to following error: [%s]", err.Error()) + // get service file name and service file path + serviceFile := fmt.Sprintf("cloudfuse-" + serviceName + ".service") + serviceFilePath := "/etc/systemd/system/" + serviceFile + if _, err := os.Stat(serviceFilePath); err == nil { + removeFileCmd := exec.Command("sudo", "rm", serviceFilePath) + err := removeFileCmd.Run() + if err != nil { + return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system due to following error: [%s]", err.Error()) + } } // reload daemon @@ -315,6 +312,7 @@ func setUser(serviceUser string, mountPath string, configPath string) error { //TODO: add wrapper function for collecting data, creating user, setting default paths, running commands. +// takes a file or folder name and returns its absolute path func getAbsPath(leaf string) (string, error) { var absPath string var err error From 26aab67f84e9609c94d6289835ba9aaa295817e1 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 13:40:29 -0700 Subject: [PATCH 43/68] corrected how serviceFile is determined for uninstall --- cmd/service_linux.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 2e276903a..9507cd4d5 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -141,10 +141,8 @@ var uninstallCmd = &cobra.Command{ return err } - // TODO: take the serviceName and simply find "cloudfuse-serviceName.service" in the /etc/systemd/system - - // get service file name and service file path - serviceFile := fmt.Sprintf("cloudfuse-" + serviceName + ".service") + folderList := strings.Split(mountPath, "/") + serviceFile := "cloudfuse-" + folderList[len(folderList)-1] + ".service" serviceFilePath := "/etc/systemd/system/" + serviceFile if _, err := os.Stat(serviceFilePath); err == nil { removeFileCmd := exec.Command("sudo", "rm", serviceFilePath) @@ -152,6 +150,8 @@ var uninstallCmd = &cobra.Command{ if err != nil { return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system due to following error: [%s]", err.Error()) } + } else if os.IsNotExist(err) { + return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system due to following error: [%s]", err.Error()) } // reload daemon From 453f21655b56174e99e248af1d4e78bbf80af994 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 13:42:41 -0700 Subject: [PATCH 44/68] default service user value set to 'cloudfuse' --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 9507cd4d5..c33059b30 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -331,7 +331,7 @@ func init() { serviceCmd.AddCommand(installCmd) installCmd.Flags().StringVar(&mountPath, "mount-path", "", "Input mount path") installCmd.Flags().StringVar(&configPath, "config-file", "", "Input config file") - installCmd.Flags().StringVar(&serviceUser, "user", "CloudfuseUser", "Input service user") + installCmd.Flags().StringVar(&serviceUser, "user", "cloudfuse", "Input service user") installCmd.MarkFlagRequired("mount-path") installCmd.MarkFlagRequired("config-file") serviceCmd.AddCommand(uninstallCmd) From 3b14cd5531a6853d6e2973b7f1b46adeb67a6afe Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 13:44:46 -0700 Subject: [PATCH 45/68] changed useradd -r to -m instead --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index c33059b30..bfe0b0048 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -257,7 +257,7 @@ func setUser(serviceUser string, mountPath string, configPath string) error { if err != nil { if strings.Contains(err.Error(), "unknown user") { //create the user - userAddCmd := exec.Command("sudo", "useradd", "-m", serviceUser) + userAddCmd := exec.Command("sudo", "useradd", "-r", serviceUser) err = userAddCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) From 5113875a06f3c558e720b2f3b276ce83ef4e61fb Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 13:48:11 -0700 Subject: [PATCH 46/68] added TODO note on suggesting usermod commands instead of implementing them directly --- cmd/service_linux.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index bfe0b0048..298b0b5f5 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -253,6 +253,8 @@ func setUser(serviceUser string, mountPath string, configPath string) error { return fmt.Errorf("failed to lookup group: %v", err) } + // TODO: use configFileGroup and mountPathGroup to check if service user has these groups. complain / warn if it doesn't + _, err = user.Lookup(serviceUser) if err != nil { if strings.Contains(err.Error(), "unknown user") { From 3b5b42f1050513f82dd97ca87baa9c36cff010ed Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 7 Jan 2025 14:12:52 -0700 Subject: [PATCH 47/68] replaced usermod commands with println output suggestions --- cmd/service_linux.go | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 298b0b5f5..c2f7e5be3 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -253,8 +253,6 @@ func setUser(serviceUser string, mountPath string, configPath string) error { return fmt.Errorf("failed to lookup group: %v", err) } - // TODO: use configFileGroup and mountPathGroup to check if service user has these groups. complain / warn if it doesn't - _, err = user.Lookup(serviceUser) if err != nil { if strings.Contains(err.Error(), "unknown user") { @@ -265,30 +263,20 @@ func setUser(serviceUser string, mountPath string, configPath string) error { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } - //add group to serviceUser group - usermodCmd := exec.Command("sudo", "usermod", "-aG", configFileGroup.Name, serviceUser) - err = usermodCmd.Run() - if err != nil { - return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) - } - usermodCmd = exec.Command("sudo", "usermod", "-aG", mountPathGroup.Name, serviceUser) - err = usermodCmd.Run() - if err != nil { - return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) - } + fmt.Println("user " + serviceUser + " has been created") - //set set folder permission on the mount path - chmodCmd := exec.Command("sudo", "chmod", "770", mountPath) - err = chmodCmd.Run() - if err != nil { - return fmt.Errorf("failed set permisions on mount path due to following error: [%s]", err.Error()) - } + //suggest usermod -aG commands here to the end user. + fmt.Println("groups: " + configFileGroup.Name + " and " + mountPathGroup.Name + " need to be added to the user, " + serviceUser) + + // suggest the chmod 770 command + fmt.Println("please ensure the " + mountPathGroup.Name + "has read and write permissions for " + mountPath) } else { fmt.Printf("An error occurred: %v\n", err) } } else { - //add group to serviceUser group + + // TODO: use configFileGroup and mountPathGroup to check if service user has these groups. complain / warn if it doesn't usermodCmd := exec.Command("sudo", "usermod", "-aG", configFileGroup.Name, serviceUser) err = usermodCmd.Run() if err != nil { From 6bf3c5e6c3488b70e077ccee875f86feaff3d2cc Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 9 Jan 2025 11:41:30 -0700 Subject: [PATCH 48/68] replaced perscriptive suggestion for a generic one --- cmd/service_linux.go | 145 ++++++++++++++----------------------------- 1 file changed, 46 insertions(+), 99 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index c2f7e5be3..07107ffd3 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -35,7 +35,6 @@ import ( "os/user" "path/filepath" "strings" - "syscall" "github.com/Seagate/cloudfuse/common" "github.com/spf13/cobra" @@ -167,27 +166,28 @@ var uninstallCmd = &cobra.Command{ //--------------- command section ends func newServiceFile(mountPath string, configPath string, serviceUser string) (string, error) { - serviceTemplate := `[Unit] - Description=Cloudfuse is an open source project developed to provide a virtual filesystem backed by S3 or Azure storage. - After=network-online.target - Requires=network-online.target - - [Service] - # User service will run as. - User={{.ServiceUser}} - # Path to the location Cloudfuse will mount to. Note this folder must currently exist. - MountingPoint={{.MountPath}} - # Path to the configuration file. - ConfigFile={{.ConfigFile}} - - # Under the hood - Type=forking - ExecStart=/usr/bin/cloudfuse mount ${MountingPoint} --config-file=${ConfigFile} -o allow_other - ExecStop=/usr/bin/fusermount -u ${MountingPoint} -z - - [Install] - WantedBy=multi-user.target - ` + serviceTemplate := ` +[Unit] +Description=Cloudfuse is an open source project developed to provide a virtual filesystem backed by S3 or Azure storage. +After=network-online.target +Requires=network-online.target + +[Service] +# User service will run as. +User={{.ServiceUser}} +# Path to the location Cloudfuse will mount to. Note this folder must currently exist. +Environment=MountingPoint={{.MountPath}} +# Path to the configuration file. +Environment=ConfigFile={{.ConfigFile}} + +# Under the hood +Type=forking +ExecStart=/usr/bin/cloudfuse mount ${MountingPoint} --config-file=${ConfigFile} -o allow_other +ExecStop=/usr/bin/fusermount -u ${MountingPoint} -z + +[Install] +WantedBy=multi-user.target +` config := serviceOptions{ ConfigFile: configPath, @@ -197,14 +197,29 @@ func newServiceFile(mountPath string, configPath string, serviceUser string) (st tmpl, err := template.New("service").Parse(serviceTemplate) if err != nil { - fmt.Errorf("error creating new service file: [%s]", err.Error()) + return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } folderList := strings.Split(mountPath, "/") serviceName := "cloudfuse-" + folderList[len(folderList)-1] + ".service" - newFile, err := os.Create("/etc/systemd/system/" + serviceName) - if err != nil { - return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) + servicePath := "/etc/systemd/system/" + serviceName + + var newFile *os.File + if _, err = os.Stat(servicePath); os.IsNotExist(err) { + newFile, err = os.Create(servicePath) + if err != nil { + return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) + } + } else { + delServFileCmd := exec.Command("rm", servicePath) + err = delServFileCmd.Run() + if err != nil { + return "", fmt.Errorf("failed to replace the service file due to the following error: [%s]", err.Error()) + } + newFile, err = os.Create(servicePath) + if err != nil { + return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) + } } err = tmpl.Execute(newFile, config) @@ -216,92 +231,24 @@ func newServiceFile(mountPath string, configPath string, serviceUser string) (st } func setUser(serviceUser string, mountPath string, configPath string) error { - - configFileInfo, err := os.Stat(configPath) - if err != nil { - return fmt.Errorf("failed to stat file: %v", err) - } - - // Get file's group ID - stat, ok := configFileInfo.Sys().(*syscall.Stat_t) - if !ok { - return fmt.Errorf("failed to get file system stats") - } - configGroupID := stat.Gid - - // Get configFileGroup name - configFileGroup, err := user.LookupGroupId(fmt.Sprint(configGroupID)) - if err != nil { - return fmt.Errorf("failed to lookup group: %v", err) - } - - mountPathInfo, err := os.Stat(mountPath) - if err != nil { - return fmt.Errorf("failed to stat file: %v", err) - } - - // Get file's group ID - stat, ok = mountPathInfo.Sys().(*syscall.Stat_t) - if !ok { - return fmt.Errorf("failed to get file system stats") - } - mountGroupID := stat.Gid - - // Get configFileGroup name - mountPathGroup, err := user.LookupGroupId(fmt.Sprint(mountGroupID)) - if err != nil { - return fmt.Errorf("failed to lookup group: %v", err) - } - - _, err = user.Lookup(serviceUser) + _, err := user.Lookup(serviceUser) if err != nil { if strings.Contains(err.Error(), "unknown user") { - //create the user - userAddCmd := exec.Command("sudo", "useradd", "-r", serviceUser) + // create the user + userAddCmd := exec.Command("sudo", "useradd", "-m", serviceUser) err = userAddCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) } - fmt.Println("user " + serviceUser + " has been created") - - //suggest usermod -aG commands here to the end user. - fmt.Println("groups: " + configFileGroup.Name + " and " + mountPathGroup.Name + " need to be added to the user, " + serviceUser) - - // suggest the chmod 770 command - fmt.Println("please ensure the " + mountPathGroup.Name + "has read and write permissions for " + mountPath) - - } else { - fmt.Printf("An error occurred: %v\n", err) } - } else { - - // TODO: use configFileGroup and mountPathGroup to check if service user has these groups. complain / warn if it doesn't - usermodCmd := exec.Command("sudo", "usermod", "-aG", configFileGroup.Name, serviceUser) - err = usermodCmd.Run() - if err != nil { - return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) - } - usermodCmd = exec.Command("sudo", "usermod", "-aG", mountPathGroup.Name, serviceUser) - err = usermodCmd.Run() - if err != nil { - return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) - } - - //set set folder permission on the mount path - chmodCmd := exec.Command("sudo", "chmod", "770", mountPath) - err = chmodCmd.Run() - if err != nil { - return fmt.Errorf("failed set permisions on mount path due to following error: [%s]", err.Error()) - } - } + // advise on required permissions + fmt.Println("ensure the user, " + serviceUser + ", has the following access: \n" + mountPath + ": read, write, and execute \n" + configPath + ": read \n") return nil } -//TODO: add wrapper function for collecting data, creating user, setting default paths, running commands. - // takes a file or folder name and returns its absolute path func getAbsPath(leaf string) (string, error) { var absPath string From e4977a65e7fdd81c2075b90f4607d2186c678fdd Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 9 Jan 2025 11:47:11 -0700 Subject: [PATCH 49/68] removed sudo from commands --- cmd/service_linux.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 07107ffd3..aacbb0dd3 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -107,14 +107,14 @@ var installCmd = &cobra.Command{ } // run systemctl daemon-reload - systemctlDaemonReloadCmd := exec.Command("sudo", "systemctl", "daemon-reload") + systemctlDaemonReloadCmd := exec.Command("systemctl", "daemon-reload") err = systemctlDaemonReloadCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) } // Enable the service to start at system boot - systemctlEnableCmd := exec.Command("sudo", "systemctl", "enable", serviceFile) + systemctlEnableCmd := exec.Command("systemctl", "enable", serviceFile) err = systemctlEnableCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) @@ -144,7 +144,7 @@ var uninstallCmd = &cobra.Command{ serviceFile := "cloudfuse-" + folderList[len(folderList)-1] + ".service" serviceFilePath := "/etc/systemd/system/" + serviceFile if _, err := os.Stat(serviceFilePath); err == nil { - removeFileCmd := exec.Command("sudo", "rm", serviceFilePath) + removeFileCmd := exec.Command("rm", serviceFilePath) err := removeFileCmd.Run() if err != nil { return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system due to following error: [%s]", err.Error()) @@ -154,7 +154,7 @@ var uninstallCmd = &cobra.Command{ } // reload daemon - systemctlDaemonReloadCmd := exec.Command("sudo", "systemctl", "daemon-reload") + systemctlDaemonReloadCmd := exec.Command("systemctl", "daemon-reload") err = systemctlDaemonReloadCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) @@ -235,7 +235,7 @@ func setUser(serviceUser string, mountPath string, configPath string) error { if err != nil { if strings.Contains(err.Error(), "unknown user") { // create the user - userAddCmd := exec.Command("sudo", "useradd", "-m", serviceUser) + userAddCmd := exec.Command("useradd", "-m", serviceUser) err = userAddCmd.Run() if err != nil { return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) From d692d202558f3acc8a336e2ae148ecf4821bb6d7 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 9 Jan 2025 11:54:56 -0700 Subject: [PATCH 50/68] changed the service template to have mount and config set directly --- cmd/service_linux.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index aacbb0dd3..d37cb4aef 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -175,15 +175,11 @@ Requires=network-online.target [Service] # User service will run as. User={{.ServiceUser}} -# Path to the location Cloudfuse will mount to. Note this folder must currently exist. -Environment=MountingPoint={{.MountPath}} -# Path to the configuration file. -Environment=ConfigFile={{.ConfigFile}} # Under the hood Type=forking -ExecStart=/usr/bin/cloudfuse mount ${MountingPoint} --config-file=${ConfigFile} -o allow_other -ExecStop=/usr/bin/fusermount -u ${MountingPoint} -z +ExecStart=/usr/bin/cloudfuse mount {{.MountPath}} --config-file={{.ConfigFile}} -o allow_other +ExecStop=/usr/bin/fusermount -u {{.MountPath}} -z [Install] WantedBy=multi-user.target From bd54f84760fb9b8c4fd0ac7037507dc152b3f493 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 16 Jan 2025 11:27:30 -0700 Subject: [PATCH 51/68] removed isAbs check in getAbsPath() --- cmd/service_linux.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index d37cb4aef..ed69dacd1 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -249,11 +249,9 @@ func setUser(serviceUser string, mountPath string, configPath string) error { func getAbsPath(leaf string) (string, error) { var absPath string var err error - if !filepath.IsAbs(leaf) { - absPath, err = filepath.Abs(leaf) - if err != nil { - return "", fmt.Errorf("couldn't format the path string", err.Error()) - } + absPath, err = filepath.Abs(leaf) + if err != nil { + return "", fmt.Errorf("couldn't format the path string due to the following error [%s]", err.Error()) } return absPath, err } From 8e2c372b9b11aea32d9d84be35df95e2fff86d5c Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 16 Jan 2025 11:31:26 -0700 Subject: [PATCH 52/68] removed old TODO notes --- cmd/service_linux.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index ed69dacd1..fb5a10827 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -85,9 +85,7 @@ var installCmd = &cobra.Command{ mountExists := common.DirectoryExists(mountPath) if !mountExists { return fmt.Errorf("the mount path provided does not exist") - // TODO: add useage output upon failure with input } - // TODO: consider logging a warning if the mount path is empty _, err = os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { From 02541d3a696d0d59355f243fe3f724b2be7c7d78 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 16 Jan 2025 11:37:56 -0700 Subject: [PATCH 53/68] removed "--user" from example --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index fb5a10827..e65671087 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -68,7 +68,7 @@ var installCmd = &cobra.Command{ Short: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", Long: "Installs a service file for a single mount with Cloudfuse. Requires elevated permissions.", SuggestFor: []string{"ins", "inst"}, - Example: "cloudfuse service install --mount-path= --config-file= --user=", + Example: "cloudfuse service install --mount-path= --config-file=", FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { From 23ebf747998b7266f4298ecc681995678c635ad0 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 16 Jan 2025 11:54:22 -0700 Subject: [PATCH 54/68] changed service name to have full path with '-' characters --- cmd/service_linux.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index e65671087..243129574 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -137,9 +137,9 @@ var uninstallCmd = &cobra.Command{ if err != nil { return err } - - folderList := strings.Split(mountPath, "/") - serviceFile := "cloudfuse-" + folderList[len(folderList)-1] + ".service" + serviceName := strings.Replace(mountPath, "/", "-", -1) + serviceName = strings.TrimPrefix(serviceName, "-") + serviceFile := serviceName + ".service" serviceFilePath := "/etc/systemd/system/" + serviceFile if _, err := os.Stat(serviceFilePath); err == nil { removeFileCmd := exec.Command("rm", serviceFilePath) @@ -194,23 +194,24 @@ WantedBy=multi-user.target return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } - folderList := strings.Split(mountPath, "/") - serviceName := "cloudfuse-" + folderList[len(folderList)-1] + ".service" - servicePath := "/etc/systemd/system/" + serviceName + serviceName := strings.Replace(mountPath, "/", "-", -1) + serviceName = strings.TrimPrefix(serviceName, "-") + serviceFile := serviceName + ".service" + serviceFilePath := "/etc/systemd/system/" + serviceFile var newFile *os.File - if _, err = os.Stat(servicePath); os.IsNotExist(err) { - newFile, err = os.Create(servicePath) + if _, err = os.Stat(serviceFilePath); os.IsNotExist(err) { + newFile, err = os.Create(serviceFilePath) if err != nil { return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } } else { - delServFileCmd := exec.Command("rm", servicePath) + delServFileCmd := exec.Command("rm", serviceFilePath) err = delServFileCmd.Run() if err != nil { return "", fmt.Errorf("failed to replace the service file due to the following error: [%s]", err.Error()) } - newFile, err = os.Create(servicePath) + newFile, err = os.Create(serviceFilePath) if err != nil { return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } From 1ddeb0aa84fbdbd564772a4db97cf87296b376ec Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 16 Jan 2025 13:06:56 -0700 Subject: [PATCH 55/68] replaced cmd running 'rm' command with os.remove. --- cmd/service_linux.go | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 243129574..b68861edd 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -199,22 +199,15 @@ WantedBy=multi-user.target serviceFile := serviceName + ".service" serviceFilePath := "/etc/systemd/system/" + serviceFile + err = os.Remove(serviceFilePath) + if err != nil && !os.IsNotExist(err) { + return "", fmt.Errorf("failed to replace the service file due to the following error: [%s]", err.Error()) + } + var newFile *os.File - if _, err = os.Stat(serviceFilePath); os.IsNotExist(err) { - newFile, err = os.Create(serviceFilePath) - if err != nil { - return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) - } - } else { - delServFileCmd := exec.Command("rm", serviceFilePath) - err = delServFileCmd.Run() - if err != nil { - return "", fmt.Errorf("failed to replace the service file due to the following error: [%s]", err.Error()) - } - newFile, err = os.Create(serviceFilePath) - if err != nil { - return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) - } + newFile, err = os.Create(serviceFilePath) + if err != nil { + return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } err = tmpl.Execute(newFile, config) From 2b6a6bdc65d8509037c0b29c1fb69e7760db2a6a Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 23 Jan 2025 12:13:15 -0700 Subject: [PATCH 56/68] added wrapper function for generating service file name --- cmd/service_linux.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index b68861edd..71bc45838 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -99,7 +99,7 @@ var installCmd = &cobra.Command{ return err } - serviceFile, err := newServiceFile(mountPath, configPath, serviceUser) + serviceName, err := newService(mountPath, configPath, serviceUser) if err != nil { return fmt.Errorf("error when attempting to create service file: [%s]", err.Error()) } @@ -112,7 +112,7 @@ var installCmd = &cobra.Command{ } // Enable the service to start at system boot - systemctlEnableCmd := exec.Command("systemctl", "enable", serviceFile) + systemctlEnableCmd := exec.Command("systemctl", "enable", serviceName) err = systemctlEnableCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) @@ -137,10 +137,7 @@ var uninstallCmd = &cobra.Command{ if err != nil { return err } - serviceName := strings.Replace(mountPath, "/", "-", -1) - serviceName = strings.TrimPrefix(serviceName, "-") - serviceFile := serviceName + ".service" - serviceFilePath := "/etc/systemd/system/" + serviceFile + serviceName, serviceFilePath := getService(mountPath) if _, err := os.Stat(serviceFilePath); err == nil { removeFileCmd := exec.Command("rm", serviceFilePath) err := removeFileCmd.Run() @@ -163,7 +160,7 @@ var uninstallCmd = &cobra.Command{ //--------------- command section ends -func newServiceFile(mountPath string, configPath string, serviceUser string) (string, error) { +func newService(mountPath string, configPath string, serviceUser string) (string, error) { serviceTemplate := ` [Unit] Description=Cloudfuse is an open source project developed to provide a virtual filesystem backed by S3 or Azure storage. @@ -194,10 +191,7 @@ WantedBy=multi-user.target return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } - serviceName := strings.Replace(mountPath, "/", "-", -1) - serviceName = strings.TrimPrefix(serviceName, "-") - serviceFile := serviceName + ".service" - serviceFilePath := "/etc/systemd/system/" + serviceFile + serviceName, serviceFilePath := getService(mountPath) err = os.Remove(serviceFilePath) if err != nil && !os.IsNotExist(err) { @@ -237,6 +231,13 @@ func setUser(serviceUser string, mountPath string, configPath string) error { return nil } +func getService(mountPath string) (string, string) { + serviceName := strings.Replace(mountPath, "/", "-", -1) + serviceFile := "cloudfuse" + serviceName + ".service" + serviceFilePath := "/etc/systemd/system/" + serviceFile + return serviceName, serviceFilePath +} + // takes a file or folder name and returns its absolute path func getAbsPath(leaf string) (string, error) { var absPath string From 29e02cdac4560be1c8deec8a56ef57767b969683 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 23 Jan 2025 12:13:41 -0700 Subject: [PATCH 57/68] corrected getAbsPath argument --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 71bc45838..e439d65b9 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -133,7 +133,7 @@ var uninstallCmd = &cobra.Command{ // get absolute path of provided relative mount path var err error - mountPath, err = getAbsPath(serviceName) + mountPath, err = getAbsPath(mountPath) if err != nil { return err } From aff425307cfa36f33526b3c910fbc07a4fadf1d0 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 23 Jan 2025 12:24:48 -0700 Subject: [PATCH 58/68] removed spaces --- cmd/service_linux.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index e439d65b9..f1638e223 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -81,17 +81,14 @@ var installCmd = &cobra.Command{ if err != nil { return err } - mountExists := common.DirectoryExists(mountPath) if !mountExists { return fmt.Errorf("the mount path provided does not exist") } - _, err = os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error, the configfile path provided does not exist") } - //create the new user and set permissions err = setUser(serviceUser, mountPath, configPath) if err != nil { @@ -103,14 +100,12 @@ var installCmd = &cobra.Command{ if err != nil { return fmt.Errorf("error when attempting to create service file: [%s]", err.Error()) } - // run systemctl daemon-reload systemctlDaemonReloadCmd := exec.Command("systemctl", "daemon-reload") err = systemctlDaemonReloadCmd.Run() if err != nil { return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) } - // Enable the service to start at system boot systemctlEnableCmd := exec.Command("systemctl", "enable", serviceName) err = systemctlEnableCmd.Run() @@ -130,7 +125,6 @@ var uninstallCmd = &cobra.Command{ Example: "cloudfuse service uninstall --mount-path=", FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { - // get absolute path of provided relative mount path var err error mountPath, err = getAbsPath(mountPath) @@ -147,7 +141,6 @@ var uninstallCmd = &cobra.Command{ } else if os.IsNotExist(err) { return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system due to following error: [%s]", err.Error()) } - // reload daemon systemctlDaemonReloadCmd := exec.Command("systemctl", "daemon-reload") err = systemctlDaemonReloadCmd.Run() @@ -179,7 +172,6 @@ ExecStop=/usr/bin/fusermount -u {{.MountPath}} -z [Install] WantedBy=multi-user.target ` - config := serviceOptions{ ConfigFile: configPath, MountPath: mountPath, @@ -190,9 +182,7 @@ WantedBy=multi-user.target if err != nil { return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } - serviceName, serviceFilePath := getService(mountPath) - err = os.Remove(serviceFilePath) if err != nil && !os.IsNotExist(err) { return "", fmt.Errorf("failed to replace the service file due to the following error: [%s]", err.Error()) @@ -208,7 +198,6 @@ WantedBy=multi-user.target if err != nil { return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) } - return serviceName, nil } @@ -227,7 +216,6 @@ func setUser(serviceUser string, mountPath string, configPath string) error { } // advise on required permissions fmt.Println("ensure the user, " + serviceUser + ", has the following access: \n" + mountPath + ": read, write, and execute \n" + configPath + ": read \n") - return nil } From 6fbaadb0b711f621702804c543330125d37de717 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 28 Jan 2025 10:18:24 -0700 Subject: [PATCH 59/68] removed var declaration --- cmd/service_linux.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index f1638e223..df3dfa235 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -116,7 +116,6 @@ var installCmd = &cobra.Command{ }, } -var serviceName string var uninstallCmd = &cobra.Command{ Use: "uninstall", Short: "Uninstall a startup process for Cloudfuse.", @@ -244,7 +243,10 @@ func init() { installCmd.Flags().StringVar(&mountPath, "mount-path", "", "Input mount path") installCmd.Flags().StringVar(&configPath, "config-file", "", "Input config file") installCmd.Flags().StringVar(&serviceUser, "user", "cloudfuse", "Input service user") - installCmd.MarkFlagRequired("mount-path") + err := installCmd.MarkFlagRequired("mount-path") + if err != nil { + return + } installCmd.MarkFlagRequired("config-file") serviceCmd.AddCommand(uninstallCmd) uninstallCmd.Flags().StringVar(&serviceName, "mount-path", "", "Input mount path") From c8a7eb54b89632587f15dde216d6905b2a431361 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 28 Jan 2025 10:24:44 -0700 Subject: [PATCH 60/68] added error handle wrapper function for init() --- cmd/service_linux.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index df3dfa235..601955934 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -236,6 +236,12 @@ func getAbsPath(leaf string) (string, error) { return absPath, err } +func markFlagErrorChk(cmd *cobra.Command, flagName string) { + if err := cmd.MarkFlagRequired(flagName); err != nil { + panic(fmt.Sprintf("Failed to mark flag as required: %v", err)) + } +} + func init() { rootCmd.AddCommand(serviceCmd) rootCmd.SilenceUsage = false @@ -243,12 +249,9 @@ func init() { installCmd.Flags().StringVar(&mountPath, "mount-path", "", "Input mount path") installCmd.Flags().StringVar(&configPath, "config-file", "", "Input config file") installCmd.Flags().StringVar(&serviceUser, "user", "cloudfuse", "Input service user") - err := installCmd.MarkFlagRequired("mount-path") - if err != nil { - return - } - installCmd.MarkFlagRequired("config-file") + markFlagErrorChk(installCmd, "mount-path") + markFlagErrorChk(installCmd, "config-file") serviceCmd.AddCommand(uninstallCmd) uninstallCmd.Flags().StringVar(&serviceName, "mount-path", "", "Input mount path") - uninstallCmd.MarkFlagRequired("mount-path") + markFlagErrorChk(uninstallCmd, "mount-path") } From 9280183cbbb980d67a62464d79f555810f2a98e3 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 28 Jan 2025 12:19:33 -0700 Subject: [PATCH 61/68] removed absPath helper function --- cmd/service_linux.go | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 601955934..50d4a3853 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -72,15 +72,15 @@ var installCmd = &cobra.Command{ FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { - var err error - mountPath, err = getAbsPath(mountPath) + mountPath, err := filepath.Abs(mountPath) if err != nil { - return err + return fmt.Errorf("couldn't couldn't determine absolute path from string [%s]", err.Error()) } - configPath, err = getAbsPath(configPath) + configPath, err := filepath.Abs(configPath) if err != nil { - return err + return fmt.Errorf("couldn't couldn't determine absolute path from string [%s]", err.Error()) } + mountExists := common.DirectoryExists(mountPath) if !mountExists { return fmt.Errorf("the mount path provided does not exist") @@ -125,10 +125,10 @@ var uninstallCmd = &cobra.Command{ FlagErrorHandling: cobra.ExitOnError, RunE: func(cmd *cobra.Command, args []string) error { // get absolute path of provided relative mount path - var err error - mountPath, err = getAbsPath(mountPath) + + mountPath, err := filepath.Abs(mountPath) if err != nil { - return err + return fmt.Errorf("couldn't couldn't determine absolute path from string [%s]", err.Error()) } serviceName, serviceFilePath := getService(mountPath) if _, err := os.Stat(serviceFilePath); err == nil { @@ -225,17 +225,6 @@ func getService(mountPath string) (string, string) { return serviceName, serviceFilePath } -// takes a file or folder name and returns its absolute path -func getAbsPath(leaf string) (string, error) { - var absPath string - var err error - absPath, err = filepath.Abs(leaf) - if err != nil { - return "", fmt.Errorf("couldn't format the path string due to the following error [%s]", err.Error()) - } - return absPath, err -} - func markFlagErrorChk(cmd *cobra.Command, flagName string) { if err := cmd.MarkFlagRequired(flagName); err != nil { panic(fmt.Sprintf("Failed to mark flag as required: %v", err)) From cc01a7babc177b3d14108ae37e828bb88056ca3e Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 28 Jan 2025 12:26:11 -0700 Subject: [PATCH 62/68] corrected uninstallCmd parameter from serviceName to mountPath --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 50d4a3853..a43bd592c 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -241,6 +241,6 @@ func init() { markFlagErrorChk(installCmd, "mount-path") markFlagErrorChk(installCmd, "config-file") serviceCmd.AddCommand(uninstallCmd) - uninstallCmd.Flags().StringVar(&serviceName, "mount-path", "", "Input mount path") + uninstallCmd.Flags().StringVar(&mountPath, "mount-path", "", "Input mount path") markFlagErrorChk(uninstallCmd, "mount-path") } From a029d677b05f0ab14aad29d9db5155a520c7d0a6 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 28 Jan 2025 12:26:42 -0700 Subject: [PATCH 63/68] removed new line from end of printeln string --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index a43bd592c..c1b002e87 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -214,7 +214,7 @@ func setUser(serviceUser string, mountPath string, configPath string) error { } } // advise on required permissions - fmt.Println("ensure the user, " + serviceUser + ", has the following access: \n" + mountPath + ": read, write, and execute \n" + configPath + ": read \n") + fmt.Println("ensure the user, " + serviceUser + ", has the following access: \n" + mountPath + ": read, write, and execute \n" + configPath + ": read") return nil } From 150fd6c3f759f0f76f65756a2598d4a7f789d0e7 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 28 Jan 2025 12:27:39 -0700 Subject: [PATCH 64/68] updated year on copyright --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index c1b002e87..39e038f13 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -3,7 +3,7 @@ /* Licensed under the MIT License . - Copyright © 2023-2024 Seagate Technology LLC and/or its Affiliates + Copyright © 2023-2025 Seagate Technology LLC and/or its Affiliates Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 9e4722f746357f1e73d68b2fff36bde1c6e88dd9 Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Tue, 28 Jan 2025 13:47:10 -0700 Subject: [PATCH 65/68] removed "error" from message --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 39e038f13..4ffffc131 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -87,7 +87,7 @@ var installCmd = &cobra.Command{ } _, err = os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { - return fmt.Errorf("error, the configfile path provided does not exist") + return fmt.Errorf("the configfile path provided does not exist") } //create the new user and set permissions err = setUser(serviceUser, mountPath, configPath) From 1a5e99c6b673c98a6967016ee589abd8a0560d7b Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Wed, 29 Jan 2025 08:48:05 -0700 Subject: [PATCH 66/68] removed 'error' from all error messages --- cmd/service_linux.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 4ffffc131..540e18328 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -92,25 +92,25 @@ var installCmd = &cobra.Command{ //create the new user and set permissions err = setUser(serviceUser, mountPath, configPath) if err != nil { - fmt.Println("Error setting permissions for user:", err) + fmt.Println("could not set up service user ", err) return err } serviceName, err := newService(mountPath, configPath, serviceUser) if err != nil { - return fmt.Errorf("error when attempting to create service file: [%s]", err.Error()) + return fmt.Errorf("unable to create service file: [%s]", err.Error()) } // run systemctl daemon-reload systemctlDaemonReloadCmd := exec.Command("systemctl", "daemon-reload") err = systemctlDaemonReloadCmd.Run() if err != nil { - return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) + return fmt.Errorf("failed to run 'systemctl daemon-reload' command [%s]", err.Error()) } // Enable the service to start at system boot systemctlEnableCmd := exec.Command("systemctl", "enable", serviceName) err = systemctlEnableCmd.Run() if err != nil { - return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) + return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following [%s]", err.Error()) } return nil }, @@ -135,16 +135,16 @@ var uninstallCmd = &cobra.Command{ removeFileCmd := exec.Command("rm", serviceFilePath) err := removeFileCmd.Run() if err != nil { - return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system due to following error: [%s]", err.Error()) + return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system [%s]", err.Error()) } } else if os.IsNotExist(err) { - return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system due to following error: [%s]", err.Error()) + return fmt.Errorf("failed to delete "+serviceName+" file from /etc/systemd/system [%s]", err.Error()) } // reload daemon systemctlDaemonReloadCmd := exec.Command("systemctl", "daemon-reload") err = systemctlDaemonReloadCmd.Run() if err != nil { - return fmt.Errorf("failed to run 'systemctl daemon-reload' command due to following error: [%s]", err.Error()) + return fmt.Errorf("failed to run 'systemctl daemon-reload' command [%s]", err.Error()) } return nil }, @@ -179,23 +179,23 @@ WantedBy=multi-user.target tmpl, err := template.New("service").Parse(serviceTemplate) if err != nil { - return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) + return "", fmt.Errorf("could not create a new service file: [%s]", err.Error()) } serviceName, serviceFilePath := getService(mountPath) err = os.Remove(serviceFilePath) if err != nil && !os.IsNotExist(err) { - return "", fmt.Errorf("failed to replace the service file due to the following error: [%s]", err.Error()) + return "", fmt.Errorf("failed to replace the service file [%s]", err.Error()) } var newFile *os.File newFile, err = os.Create(serviceFilePath) if err != nil { - return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) + return "", fmt.Errorf("could not create new service file: [%s]", err.Error()) } err = tmpl.Execute(newFile, config) if err != nil { - return "", fmt.Errorf("error creating new service file: [%s]", err.Error()) + return "", fmt.Errorf("could not create new service file: [%s]", err.Error()) } return serviceName, nil } @@ -208,7 +208,7 @@ func setUser(serviceUser string, mountPath string, configPath string) error { userAddCmd := exec.Command("useradd", "-m", serviceUser) err = userAddCmd.Run() if err != nil { - return fmt.Errorf("failed to create user due to following error: [%s]", err.Error()) + return fmt.Errorf("failed to create user [%s]", err.Error()) } fmt.Println("user " + serviceUser + " has been created") } From 99bf7bf02772e27e5495b93423e15dedef9ef0ea Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 30 Jan 2025 10:48:22 -0700 Subject: [PATCH 67/68] fixed typo --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 540e18328..64d04842e 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -128,7 +128,7 @@ var uninstallCmd = &cobra.Command{ mountPath, err := filepath.Abs(mountPath) if err != nil { - return fmt.Errorf("couldn't couldn't determine absolute path from string [%s]", err.Error()) + return fmt.Errorf("couldn't determine absolute path from string [%s]", err.Error()) } serviceName, serviceFilePath := getService(mountPath) if _, err := os.Stat(serviceFilePath); err == nil { From c9f93793ddd0e78a7d5e4bcf5af79591a6013edc Mon Sep 17 00:00:00 2001 From: David Habinsky Date: Thu, 30 Jan 2025 10:49:43 -0700 Subject: [PATCH 68/68] fixed another typo --- cmd/service_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/service_linux.go b/cmd/service_linux.go index 64d04842e..b4c316d4d 100644 --- a/cmd/service_linux.go +++ b/cmd/service_linux.go @@ -87,7 +87,7 @@ var installCmd = &cobra.Command{ } _, err = os.Stat(configPath) if errors.Is(err, fs.ErrNotExist) { - return fmt.Errorf("the configfile path provided does not exist") + return fmt.Errorf("the config file path provided does not exist") } //create the new user and set permissions err = setUser(serviceUser, mountPath, configPath)