move runCommand to pkg/util/exec and use in restic repo mgr

Signed-off-by: Steve Kriss <steve@heptio.com>
This commit is contained in:
Steve Kriss
2018-06-20 09:21:43 -07:00
parent 65ed8da4b7
commit 3481618324
5 changed files with 78 additions and 52 deletions
+2 -33
View File
@@ -17,12 +17,9 @@ limitations under the License.
package controller
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
jsonpatch "github.com/evanphx/json-patch"
@@ -40,6 +37,7 @@ import (
informers "github.com/heptio/ark/pkg/generated/informers/externalversions/ark/v1"
listers "github.com/heptio/ark/pkg/generated/listers/ark/v1"
"github.com/heptio/ark/pkg/restic"
arkexec "github.com/heptio/ark/pkg/util/exec"
"github.com/heptio/ark/pkg/util/kube"
)
@@ -184,7 +182,7 @@ func (c *podVolumeBackupController) processBackup(req *arkv1api.PodVolumeBackup)
var stdout, stderr string
if stdout, stderr, err = runCommand(resticCmd.Cmd()); err != nil {
if stdout, stderr, err = arkexec.RunCommand(resticCmd.Cmd()); err != nil {
log.WithError(errors.WithStack(err)).Errorf("Error running command=%s, stdout=%s, stderr=%s", resticCmd.String(), stdout, stderr)
return c.fail(req, fmt.Sprintf("error running restic backup, stderr=%s: %s", stderr, err.Error()), log)
}
@@ -210,35 +208,6 @@ func (c *podVolumeBackupController) processBackup(req *arkv1api.PodVolumeBackup)
return nil
}
// runCommand runs a command and returns its stdout, stderr, and its returned
// error (if any). If there are errors reading stdout or stderr, their return
// value(s) will contain the error as a string.
func runCommand(cmd *exec.Cmd) (string, string, error) {
stdoutBuf := new(bytes.Buffer)
stderrBuf := new(bytes.Buffer)
cmd.Stdout = stdoutBuf
cmd.Stderr = stderrBuf
runErr := cmd.Run()
var stdout, stderr string
if res, readErr := ioutil.ReadAll(stdoutBuf); readErr != nil {
stdout = errors.Wrap(readErr, "error reading command's stdout").Error()
} else {
stdout = string(res)
}
if res, readErr := ioutil.ReadAll(stderrBuf); readErr != nil {
stderr = errors.Wrap(readErr, "error reading command's stderr").Error()
} else {
stderr = string(res)
}
return stdout, stderr, runErr
}
func (c *podVolumeBackupController) patchPodVolumeBackup(req *arkv1api.PodVolumeBackup, mutate func(*arkv1api.PodVolumeBackup)) (*arkv1api.PodVolumeBackup, error) {
// Record original json
oldData, err := json.Marshal(req)
@@ -41,6 +41,7 @@ import (
listers "github.com/heptio/ark/pkg/generated/listers/ark/v1"
"github.com/heptio/ark/pkg/restic"
"github.com/heptio/ark/pkg/util/boolptr"
arkexec "github.com/heptio/ark/pkg/util/exec"
"github.com/heptio/ark/pkg/util/kube"
)
@@ -308,7 +309,7 @@ func restorePodVolume(req *arkv1api.PodVolumeRestore, credsFile, volumeDir strin
var stdout, stderr string
if stdout, stderr, err = runCommand(resticCmd.Cmd()); err != nil {
if stdout, stderr, err = arkexec.RunCommand(resticCmd.Cmd()); err != nil {
return errors.Wrapf(err, "error running restic restore, cmd=%s, stdout=%s, stderr=%s", resticCmd.String(), stdout, stderr)
}
log.Debugf("Ran command=%s, stdout=%s, stderr=%s", resticCmd.String(), stdout, stderr)
+4
View File
@@ -191,3 +191,7 @@ func newPodVolumeBackup(backup *arkv1api.Backup, pod *corev1api.Pod, volumeName,
},
}
}
func errorOnly(_ interface{}, err error) error {
return err
}
+16 -18
View File
@@ -20,7 +20,6 @@ import (
"context"
"fmt"
"os"
"os/exec"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -34,6 +33,7 @@ import (
clientset "github.com/heptio/ark/pkg/generated/clientset/versioned"
arkv1informers "github.com/heptio/ark/pkg/generated/informers/externalversions/ark/v1"
arkv1listers "github.com/heptio/ark/pkg/generated/listers/ark/v1"
arkexec "github.com/heptio/ark/pkg/util/exec"
)
// RepositoryManager executes commands against restic repositories.
@@ -163,7 +163,7 @@ func (rm *repositoryManager) InitRepo(name string) error {
rm.repoLocker.LockExclusive(name)
defer rm.repoLocker.UnlockExclusive(name)
return errorOnly(rm.exec(InitCommand(rm.repoPrefix, name)))
return rm.exec(InitCommand(rm.repoPrefix, name))
}
func (rm *repositoryManager) CheckRepo(name string) error {
@@ -172,7 +172,7 @@ func (rm *repositoryManager) CheckRepo(name string) error {
cmd := CheckCommand(rm.repoPrefix, name)
return errorOnly(rm.exec(cmd))
return rm.exec(cmd)
}
func (rm *repositoryManager) PruneRepo(name string) error {
@@ -181,7 +181,7 @@ func (rm *repositoryManager) PruneRepo(name string) error {
cmd := PruneCommand(rm.repoPrefix, name)
return errorOnly(rm.exec(cmd))
return rm.exec(cmd)
}
func (rm *repositoryManager) Forget(snapshot SnapshotIdentifier) error {
@@ -190,31 +190,29 @@ func (rm *repositoryManager) Forget(snapshot SnapshotIdentifier) error {
cmd := ForgetCommand(rm.repoPrefix, snapshot.Repo, snapshot.SnapshotID)
return errorOnly(rm.exec(cmd))
return rm.exec(cmd)
}
func (rm *repositoryManager) exec(cmd *Command) ([]byte, error) {
func (rm *repositoryManager) exec(cmd *Command) error {
file, err := TempCredentialsFile(rm.secretsLister, cmd.Repo)
if err != nil {
return nil, err
return err
}
// ignore error since there's nothing we can do and it's a temp file.
defer os.Remove(file)
cmd.PasswordFile = file
output, err := cmd.Cmd().Output()
rm.log.WithField("repository", cmd.Repo).Debugf("Ran restic command=%q, output=%s", cmd.String(), output)
stdout, stderr, err := arkexec.RunCommand(cmd.Cmd())
rm.log.WithFields(logrus.Fields{
"repository": cmd.Repo,
"command": cmd.String(),
"stdout": stdout,
"stderr": stderr,
}).Debugf("Ran restic command")
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
return nil, errors.Wrapf(err, "error running command, stderr=%s", exitErr.Stderr)
}
return nil, errors.Wrap(err, "error running command")
return errors.Wrapf(err, "error running command=%s, stdout=%s, stderr=%s", cmd.String(), stdout, stderr)
}
return output, nil
}
func errorOnly(_ interface{}, err error) error {
return err
return nil
}
+54
View File
@@ -0,0 +1,54 @@
/*
Copyright 2018 the Heptio Ark contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package exec
import (
"bytes"
"io/ioutil"
"os/exec"
"github.com/pkg/errors"
)
// RunCommand runs a command and returns its stdout, stderr, and its returned
// error (if any). If there are errors reading stdout or stderr, their return
// value(s) will contain the error as a string.
func RunCommand(cmd *exec.Cmd) (string, string, error) {
stdoutBuf := new(bytes.Buffer)
stderrBuf := new(bytes.Buffer)
cmd.Stdout = stdoutBuf
cmd.Stderr = stderrBuf
runErr := cmd.Run()
var stdout, stderr string
if res, readErr := ioutil.ReadAll(stdoutBuf); readErr != nil {
stdout = errors.Wrap(readErr, "error reading command's stdout").Error()
} else {
stdout = string(res)
}
if res, readErr := ioutil.ReadAll(stderrBuf); readErr != nil {
stderr = errors.Wrap(readErr, "error reading command's stderr").Error()
} else {
stderr = string(res)
}
return stdout, stderr, runErr
}