Factor into server and driver directories #120
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
testdata
|
||||
coverage.txt
|
||||
exampleftpd/exampleftpd
|
||||
.vscode
|
||||
.vscode
|
||||
*~
|
||||
|
|
76
compat.go
Normal file
76
compat.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Package server is a backwards compatibility shim for this module
|
||||
//
|
||||
// The code has been re-organised to split out the drivers from the
|
||||
// server functionality.
|
||||
//
|
||||
// New code should
|
||||
//
|
||||
// import "goftp.io/server/core"
|
||||
//
|
||||
// And if the drivers are required use
|
||||
//
|
||||
// import "goftp.io/server/driver/file"
|
||||
// import "goftp.io/server/driver/minio"
|
||||
package server
|
||||
|
||||
import (
|
||||
"goftp.io/server/core"
|
||||
"goftp.io/server/driver/file"
|
||||
"goftp.io/server/driver/minio"
|
||||
)
|
||||
|
||||
// Backwards compatible types for the server code.
|
||||
//
|
||||
// New code should import goftp.io/server/core
|
||||
type (
|
||||
Auth = core.Auth
|
||||
Command = core.Command
|
||||
Conn = core.Conn
|
||||
DataSocket = core.DataSocket
|
||||
DiscardLogger = core.DiscardLogger
|
||||
Driver = core.Driver
|
||||
DriverFactory = core.DriverFactory
|
||||
FileInfo = core.FileInfo
|
||||
Logger = core.Logger
|
||||
MultipleDriver = core.MultipleDriver
|
||||
MultipleDriverFactory = core.MultipleDriverFactory
|
||||
Notifier = core.Notifier
|
||||
NullNotifier = core.NullNotifier
|
||||
Perm = core.Perm
|
||||
Server = core.Server
|
||||
ServerOpts = core.ServerOpts
|
||||
SimpleAuth = core.SimpleAuth
|
||||
SimplePerm = core.SimplePerm
|
||||
StdLogger = core.StdLogger
|
||||
)
|
||||
|
||||
// Backwards compatible functions and variables for the server code.
|
||||
//
|
||||
// New code should import goftp.io/server/core
|
||||
var (
|
||||
ErrServerClosed = core.ErrServerClosed
|
||||
NewServer = core.NewServer
|
||||
NewSimplePerm = core.NewSimplePerm
|
||||
Version = core.Version
|
||||
)
|
||||
|
||||
// Backwards compatible types for the file driver code.
|
||||
//
|
||||
// New code should import goftp.io/server/driver/file
|
||||
type (
|
||||
FileDriver = file.Driver
|
||||
FileDriverFactory = file.DriverFactory
|
||||
)
|
||||
|
||||
// Backwards compatible types for the minio driver code.
|
||||
//
|
||||
// New code should import goftp.io/server/driver/minio
|
||||
type (
|
||||
MinioDriver = minio.Driver
|
||||
MinioDriverFactory = minio.DriverFactory
|
||||
)
|
||||
|
||||
// Backwards compatible functions for the minio driver code.
|
||||
//
|
||||
// New code should import goftp.io/server/driver/minio
|
||||
var NewMinioDriverFactory = minio.NewDriverFactory
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import "testing"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import (
|
||||
"bufio"
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import (
|
||||
"net"
|
|
@ -11,4 +11,4 @@ http://www.faqs.org/rfcs/rfc959.html
|
|||
http://tools.ietf.org/html/rfc2428
|
||||
*/
|
||||
|
||||
package server
|
||||
package core
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
15
core/file_info.go
Normal file
15
core/file_info.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package core
|
||||
|
||||
import "os"
|
||||
|
||||
// FileInfo represents an file interface
|
||||
type FileInfo interface {
|
||||
os.FileInfo
|
||||
|
||||
Owner() string
|
||||
Group() string
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
// Notifier represents a notification operator interface
|
||||
type Notifier interface {
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
@ -14,6 +14,8 @@ import (
|
|||
|
||||
"github.com/jlaffaye/ftp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"goftp.io/server/core"
|
||||
"goftp.io/server/driver/file"
|
||||
)
|
||||
|
||||
type mockNotifier struct {
|
||||
|
@ -21,72 +23,72 @@ type mockNotifier struct {
|
|||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (m *mockNotifier) BeforeLoginUser(conn *Conn, userName string) {
|
||||
func (m *mockNotifier) BeforeLoginUser(conn *core.Conn, userName string) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "BeforeLoginUser")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) BeforePutFile(conn *Conn, dstPath string) {
|
||||
func (m *mockNotifier) BeforePutFile(conn *core.Conn, dstPath string) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "BeforePutFile")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) BeforeDeleteFile(conn *Conn, dstPath string) {
|
||||
func (m *mockNotifier) BeforeDeleteFile(conn *core.Conn, dstPath string) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "BeforeDeleteFile")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) BeforeChangeCurDir(conn *Conn, oldCurDir, newCurDir string) {
|
||||
func (m *mockNotifier) BeforeChangeCurDir(conn *core.Conn, oldCurDir, newCurDir string) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "BeforeChangeCurDir")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) BeforeCreateDir(conn *Conn, dstPath string) {
|
||||
func (m *mockNotifier) BeforeCreateDir(conn *core.Conn, dstPath string) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "BeforeCreateDir")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) BeforeDeleteDir(conn *Conn, dstPath string) {
|
||||
func (m *mockNotifier) BeforeDeleteDir(conn *core.Conn, dstPath string) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "BeforeDeleteDir")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) BeforeDownloadFile(conn *Conn, dstPath string) {
|
||||
func (m *mockNotifier) BeforeDownloadFile(conn *core.Conn, dstPath string) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "BeforeDownloadFile")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) AfterUserLogin(conn *Conn, userName, password string, passMatched bool, err error) {
|
||||
func (m *mockNotifier) AfterUserLogin(conn *core.Conn, userName, password string, passMatched bool, err error) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "AfterUserLogin")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) AfterFilePut(conn *Conn, dstPath string, size int64, err error) {
|
||||
func (m *mockNotifier) AfterFilePut(conn *core.Conn, dstPath string, size int64, err error) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "AfterFilePut")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) AfterFileDeleted(conn *Conn, dstPath string, err error) {
|
||||
func (m *mockNotifier) AfterFileDeleted(conn *core.Conn, dstPath string, err error) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "AfterFileDeleted")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) AfterCurDirChanged(conn *Conn, oldCurDir, newCurDir string, err error) {
|
||||
func (m *mockNotifier) AfterCurDirChanged(conn *core.Conn, oldCurDir, newCurDir string, err error) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "AfterCurDirChanged")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) AfterDirCreated(conn *Conn, dstPath string, err error) {
|
||||
func (m *mockNotifier) AfterDirCreated(conn *core.Conn, dstPath string, err error) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "AfterDirCreated")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) AfterDirDeleted(conn *Conn, dstPath string, err error) {
|
||||
func (m *mockNotifier) AfterDirDeleted(conn *core.Conn, dstPath string, err error) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "AfterDirDeleted")
|
||||
m.lock.Unlock()
|
||||
}
|
||||
func (m *mockNotifier) AfterFileDownloaded(conn *Conn, dstPath string, size int64, err error) {
|
||||
func (m *mockNotifier) AfterFileDownloaded(conn *core.Conn, dstPath string, size int64, err error) {
|
||||
m.lock.Lock()
|
||||
m.actions = append(m.actions, "AfterFileDownloaded")
|
||||
m.lock.Unlock()
|
||||
|
@ -104,24 +106,24 @@ func assetMockNotifier(t *testing.T, mock *mockNotifier, lastActions []string) {
|
|||
func TestNotification(t *testing.T) {
|
||||
os.MkdirAll("./testdata", os.ModePerm)
|
||||
|
||||
var perm = NewSimplePerm("test", "test")
|
||||
opt := &ServerOpts{
|
||||
var perm = core.NewSimplePerm("test", "test")
|
||||
opt := &core.ServerOpts{
|
||||
Name: "test ftpd",
|
||||
Factory: &FileDriverFactory{
|
||||
Factory: &file.DriverFactory{
|
||||
RootPath: "./testdata",
|
||||
Perm: perm,
|
||||
},
|
||||
Port: 2121,
|
||||
Auth: &SimpleAuth{
|
||||
Auth: &core.SimpleAuth{
|
||||
Name: "admin",
|
||||
Password: "admin",
|
||||
},
|
||||
Logger: new(DiscardLogger),
|
||||
Logger: new(core.DiscardLogger),
|
||||
}
|
||||
|
||||
mock := &mockNotifier{}
|
||||
|
||||
runServer(t, opt, notifierList{mock}, func() {
|
||||
runServer(t, opt, []core.Notifier{mock}, func() {
|
||||
// Give server 0.5 seconds to get to the listening state
|
||||
timeout := time.NewTimer(time.Millisecond * 500)
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import "os"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import (
|
||||
"bufio"
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
@ -14,16 +14,18 @@ import (
|
|||
|
||||
"github.com/jlaffaye/ftp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"goftp.io/server/core"
|
||||
"goftp.io/server/driver/file"
|
||||
)
|
||||
|
||||
func runServer(t *testing.T, opt *ServerOpts, notifiers notifierList, execute func()) {
|
||||
s := NewServer(opt)
|
||||
func runServer(t *testing.T, opt *core.ServerOpts, notifiers []core.Notifier, execute func()) {
|
||||
s := core.NewServer(opt)
|
||||
for _, notifier := range notifiers {
|
||||
s.RegisterNotifer(notifier)
|
||||
}
|
||||
go func() {
|
||||
err := s.ListenAndServe()
|
||||
assert.EqualError(t, err, ErrServerClosed.Error())
|
||||
assert.EqualError(t, err, core.ErrServerClosed.Error())
|
||||
}()
|
||||
|
||||
execute()
|
||||
|
@ -34,19 +36,19 @@ func runServer(t *testing.T, opt *ServerOpts, notifiers notifierList, execute fu
|
|||
func TestFileDriver(t *testing.T) {
|
||||
os.MkdirAll("./testdata", os.ModePerm)
|
||||
|
||||
var perm = NewSimplePerm("test", "test")
|
||||
opt := &ServerOpts{
|
||||
var perm = core.NewSimplePerm("test", "test")
|
||||
opt := &core.ServerOpts{
|
||||
Name: "test ftpd",
|
||||
Factory: &FileDriverFactory{
|
||||
Factory: &file.DriverFactory{
|
||||
RootPath: "./testdata",
|
||||
Perm: perm,
|
||||
},
|
||||
Port: 2122,
|
||||
Auth: &SimpleAuth{
|
||||
Auth: &core.SimpleAuth{
|
||||
Name: "admin",
|
||||
Password: "admin",
|
||||
},
|
||||
Logger: new(DiscardLogger),
|
||||
Logger: new(core.DiscardLogger),
|
||||
}
|
||||
|
||||
runServer(t, opt, nil, func() {
|
||||
|
@ -138,20 +140,20 @@ func TestFileDriver(t *testing.T) {
|
|||
func TestLogin(t *testing.T) {
|
||||
os.MkdirAll("./testdata", os.ModePerm)
|
||||
|
||||
var perm = NewSimplePerm("test", "test")
|
||||
var perm = core.NewSimplePerm("test", "test")
|
||||
|
||||
// Server options without hostname or port
|
||||
opt := &ServerOpts{
|
||||
opt := &core.ServerOpts{
|
||||
Name: "test ftpd",
|
||||
Factory: &FileDriverFactory{
|
||||
Factory: &file.DriverFactory{
|
||||
RootPath: "./testdata",
|
||||
Perm: perm,
|
||||
},
|
||||
Auth: &SimpleAuth{
|
||||
Auth: &core.SimpleAuth{
|
||||
Name: "admin",
|
||||
Password: "admin",
|
||||
},
|
||||
Logger: new(DiscardLogger),
|
||||
Logger: new(core.DiscardLogger),
|
||||
}
|
||||
|
||||
// Start the listener
|
||||
|
@ -159,10 +161,10 @@ func TestLogin(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// Start the server using the listener
|
||||
s := NewServer(opt)
|
||||
s := core.NewServer(opt)
|
||||
go func() {
|
||||
err := s.Serve(l)
|
||||
assert.EqualError(t, err, ErrServerClosed.Error())
|
||||
assert.EqualError(t, err, core.ErrServerClosed.Error())
|
||||
}()
|
||||
|
||||
// Give server 0.5 seconds to get to the listening state
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package file
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -11,21 +11,23 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"goftp.io/server/core"
|
||||
)
|
||||
|
||||
// FileDriver implements Driver directly read local file system
|
||||
type FileDriver struct {
|
||||
// Driver implements Driver directly read local file system
|
||||
type Driver struct {
|
||||
RootPath string
|
||||
Perm
|
||||
core.Perm
|
||||
}
|
||||
|
||||
func (driver *FileDriver) realPath(path string) string {
|
||||
func (driver *Driver) realPath(path string) string {
|
||||
paths := strings.Split(path, "/")
|
||||
return filepath.Join(append([]string{driver.RootPath}, paths...)...)
|
||||
}
|
||||
|
||||
// Stat implements Driver
|
||||
func (driver *FileDriver) Stat(path string) (FileInfo, error) {
|
||||
func (driver *Driver) Stat(path string) (core.FileInfo, error) {
|
||||
basepath := driver.realPath(path)
|
||||
rPath, err := filepath.Abs(basepath)
|
||||
if err != nil {
|
||||
|
@ -54,7 +56,7 @@ func (driver *FileDriver) Stat(path string) (FileInfo, error) {
|
|||
}
|
||||
|
||||
// ListDir implements Driver
|
||||
func (driver *FileDriver) ListDir(path string, callback func(FileInfo) error) error {
|
||||
func (driver *Driver) ListDir(path string, callback func(core.FileInfo) error) error {
|
||||
basepath := driver.realPath(path)
|
||||
return filepath.Walk(basepath, func(f string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
|
@ -90,7 +92,7 @@ func (driver *FileDriver) ListDir(path string, callback func(FileInfo) error) er
|
|||
}
|
||||
|
||||
// DeleteDir implements Driver
|
||||
func (driver *FileDriver) DeleteDir(path string) error {
|
||||
func (driver *Driver) DeleteDir(path string) error {
|
||||
rPath := driver.realPath(path)
|
||||
f, err := os.Lstat(rPath)
|
||||
if err != nil {
|
||||
|
@ -103,7 +105,7 @@ func (driver *FileDriver) DeleteDir(path string) error {
|
|||
}
|
||||
|
||||
// DeleteFile implements Driver
|
||||
func (driver *FileDriver) DeleteFile(path string) error {
|
||||
func (driver *Driver) DeleteFile(path string) error {
|
||||
rPath := driver.realPath(path)
|
||||
f, err := os.Lstat(rPath)
|
||||
if err != nil {
|
||||
|
@ -116,20 +118,20 @@ func (driver *FileDriver) DeleteFile(path string) error {
|
|||
}
|
||||
|
||||
// Rename implements Driver
|
||||
func (driver *FileDriver) Rename(fromPath string, toPath string) error {
|
||||
func (driver *Driver) Rename(fromPath string, toPath string) error {
|
||||
oldPath := driver.realPath(fromPath)
|
||||
newPath := driver.realPath(toPath)
|
||||
return os.Rename(oldPath, newPath)
|
||||
}
|
||||
|
||||
// MakeDir implements Driver
|
||||
func (driver *FileDriver) MakeDir(path string) error {
|
||||
func (driver *Driver) MakeDir(path string) error {
|
||||
rPath := driver.realPath(path)
|
||||
return os.MkdirAll(rPath, os.ModePerm)
|
||||
}
|
||||
|
||||
// GetFile implements Driver
|
||||
func (driver *FileDriver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
func (driver *Driver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
rPath := driver.realPath(path)
|
||||
f, err := os.Open(rPath)
|
||||
if err != nil {
|
||||
|
@ -147,7 +149,7 @@ func (driver *FileDriver) GetFile(path string, offset int64) (int64, io.ReadClos
|
|||
}
|
||||
|
||||
// PutFile implements Driver
|
||||
func (driver *FileDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
|
||||
func (driver *Driver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
|
||||
rPath := driver.realPath(destPath)
|
||||
var isExist bool
|
||||
f, err := os.Lstat(rPath)
|
||||
|
@ -206,18 +208,18 @@ func (driver *FileDriver) PutFile(destPath string, data io.Reader, appendData bo
|
|||
return bytes, nil
|
||||
}
|
||||
|
||||
// FileDriverFactory implements DriverFactory
|
||||
type FileDriverFactory struct {
|
||||
// DriverFactory implements DriverFactory
|
||||
type DriverFactory struct {
|
||||
RootPath string
|
||||
Perm
|
||||
core.Perm
|
||||
}
|
||||
|
||||
// NewDriver implements DriverFactory
|
||||
func (factory *FileDriverFactory) NewDriver() (Driver, error) {
|
||||
func (factory *DriverFactory) NewDriver() (core.Driver, error) {
|
||||
var err error
|
||||
factory.RootPath, err = filepath.Abs(factory.RootPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FileDriver{factory.RootPath, factory.Perm}, nil
|
||||
return &Driver{factory.RootPath, factory.Perm}, nil
|
||||
}
|
|
@ -2,18 +2,10 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package file
|
||||
|
||||
import "os"
|
||||
|
||||
// FileInfo represents an file interface
|
||||
type FileInfo interface {
|
||||
os.FileInfo
|
||||
|
||||
Owner() string
|
||||
Group() string
|
||||
}
|
||||
|
||||
type fileInfo struct {
|
||||
os.FileInfo
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package minio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -13,16 +13,17 @@ import (
|
|||
"time"
|
||||
|
||||
minio "github.com/minio/minio-go/v6"
|
||||
"goftp.io/server/core"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Driver = &MinioDriver{}
|
||||
_ core.Driver = &Driver{}
|
||||
)
|
||||
|
||||
// MinioDriver implements Driver to store files in minio
|
||||
type MinioDriver struct {
|
||||
// Driver implements Driver to store files in minio
|
||||
type Driver struct {
|
||||
client *minio.Client
|
||||
perm Perm
|
||||
perm core.Perm
|
||||
bucket string
|
||||
}
|
||||
|
||||
|
@ -39,7 +40,7 @@ func buildMinioDir(p string) string {
|
|||
}
|
||||
|
||||
type myPerm struct {
|
||||
Perm
|
||||
core.Perm
|
||||
isDir bool
|
||||
}
|
||||
|
||||
|
@ -57,7 +58,7 @@ func (m *myPerm) GetMode(user string) (os.FileMode, error) {
|
|||
type minioFileInfo struct {
|
||||
p string
|
||||
info minio.ObjectInfo
|
||||
perm Perm
|
||||
perm core.Perm
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Name() string {
|
||||
|
@ -95,7 +96,7 @@ func (m *minioFileInfo) Group() string {
|
|||
return group
|
||||
}
|
||||
|
||||
func (driver *MinioDriver) isDir(path string) (bool, error) {
|
||||
func (driver *Driver) isDir(path string) (bool, error) {
|
||||
p := buildMinioDir(path)
|
||||
|
||||
info, err := driver.client.StatObject(driver.bucket, p, minio.StatObjectOptions{})
|
||||
|
@ -117,7 +118,7 @@ func (driver *MinioDriver) isDir(path string) (bool, error) {
|
|||
}
|
||||
|
||||
// Stat implements Driver
|
||||
func (driver *MinioDriver) Stat(path string) (FileInfo, error) {
|
||||
func (driver *Driver) Stat(path string) (core.FileInfo, error) {
|
||||
if path == "/" {
|
||||
return &minioFileInfo{
|
||||
p: "/",
|
||||
|
@ -147,7 +148,7 @@ func (driver *MinioDriver) Stat(path string) (FileInfo, error) {
|
|||
}
|
||||
|
||||
// ListDir implements Driver
|
||||
func (driver *MinioDriver) ListDir(path string, callback func(FileInfo) error) error {
|
||||
func (driver *Driver) ListDir(path string, callback func(core.FileInfo) error) error {
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
|
||||
|
@ -182,7 +183,7 @@ func (driver *MinioDriver) ListDir(path string, callback func(FileInfo) error) e
|
|||
}
|
||||
|
||||
// DeleteDir implements Driver
|
||||
func (driver *MinioDriver) DeleteDir(path string) error {
|
||||
func (driver *Driver) DeleteDir(path string) error {
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
|
||||
|
@ -201,12 +202,12 @@ func (driver *MinioDriver) DeleteDir(path string) error {
|
|||
}
|
||||
|
||||
// DeleteFile implements Driver
|
||||
func (driver *MinioDriver) DeleteFile(path string) error {
|
||||
func (driver *Driver) DeleteFile(path string) error {
|
||||
return driver.client.RemoveObject(driver.bucket, buildMinioPath(path))
|
||||
}
|
||||
|
||||
// Rename implements Driver
|
||||
func (driver *MinioDriver) Rename(fromPath string, toPath string) error {
|
||||
func (driver *Driver) Rename(fromPath string, toPath string) error {
|
||||
src := minio.NewSourceInfo(driver.bucket, buildMinioPath(fromPath), nil)
|
||||
dst, err := minio.NewDestinationInfo(driver.bucket, buildMinioPath(toPath), nil, nil)
|
||||
if err != nil {
|
||||
|
@ -221,14 +222,14 @@ func (driver *MinioDriver) Rename(fromPath string, toPath string) error {
|
|||
}
|
||||
|
||||
// MakeDir implements Driver
|
||||
func (driver *MinioDriver) MakeDir(path string) error {
|
||||
func (driver *Driver) MakeDir(path string) error {
|
||||
dirPath := buildMinioDir(path)
|
||||
_, err := driver.client.PutObject(driver.bucket, dirPath, nil, 0, minio.PutObjectOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetFile implements Driver
|
||||
func (driver *MinioDriver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
func (driver *Driver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
var opts = minio.GetObjectOptions{}
|
||||
object, err := driver.client.GetObject(driver.bucket, buildMinioPath(path), opts)
|
||||
if err != nil {
|
||||
|
@ -245,7 +246,7 @@ func (driver *MinioDriver) GetFile(path string, offset int64) (int64, io.ReadClo
|
|||
}
|
||||
|
||||
// PutFile implements Driver
|
||||
func (driver *MinioDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
|
||||
func (driver *Driver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
|
||||
p := buildMinioPath(destPath)
|
||||
if !appendData {
|
||||
return driver.client.PutObject(driver.bucket, p, data, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
|
||||
|
@ -279,20 +280,20 @@ func (driver *MinioDriver) PutFile(destPath string, data io.Reader, appendData b
|
|||
return size, driver.client.ComposeObject(dst, srcs)
|
||||
}
|
||||
|
||||
// MinioDriverFactory implements DriverFactory
|
||||
type MinioDriverFactory struct {
|
||||
// DriverFactory implements DriverFactory
|
||||
type DriverFactory struct {
|
||||
endpoint string
|
||||
accessKeyID string
|
||||
secretAccessKey string
|
||||
useSSL bool
|
||||
location string
|
||||
bucket string
|
||||
perm Perm
|
||||
perm core.Perm
|
||||
}
|
||||
|
||||
// NewMinioDriverFactory creates a DriverFactory implementation
|
||||
func NewMinioDriverFactory(endpoint, accessKeyID, secretAccessKey, location, bucket string, useSSL bool, perm Perm) *MinioDriverFactory {
|
||||
return &MinioDriverFactory{
|
||||
// NewDriverFactory creates a DriverFactory implementation
|
||||
func NewDriverFactory(endpoint, accessKeyID, secretAccessKey, location, bucket string, useSSL bool, perm core.Perm) *DriverFactory {
|
||||
return &DriverFactory{
|
||||
endpoint: endpoint,
|
||||
accessKeyID: accessKeyID,
|
||||
secretAccessKey: secretAccessKey,
|
||||
|
@ -304,7 +305,7 @@ func NewMinioDriverFactory(endpoint, accessKeyID, secretAccessKey, location, buc
|
|||
}
|
||||
|
||||
// NewDriver implements DriverFactory
|
||||
func (factory *MinioDriverFactory) NewDriver() (Driver, error) {
|
||||
func (factory *DriverFactory) NewDriver() (core.Driver, error) {
|
||||
// Initialize minio client object.
|
||||
minioClient, err := minio.New(factory.endpoint, factory.accessKeyID, factory.secretAccessKey, factory.useSSL)
|
||||
if err != nil {
|
||||
|
@ -319,7 +320,7 @@ func (factory *MinioDriverFactory) NewDriver() (Driver, error) {
|
|||
}
|
||||
}
|
||||
|
||||
return &MinioDriver{
|
||||
return &Driver{
|
||||
client: minioClient,
|
||||
bucket: factory.bucket,
|
||||
perm: factory.perm,
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package minio
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
@ -14,9 +14,25 @@ import (
|
|||
|
||||
"github.com/jlaffaye/ftp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"goftp.io/server/core"
|
||||
)
|
||||
|
||||
func TestMinioDriver(t *testing.T) {
|
||||
func runServer(t *testing.T, opt *core.ServerOpts, notifiers []core.Notifier, execute func()) {
|
||||
s := core.NewServer(opt)
|
||||
for _, notifier := range notifiers {
|
||||
s.RegisterNotifer(notifier)
|
||||
}
|
||||
go func() {
|
||||
err := s.ListenAndServe()
|
||||
assert.EqualError(t, err, core.ErrServerClosed.Error())
|
||||
}()
|
||||
|
||||
execute()
|
||||
|
||||
assert.NoError(t, s.Shutdown())
|
||||
}
|
||||
|
||||
func TestDriver(t *testing.T) {
|
||||
endpoint := os.Getenv("MINIO_SERVER_ENDPOINT")
|
||||
if endpoint == "" {
|
||||
t.Skip()
|
||||
|
@ -28,15 +44,15 @@ func TestMinioDriver(t *testing.T) {
|
|||
bucket := os.Getenv("MINIO_SERVER_BUCKET")
|
||||
useSSL, _ := strconv.ParseBool(os.Getenv("MINIO_SERVER_USE_SSL"))
|
||||
|
||||
opt := &ServerOpts{
|
||||
opt := &core.ServerOpts{
|
||||
Name: "test ftpd",
|
||||
Factory: NewMinioDriverFactory(endpoint, accessKeyID, secretKey, location, bucket, useSSL, NewSimplePerm("root", "root")),
|
||||
Factory: NewDriverFactory(endpoint, accessKeyID, secretKey, location, bucket, useSSL, core.NewSimplePerm("root", "root")),
|
||||
Port: 2120,
|
||||
Auth: &SimpleAuth{
|
||||
Auth: &core.SimpleAuth{
|
||||
Name: "admin",
|
||||
Password: "admin",
|
||||
},
|
||||
Logger: new(DiscardLogger),
|
||||
Logger: new(core.DiscardLogger),
|
||||
}
|
||||
|
||||
runServer(t, opt, nil, func() {
|
Loading…
Reference in New Issue
Block a user