2017-01-03 22:27:20 -08:00
|
|
|
package common
|
|
|
|
|
|
|
|
|
|
import (
|
2017-04-24 09:03:30 -06:00
|
|
|
"bufio"
|
2017-01-03 22:27:20 -08:00
|
|
|
"fmt"
|
2017-04-24 09:03:30 -06:00
|
|
|
"io/ioutil"
|
2017-01-03 22:27:20 -08:00
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
2020-02-03 04:37:31 -05:00
|
|
|
"path/filepath"
|
2020-02-09 20:37:03 -05:00
|
|
|
"reflect"
|
2017-01-03 22:27:20 -08:00
|
|
|
"regexp"
|
2020-02-09 20:37:03 -05:00
|
|
|
"sort"
|
2017-01-03 22:27:20 -08:00
|
|
|
"strings"
|
2017-10-04 00:48:02 -04:00
|
|
|
"unicode"
|
2017-01-03 22:27:20 -08:00
|
|
|
|
|
|
|
|
sh "github.com/codeskyblue/go-sh"
|
|
|
|
|
)
|
|
|
|
|
|
2017-01-17 11:12:38 -08:00
|
|
|
// ShellCmd represents a shell command to be run for dokku
|
|
|
|
|
type ShellCmd struct {
|
2017-01-03 22:27:20 -08:00
|
|
|
Env map[string]string
|
|
|
|
|
Command *exec.Cmd
|
|
|
|
|
CommandString string
|
|
|
|
|
Args []string
|
|
|
|
|
ShowOutput bool
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-17 11:12:38 -08:00
|
|
|
// NewShellCmd returns a new ShellCmd struct
|
|
|
|
|
func NewShellCmd(command string) *ShellCmd {
|
2017-01-03 22:27:20 -08:00
|
|
|
items := strings.Split(command, " ")
|
2017-10-12 22:38:18 -04:00
|
|
|
cmd := items[0]
|
|
|
|
|
args := items[1:]
|
2017-01-17 11:12:38 -08:00
|
|
|
return &ShellCmd{
|
2017-01-03 22:27:20 -08:00
|
|
|
Command: exec.Command(cmd, args...),
|
2017-10-12 22:38:18 -04:00
|
|
|
CommandString: command,
|
2017-01-03 22:27:20 -08:00
|
|
|
Args: args,
|
|
|
|
|
ShowOutput: true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Execute is a lightweight wrapper around exec.Command
|
2017-03-07 09:44:16 -07:00
|
|
|
func (sc *ShellCmd) Execute() bool {
|
2017-01-03 22:27:20 -08:00
|
|
|
env := os.Environ()
|
2017-03-07 09:44:16 -07:00
|
|
|
for k, v := range sc.Env {
|
2017-01-03 22:27:20 -08:00
|
|
|
env = append(env, fmt.Sprintf("%s=%s", k, v))
|
|
|
|
|
}
|
2017-03-07 09:44:16 -07:00
|
|
|
sc.Command.Env = env
|
|
|
|
|
if sc.ShowOutput {
|
|
|
|
|
sc.Command.Stdout = os.Stdout
|
|
|
|
|
sc.Command.Stderr = os.Stderr
|
2017-01-03 22:27:20 -08:00
|
|
|
}
|
2017-10-02 16:50:05 -07:00
|
|
|
if err := sc.Command.Run(); err != nil {
|
2017-01-03 22:27:20 -08:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-24 09:03:30 -06:00
|
|
|
// Output is a lightweight wrapper around exec.Command.Output()
|
|
|
|
|
func (sc *ShellCmd) Output() ([]byte, error) {
|
|
|
|
|
env := os.Environ()
|
|
|
|
|
for k, v := range sc.Env {
|
|
|
|
|
env = append(env, fmt.Sprintf("%s=%s", k, v))
|
2017-01-03 22:27:20 -08:00
|
|
|
}
|
2017-04-24 09:03:30 -06:00
|
|
|
sc.Command.Env = env
|
|
|
|
|
if sc.ShowOutput {
|
|
|
|
|
sc.Command.Stdout = os.Stdout
|
|
|
|
|
sc.Command.Stderr = os.Stderr
|
2017-01-03 22:27:20 -08:00
|
|
|
}
|
2017-04-24 09:03:30 -06:00
|
|
|
return sc.Command.Output()
|
2017-01-03 22:27:20 -08:00
|
|
|
}
|
|
|
|
|
|
2020-03-10 14:12:05 -04:00
|
|
|
// AppRoot returns the app root path
|
|
|
|
|
func AppRoot(appName string) string {
|
|
|
|
|
dokkuRoot := MustGetEnv("DOKKU_ROOT")
|
|
|
|
|
return fmt.Sprintf("%v/%v", dokkuRoot, appName)
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-03 22:27:20 -08:00
|
|
|
// GetDeployingAppImageName returns deploying image identifier for a given app, tag tuple. validate if tag is presented
|
2017-01-14 13:34:34 -08:00
|
|
|
func GetDeployingAppImageName(appName, imageTag, imageRepo string) (imageName string) {
|
2017-01-03 22:27:20 -08:00
|
|
|
if appName == "" {
|
|
|
|
|
LogFail("(GetDeployingAppImageName) APP must not be empty")
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 04:30:40 -05:00
|
|
|
b, err := PlugnTriggerOutput("deployed-app-repository", []string{appName}...)
|
2017-01-03 22:27:20 -08:00
|
|
|
if err != nil {
|
|
|
|
|
LogFail(err.Error())
|
|
|
|
|
}
|
|
|
|
|
imageRemoteRepository := string(b[:])
|
|
|
|
|
|
2020-02-22 04:30:40 -05:00
|
|
|
b, err = PlugnTriggerOutput("deployed-app-image-tag", []string{appName}...)
|
2017-01-03 22:27:20 -08:00
|
|
|
if err != nil {
|
|
|
|
|
LogFail(err.Error())
|
|
|
|
|
}
|
|
|
|
|
newImageTag := string(b[:])
|
|
|
|
|
|
2020-02-22 04:30:40 -05:00
|
|
|
b, err = PlugnTriggerOutput("deployed-app-image-repo", []string{appName}...)
|
2017-01-03 22:27:20 -08:00
|
|
|
if err != nil {
|
|
|
|
|
LogFail(err.Error())
|
|
|
|
|
}
|
|
|
|
|
newImageRepo := string(b[:])
|
|
|
|
|
|
|
|
|
|
if newImageRepo != "" {
|
|
|
|
|
imageRepo = newImageRepo
|
|
|
|
|
}
|
|
|
|
|
if newImageTag != "" {
|
|
|
|
|
imageTag = newImageTag
|
|
|
|
|
}
|
|
|
|
|
if imageRepo == "" {
|
|
|
|
|
imageRepo = GetAppImageRepo(appName)
|
|
|
|
|
}
|
|
|
|
|
if imageTag == "" {
|
|
|
|
|
imageTag = "latest"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
imageName = fmt.Sprintf("%s%s:%s", imageRemoteRepository, imageRepo, imageTag)
|
|
|
|
|
if !VerifyImage(imageName) {
|
2018-04-03 01:21:50 -04:00
|
|
|
LogFail(fmt.Sprintf("App image (%s) not found", imageName))
|
2017-01-03 22:27:20 -08:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAppImageRepo is the central definition of a dokku image repo pattern
|
|
|
|
|
func GetAppImageRepo(appName string) string {
|
|
|
|
|
return strings.Join([]string{"dokku", appName}, "/")
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-03 04:37:31 -05:00
|
|
|
// GetAppContainerIDs returns a list of docker container ids for given app and optional container_type
|
|
|
|
|
func GetAppContainerIDs(appName string, containerType string) ([]string, error) {
|
|
|
|
|
var containerIDs []string
|
|
|
|
|
if err := VerifyAppName(appName); err != nil {
|
|
|
|
|
return containerIDs, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dokkuRoot := MustGetEnv("DOKKU_ROOT")
|
|
|
|
|
appRoot := fmt.Sprintf("%v/%v", dokkuRoot, appName)
|
|
|
|
|
containerFilePath := fmt.Sprintf("%v/CONTAINER", appRoot)
|
|
|
|
|
_, err := os.Stat(containerFilePath)
|
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
|
containerIDs = append(containerIDs, ReadFirstLine(containerFilePath))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
containerPattern := fmt.Sprintf("%v/CONTAINER.*", appRoot)
|
|
|
|
|
if containerType != "" {
|
|
|
|
|
containerPattern = fmt.Sprintf("%v/CONTAINER.%v.*", appRoot, containerType)
|
|
|
|
|
if strings.Contains(".", containerType) {
|
|
|
|
|
containerPattern = fmt.Sprintf("%v/CONTAINER.%v", appRoot, containerType)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
files, _ := filepath.Glob(containerPattern)
|
|
|
|
|
for _, containerFile := range files {
|
|
|
|
|
containerIDs = append(containerIDs, ReadFirstLine(containerFile))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return containerIDs, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAppRunningContainerIDs return a list of running docker container ids for given app and optional container_type
|
|
|
|
|
func GetAppRunningContainerIDs(appName string, containerType string) ([]string, error) {
|
|
|
|
|
var runningContainerIDs []string
|
|
|
|
|
if err := VerifyAppName(appName); err != nil {
|
|
|
|
|
return runningContainerIDs, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !IsDeployed(appName) {
|
|
|
|
|
LogFail(fmt.Sprintf("App %v has not been deployed", appName))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
containerIDs, err := GetAppContainerIDs(appName, containerType)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return runningContainerIDs, nil
|
|
|
|
|
}
|
|
|
|
|
for _, containerID := range containerIDs {
|
|
|
|
|
if ContainerIsRunning(containerID) {
|
|
|
|
|
runningContainerIDs = append(runningContainerIDs, containerID)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return runningContainerIDs, nil
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-24 09:03:30 -06:00
|
|
|
// ContainerIsRunning checks to see if a container is running
|
2017-07-22 15:32:14 -06:00
|
|
|
func ContainerIsRunning(containerID string) bool {
|
|
|
|
|
b, err := DockerInspect(containerID, "'{{.State.Running}}'")
|
2017-04-24 09:03:30 -06:00
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2017-04-26 18:49:19 -06:00
|
|
|
return strings.TrimSpace(string(b[:])) == "true"
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DirectoryExists returns if a path exists and is a directory
|
|
|
|
|
func DirectoryExists(filePath string) bool {
|
|
|
|
|
fi, err := os.Stat(filePath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fi.IsDir()
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-01 20:55:16 -06:00
|
|
|
// DockerInspect runs an inspect command with a given format against a container id
|
2019-06-29 15:10:35 -04:00
|
|
|
func DockerInspect(containerOrImageID, format string) (output string, err error) {
|
|
|
|
|
b, err := sh.Command(DockerBin(), "inspect", "--format", format, containerOrImageID).Output()
|
2017-05-01 20:55:16 -06:00
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
2017-10-02 16:50:05 -07:00
|
|
|
output = strings.TrimSpace(string(b[:]))
|
2017-07-22 15:32:14 -06:00
|
|
|
if strings.HasPrefix(output, "'") && strings.HasSuffix(output, "'") {
|
2017-05-01 20:55:16 -06:00
|
|
|
output = strings.TrimSuffix(strings.TrimPrefix(output, "'"), "'")
|
|
|
|
|
}
|
2017-10-02 16:50:05 -07:00
|
|
|
return
|
2017-05-01 20:55:16 -06:00
|
|
|
}
|
|
|
|
|
|
2017-04-24 09:03:30 -06:00
|
|
|
// DokkuApps returns a list of all local apps
|
2017-10-02 16:50:05 -07:00
|
|
|
func DokkuApps() (apps []string, err error) {
|
2017-04-24 09:03:30 -06:00
|
|
|
dokkuRoot := MustGetEnv("DOKKU_ROOT")
|
|
|
|
|
files, err := ioutil.ReadDir(dokkuRoot)
|
|
|
|
|
if err != nil {
|
2017-10-02 16:50:05 -07:00
|
|
|
err = fmt.Errorf("You haven't deployed any applications yet")
|
|
|
|
|
return
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, f := range files {
|
|
|
|
|
appRoot := strings.Join([]string{dokkuRoot, f.Name()}, "/")
|
|
|
|
|
if !DirectoryExists(appRoot) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if f.Name() == "tls" || strings.HasPrefix(f.Name(), ".") {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
apps = append(apps, f.Name())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(apps) == 0 {
|
2017-10-02 16:50:05 -07:00
|
|
|
err = fmt.Errorf("You haven't deployed any applications yet")
|
|
|
|
|
return
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2017-10-02 16:50:05 -07:00
|
|
|
return
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FileToSlice reads in all the lines from a file into a string slice
|
2017-10-02 16:50:05 -07:00
|
|
|
func FileToSlice(filePath string) (lines []string, err error) {
|
2017-04-24 09:03:30 -06:00
|
|
|
f, err := os.Open(filePath)
|
|
|
|
|
if err != nil {
|
2017-10-02 16:50:05 -07:00
|
|
|
return
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
|
text := strings.TrimSpace(scanner.Text())
|
|
|
|
|
if text == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
lines = append(lines, text)
|
|
|
|
|
}
|
|
|
|
|
err = scanner.Err()
|
2017-10-02 16:50:05 -07:00
|
|
|
return
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FileExists returns if a path exists and is a file
|
|
|
|
|
func FileExists(filePath string) bool {
|
|
|
|
|
fi, err := os.Stat(filePath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fi.Mode().IsRegular()
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-26 18:49:19 -06:00
|
|
|
// GetAppImageName returns image identifier for a given app, tag tuple. validate if tag is presented
|
2017-04-24 09:03:30 -06:00
|
|
|
func GetAppImageName(appName, imageTag, imageRepo string) (imageName string) {
|
|
|
|
|
err := VerifyAppName(appName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
LogFail(err.Error())
|
2017-01-03 22:27:20 -08:00
|
|
|
}
|
2017-04-24 09:03:30 -06:00
|
|
|
|
|
|
|
|
if imageRepo == "" {
|
|
|
|
|
imageRepo = GetAppImageRepo(appName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if imageTag == "" {
|
|
|
|
|
imageName = fmt.Sprintf("%v:latest", imageRepo)
|
|
|
|
|
} else {
|
|
|
|
|
imageName = fmt.Sprintf("%v:%v", imageRepo, imageTag)
|
|
|
|
|
if !VerifyImage(imageName) {
|
2018-04-03 01:21:50 -04:00
|
|
|
LogFail(fmt.Sprintf("App image (%s) not found", imageName))
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-22 15:32:14 -06:00
|
|
|
// IsDeployed returns true if given app has a running container
|
2017-04-24 09:03:30 -06:00
|
|
|
func IsDeployed(appName string) bool {
|
|
|
|
|
dokkuRoot := MustGetEnv("DOKKU_ROOT")
|
|
|
|
|
appRoot := strings.Join([]string{dokkuRoot, appName}, "/")
|
|
|
|
|
files, err := ioutil.ReadDir(appRoot)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, f := range files {
|
|
|
|
|
if f.Name() == "CONTAINER" || strings.HasPrefix(f.Name(), "CONTAINER.") {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-03 22:27:20 -08:00
|
|
|
return false
|
|
|
|
|
}
|
2017-04-24 09:03:30 -06:00
|
|
|
|
|
|
|
|
// IsImageHerokuishBased returns true if app image is based on herokuish
|
2019-11-19 12:14:37 -07:00
|
|
|
func IsImageHerokuishBased(image string, appName string) bool {
|
2019-06-29 15:10:35 -04:00
|
|
|
output, err := DockerInspect(image, "{{range .Config.Env}}{{if eq . \"USER=herokuishuser\" }}{{println .}}{{end}}{{end}}")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
2019-06-29 15:10:35 -04:00
|
|
|
return output != ""
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2017-09-03 19:34:44 -04:00
|
|
|
// MustGetEnv returns env variable or fails if it's not set
|
2017-10-02 16:50:05 -07:00
|
|
|
func MustGetEnv(key string) (val string) {
|
|
|
|
|
val = os.Getenv(key)
|
|
|
|
|
if val == "" {
|
2017-09-03 19:34:44 -04:00
|
|
|
LogFail(fmt.Sprintf("%s not set!", key))
|
|
|
|
|
}
|
2017-10-02 16:50:05 -07:00
|
|
|
return
|
2017-09-03 19:34:44 -04:00
|
|
|
}
|
|
|
|
|
|
2019-11-27 16:35:52 -05:00
|
|
|
// GetenvWithDefault returns env variable or defaultValue if it's not set
|
|
|
|
|
func GetenvWithDefault(key string, defaultValue string) (val string) {
|
|
|
|
|
val = os.Getenv(key)
|
|
|
|
|
if val == "" {
|
|
|
|
|
val = defaultValue
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-24 09:03:30 -06:00
|
|
|
// ReadFirstLine gets the first line of a file that has contents and returns it
|
|
|
|
|
// if there are no contents, an empty string is returned
|
|
|
|
|
// will also return an empty string if the file does not exist
|
2017-10-02 16:50:05 -07:00
|
|
|
func ReadFirstLine(filename string) (text string) {
|
2017-04-24 09:03:30 -06:00
|
|
|
if !FileExists(filename) {
|
2017-10-02 16:50:05 -07:00
|
|
|
return
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
|
if err != nil {
|
2017-10-02 16:50:05 -07:00
|
|
|
return
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
|
for scanner.Scan() {
|
2017-10-02 16:50:05 -07:00
|
|
|
if text = strings.TrimSpace(scanner.Text()); text == "" {
|
2017-04-24 09:03:30 -06:00
|
|
|
continue
|
|
|
|
|
}
|
2017-10-02 16:50:05 -07:00
|
|
|
return
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
2017-10-02 16:50:05 -07:00
|
|
|
return
|
2017-04-24 09:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2020-02-09 20:37:03 -05:00
|
|
|
// ReportSingleApp is an internal function that displays a report for an app
|
2020-02-22 06:29:14 -05:00
|
|
|
func ReportSingleApp(reportType string, appName string, infoFlag string, infoFlags map[string]string, trimPrefix bool, uppercaseFirstCharacter bool) error {
|
2020-02-09 20:37:03 -05:00
|
|
|
flags := []string{}
|
2020-02-09 20:47:20 -05:00
|
|
|
for key := range infoFlags {
|
2020-02-09 20:37:03 -05:00
|
|
|
flags = append(flags, key)
|
|
|
|
|
}
|
|
|
|
|
sort.Strings(flags)
|
|
|
|
|
|
|
|
|
|
if len(infoFlag) == 0 {
|
|
|
|
|
LogInfo2Quiet(fmt.Sprintf("%s %v information", appName, reportType))
|
|
|
|
|
for _, k := range flags {
|
|
|
|
|
v := infoFlags[k]
|
|
|
|
|
prefix := "--"
|
|
|
|
|
if trimPrefix {
|
|
|
|
|
prefix = fmt.Sprintf("--%v-", reportType)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key := strings.Replace(strings.Replace(strings.TrimPrefix(k, prefix), "-", " ", -1), ".", " ", -1)
|
|
|
|
|
|
|
|
|
|
if uppercaseFirstCharacter {
|
|
|
|
|
key = UcFirst(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogVerbose(fmt.Sprintf("%s%s", RightPad(fmt.Sprintf("%s:", key), 31, " "), v))
|
|
|
|
|
}
|
2020-02-22 06:29:14 -05:00
|
|
|
return nil
|
2020-02-09 20:37:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, k := range flags {
|
|
|
|
|
if infoFlag == k {
|
|
|
|
|
v := infoFlags[k]
|
2020-02-22 04:32:51 -05:00
|
|
|
fmt.Println(v)
|
2020-02-22 06:29:14 -05:00
|
|
|
return nil
|
2020-02-09 20:37:03 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keys := reflect.ValueOf(infoFlags).MapKeys()
|
|
|
|
|
strkeys := make([]string, len(keys))
|
|
|
|
|
for i := 0; i < len(keys); i++ {
|
|
|
|
|
strkeys[i] = keys[i].String()
|
|
|
|
|
}
|
2020-02-22 06:29:14 -05:00
|
|
|
|
2020-02-22 16:56:35 -05:00
|
|
|
return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))
|
2020-02-09 20:37:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RightPad right-pads the string with pad up to len runes
|
|
|
|
|
func RightPad(str string, length int, pad string) string {
|
|
|
|
|
return str + times(pad, length-len(str))
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-24 09:03:30 -06:00
|
|
|
// StripInlineComments removes bash-style comment from input line
|
|
|
|
|
func StripInlineComments(text string) string {
|
2017-10-02 16:50:05 -07:00
|
|
|
bytes := []byte(text)
|
2017-04-24 09:03:30 -06:00
|
|
|
re := regexp.MustCompile("(?s)#.*")
|
|
|
|
|
bytes = re.ReplaceAll(bytes, nil)
|
|
|
|
|
return strings.TrimSpace(string(bytes))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToBool returns a bool value for a given string
|
|
|
|
|
func ToBool(s string) bool {
|
|
|
|
|
return s == "true"
|
|
|
|
|
}
|
2017-09-03 19:34:44 -04:00
|
|
|
|
2017-10-04 00:48:02 -04:00
|
|
|
// UcFirst uppercases the first character in a string
|
|
|
|
|
func UcFirst(str string) string {
|
|
|
|
|
for i, v := range str {
|
|
|
|
|
return string(unicode.ToUpper(v)) + str[i+1:]
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-03 19:34:44 -04:00
|
|
|
// VerifyAppName verifies app name format and app existence"
|
|
|
|
|
func VerifyAppName(appName string) (err error) {
|
|
|
|
|
if appName == "" {
|
|
|
|
|
return fmt.Errorf("App name must not be null")
|
|
|
|
|
}
|
|
|
|
|
dokkuRoot := MustGetEnv("DOKKU_ROOT")
|
|
|
|
|
appRoot := strings.Join([]string{dokkuRoot, appName}, "/")
|
|
|
|
|
if !DirectoryExists(appRoot) {
|
2019-03-13 16:56:38 -04:00
|
|
|
return fmt.Errorf("app %s does not exist", appName)
|
2017-09-03 19:34:44 -04:00
|
|
|
}
|
2017-12-12 11:12:23 -05:00
|
|
|
r, _ := regexp.Compile("^[a-z0-9].*")
|
2017-09-03 19:34:44 -04:00
|
|
|
if !r.MatchString(appName) {
|
2017-09-03 20:36:09 -04:00
|
|
|
return fmt.Errorf("app name (%s) must begin with lowercase alphanumeric character", appName)
|
2017-09-03 19:34:44 -04:00
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-03 22:27:20 -08:00
|
|
|
// VerifyImage returns true if docker image exists in local repo
|
|
|
|
|
func VerifyImage(image string) bool {
|
2019-05-29 04:05:24 -04:00
|
|
|
imageCmd := NewShellCmd(strings.Join([]string{DockerBin(), "image", "inspect", image}, " "))
|
2017-01-03 22:27:20 -08:00
|
|
|
imageCmd.ShowOutput = false
|
2017-09-03 19:34:44 -04:00
|
|
|
return imageCmd.Execute()
|
2017-01-03 22:27:20 -08:00
|
|
|
}
|
2017-01-16 23:08:02 -05:00
|
|
|
|
2019-05-29 00:08:02 -04:00
|
|
|
// DockerBin returns a string which contains a path to the current docker binary
|
|
|
|
|
func DockerBin() string {
|
|
|
|
|
dockerBin := os.Getenv("DOCKER_BIN")
|
|
|
|
|
if dockerBin == "" {
|
|
|
|
|
dockerBin = "docker"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dockerBin
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 04:30:40 -05:00
|
|
|
// PlugnTrigger fire the given plugn trigger with the given args
|
2017-12-16 11:32:06 -05:00
|
|
|
func PlugnTrigger(triggerName string, args ...string) error {
|
2017-10-12 22:38:18 -04:00
|
|
|
shellArgs := make([]interface{}, len(args)+2)
|
|
|
|
|
shellArgs[0] = "trigger"
|
|
|
|
|
shellArgs[1] = triggerName
|
|
|
|
|
for i, arg := range args {
|
|
|
|
|
shellArgs[i+2] = arg
|
2017-09-23 17:17:59 -04:00
|
|
|
}
|
2017-12-16 11:32:06 -05:00
|
|
|
return sh.Command("plugn", shellArgs...).Run()
|
2017-01-16 23:08:02 -05:00
|
|
|
}
|
2020-02-09 20:37:03 -05:00
|
|
|
|
2020-02-22 04:30:40 -05:00
|
|
|
// PlugnTriggerOutput fire the given plugn trigger with the given args
|
|
|
|
|
func PlugnTriggerOutput(triggerName string, args ...string) ([]byte, error) {
|
|
|
|
|
shellArgs := make([]interface{}, len(args)+2)
|
|
|
|
|
shellArgs[0] = "trigger"
|
|
|
|
|
shellArgs[1] = triggerName
|
|
|
|
|
for i, arg := range args {
|
|
|
|
|
shellArgs[i+2] = arg
|
|
|
|
|
}
|
|
|
|
|
return sh.Command("plugn", shellArgs...).Output()
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-09 20:37:03 -05:00
|
|
|
func times(str string, n int) (out string) {
|
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
|
out += str
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|