diff --git a/cmd/datasetIngestor/main.go b/cmd/datasetIngestor/main.go index 5813b3a..eea06f4 100644 --- a/cmd/datasetIngestor/main.go +++ b/cmd/datasetIngestor/main.go @@ -47,11 +47,12 @@ import ( "net/http" "os" "path/filepath" - "github.com/paulscherrerinstitute/scicat/datasetIngestor" - "github.com/paulscherrerinstitute/scicat/datasetUtils" "strings" "time" + "github.com/paulscherrerinstitute/scicat/datasetIngestor" + "github.com/paulscherrerinstitute/scicat/datasetUtils" + "github.com/fatih/color" ) @@ -299,7 +300,7 @@ func main() { // and unless copy flag defined via command line if !*copyFlag && !*nocopyFlag { if !beamlineAccount { - err := datasetIngestor.TestDataCentrallyAvailable(user["username"], RSYNCServer, sourceFolder) + err := datasetIngestor.CheckDataCentrallyAvailable(user["username"], RSYNCServer, sourceFolder) if err != nil { color.Set(color.FgYellow) log.Printf("The source folder %v is not centrally available (decentral use case).\nThe data must first be copied to a rsync cache server.\n ", sourceFolder) diff --git a/datasetIngestor/checkDataCentrallyAvailable.go b/datasetIngestor/checkDataCentrallyAvailable.go new file mode 100644 index 0000000..ccf9904 --- /dev/null +++ b/datasetIngestor/checkDataCentrallyAvailable.go @@ -0,0 +1,47 @@ +package datasetIngestor + +import ( + "errors" + "log" + "os" + "os/exec" + "runtime" +) + +// execCommand is a variable that points to exec.Command, allowing it to be replaced in tests. +var execCommand = exec.Command + +// CheckDataCentrallyAvailable checks if a specific directory (sourceFolder) is available on a remote server (ARCHIVEServer) +// using the provided username for SSH connection. It returns an error if the directory is not available or if there's an issue with the SSH connection. +func CheckDataCentrallyAvailable(username string, ARCHIVEServer string, sourceFolder string) (err error) { + var cmd *exec.Cmd + + // Check the operating system + switch os := runtime.GOOS; os { + case "linux", "windows", "darwin": + // Check if ssh exists + _, err := exec.LookPath("ssh") // locate a program in the user's path + if err != nil { + log.Println("SSH is not installed. Please install OpenSSH client.") + return err + } + + // Create a new exec.Command to run the SSH command. The command checks if the directory exists on the remote server. + // The "-q" option suppresses all warnings, "-l" specifies the login name on the remote server. + cmd = execCommand("ssh", "-q", "-l", username, ARCHIVEServer, "test", "-d", sourceFolder) + default: + log.Printf("%s is not supported.\n", os) + return errors.New("unsupported operating system") + } + + // Redirect the command's standard error to the process's standard error. + // This means that any error messages from the command will be displayed in the terminal. + cmd.Stderr = os.Stderr + + // Log the command that is being run for debugging purposes. + log.Printf("Running %v.\n", cmd.Args) + + // Run the command and return any error that occurs. + err = cmd.Run() + return err +} diff --git a/datasetIngestor/checkDataCentrallyAvailable_test.go b/datasetIngestor/checkDataCentrallyAvailable_test.go new file mode 100644 index 0000000..ca091ec --- /dev/null +++ b/datasetIngestor/checkDataCentrallyAvailable_test.go @@ -0,0 +1,102 @@ +package datasetIngestor + +import ( + "errors" + "fmt" + "os" + "os/exec" + "reflect" + "strconv" + "testing" +) + +// Mock for exec.Command +type execCommandMock struct { + expectedArgs []string + returnError error +} + +func (m *execCommandMock) Command(name string, arg ...string) *exec.Cmd { + if !reflect.DeepEqual(arg, m.expectedArgs) { + panic(fmt.Sprintf("unexpected arguments: got %v, want %v", arg, m.expectedArgs)) + } + + cs := []string{"-test.run=TestHelperProcess", "--", name} + cs = append(cs, arg...) + cmd := exec.Command(os.Args[0], cs...) + cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} + + if m.returnError != nil { + cmd.Env = append(cmd.Env, "EXIT_STATUS=1") + } else { + cmd.Env = append(cmd.Env, "EXIT_STATUS=0") + } + + return cmd +} + +// TestHelperProcess isn't a real test. It's used as a helper process +func TestHelperProcess(t *testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + fmt.Fprintf(os.Stdout, "output") + fmt.Fprintf(os.Stderr, "error") + exitStatus, _ := strconv.Atoi(os.Getenv("EXIT_STATUS")) + os.Exit(exitStatus) +} + +func TestCheckDataCentrallyAvailable(t *testing.T) { + tests := []struct { + name string + username string + archiveServer string + sourceFolder string + wantErr bool + errMsg string + }{ + { + name: "test data centrally available", + username: "testuser", + archiveServer: "testserver", + sourceFolder: "/test/folder", + wantErr: false, + }, + { + name: "test data not available", + username: "testuser", + archiveServer: "testserver", + sourceFolder: "/nonexistent/folder", + wantErr: true, + errMsg: "exit status 1", + }, + // Add more test cases here. + } + + for _, tt := range tests { + expectedArgs := []string{"-q", "-l", tt.username, tt.archiveServer, "test", "-d", tt.sourceFolder} + + var returnError error + if tt.wantErr { + returnError = errors.New(tt.errMsg) + } + + // Replace exec.Command with a mock + oldExecCommand := execCommand + execCommand = (&execCommandMock{ + expectedArgs: expectedArgs, + returnError: returnError, + }).Command + defer func() { execCommand = oldExecCommand }() + + t.Run(tt.name, func(t *testing.T) { + err := CheckDataCentrallyAvailable(tt.username, tt.archiveServer, tt.sourceFolder) + if (err != nil) != tt.wantErr { + t.Errorf("CheckDataCentrallyAvailable() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil && tt.wantErr && err.Error() != tt.errMsg { + t.Errorf("CheckDataCentrallyAvailable() errMsg = %v, wantErrMsg %v", err.Error(), tt.errMsg) + } + }) + } +} diff --git a/datasetIngestor/testDataCentrallyAvailable.go b/datasetIngestor/testDataCentrallyAvailable.go deleted file mode 100644 index d2b7c29..0000000 --- a/datasetIngestor/testDataCentrallyAvailable.go +++ /dev/null @@ -1,19 +0,0 @@ -package datasetIngestor - -import ( - "log" - "os" - "os/exec" -) - -func TestDataCentrallyAvailable(username string, ARCHIVEServer string, sourceFolder string) (err error) { - - cmd := exec.Command("/usr/bin/ssh", "-q", "-l", username, ARCHIVEServer, "test", "-d", sourceFolder) - // show rsync's output - //cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - log.Printf("Running %v.\n", cmd.Args) - err = cmd.Run() - return err -}