Files
dokku/plugins/storage/storage.go
2026-01-08 02:01:53 -05:00

131 lines
3.4 KiB
Go

package storage
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/dokku/dokku/plugins/common"
dockeroptions "github.com/dokku/dokku/plugins/docker-options"
)
// MountPhases are the phases where storage mounts are applied
var MountPhases = []string{"deploy", "run"}
// VerifyPaths validates the storage mount path format
func VerifyPaths(mountPath string) error {
if strings.HasPrefix(mountPath, "/") {
matched, err := regexp.MatchString(`^/.*:/`, mountPath)
if err != nil {
return err
}
if !matched {
return errors.New("Storage path must be two valid paths divided by colon.")
}
} else {
matched, err := regexp.MatchString(`^[a-zA-Z0-9][a-zA-Z0-9_.-]+:/`, mountPath)
if err != nil {
return err
}
if !matched {
return errors.New("Volume name must be two characters or more. Volume name must not contain invalid characters. Storage path must be two valid paths divided by colon.")
}
}
return nil
}
// CheckIfPathExists checks if a mount path exists in the specified phases
func CheckIfPathExists(appName string, mountPath string, phases []string) bool {
for _, phase := range phases {
options, err := dockeroptions.GetDockerOptionsForPhase(appName, phase)
if err != nil {
continue
}
for _, option := range options {
if option == fmt.Sprintf("-v %s", mountPath) {
return true
}
}
}
return false
}
// GetBindMounts returns the bind mounts for an app and phase
func GetBindMounts(appName string, phase string) ([]string, error) {
mounts := []string{}
options, err := dockeroptions.GetDockerOptionsForPhase(appName, phase)
if err != nil {
return mounts, err
}
for _, option := range options {
if strings.HasPrefix(option, "-v ") {
mount := strings.TrimPrefix(option, "-v ")
mounts = append(mounts, mount)
}
}
return mounts, nil
}
// GetBindMountsForDisplay returns the bind mounts formatted for display
func GetBindMountsForDisplay(appName string, phase string) string {
mounts, err := GetBindMounts(appName, phase)
if err != nil {
return ""
}
result := []string{}
for _, mount := range mounts {
result = append(result, fmt.Sprintf("-v %s", mount))
}
return strings.Join(result, " ")
}
// StorageListEntry represents a storage mount entry for JSON output
type StorageListEntry struct {
HostPath string `json:"host_path"`
ContainerPath string `json:"container_path"`
VolumeOptions string `json:"volume_options"`
}
// ParseMountPath parses a mount path into its components
func ParseMountPath(mountPath string) StorageListEntry {
parts := strings.SplitN(mountPath, ":", 3)
entry := StorageListEntry{}
if len(parts) >= 1 {
entry.HostPath = parts[0]
}
if len(parts) >= 2 {
entry.ContainerPath = parts[1]
}
if len(parts) >= 3 {
entry.VolumeOptions = parts[2]
}
return entry
}
// GetStorageDirectory returns the storage directory path
func GetStorageDirectory() string {
dokkuLibRoot := common.GetenvWithDefault("DOKKU_LIB_ROOT", "/var/lib/dokku")
return fmt.Sprintf("%s/data/storage", dokkuLibRoot)
}
// ValidateDirectoryName validates a storage directory name
func ValidateDirectoryName(directory string) error {
if directory == "" {
return errors.New("Please specify a directory to create")
}
matched, err := regexp.MatchString(`^[A-Za-z0-9_-]+$`, directory)
if err != nil {
return err
}
if !matched {
return errors.New("Directory can only contain the following set of characters: [A-Za-z0-9_-]")
}
return nil
}