mirror of https://github.com/portainer/portainer
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
7.0 KiB
241 lines
7.0 KiB
7 years ago
|
package filesystem
|
||
8 years ago
|
|
||
|
import (
|
||
7 years ago
|
"bytes"
|
||
7 years ago
|
"io/ioutil"
|
||
|
|
||
8 years ago
|
"github.com/portainer/portainer"
|
||
|
|
||
|
"io"
|
||
|
"os"
|
||
|
"path"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// TLSStorePath represents the subfolder where TLS files are stored in the file store folder.
|
||
|
TLSStorePath = "tls"
|
||
7 years ago
|
// LDAPStorePath represents the subfolder where LDAP TLS files are stored in the TLSStorePath.
|
||
|
LDAPStorePath = "ldap"
|
||
8 years ago
|
// TLSCACertFile represents the name on disk for a TLS CA file.
|
||
|
TLSCACertFile = "ca.pem"
|
||
|
// TLSCertFile represents the name on disk for a TLS certificate file.
|
||
|
TLSCertFile = "cert.pem"
|
||
|
// TLSKeyFile represents the name on disk for a TLS key file.
|
||
|
TLSKeyFile = "key.pem"
|
||
7 years ago
|
// ComposeStorePath represents the subfolder where compose files are stored in the file store folder.
|
||
|
ComposeStorePath = "compose"
|
||
|
// ComposeFileDefaultName represents the default name of a compose file.
|
||
|
ComposeFileDefaultName = "docker-compose.yml"
|
||
8 years ago
|
)
|
||
|
|
||
8 years ago
|
// Service represents a service for managing files and directories.
|
||
8 years ago
|
type Service struct {
|
||
8 years ago
|
dataStorePath string
|
||
8 years ago
|
fileStorePath string
|
||
|
}
|
||
|
|
||
8 years ago
|
// NewService initializes a new service. It creates a data directory and a directory to store files
|
||
|
// inside this directory if they don't exist.
|
||
|
func NewService(dataStorePath, fileStorePath string) (*Service, error) {
|
||
8 years ago
|
service := &Service{
|
||
8 years ago
|
dataStorePath: dataStorePath,
|
||
|
fileStorePath: path.Join(dataStorePath, fileStorePath),
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
// Checking if a mount directory exists is broken with Go on Windows.
|
||
|
// This will need to be reviewed after the issue has been fixed in Go.
|
||
8 years ago
|
// See: https://github.com/portainer/portainer/issues/474
|
||
8 years ago
|
// err := createDirectoryIfNotExist(dataStorePath, 0755)
|
||
|
// if err != nil {
|
||
|
// return nil, err
|
||
|
// }
|
||
8 years ago
|
|
||
8 years ago
|
err := service.createDirectoryInStoreIfNotExist(TLSStorePath)
|
||
8 years ago
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
7 years ago
|
err = service.createDirectoryInStoreIfNotExist(ComposeStorePath)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
8 years ago
|
return service, nil
|
||
|
}
|
||
|
|
||
7 years ago
|
// RemoveDirectory removes a directory on the filesystem.
|
||
|
func (service *Service) RemoveDirectory(directoryPath string) error {
|
||
|
return os.RemoveAll(directoryPath)
|
||
|
}
|
||
|
|
||
|
// GetStackProjectPath returns the absolute path on the FS for a stack based
|
||
|
// on its identifier.
|
||
|
func (service *Service) GetStackProjectPath(stackIdentifier string) string {
|
||
|
return path.Join(service.fileStorePath, ComposeStorePath, stackIdentifier)
|
||
|
}
|
||
|
|
||
|
// StoreStackFileFromString creates a subfolder in the ComposeStorePath and stores a new file using the content from a string.
|
||
|
// It returns the path to the folder where the file is stored.
|
||
|
func (service *Service) StoreStackFileFromString(stackIdentifier, stackFileContent string) (string, error) {
|
||
|
stackStorePath := path.Join(ComposeStorePath, stackIdentifier)
|
||
|
err := service.createDirectoryInStoreIfNotExist(stackStorePath)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
composeFilePath := path.Join(stackStorePath, ComposeFileDefaultName)
|
||
|
data := []byte(stackFileContent)
|
||
|
r := bytes.NewReader(data)
|
||
|
|
||
|
err = service.createFileInStore(composeFilePath, r)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return path.Join(service.fileStorePath, stackStorePath), nil
|
||
|
}
|
||
|
|
||
|
// StoreStackFileFromReader creates a subfolder in the ComposeStorePath and stores a new file using the content from an io.Reader.
|
||
|
// It returns the path to the folder where the file is stored.
|
||
|
func (service *Service) StoreStackFileFromReader(stackIdentifier string, r io.Reader) (string, error) {
|
||
|
stackStorePath := path.Join(ComposeStorePath, stackIdentifier)
|
||
|
err := service.createDirectoryInStoreIfNotExist(stackStorePath)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
composeFilePath := path.Join(stackStorePath, ComposeFileDefaultName)
|
||
|
|
||
|
err = service.createFileInStore(composeFilePath, r)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return path.Join(service.fileStorePath, stackStorePath), nil
|
||
|
}
|
||
|
|
||
7 years ago
|
// StoreTLSFile creates a folder in the TLSStorePath and stores a new file with the content from r.
|
||
|
func (service *Service) StoreTLSFile(folder string, fileType portainer.TLSFileType, r io.Reader) error {
|
||
|
storePath := path.Join(TLSStorePath, folder)
|
||
|
err := service.createDirectoryInStoreIfNotExist(storePath)
|
||
8 years ago
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
var fileName string
|
||
|
switch fileType {
|
||
|
case portainer.TLSFileCA:
|
||
|
fileName = TLSCACertFile
|
||
|
case portainer.TLSFileCert:
|
||
|
fileName = TLSCertFile
|
||
|
case portainer.TLSFileKey:
|
||
|
fileName = TLSKeyFile
|
||
|
default:
|
||
|
return portainer.ErrUndefinedTLSFileType
|
||
|
}
|
||
|
|
||
7 years ago
|
tlsFilePath := path.Join(storePath, fileName)
|
||
8 years ago
|
err = service.createFileInStore(tlsFilePath, r)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetPathForTLSFile returns the absolute path to a specific TLS file for an endpoint.
|
||
7 years ago
|
func (service *Service) GetPathForTLSFile(folder string, fileType portainer.TLSFileType) (string, error) {
|
||
8 years ago
|
var fileName string
|
||
|
switch fileType {
|
||
|
case portainer.TLSFileCA:
|
||
|
fileName = TLSCACertFile
|
||
|
case portainer.TLSFileCert:
|
||
|
fileName = TLSCertFile
|
||
|
case portainer.TLSFileKey:
|
||
|
fileName = TLSKeyFile
|
||
|
default:
|
||
|
return "", portainer.ErrUndefinedTLSFileType
|
||
|
}
|
||
7 years ago
|
return path.Join(service.fileStorePath, TLSStorePath, folder, fileName), nil
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
// DeleteTLSFiles deletes a folder in the TLS store path.
|
||
7 years ago
|
func (service *Service) DeleteTLSFiles(folder string) error {
|
||
|
storePath := path.Join(service.fileStorePath, TLSStorePath, folder)
|
||
|
err := os.RemoveAll(storePath)
|
||
8 years ago
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
7 years ago
|
// DeleteTLSFile deletes a specific TLS file from a folder.
|
||
|
func (service *Service) DeleteTLSFile(folder string, fileType portainer.TLSFileType) error {
|
||
|
var fileName string
|
||
|
switch fileType {
|
||
|
case portainer.TLSFileCA:
|
||
|
fileName = TLSCACertFile
|
||
|
case portainer.TLSFileCert:
|
||
|
fileName = TLSCertFile
|
||
|
case portainer.TLSFileKey:
|
||
|
fileName = TLSKeyFile
|
||
|
default:
|
||
|
return portainer.ErrUndefinedTLSFileType
|
||
|
}
|
||
|
|
||
|
filePath := path.Join(service.fileStorePath, TLSStorePath, folder, fileName)
|
||
|
|
||
|
err := os.Remove(filePath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
7 years ago
|
// GetFileContent returns a string content from file.
|
||
|
func (service *Service) GetFileContent(filePath string) (string, error) {
|
||
|
content, err := ioutil.ReadFile(filePath)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return string(content), nil
|
||
|
}
|
||
|
|
||
8 years ago
|
// createDirectoryInStoreIfNotExist creates a new directory in the file store if it doesn't exists on the file system.
|
||
|
func (service *Service) createDirectoryInStoreIfNotExist(name string) error {
|
||
8 years ago
|
path := path.Join(service.fileStorePath, name)
|
||
8 years ago
|
return createDirectoryIfNotExist(path, 0700)
|
||
|
}
|
||
|
|
||
|
// createDirectoryIfNotExist creates a directory if it doesn't exists on the file system.
|
||
|
func createDirectoryIfNotExist(path string, mode uint32) error {
|
||
8 years ago
|
_, err := os.Stat(path)
|
||
|
if os.IsNotExist(err) {
|
||
8 years ago
|
err = os.Mkdir(path, os.FileMode(mode))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
8 years ago
|
} else if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// createFile creates a new file in the file store with the content from r.
|
||
|
func (service *Service) createFileInStore(filePath string, r io.Reader) error {
|
||
|
path := path.Join(service.fileStorePath, filePath)
|
||
7 years ago
|
|
||
8 years ago
|
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer out.Close()
|
||
7 years ago
|
|
||
8 years ago
|
_, err = io.Copy(out, r)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
7 years ago
|
|
||
7 years ago
|
return nil
|
||
7 years ago
|
}
|