package dryad
import (
- "path/filepath"
- "strings"
+ "fmt"
)
// prefixPath is a parent directory DUT scripts.
// CopyFilesTo function is a part of DeviceCommunicationProvider interface.
// It uses tmpfs of MuxPi so caller must take into consideration size of all files that are to be copied.
func (d *deviceCommunicationProvider) CopyFilesTo(src []string, dest string) error {
- if !strings.HasSuffix(dest, "/") {
- dest += "/"
- }
-
for _, path := range src {
- fileName := filepath.Base(path)
- tmpDst := "/tmp/weles_cft_" + fileName
-
- err := d.sessionProvider.SendFile(path, tmpDst)
- if err != nil {
- return err
- }
-
- _, _, err = d.sessionProvider.Exec(prefixPath+"dut_copyto.sh", tmpDst, dest+fileName)
+ _, _, err := d.sessionProvider.Exec(prefixPath+"dut_copyto.sh", path, dest)
if err != nil {
- return err
+ return fmt.Errorf("failed to copy %s to %s: %v", path, dest, err)
}
}
return nil
// It uses tmpfs of MuxPi so caller must take into consideration size of all files that are to be copied.
func (d *deviceCommunicationProvider) CopyFilesFrom(src []string, dest string) error {
for _, path := range src {
- fileName := filepath.Base(path)
- tmpDst := "/tmp/weles_cff_" + fileName
-
- _, _, err := d.sessionProvider.Exec(prefixPath+"dut_copyfrom.sh", path, tmpDst)
- if err != nil {
- return err
- }
-
- err = d.sessionProvider.ReceiveFile(tmpDst, dest+fileName)
+ _, _, err := d.sessionProvider.Exec(prefixPath+"dut_copyfrom.sh", path, dest)
if err != nil {
- return err
+ return fmt.Errorf("failed to copy %s to %s: %v", path, dest, err)
}
}
return nil
Expect(stderr).To(BeEmpty())
})
+ It("should transfer files to and from DUT", func() {
+ file1 := "a"
+ file2 := "b"
+ file3 := "c"
+ files := []string{file1, file2, file3}
+ target := "/tmp/dl"
+ gomock.InOrder(
+ mockSession.EXPECT().Exec("/usr/local/bin/dut_copyto.sh", file1, target),
+ mockSession.EXPECT().Exec("/usr/local/bin/dut_copyto.sh", file2, target),
+ mockSession.EXPECT().Exec("/usr/local/bin/dut_copyto.sh", file3, target),
+ )
+
+ By("Sending files to DUT")
+ err := dcp.CopyFilesTo(files, target)
+ Expect(err).ToNot(HaveOccurred())
+
+ gomock.InOrder(
+ mockSession.EXPECT().Exec("/usr/local/bin/dut_copyfrom.sh", file1, target),
+ mockSession.EXPECT().Exec("/usr/local/bin/dut_copyfrom.sh", file2, target),
+ mockSession.EXPECT().Exec("/usr/local/bin/dut_copyfrom.sh", file3, target),
+ )
+
+ By("Receiving files from DUT")
+ err = dcp.CopyFilesFrom(files, target)
+ Expect(err).ToNot(HaveOccurred())
+ })
+
//TODO: Test error paths.
})
// Close terminates session to Dryad.
Close() error
-
- // SendFile sends file to Dryad.
- SendFile(src, dst string) error
-
- // ReceiveFile receives file from Dryad.
- ReceiveFile(src, dst string) error
}
// Credentials are used to login to device.
import (
"bytes"
+ "context"
"fmt"
- "io"
- "os"
- "path/filepath"
"strings"
"time"
SessionProvider
dryad Dryad
connection *sshClient
+ sshfs *reverseSSHFS
}
func prepareSSHConfig(userName string, key rsa.PrivateKey) *ssh.ClientConfig {
func (d *sessionProvider) connect() (err error) {
d.connection.client, err = ssh.Dial("tcp", d.dryad.Addr.String(), d.connection.config)
- return
+ if err != nil {
+ return err
+ }
+ session, err := d.connection.client.NewSession()
+ if err != nil {
+ return err
+ }
+ return d.sshfs.open(session)
}
func (d *sessionProvider) newSession() (*ssh.Session, error) {
}
// NewSessionProvider returns new instance of SessionProvider.
-func NewSessionProvider(dryad Dryad) SessionProvider {
+func NewSessionProvider(dryad Dryad, workdir string) SessionProvider {
cfg := prepareSSHConfig(dryad.Username, dryad.Key)
return &sessionProvider{
connection: &sshClient{
config: cfg,
},
+ sshfs: newReverseSSHFS(context.Background(), workdir, workdir),
}
}
// Exec is a part of SessionProvider interface.
// cmd parameter is used as is. Quotations should be added by the user as needed.
func (d *sessionProvider) Exec(cmd ...string) ([]byte, []byte, error) {
+ session, err := d.newSession()
+ if err != nil {
+ return nil, nil, err
+ }
+ defer session.Close()
+
+ err = d.sshfs.check(session)
+ if err != nil {
+ return nil, nil, err
+ }
+
return d.executeRemoteCommand(strings.Join(cmd, " "))
}
return nil
}
+ d.sshfs.close()
+ //TODO: log error.
err := d.connection.client.Close()
d.connection.client = nil
return err
}
-
-// SendFile is a part of SessionProvider interface.
-func (d *sessionProvider) SendFile(src, dst string) error {
- f, err := os.Open(src)
- if err != nil {
- return err
- }
- defer f.Close()
-
- s, err := f.Stat()
- if err != nil {
- return err
- }
-
- session, err := d.newSession()
- if err != nil {
- return err
- }
- defer session.Close()
-
- filename := filepath.Base(dst)
- directory := filepath.Dir(dst)
-
- w, err := session.StdinPipe()
- if err != nil {
- return err
- }
- defer w.Close()
-
- var stdout, stderr bytes.Buffer
- session.Stdout = &stdout
- session.Stderr = &stderr
-
- // Trigger SCP sink mode
- err = session.Start("scp -t " + directory)
- if err != nil {
- return err
- }
-
- _, err = fmt.Fprintln(w, "C0755", s.Size(), filename)
- if err != nil {
- return err
- }
-
- _, err = io.Copy(w, f)
- if err != nil {
- return err
- }
-
- _, err = fmt.Fprintln(w, "\x00")
- if err != nil {
- return err
- }
-
- err = session.Wait()
-
- // FIXME: unexpected <newline> is reported by scp every time the transfer is finished properly. Needs to be solved.
- // Bellow we have a very lousy trick. I hope it will be fixed in the future.
- // I don't know what is the reason or how to fix it. Has to wait a little bit. Or maybe someone else will find the solution.
- // First candidate sshfs -o slave
- if strings.Contains(stdout.String(), "unexpected <newline>") {
- return nil
- }
- return err
-}
-
-// ReceiveFile is a part of SessionProvider interface.
-func (d *sessionProvider) ReceiveFile(src, dst string) error {
- session, err := d.newSession()
- if err != nil {
- return err
- }
- defer session.Close()
-
- r, err := session.StdoutPipe()
- if err != nil {
- return err
- }
-
- file, err := os.Create(dst)
- if err != nil {
- return err
- }
- defer file.Close()
-
- err = session.Start("scp " + src + " /dev/stdout")
- if err != nil {
- return err
- }
-
- _, err = io.Copy(file, r)
- if err != nil {
- return err
- }
-
- return session.Wait()
-}
package dryad
import (
+ "io/ioutil"
+ "os"
"strings"
. "github.com/onsi/ginkgo"
)
var _ = Describe("SessionProvider", func() {
- var sp SessionProvider
+ var (
+ sp SessionProvider
+ testDir string
+ )
BeforeEach(func() {
if !accessInfoGiven {
Skip("No valid access info to Dryad")
}
- sp = NewSessionProvider(dryadInfo)
+ var err error
+ testDir, err = ioutil.TempDir("", "test")
+ Expect(err).ToNot(HaveOccurred())
+
+ sp = NewSessionProvider(dryadInfo, testDir)
})
AfterEach(func() {
sp.Close()
+
+ err := os.RemoveAll(testDir)
+ Expect(err).ToNot(HaveOccurred())
})
It("should write poem to a file and read from it", func() {
Expect(err).ToNot(HaveOccurred())
})
+ It("should read local file from remote", func() {
+ content := []byte("test file contents")
+ tmpfile, err := ioutil.TempFile(testDir, "testfile")
+ Expect(err).ToNot(HaveOccurred())
+ _, err = tmpfile.Write(content)
+ Expect(err).ToNot(HaveOccurred())
+ tmpfile.Close()
+
+ stdout, stderr, err := sp.Exec("cat", tmpfile.Name())
+ Expect(err).ToNot(HaveOccurred())
+ Expect(stdout).To(Equal(content))
+ Expect(stderr).To(BeEmpty())
+ })
+
It("should not read poem from nonexistent file", func() {
stdout, stderr, err := sp.Exec("cat", "/Ihopethispathdoesnotexist/"+flyingCowsPath+".txt")
Expect(err).To(HaveOccurred())
It("should switch to TS", func() {
Expect(sp.TS()).ToNot(HaveOccurred())
})
-
- It("should transfer file to Dryad", func() {
- err := sp.SendFile(keyFile, "/tmp/"+keyFile)
- Expect(err).ToNot(HaveOccurred())
- })
-
- It("should not transfer file to Dryad - insufficient permissions", func() {
- err := sp.SendFile(keyFile, "/root/"+keyFile)
- Expect(err).To(HaveOccurred())
- })
-
- It("should transfer file from Dryad", func() {
- err := sp.ReceiveFile("/tmp/"+keyFile, "/tmp/dl-"+keyFile)
- Expect(err).ToNot(HaveOccurred())
- })
-
- It("should not transfer nonexistent file from Dryad", func() {
- err := sp.ReceiveFile("/tmp/"+keyFile+"noway", "/tmp/dl-"+keyFile)
- Expect(err).To(HaveOccurred())
- })
})
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PowerTick", reflect.TypeOf((*MockSessionProvider)(nil).PowerTick))
}
-// ReceiveFile mocks base method
-func (m *MockSessionProvider) ReceiveFile(arg0, arg1 string) error {
- ret := m.ctrl.Call(m, "ReceiveFile", arg0, arg1)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// ReceiveFile indicates an expected call of ReceiveFile
-func (mr *MockSessionProviderMockRecorder) ReceiveFile(arg0, arg1 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveFile", reflect.TypeOf((*MockSessionProvider)(nil).ReceiveFile), arg0, arg1)
-}
-
-// SendFile mocks base method
-func (m *MockSessionProvider) SendFile(arg0, arg1 string) error {
- ret := m.ctrl.Call(m, "SendFile", arg0, arg1)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SendFile indicates an expected call of SendFile
-func (mr *MockSessionProviderMockRecorder) SendFile(arg0, arg1 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendFile", reflect.TypeOf((*MockSessionProvider)(nil).SendFile), arg0, arg1)
-}
-
// TS mocks base method
func (m *MockSessionProvider) TS() error {
ret := m.ctrl.Call(m, "TS")
/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// newDryadJob creates an instance of dryadJob and starts a goroutine
// executing phases of given job implemented by provider of DryadJobRunner interface.
func newDryadJob(job JobID, rusalka Dryad, changes chan<- DryadJobStatusChange) *dryadJob {
- session := dryad.NewSessionProvider(rusalka)
+ // FIXME: It should use the proper path to the artifactory.
+ session := dryad.NewSessionProvider(rusalka, "")
device := dryad.NewDeviceCommunicationProvider(session)
ctx, cancel := context.WithCancel(context.Background())
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PowerTick", reflect.TypeOf((*MockSessionProvider)(nil).PowerTick))
}
-// ReceiveFile mocks base method
-func (m *MockSessionProvider) ReceiveFile(arg0, arg1 string) error {
- ret := m.ctrl.Call(m, "ReceiveFile", arg0, arg1)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// ReceiveFile indicates an expected call of ReceiveFile
-func (mr *MockSessionProviderMockRecorder) ReceiveFile(arg0, arg1 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveFile", reflect.TypeOf((*MockSessionProvider)(nil).ReceiveFile), arg0, arg1)
-}
-
-// SendFile mocks base method
-func (m *MockSessionProvider) SendFile(arg0, arg1 string) error {
- ret := m.ctrl.Call(m, "SendFile", arg0, arg1)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SendFile indicates an expected call of SendFile
-func (mr *MockSessionProviderMockRecorder) SendFile(arg0, arg1 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendFile", reflect.TypeOf((*MockSessionProvider)(nil).SendFile), arg0, arg1)
-}
-
// TS mocks base method
func (m *MockSessionProvider) TS() error {
ret := m.ctrl.Call(m, "TS")