2018-01-06 17:53:12 +00:00
|
|
|
package filesystem
|
2016-12-25 20:34:02 +00:00
|
|
|
|
|
|
|
import (
|
2017-10-15 17:24:40 +00:00
|
|
|
"bytes"
|
2018-05-06 07:15:57 +00:00
|
|
|
"encoding/json"
|
|
|
|
"encoding/pem"
|
2020-07-07 21:57:52 +00:00
|
|
|
"errors"
|
2020-06-25 03:25:51 +00:00
|
|
|
"fmt"
|
2021-11-01 11:01:03 +00:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2017-09-25 16:13:56 +00:00
|
|
|
|
2020-07-06 23:18:39 +00:00
|
|
|
"github.com/gofrs/uuid"
|
2021-04-06 10:08:43 +00:00
|
|
|
portainer "github.com/portainer/portainer/api"
|
2016-12-25 20:34:02 +00:00
|
|
|
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// TLSStorePath represents the subfolder where TLS files are stored in the file store folder.
|
|
|
|
TLSStorePath = "tls"
|
2017-08-10 08:35:23 +00:00
|
|
|
// LDAPStorePath represents the subfolder where LDAP TLS files are stored in the TLSStorePath.
|
|
|
|
LDAPStorePath = "ldap"
|
2016-12-25 20:34:02 +00:00
|
|
|
// 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"
|
2017-10-15 17:24:40 +00:00
|
|
|
// 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"
|
2021-06-16 21:47:32 +00:00
|
|
|
// ManifestFileDefaultName represents the default name of a k8s manifest file.
|
|
|
|
ManifestFileDefaultName = "k8s-deployment.yml"
|
2020-05-14 02:14:28 +00:00
|
|
|
// EdgeStackStorePath represents the subfolder where edge stack files are stored in the file store folder.
|
|
|
|
EdgeStackStorePath = "edge_stacks"
|
2018-05-06 07:15:57 +00:00
|
|
|
// PrivateKeyFile represents the name on disk of the file containing the private key.
|
|
|
|
PrivateKeyFile = "portainer.key"
|
|
|
|
// PublicKeyFile represents the name on disk of the file containing the public key.
|
|
|
|
PublicKeyFile = "portainer.pub"
|
2018-12-09 03:49:27 +00:00
|
|
|
// BinaryStorePath represents the subfolder where binaries are stored in the file store folder.
|
|
|
|
BinaryStorePath = "bin"
|
2020-06-25 03:25:51 +00:00
|
|
|
// EdgeJobStorePath represents the subfolder where schedule files are stored.
|
|
|
|
EdgeJobStorePath = "edge_jobs"
|
2021-09-06 07:58:26 +00:00
|
|
|
// DockerConfigPath represents the subfolder where docker configuration is stored.
|
|
|
|
DockerConfigPath = "docker_config"
|
2018-12-09 03:49:27 +00:00
|
|
|
// ExtensionRegistryManagementStorePath represents the subfolder where files related to the
|
|
|
|
// registry management extension are stored.
|
|
|
|
ExtensionRegistryManagementStorePath = "extensions"
|
2020-07-06 23:18:39 +00:00
|
|
|
// CustomTemplateStorePath represents the subfolder where custom template files are stored in the file store folder.
|
|
|
|
CustomTemplateStorePath = "custom_templates"
|
|
|
|
// TempPath represent the subfolder where temporary files are saved
|
|
|
|
TempPath = "tmp"
|
2021-08-10 04:59:47 +00:00
|
|
|
// SSLCertPath represents the default ssl certificates path
|
|
|
|
SSLCertPath = "certs"
|
|
|
|
// DefaultSSLCertFilename represents the default ssl certificate file name
|
|
|
|
DefaultSSLCertFilename = "cert.pem"
|
|
|
|
// DefaultSSLKeyFilename represents the default ssl key file name
|
|
|
|
DefaultSSLKeyFilename = "key.pem"
|
2016-12-25 20:34:02 +00:00
|
|
|
)
|
|
|
|
|
2020-07-07 21:57:52 +00:00
|
|
|
// ErrUndefinedTLSFileType represents an error returned on undefined TLS file type
|
|
|
|
var ErrUndefinedTLSFileType = errors.New("Undefined TLS file type")
|
|
|
|
|
2017-01-02 19:32:53 +00:00
|
|
|
// Service represents a service for managing files and directories.
|
2016-12-25 20:34:02 +00:00
|
|
|
type Service struct {
|
2017-01-02 19:32:53 +00:00
|
|
|
dataStorePath string
|
2016-12-25 20:34:02 +00:00
|
|
|
fileStorePath string
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
// JoinPaths takes a trusted root path and a list of untrusted paths and joins
|
|
|
|
// them together using directory separators while enforcing that the untrusted
|
|
|
|
// paths cannot go higher up than the trusted root
|
|
|
|
func JoinPaths(trustedRoot string, untrustedPaths ...string) string {
|
|
|
|
if trustedRoot == "" {
|
|
|
|
trustedRoot = "."
|
|
|
|
}
|
|
|
|
|
|
|
|
p := filepath.Join(trustedRoot, filepath.Join(append([]string{"/"}, untrustedPaths...)...))
|
|
|
|
|
|
|
|
// avoid setting a volume name from the untrusted paths
|
|
|
|
vnp := filepath.VolumeName(p)
|
|
|
|
if filepath.VolumeName(trustedRoot) == "" && vnp != "" {
|
|
|
|
return strings.TrimPrefix(strings.TrimPrefix(p, vnp), `\`)
|
|
|
|
}
|
|
|
|
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
2017-01-02 19:32:53 +00:00
|
|
|
// 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) {
|
2016-12-25 20:34:02 +00:00
|
|
|
service := &Service{
|
2017-01-02 19:32:53 +00:00
|
|
|
dataStorePath: dataStorePath,
|
2021-11-01 11:01:03 +00:00
|
|
|
fileStorePath: JoinPaths(dataStorePath, fileStorePath),
|
2016-12-25 20:34:02 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 23:47:21 +00:00
|
|
|
err := os.MkdirAll(dataStorePath, 0755)
|
2016-12-25 20:34:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-08-10 04:59:47 +00:00
|
|
|
err = service.createDirectoryInStore(SSLCertPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-03-13 23:47:21 +00:00
|
|
|
err = service.createDirectoryInStore(TLSStorePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = service.createDirectoryInStore(ComposeStorePath)
|
2017-10-15 17:24:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-12-09 03:49:27 +00:00
|
|
|
err = service.createDirectoryInStore(BinaryStorePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-06 07:58:26 +00:00
|
|
|
err = service.createDirectoryInStore(DockerConfigPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-12-25 20:34:02 +00:00
|
|
|
return service, nil
|
|
|
|
}
|
|
|
|
|
2018-12-09 03:49:27 +00:00
|
|
|
// GetBinaryFolder returns the full path to the binary store on the filesystem
|
|
|
|
func (service *Service) GetBinaryFolder() string {
|
2021-11-01 11:01:03 +00:00
|
|
|
return JoinPaths(service.fileStorePath, BinaryStorePath)
|
2018-12-09 03:49:27 +00:00
|
|
|
}
|
|
|
|
|
2021-09-06 07:58:26 +00:00
|
|
|
// GetDockerConfigPath returns the full path to the docker config store on the filesystem
|
|
|
|
func (service *Service) GetDockerConfigPath() string {
|
2021-11-01 11:01:03 +00:00
|
|
|
return JoinPaths(service.fileStorePath, DockerConfigPath)
|
2021-09-06 07:58:26 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 17:24:40 +00:00
|
|
|
// 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 {
|
2021-11-01 11:01:03 +00:00
|
|
|
return JoinPaths(service.wrapFileStore(ComposeStorePath), stackIdentifier)
|
2017-10-15 17:24:40 +00:00
|
|
|
}
|
|
|
|
|
2021-08-10 04:59:47 +00:00
|
|
|
// Copy copies the file on fromFilePath to toFilePath
|
|
|
|
// if toFilePath exists func will fail unless deleteIfExists is true
|
|
|
|
func (service *Service) Copy(fromFilePath string, toFilePath string, deleteIfExists bool) error {
|
|
|
|
exists, err := service.FileExists(fromFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
return errors.New("File doesn't exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
finput, err := os.Open(fromFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer finput.Close()
|
|
|
|
|
|
|
|
exists, err = service.FileExists(toFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if exists {
|
|
|
|
if !deleteIfExists {
|
|
|
|
return errors.New("Destination file exists")
|
|
|
|
}
|
|
|
|
|
|
|
|
err := os.Remove(toFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foutput, err := os.Create(toFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer foutput.Close()
|
|
|
|
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
for {
|
|
|
|
n, err := finput.Read(buf)
|
|
|
|
if err != nil && err != io.EOF {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if n == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := foutput.Write(buf[:n]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-11 13:13:19 +00:00
|
|
|
// StoreStackFileFromBytes creates a subfolder in the ComposeStorePath and stores a new file from bytes.
|
2017-10-15 17:24:40 +00:00
|
|
|
// It returns the path to the folder where the file is stored.
|
2018-06-11 13:13:19 +00:00
|
|
|
func (service *Service) StoreStackFileFromBytes(stackIdentifier, fileName string, data []byte) (string, error) {
|
2021-11-01 11:01:03 +00:00
|
|
|
stackStorePath := JoinPaths(ComposeStorePath, stackIdentifier)
|
2018-03-13 23:47:21 +00:00
|
|
|
err := service.createDirectoryInStore(stackStorePath)
|
2017-10-15 17:24:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
composeFilePath := JoinPaths(stackStorePath, fileName)
|
2017-10-15 17:24:40 +00:00
|
|
|
r := bytes.NewReader(data)
|
|
|
|
|
|
|
|
err = service.createFileInStore(composeFilePath, r)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return service.wrapFileStore(stackStorePath), nil
|
2017-10-15 17:24:40 +00:00
|
|
|
}
|
|
|
|
|
2020-05-14 02:14:28 +00:00
|
|
|
// GetEdgeStackProjectPath returns the absolute path on the FS for a edge stack based
|
|
|
|
// on its identifier.
|
|
|
|
func (service *Service) GetEdgeStackProjectPath(edgeStackIdentifier string) string {
|
2021-11-01 11:01:03 +00:00
|
|
|
return JoinPaths(service.wrapFileStore(EdgeStackStorePath), edgeStackIdentifier)
|
2020-05-14 02:14:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StoreEdgeStackFileFromBytes creates a subfolder in the EdgeStackStorePath and stores a new file from bytes.
|
|
|
|
// It returns the path to the folder where the file is stored.
|
|
|
|
func (service *Service) StoreEdgeStackFileFromBytes(edgeStackIdentifier, fileName string, data []byte) (string, error) {
|
2021-11-01 11:01:03 +00:00
|
|
|
stackStorePath := JoinPaths(EdgeStackStorePath, edgeStackIdentifier)
|
2020-05-14 02:14:28 +00:00
|
|
|
err := service.createDirectoryInStore(stackStorePath)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
composeFilePath := JoinPaths(stackStorePath, fileName)
|
2020-05-14 02:14:28 +00:00
|
|
|
r := bytes.NewReader(data)
|
|
|
|
|
|
|
|
err = service.createFileInStore(composeFilePath, r)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return service.wrapFileStore(stackStorePath), nil
|
2020-05-14 02:14:28 +00:00
|
|
|
}
|
|
|
|
|
2018-12-09 03:49:27 +00:00
|
|
|
// StoreRegistryManagementFileFromBytes creates a subfolder in the
|
|
|
|
// ExtensionRegistryManagementStorePath and stores a new file from bytes.
|
|
|
|
// It returns the path to the folder where the file is stored.
|
|
|
|
func (service *Service) StoreRegistryManagementFileFromBytes(folder, fileName string, data []byte) (string, error) {
|
2021-11-01 11:01:03 +00:00
|
|
|
extensionStorePath := JoinPaths(ExtensionRegistryManagementStorePath, folder)
|
2018-12-09 03:49:27 +00:00
|
|
|
err := service.createDirectoryInStore(extensionStorePath)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
file := JoinPaths(extensionStorePath, fileName)
|
2018-12-09 03:49:27 +00:00
|
|
|
r := bytes.NewReader(data)
|
|
|
|
|
|
|
|
err = service.createFileInStore(file, r)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return service.wrapFileStore(file), nil
|
2018-12-09 03:49:27 +00:00
|
|
|
}
|
|
|
|
|
2018-06-11 13:13:19 +00:00
|
|
|
// StoreTLSFileFromBytes creates a folder in the TLSStorePath and stores a new file from bytes.
|
|
|
|
// It returns the path to the newly created file.
|
|
|
|
func (service *Service) StoreTLSFileFromBytes(folder string, fileType portainer.TLSFileType, data []byte) (string, error) {
|
2021-11-01 11:01:03 +00:00
|
|
|
storePath := JoinPaths(TLSStorePath, folder)
|
2018-03-13 23:47:21 +00:00
|
|
|
err := service.createDirectoryInStore(storePath)
|
2016-12-25 20:34:02 +00:00
|
|
|
if err != nil {
|
2018-06-11 13:13:19 +00:00
|
|
|
return "", err
|
2016-12-25 20:34:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var fileName string
|
|
|
|
switch fileType {
|
|
|
|
case portainer.TLSFileCA:
|
|
|
|
fileName = TLSCACertFile
|
|
|
|
case portainer.TLSFileCert:
|
|
|
|
fileName = TLSCertFile
|
|
|
|
case portainer.TLSFileKey:
|
|
|
|
fileName = TLSKeyFile
|
|
|
|
default:
|
2020-07-07 21:57:52 +00:00
|
|
|
return "", ErrUndefinedTLSFileType
|
2016-12-25 20:34:02 +00:00
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
tlsFilePath := JoinPaths(storePath, fileName)
|
2018-06-11 13:13:19 +00:00
|
|
|
r := bytes.NewReader(data)
|
2016-12-25 20:34:02 +00:00
|
|
|
err = service.createFileInStore(tlsFilePath, r)
|
|
|
|
if err != nil {
|
2018-06-11 13:13:19 +00:00
|
|
|
return "", err
|
2016-12-25 20:34:02 +00:00
|
|
|
}
|
2021-11-01 11:01:03 +00:00
|
|
|
return service.wrapFileStore(tlsFilePath), nil
|
2016-12-25 20:34:02 +00:00
|
|
|
}
|
|
|
|
|
2021-09-20 00:14:22 +00:00
|
|
|
// GetPathForTLSFile returns the absolute path to a specific TLS file for an environment(endpoint).
|
2017-08-10 08:35:23 +00:00
|
|
|
func (service *Service) GetPathForTLSFile(folder string, fileType portainer.TLSFileType) (string, error) {
|
2016-12-25 20:34:02 +00:00
|
|
|
var fileName string
|
|
|
|
switch fileType {
|
|
|
|
case portainer.TLSFileCA:
|
|
|
|
fileName = TLSCACertFile
|
|
|
|
case portainer.TLSFileCert:
|
|
|
|
fileName = TLSCertFile
|
|
|
|
case portainer.TLSFileKey:
|
|
|
|
fileName = TLSKeyFile
|
|
|
|
default:
|
2020-07-07 21:57:52 +00:00
|
|
|
return "", ErrUndefinedTLSFileType
|
2016-12-25 20:34:02 +00:00
|
|
|
}
|
2021-11-01 11:01:03 +00:00
|
|
|
return JoinPaths(service.wrapFileStore(TLSStorePath), folder, fileName), nil
|
2016-12-25 20:34:02 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 06:08:37 +00:00
|
|
|
// DeleteTLSFiles deletes a folder in the TLS store path.
|
2017-08-10 08:35:23 +00:00
|
|
|
func (service *Service) DeleteTLSFiles(folder string) error {
|
2021-11-01 11:01:03 +00:00
|
|
|
storePath := JoinPaths(service.wrapFileStore(TLSStorePath), folder)
|
|
|
|
return os.RemoveAll(storePath)
|
2016-12-25 20:34:02 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 06:08:37 +00:00
|
|
|
// 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:
|
2020-07-07 21:57:52 +00:00
|
|
|
return ErrUndefinedTLSFileType
|
2017-09-14 06:08:37 +00:00
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
filePath := JoinPaths(service.wrapFileStore(TLSStorePath), folder, fileName)
|
2017-09-14 06:08:37 +00:00
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return os.Remove(filePath)
|
2017-09-14 06:08:37 +00:00
|
|
|
}
|
|
|
|
|
2018-07-03 18:31:02 +00:00
|
|
|
// GetFileContent returns the content of a file as bytes.
|
2021-11-01 11:01:03 +00:00
|
|
|
func (service *Service) GetFileContent(trustedRoot, filePath string) ([]byte, error) {
|
|
|
|
content, err := os.ReadFile(JoinPaths(trustedRoot, filePath))
|
2017-10-15 17:24:40 +00:00
|
|
|
if err != nil {
|
2021-11-01 11:01:03 +00:00
|
|
|
if filePath == "" {
|
|
|
|
filePath = trustedRoot
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("could not get the contents of the file '%s'", filePath)
|
2017-10-15 17:24:40 +00:00
|
|
|
}
|
|
|
|
|
2018-07-03 18:31:02 +00:00
|
|
|
return content, nil
|
2017-10-15 17:24:40 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 10:07:56 +00:00
|
|
|
// Rename renames a file or directory
|
|
|
|
func (service *Service) Rename(oldPath, newPath string) error {
|
|
|
|
return os.Rename(oldPath, newPath)
|
|
|
|
}
|
|
|
|
|
2018-05-06 07:15:57 +00:00
|
|
|
// WriteJSONToFile writes JSON to the specified file.
|
|
|
|
func (service *Service) WriteJSONToFile(path string, content interface{}) error {
|
|
|
|
jsonContent, err := json.Marshal(content)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return os.WriteFile(path, jsonContent, 0644)
|
2018-05-06 07:15:57 +00:00
|
|
|
}
|
|
|
|
|
2018-06-19 11:15:10 +00:00
|
|
|
// FileExists checks for the existence of the specified file.
|
|
|
|
func (service *Service) FileExists(filePath string) (bool, error) {
|
2021-06-15 21:11:35 +00:00
|
|
|
return FileExists(filePath)
|
2018-06-19 11:15:10 +00:00
|
|
|
}
|
|
|
|
|
2018-05-06 07:15:57 +00:00
|
|
|
// KeyPairFilesExist checks for the existence of the key files.
|
|
|
|
func (service *Service) KeyPairFilesExist() (bool, error) {
|
2021-11-01 11:01:03 +00:00
|
|
|
privateKeyPath := JoinPaths(service.dataStorePath, PrivateKeyFile)
|
2018-06-19 11:15:10 +00:00
|
|
|
exists, err := service.FileExists(privateKeyPath)
|
2021-11-01 11:01:03 +00:00
|
|
|
if err != nil || !exists {
|
2018-05-06 07:15:57 +00:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
publicKeyPath := JoinPaths(service.dataStorePath, PublicKeyFile)
|
2018-06-19 11:15:10 +00:00
|
|
|
exists, err = service.FileExists(publicKeyPath)
|
2021-11-01 11:01:03 +00:00
|
|
|
if err != nil || !exists {
|
2018-05-06 07:15:57 +00:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StoreKeyPair store the specified keys content as PEM files on disk.
|
|
|
|
func (service *Service) StoreKeyPair(private, public []byte, privatePEMHeader, publicPEMHeader string) error {
|
|
|
|
err := service.createPEMFileInStore(private, privatePEMHeader, PrivateKeyFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return service.createPEMFileInStore(public, publicPEMHeader, PublicKeyFile)
|
2018-05-06 07:15:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// LoadKeyPair retrieve the content of both key files on disk.
|
|
|
|
func (service *Service) LoadKeyPair() ([]byte, []byte, error) {
|
|
|
|
privateKey, err := service.getContentFromPEMFile(PrivateKeyFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
publicKey, err := service.getContentFromPEMFile(PublicKeyFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return privateKey, publicKey, nil
|
|
|
|
}
|
|
|
|
|
2018-03-13 23:47:21 +00:00
|
|
|
// createDirectoryInStore creates a new directory in the file store
|
|
|
|
func (service *Service) createDirectoryInStore(name string) error {
|
2021-11-01 11:01:03 +00:00
|
|
|
path := service.wrapFileStore(name)
|
2018-03-13 23:47:21 +00:00
|
|
|
return os.MkdirAll(path, 0700)
|
2016-12-25 20:34:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// createFile creates a new file in the file store with the content from r.
|
|
|
|
func (service *Service) createFileInStore(filePath string, r io.Reader) error {
|
2021-11-01 11:01:03 +00:00
|
|
|
path := service.wrapFileStore(filePath)
|
2017-10-15 17:24:40 +00:00
|
|
|
|
2016-12-25 20:34:02 +00:00
|
|
|
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer out.Close()
|
2017-10-15 17:24:40 +00:00
|
|
|
|
2016-12-25 20:34:02 +00:00
|
|
|
_, err = io.Copy(out, r)
|
2021-11-01 11:01:03 +00:00
|
|
|
return err
|
2017-09-25 16:13:56 +00:00
|
|
|
}
|
2018-05-06 07:15:57 +00:00
|
|
|
|
|
|
|
func (service *Service) createPEMFileInStore(content []byte, fileType, filePath string) error {
|
2021-11-01 11:01:03 +00:00
|
|
|
path := service.wrapFileStore(filePath)
|
2018-05-06 07:15:57 +00:00
|
|
|
block := &pem.Block{Type: fileType, Bytes: content}
|
|
|
|
|
|
|
|
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return pem.Encode(out, block)
|
2018-05-06 07:15:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (service *Service) getContentFromPEMFile(filePath string) ([]byte, error) {
|
2021-11-01 11:01:03 +00:00
|
|
|
path := service.wrapFileStore(filePath)
|
2018-05-06 07:15:57 +00:00
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
fileContent, err := os.ReadFile(path)
|
2018-05-06 07:15:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
block, _ := pem.Decode(fileContent)
|
|
|
|
return block.Bytes, nil
|
|
|
|
}
|
2018-11-05 20:58:15 +00:00
|
|
|
|
2020-07-06 23:18:39 +00:00
|
|
|
// GetCustomTemplateProjectPath returns the absolute path on the FS for a custom template based
|
|
|
|
// on its identifier.
|
|
|
|
func (service *Service) GetCustomTemplateProjectPath(identifier string) string {
|
2021-11-01 11:01:03 +00:00
|
|
|
return JoinPaths(service.wrapFileStore(CustomTemplateStorePath), identifier)
|
2020-07-06 23:18:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StoreCustomTemplateFileFromBytes creates a subfolder in the CustomTemplateStorePath and stores a new file from bytes.
|
|
|
|
// It returns the path to the folder where the file is stored.
|
|
|
|
func (service *Service) StoreCustomTemplateFileFromBytes(identifier, fileName string, data []byte) (string, error) {
|
2021-11-01 11:01:03 +00:00
|
|
|
customTemplateStorePath := JoinPaths(CustomTemplateStorePath, identifier)
|
2020-07-06 23:18:39 +00:00
|
|
|
err := service.createDirectoryInStore(customTemplateStorePath)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
templateFilePath := JoinPaths(customTemplateStorePath, fileName)
|
2020-07-06 23:18:39 +00:00
|
|
|
r := bytes.NewReader(data)
|
|
|
|
|
|
|
|
err = service.createFileInStore(templateFilePath, r)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return service.wrapFileStore(customTemplateStorePath), nil
|
2020-07-06 23:18:39 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 03:25:51 +00:00
|
|
|
// GetEdgeJobFolder returns the absolute path on the filesystem for an Edge job based
|
2018-11-05 20:58:15 +00:00
|
|
|
// on its identifier.
|
2020-06-25 03:25:51 +00:00
|
|
|
func (service *Service) GetEdgeJobFolder(identifier string) string {
|
2021-11-01 11:01:03 +00:00
|
|
|
return JoinPaths(service.wrapFileStore(EdgeJobStorePath), identifier)
|
2018-11-05 20:58:15 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 03:25:51 +00:00
|
|
|
// StoreEdgeJobFileFromBytes creates a subfolder in the EdgeJobStorePath and stores a new file from bytes.
|
2018-11-05 20:58:15 +00:00
|
|
|
// It returns the path to the folder where the file is stored.
|
2020-06-25 03:25:51 +00:00
|
|
|
func (service *Service) StoreEdgeJobFileFromBytes(identifier string, data []byte) (string, error) {
|
2021-11-01 11:01:03 +00:00
|
|
|
edgeJobStorePath := JoinPaths(EdgeJobStorePath, identifier)
|
2020-06-25 03:25:51 +00:00
|
|
|
err := service.createDirectoryInStore(edgeJobStorePath)
|
2018-11-05 20:58:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
filePath := JoinPaths(edgeJobStorePath, createEdgeJobFileName(identifier))
|
2018-11-05 20:58:15 +00:00
|
|
|
r := bytes.NewReader(data)
|
|
|
|
err = service.createFileInStore(filePath, r)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return service.wrapFileStore(filePath), nil
|
2018-11-05 20:58:15 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 03:25:51 +00:00
|
|
|
func createEdgeJobFileName(identifier string) string {
|
2018-11-05 20:58:15 +00:00
|
|
|
return "job_" + identifier + ".sh"
|
|
|
|
}
|
2020-06-25 03:25:51 +00:00
|
|
|
|
|
|
|
// ClearEdgeJobTaskLogs clears the Edge job task logs
|
|
|
|
func (service *Service) ClearEdgeJobTaskLogs(edgeJobID string, taskID string) error {
|
|
|
|
path := service.getEdgeJobTaskLogPath(edgeJobID, taskID)
|
2021-11-01 11:01:03 +00:00
|
|
|
return os.Remove(path)
|
2020-06-25 03:25:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetEdgeJobTaskLogFileContent fetches the Edge job task logs
|
|
|
|
func (service *Service) GetEdgeJobTaskLogFileContent(edgeJobID string, taskID string) (string, error) {
|
|
|
|
path := service.getEdgeJobTaskLogPath(edgeJobID, taskID)
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
fileContent, err := os.ReadFile(path)
|
2020-06-25 03:25:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(fileContent), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StoreEdgeJobTaskLogFileFromBytes stores the log file
|
|
|
|
func (service *Service) StoreEdgeJobTaskLogFileFromBytes(edgeJobID, taskID string, data []byte) error {
|
2021-11-01 11:01:03 +00:00
|
|
|
edgeJobStorePath := JoinPaths(EdgeJobStorePath, edgeJobID)
|
2020-06-25 03:25:51 +00:00
|
|
|
err := service.createDirectoryInStore(edgeJobStorePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
filePath := JoinPaths(edgeJobStorePath, fmt.Sprintf("logs_%s", taskID))
|
2020-06-25 03:25:51 +00:00
|
|
|
r := bytes.NewReader(data)
|
2021-11-01 11:01:03 +00:00
|
|
|
return service.createFileInStore(filePath, r)
|
2020-06-25 03:25:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (service *Service) getEdgeJobTaskLogPath(edgeJobID string, taskID string) string {
|
|
|
|
return fmt.Sprintf("%s/logs_%s", service.GetEdgeJobFolder(edgeJobID), taskID)
|
|
|
|
}
|
2020-07-06 23:18:39 +00:00
|
|
|
|
|
|
|
// GetTemporaryPath returns a temp folder
|
|
|
|
func (service *Service) GetTemporaryPath() (string, error) {
|
|
|
|
uid, err := uuid.NewV4()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:01:03 +00:00
|
|
|
return JoinPaths(service.wrapFileStore(TempPath), uid.String()), nil
|
2020-07-06 23:18:39 +00:00
|
|
|
}
|
2021-04-06 10:08:43 +00:00
|
|
|
|
|
|
|
// GetDataStorePath returns path to data folder
|
|
|
|
func (service *Service) GetDatastorePath() string {
|
|
|
|
return service.dataStorePath
|
|
|
|
}
|
2021-06-15 21:11:35 +00:00
|
|
|
|
2021-08-10 04:59:47 +00:00
|
|
|
func (service *Service) wrapFileStore(filepath string) string {
|
2021-11-01 11:01:03 +00:00
|
|
|
return JoinPaths(service.fileStorePath, filepath)
|
2021-08-10 04:59:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func defaultCertPathUnderFileStore() (string, string) {
|
2021-11-01 11:01:03 +00:00
|
|
|
certPath := JoinPaths(SSLCertPath, DefaultSSLCertFilename)
|
|
|
|
keyPath := JoinPaths(SSLCertPath, DefaultSSLKeyFilename)
|
2021-08-10 04:59:47 +00:00
|
|
|
return certPath, keyPath
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetDefaultSSLCertsPath returns the ssl certs path
|
|
|
|
func (service *Service) GetDefaultSSLCertsPath() (string, string) {
|
|
|
|
certPath, keyPath := defaultCertPathUnderFileStore()
|
|
|
|
return service.wrapFileStore(certPath), service.wrapFileStore(keyPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
// StoreSSLCertPair stores a ssl certificate pair
|
|
|
|
func (service *Service) StoreSSLCertPair(cert, key []byte) (string, string, error) {
|
|
|
|
certPath, keyPath := defaultCertPathUnderFileStore()
|
|
|
|
|
|
|
|
r := bytes.NewReader(cert)
|
|
|
|
err := service.createFileInStore(certPath, r)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
r = bytes.NewReader(key)
|
|
|
|
err = service.createFileInStore(keyPath, r)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return service.wrapFileStore(certPath), service.wrapFileStore(keyPath), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CopySSLCertPair copies a ssl certificate pair
|
|
|
|
func (service *Service) CopySSLCertPair(certPath, keyPath string) (string, string, error) {
|
|
|
|
defCertPath, defKeyPath := service.GetDefaultSSLCertsPath()
|
|
|
|
|
|
|
|
err := service.Copy(certPath, defCertPath, false)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = service.Copy(keyPath, defKeyPath, false)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return defCertPath, defKeyPath, nil
|
|
|
|
}
|
|
|
|
|
2021-06-15 21:11:35 +00:00
|
|
|
// FileExists checks for the existence of the specified file.
|
|
|
|
func FileExists(filePath string) (bool, error) {
|
|
|
|
if _, err := os.Stat(filePath); err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func MoveDirectory(originalPath, newPath string) error {
|
|
|
|
if _, err := os.Stat(originalPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
alreadyExists, err := FileExists(newPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if alreadyExists {
|
|
|
|
return errors.New("Target path already exists")
|
|
|
|
}
|
|
|
|
|
|
|
|
return os.Rename(originalPath, newPath)
|
|
|
|
}
|