refactor: remove all calls to common.NewShellCmd

At this point, the only usage of go-sh should be by plugin trigger calls.
This commit is contained in:
Jose Diaz-Gonzalez
2024-02-13 01:09:24 -05:00
parent 24b32432e6
commit 44dd933638
10 changed files with 207 additions and 234 deletions

View File

@@ -1,8 +1,7 @@
package builder package builder
import ( import (
"bytes" "fmt"
"errors"
"os" "os"
"path" "path"
"strings" "strings"
@@ -11,25 +10,18 @@ import (
) )
func listImagesByImageRepo(imageRepo string) ([]string, error) { func listImagesByImageRepo(imageRepo string) ([]string, error) {
command := []string{ result, err := common.CallExecCommand(common.ExecCommandInput{
common.DockerBin(), Command: common.DockerBin(),
"image", Args: []string{"image", "ls", "--quiet", imageRepo},
"ls", })
"--quiet",
imageRepo,
}
var stderr bytes.Buffer
listCmd := common.NewShellCmd(strings.Join(command, " "))
listCmd.ShowOutput = false
listCmd.Command.Stderr = &stderr
b, err := listCmd.Output()
if err != nil { if err != nil {
return []string{}, errors.New(strings.TrimSpace(stderr.String())) return []string{}, fmt.Errorf("Unable to list images: %w", err)
}
if result.ExitCode != 0 {
return []string{}, fmt.Errorf("Unable to list images: %s", result.StderrContents())
} }
output := strings.Split(strings.TrimSpace(string(b[:])), "\n") output := strings.Split(result.StdoutContents(), "\n")
return output, nil return output, nil
} }

View File

@@ -1,8 +1,6 @@
package common package common
import ( import (
"bytes"
"errors"
"fmt" "fmt"
"os" "os"
"strings" "strings"
@@ -108,32 +106,20 @@ func CopyFromImage(appName string, image string, source string, destination stri
if err != nil { if err != nil {
return fmt.Errorf("Unable to create temporary container: %v", err) return fmt.Errorf("Unable to create temporary container: %v", err)
} }
defer ContainerRemove(containerID)
// docker cp exits with status 1 when run as non-root user when it tries to chown the file // docker cp exits with status 1 when run as non-root user when it tries to chown the file
// after successfully copying the file. Thus, we suppress stderr. // after successfully copying the file. Thus, we suppress stderr.
// ref: https://github.com/dotcloud/docker/issues/3986 // ref: https://github.com/dotcloud/docker/issues/3986
containerCopyCmd := NewShellCmd(strings.Join([]string{ result, err := CallExecCommand(ExecCommandInput{
DockerBin(), Command: DockerBin(),
"container", Args: []string{"container", "cp", fmt.Sprintf("%s:%s", containerID, source), tmpFile.Name()},
"cp", })
fmt.Sprintf("%s:%s", containerID, source), if err != nil {
tmpFile.Name(), return fmt.Errorf("Unable to copy file %s from image: %w", source, err)
}, " ")) }
containerCopyCmd.ShowOutput = false if result.ExitCode != 0 {
fileCopied := containerCopyCmd.Execute() return fmt.Errorf("Unable to copy file %s from image: %v", source, result.StderrContents())
containerRemoveCmd := NewShellCmd(strings.Join([]string{
DockerBin(),
"container",
"rm",
"--force",
containerID,
}, " "))
containerRemoveCmd.ShowOutput = false
containerRemoveCmd.Execute()
if !fileCopied {
return fmt.Errorf("Unable to copy file %s from image", source)
} }
fi, err := os.Stat(tmpFile.Name()) fi, err := os.Stat(tmpFile.Name())
@@ -146,18 +132,13 @@ func CopyFromImage(appName string, image string, source string, destination stri
} }
// workaround when owner is root. seems to only happen when running inside docker // workaround when owner is root. seems to only happen when running inside docker
dos2unixCmd := NewShellCmd(strings.Join([]string{ CallExecCommand(ExecCommandInput{
"dos2unix", Command: "dos2unix",
"-l", Args: []string{"-l", "-n", tmpFile.Name(), destination},
"-n", }) // nolint: errcheck
tmpFile.Name(),
destination,
}, " "))
dos2unixCmd.ShowOutput = false
dos2unixCmd.Execute()
// add trailing newline for certain places where file parsing depends on it // add trailing newline for certain places where file parsing depends on it
result, err := CallExecCommand(ExecCommandInput{ result, err = CallExecCommand(ExecCommandInput{
Command: "tail", Command: "tail",
Args: []string{"-c1", destination}, Args: []string{"-c1", destination},
}) })
@@ -244,25 +225,26 @@ func DockerCleanup(appName string, forceCleanup bool) error {
// DockerContainerCreate creates a new container and returns the container ID // DockerContainerCreate creates a new container and returns the container ID
func DockerContainerCreate(image string, containerCreateArgs []string) (string, error) { func DockerContainerCreate(image string, containerCreateArgs []string) (string, error) {
cmd := []string{ args := []string{
DockerBin(),
"container", "container",
"create", "create",
} }
cmd = append(cmd, containerCreateArgs...) args = append(args, containerCreateArgs...)
cmd = append(cmd, image) args = append(args, image)
var stderr bytes.Buffer result, err := CallExecCommand(ExecCommandInput{
containerCreateCmd := NewShellCmd(strings.Join(cmd, " ")) Command: DockerBin(),
containerCreateCmd.ShowOutput = false Args: args,
containerCreateCmd.Command.Stderr = &stderr })
b, err := containerCreateCmd.Output()
if err != nil { if err != nil {
return "", errors.New(strings.TrimSpace(stderr.String())) return "", fmt.Errorf("Unable to create container: %w", err)
}
if result.ExitCode != 0 {
return "", fmt.Errorf("Unable to create container: %s", result.StderrContents())
} }
return strings.TrimSpace(string(b[:])), nil return result.StdoutContents(), nil
} }
// DockerInspect runs an inspect command with a given format against a container or image ID // DockerInspect runs an inspect command with a given format against a container or image ID
@@ -359,21 +341,22 @@ func RemoveImages(imageIDs []string) error {
return nil return nil
} }
command := []string{ args := []string{
DockerBin(),
"image", "image",
"rm", "rm",
} }
command = append(command, imageIDs...) args = append(args, imageIDs...)
var stderr bytes.Buffer result, err := CallExecCommand(ExecCommandInput{
rmCmd := NewShellCmd(strings.Join(command, " ")) Command: DockerBin(),
rmCmd.ShowOutput = false Args: args,
rmCmd.Command.Stderr = &stderr })
rmCmd.Execute() if err != nil {
if _, err := rmCmd.Output(); err != nil { return fmt.Errorf("Unable to remove images: %w", err)
return errors.New(strings.TrimSpace(stderr.String())) }
if result.ExitCode != 0 {
return fmt.Errorf("Unable to remove images: %s", result.StderrContents())
} }
return nil return nil
@@ -381,15 +364,16 @@ func RemoveImages(imageIDs []string) error {
// VerifyImage returns true if docker image exists in local repo // VerifyImage returns true if docker image exists in local repo
func VerifyImage(image string) bool { func VerifyImage(image string) bool {
imageCmd := NewShellCmd(strings.Join([]string{DockerBin(), "image", "inspect", image}, " ")) result, err := CallExecCommand(ExecCommandInput{
imageCmd.ShowOutput = false Command: DockerBin(),
return imageCmd.Execute() Args: []string{"image", "inspect", image},
})
return err == nil && result.ExitCode == 0
} }
// DockerFilterContainers returns a slice of container IDs based on the passed in filters // DockerFilterContainers returns a slice of container IDs based on the passed in filters
func DockerFilterContainers(filters []string) ([]string, error) { func DockerFilterContainers(filters []string) ([]string, error) {
command := []string{ args := []string{
DockerBin(),
"container", "container",
"ls", "ls",
"--quiet", "--quiet",
@@ -397,27 +381,27 @@ func DockerFilterContainers(filters []string) ([]string, error) {
} }
for _, filter := range filters { for _, filter := range filters {
command = append(command, "--filter", filter) args = append(args, "--filter", filter)
} }
var stderr bytes.Buffer result, err := CallExecCommand(ExecCommandInput{
listCmd := NewShellCmd(strings.Join(command, " ")) Command: DockerBin(),
listCmd.ShowOutput = false Args: args,
listCmd.Command.Stderr = &stderr })
b, err := listCmd.Output()
if err != nil { if err != nil {
return []string{}, errors.New(strings.TrimSpace(stderr.String())) return []string{}, fmt.Errorf("Unable to filter containers: %w", err)
}
if result.ExitCode != 0 {
return []string{}, fmt.Errorf("Unable to filter containers: %s", result.StderrContents())
} }
output := strings.Split(strings.TrimSpace(string(b[:])), "\n") output := strings.Split(result.StdoutContents(), "\n")
return output, nil return output, nil
} }
// DockerFilterImages returns a slice of image IDs based on the passed in filters // DockerFilterImages returns a slice of image IDs based on the passed in filters
func DockerFilterImages(filters []string) ([]string, error) { func DockerFilterImages(filters []string) ([]string, error) {
command := []string{ args := []string{
DockerBin(),
"image", "image",
"ls", "ls",
"--quiet", "--quiet",
@@ -425,20 +409,21 @@ func DockerFilterImages(filters []string) ([]string, error) {
} }
for _, filter := range filters { for _, filter := range filters {
command = append(command, "--filter", filter) args = append(args, "--filter", filter)
} }
var stderr bytes.Buffer result, err := CallExecCommand(ExecCommandInput{
listCmd := NewShellCmd(strings.Join(command, " ")) Command: DockerBin(),
listCmd.ShowOutput = false Args: args,
listCmd.Command.Stderr = &stderr })
b, err := listCmd.Output()
if err != nil { if err != nil {
return []string{}, errors.New(strings.TrimSpace(stderr.String())) return []string{}, fmt.Errorf("Unable to filter images: %w", err)
}
if result.ExitCode != 0 {
return []string{}, fmt.Errorf("Unable to filter images: %s", result.StderrContents())
} }
output := strings.Split(strings.TrimSpace(string(b[:])), "\n") output := strings.Split(result.StdoutContents(), "\n")
return output, nil return output, nil
} }
@@ -455,8 +440,7 @@ func listContainers(status string, appName string) ([]string, error) {
} }
func pruneUnusedImages(appName string) { func pruneUnusedImages(appName string) {
command := []string{ args := []string{
DockerBin(),
"image", "image",
"prune", "prune",
"--all", "--all",
@@ -465,26 +449,23 @@ func pruneUnusedImages(appName string) {
fmt.Sprintf("label=com.dokku.app-name=%v", appName), fmt.Sprintf("label=com.dokku.app-name=%v", appName),
} }
var stderr bytes.Buffer CallExecCommand(ExecCommandInput{
pruneCmd := NewShellCmd(strings.Join(command, " ")) Command: DockerBin(),
pruneCmd.ShowOutput = false Args: args,
pruneCmd.Command.Stderr = &stderr }) // nolint: errcheck
pruneCmd.Execute()
} }
// DockerRemoveContainers will call `docker container rm` on the specified containers // DockerRemoveContainers will call `docker container rm` on the specified containers
func DockerRemoveContainers(containerIDs []string) { func DockerRemoveContainers(containerIDs []string) {
command := []string{ args := []string{
DockerBin(),
"container", "container",
"rm", "rm",
} }
command = append(command, containerIDs...) args = append(args, containerIDs...)
var stderr bytes.Buffer CallExecCommand(ExecCommandInput{
rmCmd := NewShellCmd(strings.Join(command, " ")) Command: DockerBin(),
rmCmd.ShowOutput = false Args: args,
rmCmd.Command.Stderr = &stderr }) // nolint: errcheck
rmCmd.Execute()
} }

View File

@@ -38,15 +38,16 @@ func Copy(src, dst string) error {
} }
// ensure file has the correct line endings // ensure file has the correct line endings
dos2unixCmd := NewShellCmd(strings.Join([]string{ result, err := CallExecCommand(ExecCommandInput{
"dos2unix", Command: "dos2unix",
"-l", Args: []string{"-l", "-n", src, dst},
"-n", })
src, if err != nil {
dst, return fmt.Errorf("Error running dos2unix: %s", err)
}, " ")) }
dos2unixCmd.ShowOutput = false if result.ExitCode != 0 {
dos2unixCmd.Execute() return fmt.Errorf("Error running dos2unix: %s", result.StderrContents())
}
// ensure file permissions are correct // ensure file permissions are correct
b, err := os.ReadFile(dst) b, err := os.ReadFile(dst)

View File

@@ -1,7 +1,6 @@
package network package network
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@@ -16,7 +15,6 @@ func attachAppToNetwork(containerID string, networkName string, appName string,
} }
cmdParts := []string{ cmdParts := []string{
common.DockerBin(),
"network", "network",
"connect", "connect",
} }
@@ -43,14 +41,16 @@ func attachAppToNetwork(containerID string, networkName string, appName string,
cmdParts = append(cmdParts, networkName) cmdParts = append(cmdParts, networkName)
cmdParts = append(cmdParts, containerID) cmdParts = append(cmdParts, containerID)
attachCmd := common.NewShellCmd(strings.Join(cmdParts, " "))
var stderr bytes.Buffer result, err := common.CallExecCommand(common.ExecCommandInput{
attachCmd.ShowOutput = false Command: common.DockerBin(),
attachCmd.Command.Stderr = &stderr Args: cmdParts,
_, err := attachCmd.Output() })
if err != nil { if err != nil {
err = errors.New(strings.TrimSpace(stderr.String())) return fmt.Errorf("Unable to attach container to network: %w", err)
return fmt.Errorf("Unable to attach container to network: %v", err.Error()) }
if result.ExitCode != 0 {
return fmt.Errorf("Unable to attach container to network: %s", result.StderrContents())
} }
return nil return nil

View File

@@ -1,7 +1,6 @@
package network package network
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@@ -13,22 +12,15 @@ import (
// CommandCreate is an alias for "docker network create" // CommandCreate is an alias for "docker network create"
func CommandCreate(networkName string) error { func CommandCreate(networkName string) error {
common.LogInfo1Quiet(fmt.Sprintf("Creating network %v", networkName)) common.LogInfo1Quiet(fmt.Sprintf("Creating network %v", networkName))
createCmd := common.NewShellCmd(strings.Join([]string{ result, err := common.CallExecCommand(common.ExecCommandInput{
common.DockerBin(), Command: common.DockerBin(),
"network", Args: []string{"network", "create", "--attachable", "--label", fmt.Sprintf("com.dokku.network-name=%v", networkName), networkName},
"create", })
"--attachable",
"--label",
fmt.Sprintf("com.dokku.network-name=%v", networkName),
networkName,
}, " "))
var stderr bytes.Buffer
createCmd.ShowOutput = false
createCmd.Command.Stderr = &stderr
_, err := createCmd.Output()
if err != nil { if err != nil {
err = errors.New(strings.TrimSpace(stderr.String())) return fmt.Errorf("Unable to create network: %w", err)
}
if result.ExitCode != 0 {
return fmt.Errorf("Unable to create network: %s", result.StderrContents())
} }
return err return err
@@ -47,22 +39,18 @@ func CommandDestroy(networkName string, forceDestroy bool) error {
} }
common.LogInfo1Quiet(fmt.Sprintf("Destroying network %v", networkName)) common.LogInfo1Quiet(fmt.Sprintf("Destroying network %v", networkName))
destroyCmd := common.NewShellCmd(strings.Join([]string{ result, err := common.CallExecCommand(common.ExecCommandInput{
common.DockerBin(), Command: common.DockerBin(),
"network", Args: []string{"network", "rm", networkName},
"rm", })
networkName,
}, " "))
var stderr bytes.Buffer
destroyCmd.ShowOutput = false
destroyCmd.Command.Stderr = &stderr
_, err := destroyCmd.Output()
if err != nil { if err != nil {
err = errors.New(strings.TrimSpace(stderr.String())) return fmt.Errorf("Unable to destroy network: %w", err)
}
if result.ExitCode != 0 {
return fmt.Errorf("Unable to destroy network: %s", result.StderrContents())
} }
return err return nil
} }
// CommandExists checks if a network exists // CommandExists checks if a network exists

View File

@@ -1,7 +1,6 @@
package ps package ps
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@@ -24,26 +23,18 @@ func getProcfileCommand(procfilePath string, processType string, port int) (stri
return "", errors.New("No procfile found") return "", errors.New("No procfile found")
} }
shellCmd := common.NewShellCmd(strings.Join([]string{ result, err := common.CallExecCommand(common.ExecCommandInput{
"procfile-util", Command: "procfile-util",
"show", Args: []string{"show", "--procfile", procfilePath, "--process-type", processType, "--default-port", strconv.Itoa(port)},
"--procfile", })
procfilePath,
"--process-type",
processType,
"--default-port",
strconv.Itoa(port),
}, " "))
var stderr bytes.Buffer
shellCmd.ShowOutput = false
shellCmd.Command.Stderr = &stderr
b, err := shellCmd.Output()
if err != nil { if err != nil {
return "", fmt.Errorf(strings.TrimSpace(stderr.String())) return "", fmt.Errorf("Error running procfile-util: %s", err)
}
if result.ExitCode != 0 {
return "", fmt.Errorf("Error running procfile-util: %s", result.StderrContents())
} }
return strings.TrimSpace(string(b[:])), nil return result.StdoutContents(), nil
} }
func getProcfilePath(appName string) string { func getProcfilePath(appName string) string {
@@ -154,22 +145,18 @@ func parseProcessTuples(processTuples []string) (FormationSlice, error) {
func processesInProcfile(procfilePath string) (map[string]bool, error) { func processesInProcfile(procfilePath string) (map[string]bool, error) {
processes := map[string]bool{} processes := map[string]bool{}
shellCmd := common.NewShellCmd(strings.Join([]string{ result, err := common.CallExecCommand(common.ExecCommandInput{
"procfile-util", Command: "procfile-util",
"list", Args: []string{"list", "--procfile", procfilePath},
"--procfile", })
procfilePath,
}, " "))
var stderr bytes.Buffer
shellCmd.ShowOutput = false
shellCmd.Command.Stderr = &stderr
b, err := shellCmd.Output()
if err != nil { if err != nil {
return processes, fmt.Errorf(strings.TrimSpace(stderr.String())) return processes, fmt.Errorf("Error listing processes: %s", err)
}
if result.ExitCode != 0 {
return processes, fmt.Errorf("Error listing processes: %s", result.StderrContents())
} }
for _, s := range strings.Split(strings.TrimSpace(string(b[:])), "\n") { for _, s := range strings.Split(result.StdoutContents(), "\n") {
processes[s] = true processes[s] = true
} }

View File

@@ -3,6 +3,7 @@ package registry
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt"
"io" "io"
"os" "os"
"strings" "strings"
@@ -35,25 +36,23 @@ func CommandLogin(server string, username string, password string, passwordStdin
server = "docker.io" server = "docker.io"
} }
command := []string{
common.DockerBin(),
"login",
"--username",
username,
"--password-stdin",
server,
}
buffer := bytes.Buffer{} buffer := bytes.Buffer{}
buffer.Write([]byte(password + "\n")) buffer.Write([]byte(password + "\n"))
loginCmd := common.NewShellCmd(strings.Join(command, " ")) result, err := common.CallExecCommand(common.ExecCommandInput{
loginCmd.Command.Stdin = &buffer Command: common.DockerBin(),
if !loginCmd.Execute() { Args: []string{"login", "--username", username, "--password-stdin", server},
return errors.New("Failed to log into registry") Stdin: &buffer,
StreamStderr: true,
})
if err != nil {
return fmt.Errorf("Unable to run docker login: %w", err)
}
if result.ExitCode != 0 {
return fmt.Errorf("Unable to run docker login: %s", result.StderrContents())
} }
_, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ _, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
Trigger: "post-registry-login", Trigger: "post-registry-login",
Args: []string{server, username}, Args: []string{server, username},
StreamStdio: true, StreamStdio: true,

View File

@@ -2,8 +2,6 @@ package repo
import ( import (
"fmt" "fmt"
"os"
"strings"
"github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/common"
) )
@@ -33,18 +31,16 @@ func PurgeCache(appName string) error {
if len(containerIDs) > 0 { if len(containerIDs) > 0 {
common.DockerRemoveContainers(containerIDs) common.DockerRemoveContainers(containerIDs)
} }
purgeCacheCmd := common.NewShellCmd(strings.Join([]string{ result, err := common.CallExecCommand(common.ExecCommandInput{
common.DockerBin(), Command: common.DockerBin(),
"volume", Args: []string{"volume", "rm", "-f", fmt.Sprintf("cache-%s", appName)},
"rm", "-f", fmt.Sprintf("cache-%s", appName)}, " ")) StreamStderr: true,
purgeCacheCmd.ShowOutput = false })
purgeCacheCmd.Command.Stderr = os.Stderr if err != nil {
if !purgeCacheCmd.Execute() { return fmt.Errorf("Unable to remove cache volume: %w", err)
exitCode := 1 }
if purgeCacheCmd.ExitError != nil { if result.ExitCode != 0 {
exitCode = purgeCacheCmd.ExitError.ExitCode() return &PurgeCacheFailed{result.ExitCode}
}
return &PurgeCacheFailed{exitCode}
} }
return nil return nil

View File

@@ -1,6 +1,8 @@
package repo package repo
import ( import (
"fmt"
"github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/common"
) )
@@ -14,9 +16,20 @@ func CommandGc(appName string) error {
cmdEnv := map[string]string{ cmdEnv := map[string]string{
"GIT_DIR": appRoot, "GIT_DIR": appRoot,
} }
gitGcCmd := common.NewShellCmd("git gc --aggressive")
gitGcCmd.Env = cmdEnv result, err := common.CallExecCommand(common.ExecCommandInput{
gitGcCmd.Execute() Command: "git",
Args: []string{"gc", "--aggressive"},
Env: cmdEnv,
StreamStdio: true,
})
if err != nil {
return fmt.Errorf("Unable to run git gc: %w", err)
}
if result.ExitCode != 0 {
return fmt.Errorf("Unable to run git gc: %s", result.StderrContents())
}
return nil return nil
} }

View File

@@ -15,17 +15,28 @@ import (
) )
func deleteCrontab() error { func deleteCrontab() error {
command := common.NewShellCmd("sudo /usr/bin/crontab -l -u dokku") result, err := common.CallExecCommand(common.ExecCommandInput{
command.ShowOutput = false Command: "/usr/bin/crontab",
if !command.Execute() { Args: []string{"-l", "-u", "dokku"},
return nil Sudo: true,
})
if err != nil {
return fmt.Errorf("Unable to remove schedule file: %w", err)
}
if result.ExitCode != 0 {
return fmt.Errorf("Unable to remove schedule file: %s", result.StderrContents())
} }
command = common.NewShellCmd("sudo /usr/bin/crontab -r -u dokku") result, err = common.CallExecCommand(common.ExecCommandInput{
command.ShowOutput = false Command: "/usr/bin/crontab",
out, err := command.CombinedOutput() Args: []string{"-r", "-u", "dokku"},
Sudo: true,
})
if err != nil { if err != nil {
return fmt.Errorf("Unable to remove schedule file: %v", string(out)) return fmt.Errorf("Unable to remove schedule file: %w", err)
}
if result.ExitCode != 0 {
return fmt.Errorf("Unable to remove schedule file: %s", result.StderrContents())
} }
common.LogInfo1("Removed") common.LogInfo1("Removed")
@@ -139,11 +150,16 @@ func writeCronEntries() error {
return fmt.Errorf("Unable to template out schedule file: %v", err) return fmt.Errorf("Unable to template out schedule file: %v", err)
} }
command := common.NewShellCmd(fmt.Sprintf("sudo /usr/bin/crontab -u dokku %s", tmpFile.Name())) result, err := common.CallExecCommand(common.ExecCommandInput{
command.ShowOutput = false Command: "/usr/bin/crontab",
out, err := command.CombinedOutput() Args: []string{"-u", "dokku", tmpFile.Name()},
Sudo: true,
})
if err != nil { if err != nil {
return fmt.Errorf("Unable to update schedule file: %s", string(out)) return fmt.Errorf("Unable to update schedule file: %w", err)
}
if result.ExitCode != 0 {
return fmt.Errorf("Unable to update schedule file: %s", result.StderrContents())
} }
common.LogInfo1("Updated schedule file") common.LogInfo1("Updated schedule file")