Merge pull request #5039 from travisn/bulk-cleanup

Logging and exec package cleanup
This commit is contained in:
Sébastien Han authored and GitHub committed 2020-03-19 16:54:19 +01:00
commit 7caedefb4f
92 files changed
+339 -1428

No files matched your search

-11
View File
@@ -24,7 +24,6 @@ import (
"github.com/rook/rook/pkg/clusterd"
cephconfig "github.com/rook/rook/pkg/daemon/ceph/config"
osdconfig "github.com/rook/rook/pkg/operator/ceph/cluster/osd/config"
"github.com/rook/rook/pkg/util/flags"
)
// Cmd is the main command for operator and daemons.
@@ -83,16 +82,6 @@ func addCephFlags(command *cobra.Command) {
// TODO: remove these legacy flags in the future
command.Flags().StringVar(&cfg.networkInfo.PublicAddrIPv4, "public-ipv4", "", "public IPv4 address for this machine")
command.Flags().StringVar(&cfg.networkInfo.ClusterAddrIPv4, "private-ipv4", "", "private IPv4 address for this machine")
command.Flags().MarkDeprecated("public-ipv4", "Use --public-ip instead. Will be removed in a future version.")
command.Flags().MarkDeprecated("private-ipv4", "Use --private-ip instead. Will be removed in a future version.")
}
func verifyRenamedFlags(cmd *cobra.Command) error {
renamed := []flags.RenamedFlag{
{NewFlagName: "public-ip", OldFlagName: "public-ipv4"},
{NewFlagName: "private-ip", OldFlagName: "private-ipv4"},
}
return flags.VerifyRenamedFlags(cmd, renamed)
}
func (c *config) NetworkInfo() clusterd.NetworkInfo {
-4
View File
@@ -175,10 +175,6 @@ func prepareOSD(cmd *cobra.Command, args []string) error {
return err
}
if err := verifyRenamedFlags(osdCmd); err != nil {
return err
}
var dataDevices []osddaemon.DesiredDevice
if osdDataDeviceFilter != "" {
if cfg.devices != "" || osdDataDevicePathFilter != "" {
+1 -2
View File
@@ -25,7 +25,6 @@ import (
const (
edgefStartCmdPath = "/opt/nedge/sbin/edgefs-start.sh"
logSectionName = "edgefs-internal"
)
var startCmd = &cobra.Command{
@@ -44,7 +43,7 @@ func start(cmd *cobra.Command, args []string) error {
rook.SetLogLevel()
rook.LogStartupInfo(operatorCmd.Flags())
executor := &exec.CommandExecutor{}
err := executor.ExecuteCommand(true, logSectionName, edgefStartCmdPath, args...)
err := executor.ExecuteCommand(edgefStartCmdPath, args...)
logger.Infof("Start script %s exited: %+v", edgefStartCmdPath, err)
return nil
-2
View File
@@ -125,8 +125,6 @@ func NewContext() *clusterd.Context {
context.Executor = &exec.TranslateCommandExecutor{
Executor: context.Executor,
Translator: func(
debug bool,
actionName string,
command string,
arg ...string,
) (string, []string) {
+1 -1
View File
@@ -15,7 +15,6 @@ require (
github.com/google/go-cmp v0.3.1
github.com/google/uuid v1.1.1
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428
github.com/jbw976/go-ps v0.0.0-20170713234100-82859aed1b5d
github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20200107223247-51020689f1fb
github.com/openshift/cluster-api v0.0.0-20191129101638-b09907ac6668
github.com/openshift/machine-api-operator v0.2.1-0.20190903202259-474e14e4965a
@@ -30,6 +29,7 @@ require (
k8s.io/apimachinery v0.17.2
k8s.io/apiserver v0.17.2
k8s.io/client-go v12.0.0+incompatible
k8s.io/code-generator v0.17.2
k8s.io/kube-controller-manager v0.0.0
k8s.io/kubernetes v1.17.2
k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6
+7 -2
View File
@@ -112,6 +112,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea h1:n2Ltr3SrfQlf/9nOna1DoGKxLx3qTSI8Ttl6Xrqp6mw=
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/prometheus-operator v0.34.0 h1:TF9qaydNeUamLKs0hniaapa4FBz8U8TIlRRtJX987A4=
github.com/coreos/prometheus-operator v0.34.0/go.mod h1:Li6rMllG/hYIyXfMuvUwhyC+hqwJVHdsDdP21hypT1M=
github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U=
@@ -376,8 +377,6 @@ github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/improbable-eng/thanos v0.3.2/go.mod h1:GZewVGILKuJVPNRn7L4Zw+7X96qzFOwj63b22xYGXBE=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbw976/go-ps v0.0.0-20170713234100-82859aed1b5d h1:Eu2H80hepnodkFlP5JSPijRi3uD5RnGUjCEJBYLL1Qg=
github.com/jbw976/go-ps v0.0.0-20170713234100-82859aed1b5d/go.mod h1:jNoCLbavHxJDd+Zl3/DAnYwbD8Ml5PxMMf0JOphQQKo=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
@@ -697,6 +696,7 @@ golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba h1:9bFeDpN3gTqNanMVqNcoR/
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -824,13 +824,16 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72 h1:bw9doJza/SFBEweII/rHQh338oozWyiFsBRHtrflcws=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
@@ -908,12 +911,14 @@ k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI=
k8s.io/cloud-provider v0.17.2 h1:5JVAaJ3JIlaXlOqAX/LkdnXPl1UlcjHMPbepo8LFcyo=
k8s.io/cloud-provider v0.17.2/go.mod h1:9rEcGqEsUFHxC83oMUGBcsXTBpRVNVPX/U+nyQJTvHU=
k8s.io/cluster-bootstrap v0.17.2/go.mod h1:qiazpAM05fjAc+PEkrY8HSUhKlJSMBuLnVUSO6nvZL4=
k8s.io/code-generator v0.17.2 h1:pTwl3rLB1fUyxmvEzmVPMM0tBSdUehd7z+bDzpj4lPE=
k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
k8s.io/component-base v0.17.2 h1:0XHf+cerTvL9I5Xwn9v+0jmqzGAZI7zNydv4tL6Cw6A=
k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs=
k8s.io/cri-api v0.17.2/go.mod h1:BzAkbBHHp81d+aXzbiIcUbilLkbXa40B8mUHOk6EX3s=
k8s.io/csi-translation-lib v0.17.2/go.mod h1:NrhnhXJg/V6cHRTdPbmxvBuV3rJSqXsdLBE5JSRzcVI=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM=
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+4 -4
View File
@@ -24,12 +24,12 @@ import (
func TestDiscoverDevices(t *testing.T) {
executor := &exectest.MockExecutor{
MockExecuteCommand: func(debug bool, actionName string, command string, arg ...string) error {
logger.Infof("mock execute. %s. %s", actionName, command)
MockExecuteCommand: func(command string, arg ...string) error {
logger.Infof("mock execute. %s", command)
return nil
},
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, arg ...string) (string, error) {
logger.Infof("mock execute with output. %s. %s", actionName, command)
MockExecuteCommandWithOutput: func(command string, arg ...string) (string, error) {
logger.Infof("mock execute with output. %s", command)
return "", nil
},
}
@@ -50,13 +50,13 @@ func TestInitLoadRBDModSingleMajor(t *testing.T) {
modprobeCalled := false
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
assert.Equal(t, "modinfo", command)
assert.Equal(t, "rbd", args[2])
modInfoCalled = true
return "single_major:Use a single major number for all rbd devices (default: false) (bool)", nil
},
MockExecuteCommand: func(debug bool, actionName string, command string, args ...string) error {
MockExecuteCommand: func(command string, args ...string) error {
assert.Equal(t, "modprobe", command)
assert.Equal(t, "rbd", args[0])
assert.Equal(t, "single_major=Y", args[1])
@@ -78,13 +78,13 @@ func TestInitLoadRBDModNoSingleMajor(t *testing.T) {
modprobeCalled := false
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
assert.Equal(t, "modinfo", command)
assert.Equal(t, "rbd", args[2])
modInfoCalled = true
return "", nil
},
MockExecuteCommand: func(debug bool, actionName string, command string, args ...string) error {
MockExecuteCommand: func(command string, args ...string) error {
assert.Equal(t, "modprobe", command)
assert.Equal(t, 1, len(args))
assert.Equal(t, "rbd", args[0])
@@ -117,14 +117,14 @@ func TestAttach(t *testing.T) {
runCount := 1
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if strings.Contains(command, "ceph-authtool") {
err := cephtest.CreateConfigDir(path.Join(configDir, clusterNamespace))
assert.Nil(t, err)
}
return "", nil
},
MockExecuteCommandWithTimeout: func(debug bool, timeout time.Duration, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithTimeout: func(timeout time.Duration, command string, args ...string) (string, error) {
assert.Equal(t, "rbd", command)
assert.Equal(t, "map", args[0])
assert.Equal(t, fmt.Sprintf("testpool/image%d", runCount), args[1])
@@ -201,14 +201,14 @@ func TestDetach(t *testing.T) {
clientset.CoreV1().ConfigMaps(clusterNamespace).Create(cm)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if strings.Contains(command, "ceph-authtool") {
err := cephtest.CreateConfigDir(path.Join(configDir, clusterNamespace))
assert.Nil(t, err)
}
return "", nil
},
MockExecuteCommandWithTimeout: func(debug bool, timeout time.Duration, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithTimeout: func(timeout time.Duration, command string, args ...string) (string, error) {
assert.Equal(t, "rbd", command)
assert.Equal(t, "unmap", args[0])
assert.Equal(t, "testpool/image1", args[1])
@@ -253,14 +253,14 @@ func TestDetachCustomKeyring(t *testing.T) {
clientset.CoreV1().ConfigMaps(clusterNamespace).Create(cm)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if strings.Contains(command, "ceph-authtool") {
err := cephtest.CreateConfigDir(path.Join(configDir, clusterNamespace))
assert.Nil(t, err)
}
return "", nil
},
MockExecuteCommandWithTimeout: func(debug bool, timeout time.Duration, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithTimeout: func(timeout time.Duration, command string, args ...string) (string, error) {
assert.Equal(t, "rbd", command)
assert.Equal(t, "unmap", args[0])
assert.Equal(t, "testpool/image1", args[1])
+6 -13
View File
@@ -22,23 +22,11 @@ import (
"github.com/rook/rook/pkg/clusterd"
)
// AuthAdd will create a new user with the given capabilities and using the already generated keyring
// found at the given keyring path. This should not be used when the user may already exist.
func AuthAdd(context *clusterd.Context, clusterName, name, keyringPath string, caps []string) error {
args := append([]string{"auth", "add", name, "-i", keyringPath}, caps...)
_, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
return errors.Wrapf(err, "failed to auth add for %s", name)
}
return nil
}
// AuthGetOrCreate will either get or create a user with the given capabilities. The keyring for the
// user will be written to the given keyring path.
func AuthGetOrCreate(context *clusterd.Context, clusterName, name, keyringPath string, caps []string) error {
logger.Infof("getting or creating ceph auth %q", name)
args := append([]string{"auth", "get-or-create", name, "-o", keyringPath}, caps...)
cmd := NewCephCommand(context, clusterName, args)
cmd.JsonOutput = false
cmd.OutputFile = false
@@ -52,6 +40,7 @@ func AuthGetOrCreate(context *clusterd.Context, clusterName, name, keyringPath s
// AuthGetKey gets the key for the given user.
func AuthGetKey(context *clusterd.Context, clusterName, name string) (string, error) {
logger.Infof("getting ceph auth key %q", name)
args := []string{"auth", "get-key", name}
buf, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
@@ -63,6 +52,7 @@ func AuthGetKey(context *clusterd.Context, clusterName, name string) (string, er
// AuthGetOrCreateKey gets or creates the key for the given user.
func AuthGetOrCreateKey(context *clusterd.Context, clusterName, name string, caps []string) (string, error) {
logger.Infof("getting or creating ceph auth key %q", name)
args := append([]string{"auth", "get-or-create-key", name}, caps...)
buf, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
@@ -74,6 +64,7 @@ func AuthGetOrCreateKey(context *clusterd.Context, clusterName, name string, cap
// AuthUpdateCaps updates the capabilities for the given user.
func AuthUpdateCaps(context *clusterd.Context, clusterName, name string, caps []string) error {
logger.Infof("updating ceph auth caps %q to %v", name, caps)
args := append([]string{"auth", "caps", name}, caps...)
_, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
@@ -84,6 +75,7 @@ func AuthUpdateCaps(context *clusterd.Context, clusterName, name string, caps []
// AuthGetCaps gets the capabilities for the given user.
func AuthGetCaps(context *clusterd.Context, clusterName, name string) (caps map[string]string, error error) {
logger.Infof("getting ceph auth caps for %q", name)
args := append([]string{"auth", "get", name})
output, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
@@ -115,6 +107,7 @@ func AuthGetCaps(context *clusterd.Context, clusterName, name string) (caps map[
// AuthDelete will delete the given user.
func AuthDelete(context *clusterd.Context, clusterName, name string) error {
logger.Infof("deleting ceph auth %q", name)
args := []string{"auth", "del", name}
_, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
+15 -32
View File
@@ -18,7 +18,6 @@ package client
import (
"fmt"
"os"
"path"
"time"
@@ -91,43 +90,27 @@ type CephToolCommand struct {
clusterName string
args []string
timeout time.Duration
Debug bool
JsonOutput bool
OutputFile bool
}
func newCephToolCommand(tool string, context *clusterd.Context, clusterName string, args []string, debug bool) *CephToolCommand {
func newCephToolCommand(tool string, context *clusterd.Context, clusterName string, args []string) *CephToolCommand {
return &CephToolCommand{
context: context,
tool: tool,
clusterName: clusterName,
args: args,
Debug: debug,
JsonOutput: true,
OutputFile: true,
}
}
// TODO: make this work even if the operator config comes from the CM?
// IsDebugLevel detects operator log level
func IsDebugLevel() bool {
logLevel := os.Getenv("ROOK_LOG_LEVEL")
switch logLevel {
case "INFO":
return true
case "DEBUG":
return false
default:
return true
}
}
func NewCephCommand(context *clusterd.Context, clusterName string, args []string) *CephToolCommand {
return newCephToolCommand(CephTool, context, clusterName, args, IsDebugLevel())
return newCephToolCommand(CephTool, context, clusterName, args)
}
func NewRBDCommand(context *clusterd.Context, clusterName string, args []string) *CephToolCommand {
cmd := newCephToolCommand(RBDTool, context, clusterName, args, IsDebugLevel())
cmd := newCephToolCommand(RBDTool, context, clusterName, args)
cmd.JsonOutput = false
cmd.OutputFile = false
return cmd
@@ -153,22 +136,22 @@ func (c *CephToolCommand) run() ([]byte, error) {
// file in the wrong place, so we will instead capture the output
// from stdout for the tests
if c.timeout == 0 {
output, err = c.context.Executor.ExecuteCommandWithOutput(c.Debug, "", command, args...)
output, err = c.context.Executor.ExecuteCommandWithOutput(command, args...)
} else {
output, err = c.context.Executor.ExecuteCommandWithTimeout(c.Debug, c.timeout, "", command, args...)
output, err = c.context.Executor.ExecuteCommandWithTimeout(c.timeout, command, args...)
}
} else {
if c.timeout == 0 {
output, err = c.context.Executor.ExecuteCommandWithOutputFile(c.Debug, "", command, "--out-file", args...)
output, err = c.context.Executor.ExecuteCommandWithOutputFile(command, "--out-file", args...)
} else {
output, err = c.context.Executor.ExecuteCommandWithOutputFileTimeout(c.Debug, c.timeout, "", command, "--out-file", args...)
output, err = c.context.Executor.ExecuteCommandWithOutputFileTimeout(c.timeout, command, "--out-file", args...)
}
}
} else {
if c.timeout == 0 {
output, err = c.context.Executor.ExecuteCommandWithOutput(c.Debug, "", command, args...)
output, err = c.context.Executor.ExecuteCommandWithOutput(command, args...)
} else {
output, err = c.context.Executor.ExecuteCommandWithTimeout(c.Debug, c.timeout, "", command, args...)
output, err = c.context.Executor.ExecuteCommandWithTimeout(c.timeout, command, args...)
}
}
@@ -190,32 +173,32 @@ func (c *CephToolCommand) RunWithTimeout(timeout time.Duration) ([]byte, error)
// configured its arguments. It is future work to integrate this case into the
// generalization.
func ExecuteRBDCommandWithTimeout(context *clusterd.Context, clusterName string, args []string) (string, error) {
output, err := context.Executor.ExecuteCommandWithTimeout(false, CmdExecuteTimeout, "", RBDTool, args...)
output, err := context.Executor.ExecuteCommandWithTimeout(CmdExecuteTimeout, RBDTool, args...)
return output, err
}
func ExecuteCephCommandWithRetry(
cmd func() ([]byte, error),
cmd func() (string, []byte, error),
getExitCode func(err error) (int, bool),
retries int,
retryOnExitCode int,
waitTime time.Duration,
) ([]byte, error) {
for i := 0; i < retries; i++ {
data, err := cmd()
action, data, err := cmd()
if err != nil {
exitCode, parsed := getExitCode(err)
if parsed {
if exitCode == retryOnExitCode {
logger.Infof("command failed. trying again...")
logger.Infof("command failed for %s. trying again...", action)
time.Sleep(waitTime)
continue
}
}
return nil, errors.Wrapf(err, "failed to complete command")
return nil, errors.Wrapf(err, "failed to complete command for %s", action)
}
if i > 0 {
logger.Infof("command succeeded on attempt %d", i)
logger.Infof("action %s succeeded on attempt %d", action, i)
}
return data, nil
}
+1 -1
View File
@@ -229,7 +229,7 @@ const testCrushMap = `{
func TestGetCrushMap(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[1] == "crush" && args[2] == "dump" {
return testCrushMap, nil
@@ -44,7 +44,7 @@ func testCreateProfile(t *testing.T, failureDomain, crushRoot, deviceClass strin
executor := &exectest.MockExecutor{}
context := &clusterd.Context{Executor: executor}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[1] == "erasure-code-profile" {
if args[2] == "get" {
+2
View File
@@ -112,6 +112,7 @@ func GetFilesystem(context *clusterd.Context, clusterName string, fsName string)
// AllowStandbyReplay gets detailed status information about a Ceph filesystem.
func AllowStandbyReplay(context *clusterd.Context, clusterName string, fsName string, allowStandbyReplay bool) error {
logger.Infof("setting allow_standby_replay for filesystem %q", fsName)
args := []string{"fs", "set", fsName, "allow_standby_replay", strconv.FormatBool(allowStandbyReplay)}
_, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
@@ -127,6 +128,7 @@ func CreateFilesystem(context *clusterd.Context, clusterName, name, metadataPool
return errors.New("at least one data pool is required")
}
logger.Infof("creating filesystem %q with metadata pool %q and data pools %v", name, metadataPool, dataPools)
args := []string{}
var err error
+2 -2
View File
@@ -103,7 +103,7 @@ func TestFilesystemRemove(t *testing.T) {
DataPools: []int{1},
},
}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[0] == "fs" {
if args[1] == "get" {
@@ -149,7 +149,7 @@ func TestFilesystemRemove(t *testing.T) {
}
return "", errors.Errorf("unexpected ceph command %q", args)
}
executor.MockExecuteCommandWithOutput = func(debug bool, actionName string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
emptyPool := "{\"images\":{\"count\":0,\"provisioned_bytes\":0,\"snap_count\":0},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}"
if args[0] == "pool" {
+4 -2
View File
@@ -92,11 +92,11 @@ func CreateImage(context *clusterd.Context, clusterName, name, poolName, dataPoo
if dataPoolName != "" {
args = append(args, fmt.Sprintf("--data-pool=%s", dataPoolName))
}
logger.Infof("creating rbd image %q with size %dMB in pool %q", imageSpec, sizeMB, dataPoolName)
buf, err := NewRBDCommand(context, clusterName, args).Run()
if err != nil {
cmdErr, ok := err.(*exec.CommandError)
if ok && cmdErr.ExitStatus() == int(syscall.EEXIST) {
if code, ok := exec.ExitStatus(err); ok && code == int(syscall.EEXIST) {
// Image with the same name already exists in the given rbd pool. Continuing with the link to PV.
logger.Warningf("Requested image %s exists in pool %s. Continuing", name, poolName)
} else {
@@ -117,6 +117,7 @@ func CreateImage(context *clusterd.Context, clusterName, name, poolName, dataPoo
}
func DeleteImage(context *clusterd.Context, clusterName, name, poolName string) error {
logger.Infof("deleting rbd image %q from pool %q", name, poolName)
imageSpec := getImageSpec(name, poolName)
args := []string{"rm", imageSpec}
buf, err := NewRBDCommand(context, clusterName, args).Run()
@@ -129,6 +130,7 @@ func DeleteImage(context *clusterd.Context, clusterName, name, poolName string)
}
func ExpandImage(context *clusterd.Context, clusterName, name, poolName, monitors, keyring string, size uint64) error {
logger.Infof("expanding rbd image %q in pool %q to size %dMB", name, poolName, display.BToMb(size))
imageSpec := getImageSpec(name, poolName)
args := []string{
"resize",
+5 -5
View File
@@ -38,7 +38,7 @@ func TestCreateImage(t *testing.T) {
// mock an error during the create image call. rbd tool returns error information to the output stream,
// separate from the error object, so verify that information also makes it back to us (because it is useful).
executor.MockExecuteCommandWithOutput = func(debug bool, actionName string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
switch {
case command == "rbd" && args[0] == "create":
return "mocked detailed ceph error output stream", errors.New("some mocked error")
@@ -53,7 +53,7 @@ func TestCreateImage(t *testing.T) {
// (except for 0, that's OK)
createCalled := false
expectedSizeArg := ""
executor.MockExecuteCommandWithOutput = func(debug bool, actionName string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
switch {
case command == "rbd" && args[0] == "create":
createCalled = true
@@ -146,7 +146,7 @@ func TestCreateImage(t *testing.T) {
func TestExpandImage(t *testing.T) {
executor := &exectest.MockExecutor{}
context := &clusterd.Context{Executor: executor}
executor.MockExecuteCommandWithTimeout = func(debug bool, timeout time.Duration, actionName string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithTimeout = func(timeout time.Duration, command string, args ...string) (string, error) {
switch {
case args[1] != "kube/some-image":
return "", errors.Errorf("no image %s", args[1])
@@ -171,7 +171,7 @@ func TestListImageLogLevelInfo(t *testing.T) {
var err error
listCalled := false
emptyListResult := false
executor.MockExecuteCommandWithOutput = func(debug bool, actionName string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
switch {
case command == "rbd" && args[0] == "ls" && args[1] == "-l":
listCalled = true
@@ -235,7 +235,7 @@ func TestListImageLogLevelDebug(t *testing.T) {
listCalled := false
emptyListResult := false
executor.MockExecuteCommandWithOutput = func(debug bool, actionName string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
switch {
case command == "rbd" && args[0] == "ls" && args[1] == "-l":
listCalled = true
+4 -4
View File
@@ -29,7 +29,7 @@ func TestEnableModuleRetries(t *testing.T) {
moduleEnableRetries := 0
moduleEnableWaitTime = 0
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
switch {
case args[0] == "balancer" && args[1] == "on":
@@ -66,7 +66,7 @@ func TestEnableModuleRetries(t *testing.T) {
func TestEnableModule(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
switch {
case args[0] == "mgr" && args[1] == "module" && args[2] == "enable":
@@ -98,7 +98,7 @@ func TestEnableModule(t *testing.T) {
func TestEnableDisableBalancerModule(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
switch {
case args[0] == "balancer" && args[1] == "on":
@@ -121,7 +121,7 @@ func TestEnableDisableBalancerModule(t *testing.T) {
func TestSetBalancerMode(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[0] == "balancer" && args[1] == "mode" && args[2] == "upmap" {
return "", nil
+1 -43
View File
@@ -31,17 +31,6 @@ type MonStatusResponse struct {
} `json:"monmap"`
}
// request to simplify deserialization of a test request
type MonStatusRequest struct {
Prefix string `json:"prefix"`
Format string `json:"format"`
ID int `json:"id"`
Weight float32 `json:"weight"`
Pool string `json:"pool"`
Var string `json:"var"`
Args []string `json:"args"`
}
// MonMapEntry represents an entry in the monitor map
type MonMapEntry struct {
Name string `json:"name"`
@@ -61,10 +50,9 @@ type AddrvecEntry struct {
}
// GetMonQuorumStatus calls quorum_status mon_command
func GetMonQuorumStatus(context *clusterd.Context, clusterName string, debug bool) (MonStatusResponse, error) {
func GetMonQuorumStatus(context *clusterd.Context, clusterName string) (MonStatusResponse, error) {
args := []string{"quorum_status"}
cmd := NewCephCommand(context, clusterName, args)
cmd.Debug = debug
buf, err := cmd.Run()
if err != nil {
return MonStatusResponse{}, errors.Wrapf(err, "mon quorum status failed")
@@ -78,33 +66,3 @@ func GetMonQuorumStatus(context *clusterd.Context, clusterName string, debug boo
return resp, nil
}
type MonTimeStatus struct {
Skew map[string]MonTimeSkewStatus `json:"time_skew_status"`
Checks struct {
Epoch int `json:"epoch"`
Round int `json:"round"`
RoundStatus string `json:"round_status"`
} `json:"timechecks"`
}
type MonTimeSkewStatus struct {
Skew json.Number `json:"skew"`
Latency json.Number `json:"latency"`
Health string `json:"health"`
}
func GetMonTimeStatus(context *clusterd.Context, clusterName string) (*MonTimeStatus, error) {
args := []string{"time-sync-status"}
buf, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
return nil, errors.Wrapf(err, "failed to get time sync status")
}
var timeStatus MonTimeStatus
if err := json.Unmarshal(buf, &timeStatus); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal time sync status response")
}
return &timeStatus, nil
}
-17
View File
@@ -234,7 +234,6 @@ func GetOSDPerfStats(context *clusterd.Context, clusterName string) (*OSDPerfSta
func GetOSDDump(context *clusterd.Context, clusterName string) (*OSDDump, error) {
args := []string{"osd", "dump"}
cmd := NewCephCommand(context, clusterName, args)
cmd.Debug = true
buf, err := cmd.Run()
if err != nil {
return nil, errors.Wrapf(err, "failed to get osd dump")
@@ -254,12 +253,6 @@ func OSDOut(context *clusterd.Context, clusterName string, osdID int) (string, e
return string(buf), err
}
func OSDRemove(context *clusterd.Context, clusterName string, osdID int) (string, error) {
args := []string{"osd", "rm", strconv.Itoa(osdID)}
buf, err := NewCephCommand(context, clusterName, args).Run()
return string(buf), err
}
func OsdSafeToDestroy(context *clusterd.Context, clusterName string, osdID int, cephVersion cephver.CephVersion) (bool, error) {
args := []string{"osd", "safe-to-destroy", strconv.Itoa(osdID)}
cmd := NewCephCommand(context, clusterName, args)
@@ -278,16 +271,6 @@ func OsdSafeToDestroy(context *clusterd.Context, clusterName string, osdID int,
return false, nil
}
func (usage *OSDUsage) ByID(osdID int) *OSDNodeUsage {
for i := range usage.OSDNodes {
if usage.OSDNodes[i].ID == osdID {
return &usage.OSDNodes[i]
}
}
return nil
}
// HostTree returns the osd tree
func HostTree(context *clusterd.Context, clusterName string) (OsdTree, error) {
var output OsdTree
+2 -2
View File
@@ -61,7 +61,7 @@ var (
func TestHostTree(t *testing.T) {
executor := &exectest.MockExecutor{}
emptyTreeResult := false
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
switch {
case args[0] == "osd" && args[1] == "tree":
@@ -89,7 +89,7 @@ func TestHostTree(t *testing.T) {
func TestOsdListNum(t *testing.T) {
executor := &exectest.MockExecutor{}
emptyOsdListNumResult := false
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
switch {
case args[0] == "osd" && args[1] == "ls":
-62
View File
@@ -1,62 +0,0 @@
/*
Copyright 2018 The Rook Authors. All rights reserved.
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 client
import (
"encoding/json"
"github.com/pkg/errors"
"github.com/rook/rook/pkg/clusterd"
)
type PGDumpBrief struct {
PgStats []PgStats `json:"pg_stats"`
}
type PgStats struct {
ID string `json:"pgid"`
State string `json:"state"`
UpOsdIDs []int `json:"up"`
UpPrimaryID int `json:"up_primary"`
ActingOsdIDs []int `json:"acting"`
ActingPrimaryID int `json:"acting_primary"`
}
func GetPGDumpBrief(context *clusterd.Context, clusterName string, isNautilusOrNewer bool) (*PGDumpBrief, error) {
var pgDump PGDumpBrief
var pgStats []PgStats
args := []string{"pg", "dump", "pgs_brief"}
buf, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
return nil, errors.Wrapf(err, "failed to get pg dump")
}
if !isNautilusOrNewer {
if err := json.Unmarshal(buf, &pgStats); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal pg dump response")
}
pgDump = PGDumpBrief{
PgStats: pgStats,
}
return &pgDump, nil
}
if err := json.Unmarshal(buf, &pgDump); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal pg dump response")
}
return &pgDump, nil
}
+6 -6
View File
@@ -32,7 +32,7 @@ func TestCreateECPoolWithOverwrites(t *testing.T) {
p := CephStoragePoolDetails{Name: "mypool", Size: 12345, ErasureCodeProfile: "myecprofile", FailureDomain: "host"}
executor := &exectest.MockExecutor{}
context := &clusterd.Context{Executor: executor}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[1] == "pool" {
if args[2] == "create" {
@@ -66,7 +66,7 @@ func TestCreateECPoolWithoutOverwrites(t *testing.T) {
p := CephStoragePoolDetails{Name: "mypool", Size: 12345, ErasureCodeProfile: "myecprofile", FailureDomain: "host"}
executor := &exectest.MockExecutor{}
context := &clusterd.Context{Executor: executor}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[1] == "pool" {
if args[2] == "create" {
@@ -109,7 +109,7 @@ func testCreateReplicaPool(t *testing.T, failureDomain, crushRoot, deviceClass s
crushRuleCreated := false
executor := &exectest.MockExecutor{}
context := &clusterd.Context{Executor: executor}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[1] == "pool" {
if args[2] == "create" {
@@ -180,7 +180,7 @@ func TestGetPoolStatistics(t *testing.T) {
p.Trash.SnapCount = 0
executor := &exectest.MockExecutor{}
context := &clusterd.Context{Executor: executor}
executor.MockExecuteCommandWithOutput = func(debug bool, actionName string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
a := "{\"images\":{\"count\":1,\"provisioned_bytes\":1024,\"snap_count\":1},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}"
logger.Infof("Command: %s %v", command, args)
@@ -210,7 +210,7 @@ func TestSetPoolReplicatedSizeProperty(t *testing.T) {
p := CephStoragePoolDetails{Name: "mypool", Size: 12345}
executor := &exectest.MockExecutor{}
context := &clusterd.Context{Executor: executor}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[2] == "set" {
@@ -228,7 +228,7 @@ func TestSetPoolReplicatedSizeProperty(t *testing.T) {
assert.NoError(t, err)
// TEST POOL SIZE 1 AND RequireSafeReplicaSize True
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[2] == "set" {
+4 -5
View File
@@ -142,10 +142,9 @@ type Fsmap struct {
UpStandby int `json:"up:standby"`
}
func Status(context *clusterd.Context, clusterName string, debug bool) (CephStatus, error) {
func Status(context *clusterd.Context, clusterName string) (CephStatus, error) {
args := []string{"status"}
cmd := NewCephCommand(context, clusterName, args)
cmd.Debug = debug
buf, err := cmd.Run()
if err != nil {
return CephStatus{}, errors.Wrapf(err, "failed to get status. %s", string(buf))
@@ -164,7 +163,7 @@ func Status(context *clusterd.Context, clusterName string, debug bool) (CephStat
// clean is true if the cluster is clean
// err is not nil if getting the status failed.
func IsClusterClean(context *clusterd.Context, clusterName string) (string, bool, error) {
status, err := Status(context, clusterName, true)
status, err := Status(context, clusterName)
if err != nil {
return "unable to get PG health", false, err
}
@@ -234,7 +233,7 @@ func getMDSRank(status CephStatus, clusterName, fsName string) (int, error) {
// MdsActiveOrStandbyReplay returns wether a given MDS is active or in standby
func MdsActiveOrStandbyReplay(context *clusterd.Context, clusterName, fsName string) error {
status, err := Status(context, clusterName, false)
status, err := Status(context, clusterName)
if err != nil {
return errors.Wrapf(err, "failed to get ceph status")
}
@@ -261,7 +260,7 @@ func MdsActiveOrStandbyReplay(context *clusterd.Context, clusterName, fsName str
// IsCephHealthy verifies Ceph is healthy, useful when performing an upgrade
// check if it's a minor or major upgrade... too!
func IsCephHealthy(context *clusterd.Context, clusterName string) bool {
cephStatus, err := Status(context, clusterName, false)
cephStatus, err := Status(context, clusterName)
if err != nil {
logger.Errorf("failed to detect if Ceph is healthy. failed to get ceph status. %v", err)
return false
+1 -1
View File
@@ -366,7 +366,7 @@ func buildHostListFromTree(tree OsdTree) (OsdTree, error) {
return osdList, nil
}
// osdDoNothing determines wether we should perfom upgrade pre-check and post-checks for the OSD daemon
// osdDoNothing determines wether we should perform upgrade pre-check and post-checks for the OSD daemon
// it checks for various cluster info like number of OSD and their placement
// it returns 'true' if we need to do nothing and false and we should pre-check/post-check
func osdDoNothing(context *clusterd.Context, clusterName string) bool {
+7 -7
View File
@@ -28,7 +28,7 @@ import (
func TestGetCephMonVersionString(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
assert.Equal(t, "version", args[0])
return "", nil
}
@@ -40,7 +40,7 @@ func TestGetCephMonVersionString(t *testing.T) {
func TestGetCephMonVersionsString(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
assert.Equal(t, "versions", args[0])
return "", nil
}
@@ -52,7 +52,7 @@ func TestGetCephMonVersionsString(t *testing.T) {
func TestEnableMessenger2(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
assert.Equal(t, "mon", args[0])
assert.Equal(t, "enable-msgr2", args[1])
return "", nil
@@ -65,7 +65,7 @@ func TestEnableMessenger2(t *testing.T) {
func TestEnableReleaseOSDFunctionality(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
assert.Equal(t, "osd", args[0])
assert.Equal(t, "require-osd-release", args[1])
assert.Equal(t, 3, len(args))
@@ -80,7 +80,7 @@ func TestEnableReleaseOSDFunctionality(t *testing.T) {
func TestOkToStopDaemon(t *testing.T) {
// First test
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
switch {
case args[0] == "mon" && args[1] == "ok-to-stop" && args[2] == "a":
return "", nil
@@ -94,7 +94,7 @@ func TestOkToStopDaemon(t *testing.T) {
assert.NoError(t, err)
// Second test
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
assert.Equal(t, "mgr", args[0])
assert.Equal(t, "ok-to-stop", args[1])
assert.Equal(t, "a", args[2])
@@ -107,7 +107,7 @@ func TestOkToStopDaemon(t *testing.T) {
assert.NoError(t, err)
// Third test
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
assert.Equal(t, "dummy", args[0])
assert.Equal(t, "ok-to-stop", args[1])
assert.Equal(t, "a", args[2])
-47
View File
@@ -1,47 +0,0 @@
/*
Copyright 2016 The Rook Authors. All rights reserved.
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 client
import (
"encoding/json"
"github.com/pkg/errors"
"github.com/rook/rook/pkg/clusterd"
)
type CephUsage struct {
Stats struct {
TotalBytes json.Number `json:"total_bytes"`
TotalUsedBytes json.Number `json:"total_used_bytes"`
TotalAvailBytes json.Number `json:"total_avail_bytes"`
TotalObjects json.Number `json:"total_objects"`
} `json:"stats"`
}
func Usage(context *clusterd.Context, clusterName string) (*CephUsage, error) {
args := []string{"df", "detail"}
buf, err := NewCephCommand(context, clusterName, args).Run()
if err != nil {
return nil, errors.Wrapf(err, "failed to get usage")
}
var usage CephUsage
if err := json.Unmarshal(buf, &usage); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal usage response")
}
return &usage, nil
}
+1 -9
View File
@@ -17,13 +17,10 @@ limitations under the License.
package osd
import (
"fmt"
"github.com/rook/rook/pkg/clusterd"
cephconfig "github.com/rook/rook/pkg/daemon/ceph/config"
"github.com/rook/rook/pkg/operator/ceph/cluster/osd/config"
"github.com/rook/rook/pkg/operator/k8sutil"
"github.com/rook/rook/pkg/util/proc"
)
const (
@@ -35,10 +32,8 @@ type OsdAgent struct {
cluster *cephconfig.ClusterInfo
nodeName string
forceFormat bool
osdProc map[int]*proc.MonitoredProc
devices []DesiredDevice
metadataDevice string
procMan *proc.ProcManager
storeConfig config.StoreConfig
kv *k8sutil.ConfigMapKVStore
pvcBacked bool
@@ -64,14 +59,11 @@ func NewAgent(context *clusterd.Context, devices []DesiredDevice, metadataDevice
nodeName: nodeName,
kv: kv,
pvcBacked: pvcBacked,
procMan: proc.New(context.Executor),
osdProc: make(map[int]*proc.MonitoredProc),
}
}
func getDeviceLVPath(context *clusterd.Context, deviceName string) string {
cmd := fmt.Sprintf("get logical volume path for device")
output, err := context.Executor.ExecuteCommandWithOutput(false, cmd, "pvdisplay", "-C", "-o", "lvpath", "--noheadings", deviceName)
output, err := context.Executor.ExecuteCommandWithOutput("pvdisplay", "-C", "-o", "lvpath", "--noheadings", deviceName)
if err != nil {
logger.Warningf("failed to retrieve logical volume path for %q. %v", deviceName, err)
return ""
+7 -7
View File
@@ -68,23 +68,23 @@ func StartOSD(context *clusterd.Context, osdType, osdID, osdUUID, lvPath string,
go handleTerminate(context, lvPath, volumeGroupName)
if err := context.Executor.ExecuteCommand(false, "", "vgchange", "-an", volumeGroupName); err != nil {
if err := context.Executor.ExecuteCommand("vgchange", "-an", volumeGroupName); err != nil {
return errors.Wrapf(err, "failed to deactivate volume group for lv %q", lvPath)
}
if err := context.Executor.ExecuteCommand(false, "", "vgchange", "-ay", volumeGroupName); err != nil {
if err := context.Executor.ExecuteCommand("vgchange", "-ay", volumeGroupName); err != nil {
return errors.Wrapf(err, "failed to activate volume group for lv %q", lvPath)
}
}
// activate the osd with ceph-volume
storeFlag := "--" + osdType
if err := context.Executor.ExecuteCommand(false, "", "stdbuf", "-oL", "ceph-volume", "lvm", "activate", "--no-systemd", storeFlag, osdID, osdUUID); err != nil {
if err := context.Executor.ExecuteCommand("stdbuf", "-oL", "ceph-volume", "lvm", "activate", "--no-systemd", storeFlag, osdID, osdUUID); err != nil {
return errors.Wrapf(err, "failed to activate osd")
}
// run the ceph-osd daemon
if err := context.Executor.ExecuteCommand(false, "", "ceph-osd", cephArgs...); err != nil {
if err := context.Executor.ExecuteCommand("ceph-osd", cephArgs...); err != nil {
// Instead of returning, we want to allow the lvm release to happen below, so we just log the err
logger.Errorf("failed to start osd or shutting down. %v", err)
}
@@ -116,7 +116,7 @@ func handleTerminate(context *clusterd.Context, lvPath, volumeGroupName string)
func killCephOSDProcess(context *clusterd.Context, lvPath string) error {
pid, err := context.Executor.ExecuteCommandWithOutput(false, "", "fuser", "-a", lvPath)
pid, err := context.Executor.ExecuteCommandWithOutput("fuser", "-a", lvPath)
if err != nil {
return errors.Wrapf(err, "failed to retrieve process ID for %q", lvPath)
}
@@ -131,7 +131,7 @@ func killCephOSDProcess(context *clusterd.Context, lvPath string) error {
// been testing with kill -9 so this is expected to be safe. There is a fix upstream Ceph that will
// improve the shutdown time of the OSD. For cleanliness we should consider removing the -9
// once it is backported to Nautilus: https://github.com/ceph/ceph/pull/31677.
if err := context.Executor.ExecuteCommand(false, "", "kill", "-9", pid); err != nil {
if err := context.Executor.ExecuteCommand("kill", "-9", pid); err != nil {
return errors.Wrapf(err, "failed to kill ceph-osd process")
}
}
@@ -429,7 +429,7 @@ func getAvailableDevices(context *clusterd.Context, desiredDevices []DesiredDevi
// releaseLVMDevice deactivates the LV to release the device.
func releaseLVMDevice(context *clusterd.Context, volumeGroupName string) error {
if err := context.Executor.ExecuteCommand(false, "", "lvchange", "-an", volumeGroupName); err != nil {
if err := context.Executor.ExecuteCommand("lvchange", "-an", volumeGroupName); err != nil {
return errors.Wrapf(err, "failed to deactivate LVM %s", volumeGroupName)
}
logger.Info("Successfully released device from lvm")
+5 -5
View File
@@ -137,23 +137,23 @@ USEC_INITIALIZED=15981915740802
func TestAvailableDevices(t *testing.T) {
executor := &exectest.MockExecutor{}
// set up a mock function to return "rook owned" partitions on the device and it does not have a filesystem
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
logger.Infof("OUTPUT for %s. %s %+v", name, command, args)
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
logger.Infof("OUTPUT for %s %v", command, args)
if command == "lsblk" {
if strings.Index(name, "sdb") != -1 {
if strings.Index(args[3], "sdb") != -1 {
// /dev/sdb has a partition
return `NAME="sdb" SIZE="65" TYPE="disk" PKNAME=""
NAME="sdb1" SIZE="30" TYPE="part" PKNAME="sdb"`, nil
}
return "", nil
} else if command == "blkid" {
if strings.Index(name, "sdb1") != -1 {
if strings.Index(args[3], "sdb1") != -1 {
// partition sdb1 has a label MY-PART
return "MY-PART", nil
}
} else if command == "udevadm" {
if strings.Index(name, "sdc") != -1 {
if strings.Index(args[3], "sdc") != -1 {
// /dev/sdc has a file system
return udevFSOutput, nil
}
-20
View File
@@ -19,7 +19,6 @@ package osd
import (
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
@@ -29,7 +28,6 @@ import (
cephconfig "github.com/rook/rook/pkg/daemon/ceph/config"
"github.com/rook/rook/pkg/operator/ceph/cluster/osd/config"
"github.com/rook/rook/pkg/operator/k8sutil"
"github.com/rook/rook/pkg/util/exec"
)
const (
@@ -101,24 +99,6 @@ func (m *DeviceOsdMapping) String() string {
return string(b)
}
func waitForPath(path string, executor exec.Executor) error {
retryCount := 0
retryMax := 25
sleepTime := 250
for {
_, err := executor.ExecuteStat(path)
if err == nil {
return nil
}
retryCount++
if retryCount > retryMax {
return err
}
<-time.After(time.Duration(sleepTime) * time.Millisecond)
}
}
func initializeOSD(config *osdConfig, context *clusterd.Context, cluster *cephconfig.ClusterInfo) error {
// add auth privileges for the OSD, the bootstrap-osd privileges were very limited
if err := addOSDAuth(context, cluster.Name, config.id, config.rootPath); err != nil {
+1 -1
View File
@@ -33,7 +33,7 @@ func TestOSDBootstrap(t *testing.T) {
defer os.RemoveAll(configDir)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return "{\"key\":\"mysecurekey\"}", nil
},
}
+10 -13
View File
@@ -294,7 +294,7 @@ func (a *OsdAgent) initializeBlockPVC(context *clusterd.Context, devices *Device
}
// execute ceph-volume with the device
op, err := context.Executor.ExecuteCommandWithCombinedOutput(false, "", baseCommand, immediateExecuteArgs...)
op, err := context.Executor.ExecuteCommandWithCombinedOutput(baseCommand, immediateExecuteArgs...)
if err != nil {
cvLogFilePath := path.Join(cvLogDir, "ceph-volume.log")
@@ -341,12 +341,11 @@ func getLVPath(op string) string {
}
func getLVNameFromDevicePath(context *clusterd.Context, devicePath string) (string, error) {
devInfo, err := context.Executor.ExecuteCommandWithOutput(true, "",
"dmsetup", "info", "-c", "--noheadings", "-o", "name", devicePath)
devInfo, err := context.Executor.ExecuteCommandWithOutput("dmsetup", "info", "-c", "--noheadings", "-o", "name", devicePath)
if err != nil {
return "", errors.Wrapf(err, "failed dmsetup info. output: %q", devInfo)
}
out, err := context.Executor.ExecuteCommandWithOutput(true, "", "dmsetup", "splitname", devInfo, "--noheadings")
out, err := context.Executor.ExecuteCommandWithOutput("dmsetup", "splitname", devInfo, "--noheadings")
if err != nil {
return "", errors.Wrapf(err, "failed dmsetup splitname %q", devInfo)
}
@@ -487,12 +486,12 @@ func (a *OsdAgent) initializeDevices(context *clusterd.Context, devices *DeviceO
logger.Infof("Base command - %+v", baseCommand)
logger.Infof("immediateReportArgs - %+v", baseCommand)
logger.Infof("immediateExecuteArgs - %+v", immediateExecuteArgs)
if err := context.Executor.ExecuteCommand(false, "", baseCommand, immediateReportArgs...); err != nil {
if err := context.Executor.ExecuteCommand(baseCommand, immediateReportArgs...); err != nil {
return errors.Wrapf(err, "failed ceph-volume report") // fail return here as validation provided by ceph-volume
}
// execute ceph-volume immediately with the device-specific setting instead of batching up multiple devices together
if err := context.Executor.ExecuteCommand(false, "", baseCommand, immediateExecuteArgs...); err != nil {
if err := context.Executor.ExecuteCommand(baseCommand, immediateExecuteArgs...); err != nil {
return errors.Wrapf(err, "failed ceph-volume")
}
@@ -535,7 +534,7 @@ func (a *OsdAgent) initializeDevices(context *clusterd.Context, devices *DeviceO
"--report",
}...)
if err := context.Executor.ExecuteCommand(false, "", baseCommand, reportArgs...); err != nil {
if err := context.Executor.ExecuteCommand(baseCommand, reportArgs...); err != nil {
return errors.Wrapf(err, "failed ceph-volume report") // fail return here as validation provided by ceph-volume
}
@@ -544,7 +543,7 @@ func (a *OsdAgent) initializeDevices(context *clusterd.Context, devices *DeviceO
"json",
}...)
cvOut, err := context.Executor.ExecuteCommandWithCombinedOutput(false, "", baseCommand, reportArgs...)
cvOut, err := context.Executor.ExecuteCommandWithCombinedOutput(baseCommand, reportArgs...)
if err != nil {
return errors.Wrapf(err, "failed ceph-volume json report: %s", cvOut) // fail return here as validation provided by ceph-volume
}
@@ -561,7 +560,7 @@ func (a *OsdAgent) initializeDevices(context *clusterd.Context, devices *DeviceO
}
// execute ceph-volume batching up multiple devices
if err := context.Executor.ExecuteCommand(false, "", baseCommand, mdArgs...); err != nil {
if err := context.Executor.ExecuteCommand(baseCommand, mdArgs...); err != nil {
return errors.Wrapf(err, "failed ceph-volume") // fail return here as validation provided by ceph-volume
}
}
@@ -587,11 +586,10 @@ func getCephVolumeLVMOSDs(context *clusterd.Context, clusterName string, cephfsi
// lv can be a block device if raw mode is used
cvMode := "lvm"
result, err := context.Executor.ExecuteCommandWithOutput(false, "", cephVolumeCmd, cvMode, "list", lv, "--format", "json")
result, err := context.Executor.ExecuteCommandWithOutput(cephVolumeCmd, cvMode, "list", lv, "--format", "json")
if err != nil {
return nil, errors.Wrapf(err, "failed to retrieve ceph-volume %s list results", cvMode)
}
logger.Debugf("%v", result)
var osds []oposd.OSDInfo
var cephVolumeResult map[string][]osdInfo
@@ -664,11 +662,10 @@ func getCephVolumeRawOSDs(context *clusterd.Context, clusterName string, cephfsi
// lv can be a block device if raw mode is used
cvMode := "raw"
result, err := context.Executor.ExecuteCommandWithOutput(false, "", cephVolumeCmd, cvMode, "list", block, "--format", "json")
result, err := context.Executor.ExecuteCommandWithOutput(cephVolumeCmd, cvMode, "list", block, "--format", "json")
if err != nil {
return nil, errors.Wrapf(err, "failed to retrieve ceph-volume %s list results", cvMode)
}
logger.Debugf("%v", result)
var osds []oposd.OSDInfo
var cephVolumeResult map[string]osdInfoBlock
+8 -8
View File
@@ -262,8 +262,8 @@ var cephVolumeRAWTestResult = `{
func TestParseCephVolumeLVMResult(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
logger.Infof("%s %+v", command, args)
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
logger.Infof("%s %v", command, args)
if command == "ceph-volume" {
return cephVolumeLVMTestResult, nil
@@ -281,8 +281,8 @@ func TestParseCephVolumeLVMResult(t *testing.T) {
func TestParseCephVolumeRawResult(t *testing.T) {
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
logger.Infof("%s %+v", command, args)
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
logger.Infof("%s %v", command, args)
if command == "ceph-volume" {
return cephVolumeRAWTestResult, nil
@@ -301,8 +301,8 @@ func TestParseCephVolumeRawResult(t *testing.T) {
func TestCephVolumeResultMultiClusterSingleOSD(t *testing.T) {
executor := &exectest.MockExecutor{}
// set up a mock function to return "rook owned" partitions on the device and it does not have a filesystem
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
logger.Infof("%s %+v", command, args)
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
logger.Infof("%s %v", command, args)
if command == "ceph-volume" {
return cephVolumeTestResultMultiCluster, nil
@@ -322,8 +322,8 @@ func TestCephVolumeResultMultiClusterSingleOSD(t *testing.T) {
func TestCephVolumeResultMultiClusterMultiOSD(t *testing.T) {
executor := &exectest.MockExecutor{}
// set up a mock function to return "rook owned" partitions on the device and it does not have a filesystem
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
logger.Infof("%s %+v", command, args)
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
logger.Infof("%s %v", command, args)
if command == "ceph-volume" {
return cephVolumeTestResultMultiClusterMultiOSD, nil
+1 -1
View File
@@ -460,7 +460,7 @@ func probeDevices(context *clusterd.Context) ([]sys.LocalDisk, error) {
// getCephVolumeInventory: Return a map of strings indexed by device with the
// information about the device returned by the command <ceph-volume inventory>
func getCephVolumeInventory(context *clusterd.Context) (*map[string]string, error) {
inventory, err := context.Executor.ExecuteCommandWithOutput(false, "", "ceph-volume", "inventory", "--format", "json")
inventory, err := context.Executor.ExecuteCommandWithOutput("ceph-volume", "inventory", "--format", "json")
if err != nil {
return nil, fmt.Errorf("failed to execute ceph-volume inventory. %+v", err)
}
+7 -23
View File
@@ -71,31 +71,16 @@ Partition table holds up to 128 entries
func TestProbeDevices(t *testing.T) {
// set up mock execute so we can verify the partitioning happens on sda
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutput = func(debug bool, name string, command string, args ...string) (string, error) {
logger.Infof("RUN Command for '%s'. %s arg %+v", name, command, args)
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
logger.Infof("RUN Command %s %v", command, args)
output := ""
switch name {
case "lsblk all":
if args[0] == "--all" {
output = "testa"
case "lsblk /dev/testa":
} else if args[0] == "/dev/testa" {
output = `SIZE="249510756352" ROTA="1" RO="0" TYPE="disk" PKNAME=""`
case "get filesystem type for /dev/testa":
output = udevOutput
case "get parent for device testa":
output = ` testa
testa testa1
testa testa2
testa2 centos_host13-root
testa2 centos_host13-swap
testa2 centos_host13-home
`
case "get disk /dev/testa uuid":
output = sgdiskOutput
case "get disk testa fs serial":
} else if args[0] == "info" && args[1] == "--query=property" {
output = udevOutput
}
return output, nil
}
@@ -105,7 +90,6 @@ testa2 centos_host13-home
assert.Nil(t, err)
assert.Equal(t, 1, len(devices))
assert.Equal(t, "ext2", devices[0].Filesystem)
}
func TestMatchUdevMonitorFiltering(t *testing.T) {
@@ -354,9 +338,9 @@ func TestDeviceListsEqual(t *testing.T) {
func TestGetCephVolumeInventory(t *testing.T) {
run := 0
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, arg ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, arg ...string) (string, error) {
run++
logger.Infof("run %d action %s command %s", run, actionName, command)
logger.Infof("run %d command %s", run, command)
switch {
case run == 1:
return `[{"available": true, "rejected_reasons": [], "sys_api": {"scheduler_mode": "noop",
+3 -3
View File
@@ -102,7 +102,7 @@ func TestGenerateClient(t *testing.T) {
func TestCreateClient(t *testing.T) {
clientset := testop.New(t, 1)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command, outfileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outfileArg string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if command == "ceph" && args[1] == "get-or-create-key" {
return `{"key":"AQC7ilJdAPijOBAABp+YAzg2QupRAWdnIh7w/Q=="}`, nil
@@ -146,7 +146,7 @@ func TestCreateClient(t *testing.T) {
func TestUpdateClient(t *testing.T) {
clientset := testop.New(t, 1)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command, outfileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outfileArg string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
return "", nil
},
@@ -180,7 +180,7 @@ func TestUpdateClient(t *testing.T) {
func TestDeleteClient(t *testing.T) {
clientset := testop.New(t, 2)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command, outfileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outfileArg string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if command == "ceph" && args[1] == "get-key" && args[2] == "client1" {
return `{"key":"AQC7ilJdAPijOBAABp+YAzg2QupRAWdnIh7w/Q=="}`, nil
+1 -1
View File
@@ -81,7 +81,7 @@ func (c *cephStatusChecker) checkCephStatus(stopCh chan struct{}) {
// checkStatus queries the status of ceph health then updates the CR status
func (c *cephStatusChecker) checkStatus() {
logger.Debugf("checking health of cluster")
status, err := client.Status(c.context, c.namespace, true)
status, err := client.Status(c.context, c.namespace)
if err != nil {
logger.Errorf("failed to get ceph status. %v", err)
return
-1
View File
@@ -118,7 +118,6 @@ func Add(mgr manager.Manager, context *clusterd.Context) error {
req := reconcile.Request{NamespacedName: types.NamespacedName{Name: nodeName}}
return []reconcile.Request{req}
}
logger.Debugf("%q is not a ceph pod", pod.Name)
return []reconcile.Request{}
}),
},
+7 -15
View File
@@ -20,7 +20,6 @@ package mgr
import (
"context"
"crypto/rand"
"os/exec"
"strconv"
"syscall"
"time"
@@ -210,29 +209,22 @@ func (c *Cluster) createSelfSignedCert() (bool, error) {
return false, nil
}
// Get the return code from the process
func getExitCode(err error) (int, bool) {
if exiterr, ok := err.(*exec.ExitError); ok {
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
return status.ExitStatus(), true
}
}
return 0, false
}
func (c *Cluster) setLoginCredentials(password string) error {
// Set the login credentials. Write the command/args to the debug log so we don't write the password by default to the log.
logger.Infof("Running command: ceph dashboard set-login-credentials admin *******")
logger.Infof("setting ceph dashboard %q login creds", dashboardUsername)
// retry a few times in the case that the mgr module is not ready to accept commands
_, err := client.ExecuteCephCommandWithRetry(func() ([]byte, error) {
_, err := client.ExecuteCephCommandWithRetry(func() (string, []byte, error) {
args := []string{"dashboard", "set-login-credentials", dashboardUsername, password}
cmd := client.NewCephCommand(c.context, c.Namespace, args)
cmd.Debug = true
return cmd.RunWithTimeout(client.CmdExecuteTimeout)
output, err := cmd.RunWithTimeout(client.CmdExecuteTimeout)
return "set dashboard creds", output, err
}, c.exitCode, 5, invalidArgErrorCode, dashboardInitWaitTime)
if err != nil {
return errors.Wrapf(err, "failed to set login creds on mgr")
}
logger.Infof("successfully set ceph dashboard creds")
return nil
}
@@ -78,7 +78,7 @@ func TestStartSecureDashboard(t *testing.T) {
exitCodeResponse := 0
clientset := test.New(t, 3)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
logger.Infof("command: %s %v", command, args)
exitCodeResponse = 0
if args[1] == "module" {
@@ -99,8 +99,8 @@ func TestStartSecureDashboard(t *testing.T) {
return "", nil
},
}
executor.MockExecuteCommandWithOutputFileTimeout = func(debug bool, timeout time.Duration, actionName string, command, outfileArg string, arg ...string) (string, error) {
return executor.MockExecuteCommandWithOutputFile(debug, actionName, command, outfileArg, arg...)
executor.MockExecuteCommandWithOutputFileTimeout = func(timeout time.Duration, command, outfileArg string, arg ...string) (string, error) {
return executor.MockExecuteCommandWithOutputFile(command, outfileArg, arg...)
}
clusterInfo := &cephconfig.ClusterInfo{
+2 -2
View File
@@ -37,6 +37,7 @@ import (
"github.com/rook/rook/pkg/operator/ceph/controller"
cephver "github.com/rook/rook/pkg/operator/ceph/version"
"github.com/rook/rook/pkg/operator/k8sutil"
"github.com/rook/rook/pkg/util/exec"
v1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -121,7 +122,7 @@ func New(
resources: resources,
priorityClassName: priorityClassName,
ownerRef: ownerRef,
exitCode: getExitCode,
exitCode: exec.ExitStatus,
dataDirHostPath: dataDirHostPath,
skipUpgradeChecks: skipUpgradeChecks,
}
@@ -167,7 +168,6 @@ func (c *Cluster) Start() error {
// start the deployment
d := c.makeDeployment(mgrConfig)
logger.Debugf("starting mgr deployment: %+v", d)
// Set the deployment hash as an annotation
err = patch.DefaultAnnotator.SetLastAppliedAnnotation(d)
+2 -2
View File
@@ -43,7 +43,7 @@ func TestStartMGR(t *testing.T) {
updateDeploymentAndWait, deploymentsUpdated = testopk8s.UpdateDeploymentAndWaitStub()
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return "{\"key\":\"mysecurekey\"}", nil
},
}
@@ -139,7 +139,7 @@ func TestConfigureModules(t *testing.T) {
configSettings := map[string]string{}
lastModuleConfigured := ""
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if command == "ceph" && len(args) > 3 {
if args[0] == "mgr" && args[1] == "module" {
@@ -55,9 +55,10 @@ func (c *Cluster) setRookOrchestratorBackend() error {
orchestratorCLIName = orchestratorNewCLIName
}
// retry a few times in the case that the mgr module is not ready to accept commands
_, err := client.ExecuteCephCommandWithRetry(func() ([]byte, error) {
_, err := client.ExecuteCephCommandWithRetry(func() (string, []byte, error) {
args := []string{orchestratorCLIName, "set", "backend", "rook"}
return client.NewCephCommand(c.context, c.Namespace, args).RunWithTimeout(client.CmdExecuteTimeout)
output, err := client.NewCephCommand(c.context, c.Namespace, args).RunWithTimeout(client.CmdExecuteTimeout)
return "set rook backend", output, err
}, c.exitCode, 5, invalidArgErrorCode, orchestratorInitWaitTime)
if err != nil {
return errors.Wrapf(err, "failed to set rook as the orchestrator backend")
@@ -34,7 +34,7 @@ func TestOrchestratorModules(t *testing.T) {
rookModuleEnabled := false
rookBackendSet := false
backendErrorCount := 0
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[0] == "mgr" && args[1] == "module" && args[2] == "enable" {
if args[3] == "orchestrator_cli" {
@@ -48,7 +48,7 @@ func TestOrchestratorModules(t *testing.T) {
}
return "", errors.Errorf("unexpected ceph command '%v'", args)
}
executor.MockExecuteCommandWithOutputFileTimeout = func(debug bool, timeout time.Duration, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFileTimeout = func(timeout time.Duration, command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[0] == "orchestrator" && args[1] == "set" && args[2] == "backend" && args[3] == "rook" {
if backendErrorCount < 5 {
@@ -93,7 +93,7 @@ func TestOrchestratorModules(t *testing.T) {
c.clusterInfo.CephVersion = cephver.Octopus
err = c.setRookOrchestratorBackend()
assert.Error(t, err)
executor.MockExecuteCommandWithOutputFileTimeout = func(debug bool, timeout time.Duration, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFileTimeout = func(timeout time.Duration, command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[0] == "orch" && args[1] == "set" && args[2] == "backend" && args[3] == "rook" {
if backendErrorCount < 5 {
+1 -1
View File
@@ -249,7 +249,7 @@ func genSecret(executor exec.Executor, configDir, name string, args []string) (s
"-n", name,
}
args = append(base, args...)
_, err := executor.ExecuteCommandWithOutput(false, "gen secret", "ceph-authtool", args...)
_, err := executor.ExecuteCommandWithOutput("ceph-authtool", args...)
if err != nil {
return "", errors.Wrapf(err, "failed to gen secret")
}
+1 -1
View File
@@ -38,7 +38,7 @@ func TestCreateClusterSecrets(t *testing.T) {
defer os.RemoveAll(configDir)
adminSecret := "AQDkLIBd9vLGJxAAnXsIKPrwvUXAmY+D1g0X1Q=="
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
logger.Infof("COMMAND: %s %v", command, args)
if command == "ceph-authtool" && args[0] == "--create-keyring" {
filename := args[1]
+1 -1
View File
@@ -83,7 +83,7 @@ func (c *Cluster) checkHealth() error {
// connect to the mons
// get the status and check for quorum
quorumStatus, err := client.GetMonQuorumStatus(c.context, c.ClusterInfo.Name, client.IsDebugLevel())
quorumStatus, err := client.GetMonQuorumStatus(c.context, c.ClusterInfo.Name)
if err != nil {
return errors.Wrapf(err, "failed to get mon quorum status")
}
+3 -3
View File
@@ -42,7 +42,7 @@ func TestCheckHealth(t *testing.T) {
updateDeploymentAndWait, deploymentsUpdated = testopk8s.UpdateDeploymentAndWaitStub()
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return clienttest.MonInQuorumResponse(), nil
},
}
@@ -97,7 +97,7 @@ func TestCheckHealthNotFound(t *testing.T) {
updateDeploymentAndWait, deploymentsUpdated = testopk8s.UpdateDeploymentAndWaitStub()
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return clienttest.MonInQuorumResponse(), nil
},
}
@@ -153,7 +153,7 @@ func TestAddRemoveMons(t *testing.T) {
monQuorumResponse := clienttest.MonInQuorumResponse()
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return monQuorumResponse, nil
},
}
+1 -1
View File
@@ -974,7 +974,7 @@ func waitForQuorumWithMons(context *clusterd.Context, clusterName string, mons [
// get the quorum_status response that contains info about all monitors in the mon map and
// their quorum status
monQuorumStatusResp, err := client.GetMonQuorumStatus(context, clusterName, client.IsDebugLevel())
monQuorumStatusResp, err := client.GetMonQuorumStatus(context, clusterName)
if err != nil {
logger.Debugf("failed to get quorum_status. %v", err)
continue
+2 -2
View File
@@ -84,14 +84,14 @@ func newTestStartClusterWithQuorumResponse(t *testing.T, namespace string, monRe
configDir, _ := ioutil.TempDir("", "")
defer os.RemoveAll(configDir)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if strings.Contains(command, "ceph-authtool") {
err := cephtest.CreateConfigDir(path.Join(configDir, namespace))
return "", errors.Wrapf(err, "failed testing of start cluster without quorum response")
}
return "", nil
},
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
// mock quorum health check because a second `Start()` triggers a health check
return monResponse()
},
+3 -3
View File
@@ -36,11 +36,11 @@ func TestOSDStatus(t *testing.T) {
var execCount = 0
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return "{\"key\":\"mysecurekey\", \"osdid\":3.0}", nil
},
}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command string, outFileArg string, args ...string) (string, error) {
logger.Infof("ExecuteCommandWithOutputFile: %s %v", command, args)
execCount++
if args[1] == "dump" {
@@ -53,7 +53,7 @@ func TestOSDStatus(t *testing.T) {
return "", nil
}
executor.MockExecuteCommandWithOutput = func(debug bool, actionName string, command string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
logger.Infof("ExecuteCommandWithOutput: %s %v", command, args)
return "", nil
}
+2 -2
View File
@@ -124,7 +124,7 @@ func TestAddRemoveNode(t *testing.T) {
}
generateKey := "expected key"
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return "{\"key\": \"" + generateKey + "\"}", nil
},
}
@@ -158,7 +158,7 @@ func TestAddRemoveNode(t *testing.T) {
// mock the ceph calls that will be called during remove node
mockExec := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[0] == "status" {
return `{"pgmap":{"num_pgs":100,"pgs_by_state":[{"state_name":"active+clean","count":100}]}}`, nil
+11
View File
@@ -823,6 +823,9 @@ func (c *Cluster) provisionOSDContainer(osdProps osdProperties, copyBinariesMoun
osdOnPVCMetadata := osdProps.metadataPVC.ClaimName != ""
envVars := c.getConfigEnvVars(osdProps, k8sutil.DataDir)
// enable debug logging in the prepare job
envVars = append(envVars, setDebugLogLevelEnvVar(true))
// only 1 of device list, device filter, device path filter and use all devices can be specified. We prioritize in that order.
if len(osdProps.devices) > 0 {
deviceNames := make([]string, len(osdProps.devices))
@@ -1010,6 +1013,14 @@ func pvcBackedOSDEnvVar(pvcBacked string) v1.EnvVar {
return v1.EnvVar{Name: pvcBackedOSDVarName, Value: pvcBacked}
}
func setDebugLogLevelEnvVar(debug bool) v1.EnvVar {
level := "INFO"
if debug {
level = "DEBUG"
}
return v1.EnvVar{Name: "ROOK_LOG_LEVEL", Value: level}
}
func blockPathEnvVariable(lvPath string) v1.EnvVar {
return v1.EnvVar{Name: blockPathVarName, Value: lvPath}
}
+1 -1
View File
@@ -34,7 +34,7 @@ func TestRBDMirror(t *testing.T) {
clientset := testop.New(t, 1)
keysCreated := map[string]bool{}
executor := &exectest.MockExecutor{}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command string, outFileArg string, args ...string) (string, error) {
logger.Infof("%s %+v", command, args)
if args[0] == "auth" && args[1] == "get-or-create-key" {
daemonName := args[2]
@@ -35,7 +35,7 @@ func TestGenerateKey(t *testing.T) {
var generateKey = ""
var failGenerateKey = false
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
if failGenerateKey {
return "", errors.New("test error")
}
+2 -2
View File
@@ -40,7 +40,7 @@ func TestMonStore_Set(t *testing.T) {
execedCmd := ""
execInjectErr := false
executor.MockExecuteCommandWithOutputFile =
func(debug bool, actionName string, command string, outfile string, args ...string) (string, error) {
func(command string, outfile string, args ...string) (string, error) {
execedCmd = command + " " + strings.Join(args, " ")
if execInjectErr {
return "output from cmd with error", errors.New("mocked error")
@@ -85,7 +85,7 @@ func TestMonStore_SetAll(t *testing.T) {
execedCmds := []string{}
execInjectErrOnKeyword := "donotinjectanerror"
executor.MockExecuteCommandWithOutputFile =
func(debug bool, actionName string, command string, outfile string, args ...string) (string, error) {
func(command string, outfile string, args ...string) (string, error) {
execedCmd := command + " " + strings.Join(args, " ")
execedCmds = append(execedCmds, execedCmd)
k := execInjectErrOnKeyword
@@ -69,7 +69,7 @@ func IsReadyToReconcile(client client.Client, clustercontext *clusterd.Context,
if cephCluster.Status.Phase == k8sutil.ReadyStatus {
// Test a Ceph command to verify the Operator is ready
// This is done to silence errors when the operator just started and cannot reconcile yet
_, err = cephclient.Status(clustercontext, namespacedName.Namespace, true)
_, err = cephclient.Status(clustercontext, namespacedName.Namespace)
if err != nil {
if strings.Contains(err.Error(), "error calling conf_read_file") {
logger.Info("operator is not ready to run ceph command, cannot reconcile yet.")
+6 -6
View File
@@ -77,10 +77,10 @@ func TestCreateFilesystem(t *testing.T) {
fses := `[{"name":"myfs","metadata_pool":"myfs-metadata","metadata_pool_id":1,"data_pool_ids":[2],"data_pools":["myfs-data0"]}]`
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return "{\"key\":\"mysecurekey\"}", nil
},
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if strings.Contains(command, "ceph-authtool") {
err := cephtest.CreateConfigDir(path.Join(configDir, "ns"))
assert.Nil(t, err)
@@ -131,7 +131,7 @@ func TestCreateFilesystem(t *testing.T) {
// Test multiple filesystem creation
executor = &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
if contains(args, "ls") {
return fses, nil
}
@@ -155,10 +155,10 @@ func TestCreateNopoolFilesystem(t *testing.T) {
fses := `[{"name":"myfs"}]`
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return "{\"key\":\"mysecurekey\"}", nil
},
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if strings.Contains(command, "ceph-authtool") {
err := cephtest.CreateConfigDir(path.Join(configDir, "ns"))
assert.Nil(t, err)
@@ -194,7 +194,7 @@ func TestCreateNopoolFilesystem(t *testing.T) {
// Test multiple filesystem creation
executor = &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
if contains(args, "ls") {
return fses, nil
}
+3 -5
View File
@@ -127,15 +127,13 @@ func (c *CephNFSController) addRADOSConfigFile(n cephv1.CephNFS, name string) er
"--namespace", n.Spec.RADOS.Namespace,
"--conf", cephclient.CephConfFilePath(c.context.ConfigDir, c.namespace),
}
moniker := "rados stat " + config
err := c.context.Executor.ExecuteCommand(false, moniker, cmd, append(args, "stat", config)...)
err := c.context.Executor.ExecuteCommand(cmd, append(args, "stat", config)...)
if err == nil {
// If stat works then we assume it's present already
return nil
}
// try to create it
moniker = "rados create " + config
return c.context.Executor.ExecuteCommand(false, moniker, cmd, append(args, "create", config)...)
return c.context.Executor.ExecuteCommand(cmd, append(args, "create", config)...)
}
func (c *CephNFSController) addServerToDatabase(nfs cephv1.CephNFS, name string) {
@@ -173,7 +171,7 @@ stderr: %s`, moniker, x.Stdout, x.Stderr)
}
return nil
// The below can be used when/if `ganesha-rados-grace` supports a `--cephconf` or similar option
// return c.context.Executor.ExecuteCommand(false, moniker, cmd, args...)
// return c.context.Executor.ExecuteCommand(cmd, args...)
}
func (c *CephNFSController) generateConfig(n cephv1.CephNFS, name string) (string, error) {
+1 -1
View File
@@ -40,7 +40,7 @@ func runAdminCommandNoRealm(c *Context, args ...string) (string, error) {
command, args := client.FinalizeCephCommandArgs("radosgw-admin", args, c.Context.ConfigDir, c.ClusterName)
// start the rgw admin command
output, err := c.Context.Executor.ExecuteCommandWithOutput(client.IsDebugLevel(), "", command, args...)
output, err := c.Context.Executor.ExecuteCommandWithOutput(command, args...)
if err != nil {
return "", errors.Wrapf(err, "failed to run radosgw-admin")
}
+1 -1
View File
@@ -78,7 +78,7 @@ func GetBucketStats(c *Context, bucketName string) (*ObjectBucketStats, bool, er
"--bucket", bucketName)
if err != nil {
if strings.Contains(err.Error(), "No such file or directory") {
if strings.Contains(err.Error(), "exit status 2") {
return nil, true, errors.New("not found")
} else {
return nil, false, errors.Wrapf(err, "failed to get bucket stats")
+2 -2
View File
@@ -166,7 +166,7 @@ func TestCephObjectStoreController(t *testing.T) {
}
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName, command, outfile string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outfile string, args ...string) (string, error) {
if args[0] == "status" {
return `{"pgmap":{"num_pgs":100,"pgs_by_state":[{"state_name":"active+clean","count":100}]}}`, nil
}
@@ -175,7 +175,7 @@ func TestCephObjectStoreController(t *testing.T) {
}
return "", nil
},
MockExecuteCommandWithOutput: func(debug bool, actionName, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if args[0] == "realm" && args[1] == "list" {
return realmListJSON, nil
}
+3 -3
View File
@@ -28,7 +28,7 @@ import (
func TestReconcileRealm(t *testing.T) {
defaultStore := true
executorFunc := func(debug bool, actionName string, command string, args ...string) (string, error) {
executorFunc := func(command string, args ...string) (string, error) {
idResponse := `{"id":"test-id"}`
logger.Infof("Execute: %s %v", command, args)
if args[1] == "get" {
@@ -81,7 +81,7 @@ func deleteStore(t *testing.T, name string, existingStores string, expectedDelet
executor := &exectest.MockExecutor{}
deletedRootPool := false
deletedErasureCodeProfile := false
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
//logger.Infof("command: %s %v", command, args)
if args[0] == "osd" {
if args[1] == "pool" {
@@ -118,7 +118,7 @@ func deleteStore(t *testing.T, name string, existingStores string, expectedDelet
}
return "", errors.Errorf("unexpected ceph command %q", args)
}
executorFunc := func(debug bool, actionName, command string, args ...string) (string, error) {
executorFunc := func(command string, args ...string) (string, error) {
//logger.Infof("Command: %s %v", command, args)
if args[0] == "realm" {
if args[1] == "delete" {
+4 -4
View File
@@ -38,10 +38,10 @@ import (
func TestStartRGW(t *testing.T) {
clientset := testop.New(t, 3)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return `{"key":"mysecurekey"}`, nil
},
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
return `{"id":"test-id"}`, nil
},
}
@@ -76,13 +76,13 @@ func validateStart(t *testing.T, c *clusterConfig, clientset *fclient.Clientset)
}
func TestCreateObjectStore(t *testing.T) {
commandWithOutputFunc := func(debug bool, actionName, command string, args ...string) (string, error) {
commandWithOutputFunc := func(command string, args ...string) (string, error) {
return `{"realms": []}`, nil
}
executor := &exectest.MockExecutor{
MockExecuteCommandWithCombinedOutput: commandWithOutputFunc,
MockExecuteCommandWithOutput: commandWithOutputFunc,
MockExecuteCommandWithOutputFile: func(debug bool, actionName, command, outfile string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outfile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if command == "ceph" {
if args[1] == "erasure-code-profile" {
@@ -116,13 +116,13 @@ func TestCephObjectStoreUserController(t *testing.T) {
}
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName, command, outfile string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outfile string, args ...string) (string, error) {
if args[0] == "status" {
return `{"pgmap":{"num_pgs":100,"pgs_by_state":[{"state_name":"active+clean","count":100}]}}`, nil
}
return "", nil
},
MockExecuteCommandWithOutput: func(debug bool, actionName, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if args[0] == "user" {
return userCreateJSON, nil
}
+5 -5
View File
@@ -89,7 +89,7 @@ func TestValidatePool(t *testing.T) {
func TestValidateCrushProperties(t *testing.T) {
executor := &exectest.MockExecutor{}
context := &clusterd.Context{Executor: executor}
executor.MockExecuteCommandWithOutputFile = func(debug bool, actionName, command, outputFile string, args ...string) (string, error) {
executor.MockExecuteCommandWithOutputFile = func(command, outputFile string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[1] == "crush" && args[2] == "dump" {
return `{"types":[{"type_id": 0,"name": "osd"}],"buckets":[{"id": -1,"name":"default"},{"id": -2,"name":"good"}]}`, nil
@@ -127,7 +127,7 @@ func TestValidateCrushProperties(t *testing.T) {
func TestCreatePool(t *testing.T) {
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName, command, outfile string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outfile string, args ...string) (string, error) {
if command == "ceph" && args[1] == "erasure-code-profile" {
return `{"k":"2","m":"1","plugin":"jerasure","technique":"reed_sol_van"}`, nil
}
@@ -152,7 +152,7 @@ func TestCreatePool(t *testing.T) {
func TestDeletePool(t *testing.T) {
failOnDelete := false
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName, command, outfile string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outfile string, args ...string) (string, error) {
if command == "ceph" && args[1] == "lspools" {
return `[{"poolnum":1,"poolname":"mypool"}]`, nil
} else if command == "ceph" && args[1] == "pool" && args[2] == "get" {
@@ -161,7 +161,7 @@ func TestDeletePool(t *testing.T) {
return "", nil
},
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
emptyPool := "{\"images\":{\"count\":0,\"provisioned_bytes\":0,\"snap_count\":0},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}"
p := "{\"images\":{\"count\":1,\"provisioned_bytes\":1024,\"snap_count\":0},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}"
logger.Infof("Command: %s %v", command, args)
@@ -236,7 +236,7 @@ func TestCephBlockPoolController(t *testing.T) {
}
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName, command, outfile string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command, outfile string, args ...string) (string, error) {
if args[0] == "status" {
return `{"pgmap":{"num_pgs":100,"pgs_by_state":[{"state_name":"active+clean","count":100}]}}`, nil
}
@@ -46,7 +46,7 @@ func TestProvisionImage(t *testing.T) {
defer os.Setenv("POD_NAMESPACE", "")
defer os.RemoveAll(configDir)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if strings.Contains(command, "ceph-authtool") {
err := cephtest.CreateConfigDir(path.Join(configDir, namespace))
assert.Nil(t, err)
@@ -113,7 +113,7 @@ func TestReclaimPolicyForProvisionedImages(t *testing.T) {
defer os.Setenv("POD_NAMESPACE", "")
defer os.RemoveAll(configDir)
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
if strings.Contains(command, "ceph-authtool") {
err := cephtest.CreateConfigDir(path.Join(configDir, namespace))
assert.Nil(t, err)
+1 -2
View File
@@ -486,8 +486,7 @@ func (c *ClusterController) initCluster(cluster *cluster) error {
}
hostFlag := fmt.Sprintf("--host=%s", createQualifiedReplicaServiceName(0, cluster.namespace))
out, err := c.context.Executor.ExecuteCommandWithCombinedOutput(false, "cockroachdb init",
"/cockroach/cockroach", "init", "--insecure", hostFlag)
out, err := c.context.Executor.ExecuteCommandWithCombinedOutput("/cockroach/cockroach", "init", "--insecure", hostFlag)
if err != nil {
return fmt.Errorf("cluster init failed for namespace %s: %+v. %s", cluster.namespace, err, out)
}
+1 -1
View File
@@ -151,7 +151,7 @@ func TestOnAdd(t *testing.T) {
// keep track of if the cockroachdb init command was called
initCalled := false
executor := &exectest.MockExecutor{
MockExecuteCommandWithCombinedOutput: func(debug bool, actionName string, command string, arg ...string) (string, error) {
MockExecuteCommandWithCombinedOutput: func(command string, arg ...string) (string, error) {
if strings.Contains(command, "cockroach") && arg[0] == "init" {
initCalled = true
}
+1 -1
View File
@@ -34,7 +34,7 @@ import (
func TestStartMGR(t *testing.T) {
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutputFile: func(debug bool, actionName string, command string, outFileArg string, args ...string) (string, error) {
MockExecuteCommandWithOutputFile: func(command string, outFileArg string, args ...string) (string, error) {
return "{\"key\":\"mysecurekey\"}", nil
},
}
+5 -2
View File
@@ -122,7 +122,7 @@ func deleteResourceAndWait(namespace, name, resourceType string,
// wait for the resource to be deleted
sleepTime := 2 * time.Second
for i := 0; i < 30; i++ {
for i := 0; i < 45; i++ {
// check for the existence of the resource
err = getAction()
if err != nil {
@@ -133,7 +133,10 @@ func deleteResourceAndWait(namespace, name, resourceType string,
return fmt.Errorf("failed to get %s. %+v", name, err)
}
logger.Infof("%s still found. waiting...", name)
if i%5 == 0 {
// occasionally print a message
logger.Infof("%q still found. waiting...", name)
}
time.Sleep(sleepTime)
}
-26
View File
@@ -1,26 +0,0 @@
/*
Copyright 2016 The Rook Authors. All rights reserved.
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 display
import "strconv"
func NumToStrOmitEmpty(num uint) string {
if num == 0 {
return ""
}
return strconv.FormatUint(uint64(num), 10)
}
-30
View File
@@ -1,30 +0,0 @@
/*
Copyright 2016 The Rook Authors. All rights reserved.
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 display
import (
"math"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNumToStrOmitEmpty(t *testing.T) {
assert.Equal(t, "", NumToStrOmitEmpty(0))
assert.Equal(t, "1", NumToStrOmitEmpty(1))
assert.Equal(t, "9999", NumToStrOmitEmpty(9999))
assert.Equal(t, "18446744073709551615", NumToStrOmitEmpty(math.MaxUint64))
}
+4 -25
View File
@@ -16,38 +16,17 @@ limitations under the License.
package exec
import (
"fmt"
"os/exec"
"syscall"
)
type CommandError struct {
ActionName string
Err error
}
func (e *CommandError) Error() string {
var exitErrMsg string
if exitErr, ok := e.Err.(*exec.ExitError); ok {
// the error is an ExitError, include the stderr output in the final error output
exitErrMsg = string(exitErr.Stderr)
}
return fmt.Sprintf("Failed to complete '%s': %+v. %s", e.ActionName, e.Err, exitErrMsg)
}
func (e *CommandError) ExitStatus() int {
exitStatus := -1
exitErr, ok := e.Err.(*exec.ExitError)
func ExitStatus(err error) (int, bool) {
exitErr, ok := err.(*exec.ExitError)
if ok {
waitStatus, ok := exitErr.ProcessState.Sys().(syscall.WaitStatus)
if ok {
exitStatus = waitStatus.ExitStatus()
return waitStatus.ExitStatus(), true
}
}
return exitStatus
}
func createCommandError(err error, actionName string) error {
return &CommandError{ActionName: actionName, Err: err}
return 0, false
}
+48 -72
View File
@@ -31,50 +31,36 @@ import (
)
type Executor interface {
StartExecuteCommand(debug bool, actionName string, command string, arg ...string) (*exec.Cmd, error)
ExecuteCommand(debug bool, actionName string, command string, arg ...string) error
ExecuteCommandWithOutput(debug bool, actionName string, command string, arg ...string) (string, error)
ExecuteCommandWithCombinedOutput(debug bool, actionName string, command string, arg ...string) (string, error)
ExecuteCommandWithOutputFile(debug bool, actionName, command, outfileArg string, arg ...string) (string, error)
ExecuteCommandWithOutputFileTimeout(debug bool, timeout time.Duration, actionName, command, outfileArg string, arg ...string) (string, error)
ExecuteCommandWithTimeout(debug bool, timeout time.Duration, actionName string, command string, arg ...string) (string, error)
ExecuteStat(name string) (os.FileInfo, error)
ExecuteCommand(command string, arg ...string) error
ExecuteCommandWithOutput(command string, arg ...string) (string, error)
ExecuteCommandWithCombinedOutput(command string, arg ...string) (string, error)
ExecuteCommandWithOutputFile(command, outfileArg string, arg ...string) (string, error)
ExecuteCommandWithOutputFileTimeout(timeout time.Duration, command, outfileArg string, arg ...string) (string, error)
ExecuteCommandWithTimeout(timeout time.Duration, command string, arg ...string) (string, error)
}
type CommandExecutor struct {
}
// Start a process and return immediately
func (*CommandExecutor) StartExecuteCommand(debug bool, actionName string, command string, arg ...string) (*exec.Cmd, error) {
cmd, stdout, stderr, err := startCommand(debug, command, arg...)
if err != nil {
return cmd, createCommandError(err, actionName)
}
go logOutput(actionName, stdout, stderr)
return cmd, nil
}
// Start a process and wait for its completion
func (*CommandExecutor) ExecuteCommand(debug bool, actionName string, command string, arg ...string) error {
cmd, stdout, stderr, err := startCommand(debug, command, arg...)
func (*CommandExecutor) ExecuteCommand(command string, arg ...string) error {
cmd, stdout, stderr, err := startCommand(command, arg...)
if err != nil {
return createCommandError(err, actionName)
return err
}
logOutput(actionName, stdout, stderr)
logOutput(stdout, stderr)
if err := cmd.Wait(); err != nil {
return createCommandError(err, actionName)
return err
}
return nil
}
// ExecuteCommandWithTimeout starts a process and wait for its completion with timeout.
func (*CommandExecutor) ExecuteCommandWithTimeout(debug bool, timeout time.Duration, actionName string, command string, arg ...string) (string, error) {
logCommand(debug, command, arg...)
func (*CommandExecutor) ExecuteCommandWithTimeout(timeout time.Duration, command string, arg ...string) (string, error) {
logCommand(command, arg...)
cmd := exec.Command(command, arg...)
var b bytes.Buffer
@@ -82,7 +68,7 @@ func (*CommandExecutor) ExecuteCommandWithTimeout(debug bool, timeout time.Durat
cmd.Stderr = &b
if err := cmd.Start(); err != nil {
return "", createCommandError(err, actionName)
return "", err
}
done := make(chan error, 1)
@@ -90,55 +76,54 @@ func (*CommandExecutor) ExecuteCommandWithTimeout(debug bool, timeout time.Durat
done <- cmd.Wait()
}()
interrupSent := false
interruptSent := false
for {
select {
case <-time.After(timeout):
if interrupSent {
logger.Infof("Timeout waiting for process %s to return after interrupt signal was sent. Sending kill signal to the process", command)
if interruptSent {
logger.Infof("timeout waiting for process %s to return after interrupt signal was sent. Sending kill signal to the process", command)
var e error
if err := cmd.Process.Kill(); err != nil {
logger.Errorf("Failed to kill process %s: %+v", command, err)
e = fmt.Errorf("Timeout waiting for the command %s to return after interrupt signal was sent. Tried to kill the process but that failed: %+v", command, err)
logger.Errorf("Failed to kill process %s: %v", command, err)
e = fmt.Errorf("timeout waiting for the command %s to return after interrupt signal was sent. Tried to kill the process but that failed: %v", command, err)
} else {
e = fmt.Errorf("Timeout waiting for the command %s to return", command)
e = fmt.Errorf("timeout waiting for the command %s to return", command)
}
return strings.TrimSpace(b.String()), createCommandError(e, command)
return strings.TrimSpace(b.String()), e
}
logger.Infof("Timeout waiting for process %s to return. Sending interrupt signal to the process", command)
logger.Infof("timeout waiting for process %s to return. Sending interrupt signal to the process", command)
if err := cmd.Process.Signal(os.Interrupt); err != nil {
logger.Errorf("Failed to send interrupt signal to process %s: %+v", command, err)
logger.Errorf("Failed to send interrupt signal to process %s: %v", command, err)
// kill signal will be sent next loop
}
interrupSent = true
interruptSent = true
case err := <-done:
if err != nil {
return strings.TrimSpace(b.String()), createCommandError(err, command)
return strings.TrimSpace(b.String()), err
}
if interrupSent {
e := fmt.Errorf("Timeout waiting for the command %s to return", command)
return strings.TrimSpace(b.String()), createCommandError(e, command)
if interruptSent {
return strings.TrimSpace(b.String()), fmt.Errorf("timeout waiting for the command %s to return", command)
}
return strings.TrimSpace(b.String()), nil
}
}
}
func (*CommandExecutor) ExecuteCommandWithOutput(debug bool, actionName string, command string, arg ...string) (string, error) {
logCommand(debug, command, arg...)
func (*CommandExecutor) ExecuteCommandWithOutput(command string, arg ...string) (string, error) {
logCommand(command, arg...)
cmd := exec.Command(command, arg...)
return runCommandWithOutput(actionName, cmd, false)
return runCommandWithOutput(cmd, false)
}
func (*CommandExecutor) ExecuteCommandWithCombinedOutput(debug bool, actionName string, command string, arg ...string) (string, error) {
logCommand(debug, command, arg...)
func (*CommandExecutor) ExecuteCommandWithCombinedOutput(command string, arg ...string) (string, error) {
logCommand(command, arg...)
cmd := exec.Command(command, arg...)
return runCommandWithOutput(actionName, cmd, true)
return runCommandWithOutput(cmd, true)
}
// Same as ExecuteCommandWithOutputFile but with a timeout limit.
func (*CommandExecutor) ExecuteCommandWithOutputFileTimeout(debug bool, timeout time.Duration, actionName string,
func (*CommandExecutor) ExecuteCommandWithOutputFileTimeout(timeout time.Duration,
command, outfileArg string, arg ...string) (string, error) {
outFile, err := ioutil.TempFile("", "")
@@ -149,7 +134,7 @@ func (*CommandExecutor) ExecuteCommandWithOutputFileTimeout(debug bool, timeout
defer os.Remove(outFile.Name())
arg = append(arg, outfileArg, outFile.Name())
logCommand(debug, command, arg...)
logCommand(command, arg...)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
@@ -160,7 +145,7 @@ func (*CommandExecutor) ExecuteCommandWithOutputFileTimeout(debug bool, timeout
// if there was anything that went to stdout/stderr then log it, even before
// we return an error
if string(cmdOut) != "" {
logger.Info(string(cmdOut))
logger.Debug(string(cmdOut))
}
if ctx.Err() == context.DeadlineExceeded {
@@ -175,7 +160,7 @@ func (*CommandExecutor) ExecuteCommandWithOutputFileTimeout(debug bool, timeout
return string(fileOut), err
}
func (*CommandExecutor) ExecuteCommandWithOutputFile(debug bool, actionName string, command, outfileArg string, arg ...string) (string, error) {
func (*CommandExecutor) ExecuteCommandWithOutputFile(command, outfileArg string, arg ...string) (string, error) {
// create a temporary file to serve as the output file for the command to be run and ensure
// it is cleaned up after this function is done
@@ -189,7 +174,7 @@ func (*CommandExecutor) ExecuteCommandWithOutputFile(debug bool, actionName stri
// append the output file argument to the list or args
arg = append(arg, outfileArg, outFile.Name())
logCommand(debug, command, arg...)
logCommand(command, arg...)
cmd := exec.Command(command, arg...)
cmdOut, err := cmd.CombinedOutput()
// if there was anything that went to stdout/stderr then log it, even before we return an error
@@ -205,8 +190,8 @@ func (*CommandExecutor) ExecuteCommandWithOutputFile(debug bool, actionName stri
return string(fileOut), err
}
func startCommand(debug bool, command string, arg ...string) (*exec.Cmd, io.ReadCloser, io.ReadCloser, error) {
logCommand(debug, command, arg...)
func startCommand(command string, arg ...string) (*exec.Cmd, io.ReadCloser, io.ReadCloser, error) {
logCommand(command, arg...)
cmd := exec.Command(command, arg...)
stdout, err := cmd.StdoutPipe()
@@ -223,21 +208,17 @@ func startCommand(debug bool, command string, arg ...string) (*exec.Cmd, io.Read
return cmd, stdout, stderr, err
}
func (*CommandExecutor) ExecuteStat(name string) (os.FileInfo, error) {
return os.Stat(name)
}
// read from reader line by line and write it to the log
func logFromReader(logger *capnslog.PackageLogger, reader io.ReadCloser) {
in := bufio.NewScanner(reader)
lastLine := ""
for in.Scan() {
lastLine = in.Text()
logger.Info(lastLine)
logger.Debug(lastLine)
}
}
func logOutput(name string, stdout, stderr io.ReadCloser) {
func logOutput(stdout, stderr io.ReadCloser) {
if stdout == nil || stderr == nil {
logger.Warningf("failed to collect stdout and stderr")
return
@@ -246,11 +227,11 @@ func logOutput(name string, stdout, stderr io.ReadCloser) {
// The child processes should appropriately be outputting at the desired global level. Therefore,
// we always log at INFO level here, so that log statements from child procs at higher levels
// (e.g., WARNING) will still be displayed. We are relying on the child procs to output appropriately.
childLogger := capnslog.NewPackageLogger("github.com/rook/rook", name)
childLogger := capnslog.NewPackageLogger("github.com/rook/rook", "exec")
if !childLogger.LevelAt(capnslog.INFO) {
rl, err := capnslog.GetRepoLogger("github.com/rook/rook")
if err == nil {
rl.SetLogLevel(map[string]capnslog.LogLevel{name: capnslog.INFO})
rl.SetLogLevel(map[string]capnslog.LogLevel{"exec": capnslog.INFO})
}
}
@@ -258,7 +239,7 @@ func logOutput(name string, stdout, stderr io.ReadCloser) {
logFromReader(childLogger, stdout)
}
func runCommandWithOutput(actionName string, cmd *exec.Cmd, combinedOutput bool) (string, error) {
func runCommandWithOutput(cmd *exec.Cmd, combinedOutput bool) (string, error) {
var output []byte
var err error
@@ -271,17 +252,12 @@ func runCommandWithOutput(actionName string, cmd *exec.Cmd, combinedOutput bool)
out := strings.TrimSpace(string(output))
if err != nil {
return out, createCommandError(err, actionName)
return out, err
}
return out, nil
}
func logCommand(debug bool, command string, arg ...string) {
msg := fmt.Sprintf("Running command: %s %s", command, strings.Join(arg, " "))
if debug {
logger.Debug(msg)
} else {
logger.Info(msg)
}
func logCommand(command string, arg ...string) {
logger.Debugf("Running command: %s %s", command, strings.Join(arg, " "))
}
+19 -38
View File
@@ -16,85 +16,66 @@ limitations under the License.
package test
import (
"os"
"os/exec"
"time"
)
// ******************** MockExecutor ********************
type MockExecutor struct {
MockExecuteCommand func(debug bool, actionName string, command string, arg ...string) error
MockStartExecuteCommand func(debug bool, actionName string, command string, arg ...string) (*exec.Cmd, error)
MockExecuteCommandWithOutput func(debug bool, actionName string, command string, arg ...string) (string, error)
MockExecuteCommandWithCombinedOutput func(debug bool, actionName string, command string, arg ...string) (string, error)
MockExecuteCommandWithOutputFile func(debug bool, actionName string, command, outfileArg string, arg ...string) (string, error)
MockExecuteCommandWithOutputFileTimeout func(debug bool, timeout time.Duration, actionName string, command, outfileArg string, arg ...string) (string, error)
MockExecuteCommandWithTimeout func(debug bool, timeout time.Duration, actionName string, command string, arg ...string) (string, error)
MockExecuteStat func(name string) (os.FileInfo, error)
MockExecuteCommand func(command string, arg ...string) error
MockStartExecuteCommand func(command string, arg ...string) (*exec.Cmd, error)
MockExecuteCommandWithOutput func(command string, arg ...string) (string, error)
MockExecuteCommandWithCombinedOutput func(command string, arg ...string) (string, error)
MockExecuteCommandWithOutputFile func(command, outfileArg string, arg ...string) (string, error)
MockExecuteCommandWithOutputFileTimeout func(timeout time.Duration, command, outfileArg string, arg ...string) (string, error)
MockExecuteCommandWithTimeout func(timeout time.Duration, command string, arg ...string) (string, error)
}
func (e *MockExecutor) ExecuteCommand(debug bool, actionName string, command string, arg ...string) error {
func (e *MockExecutor) ExecuteCommand(command string, arg ...string) error {
if e.MockExecuteCommand != nil {
return e.MockExecuteCommand(debug, actionName, command, arg...)
return e.MockExecuteCommand(command, arg...)
}
return nil
}
func (e *MockExecutor) StartExecuteCommand(debug bool, actionName string, command string, arg ...string) (*exec.Cmd, error) {
if e.MockStartExecuteCommand != nil {
return e.MockStartExecuteCommand(debug, actionName, command, arg...)
}
args := []string{command}
return &exec.Cmd{Args: append(args, arg...)}, nil
}
func (e *MockExecutor) ExecuteCommandWithOutput(debug bool, actionName string, command string, arg ...string) (string, error) {
func (e *MockExecutor) ExecuteCommandWithOutput(command string, arg ...string) (string, error) {
if e.MockExecuteCommandWithOutput != nil {
return e.MockExecuteCommandWithOutput(debug, actionName, command, arg...)
return e.MockExecuteCommandWithOutput(command, arg...)
}
return "", nil
}
func (e *MockExecutor) ExecuteCommandWithTimeout(debug bool, timeout time.Duration, actionName string, command string, arg ...string) (string, error) {
func (e *MockExecutor) ExecuteCommandWithTimeout(timeout time.Duration, command string, arg ...string) (string, error) {
if e.MockExecuteCommandWithTimeout != nil {
return e.MockExecuteCommandWithTimeout(debug, time.Second, actionName, command, arg...)
return e.MockExecuteCommandWithTimeout(time.Second, command, arg...)
}
return "", nil
}
func (e *MockExecutor) ExecuteCommandWithCombinedOutput(debug bool, actionName string, command string, arg ...string) (string, error) {
func (e *MockExecutor) ExecuteCommandWithCombinedOutput(command string, arg ...string) (string, error) {
if e.MockExecuteCommandWithCombinedOutput != nil {
return e.MockExecuteCommandWithCombinedOutput(debug, actionName, command, arg...)
return e.MockExecuteCommandWithCombinedOutput(command, arg...)
}
return "", nil
}
func (e *MockExecutor) ExecuteCommandWithOutputFile(debug bool, actionName string, command, outfileArg string, arg ...string) (string, error) {
func (e *MockExecutor) ExecuteCommandWithOutputFile(command, outfileArg string, arg ...string) (string, error) {
if e.MockExecuteCommandWithOutputFile != nil {
return e.MockExecuteCommandWithOutputFile(debug, actionName, command, outfileArg, arg...)
return e.MockExecuteCommandWithOutputFile(command, outfileArg, arg...)
}
return "", nil
}
func (e *MockExecutor) ExecuteCommandWithOutputFileTimeout(debug bool, timeout time.Duration, actionName string, command, outfileArg string, arg ...string) (string, error) {
func (e *MockExecutor) ExecuteCommandWithOutputFileTimeout(timeout time.Duration, command, outfileArg string, arg ...string) (string, error) {
if e.MockExecuteCommandWithOutputFileTimeout != nil {
return e.MockExecuteCommandWithOutputFileTimeout(debug, timeout, actionName, command, outfileArg, arg...)
return e.MockExecuteCommandWithOutputFileTimeout(timeout, command, outfileArg, arg...)
}
return "", nil
}
func (e *MockExecutor) ExecuteStat(name string) (os.FileInfo, error) {
if e.MockExecuteStat != nil {
return e.MockExecuteStat(name)
}
return nil, nil
}
+19 -33
View File
@@ -16,9 +16,6 @@ limitations under the License.
package exec
import (
"fmt"
"os"
"os/exec"
"time"
)
@@ -31,54 +28,43 @@ type TranslateCommandExecutor struct {
Executor Executor
// Translator translates every command before running it
Translator func(debug bool, actionName string, command string, arg ...string) (string, []string)
}
// StartExecuteCommand starts a process and return immediately
func (e *TranslateCommandExecutor) StartExecuteCommand(debug bool, actionName string, command string, arg ...string) (*exec.Cmd, error) {
transCommand, transArgs := e.Translator(debug, actionName, command, arg...)
return e.Executor.StartExecuteCommand(debug, actionName, transCommand, transArgs...)
Translator func(command string, arg ...string) (string, []string)
}
// ExecuteCommand starts a process and wait for its completion
func (e *TranslateCommandExecutor) ExecuteCommand(debug bool, actionName string, command string, arg ...string) error {
transCommand, transArgs := e.Translator(debug, actionName, command, arg...)
return e.Executor.ExecuteCommand(debug, actionName, transCommand, transArgs...)
func (e *TranslateCommandExecutor) ExecuteCommand(command string, arg ...string) error {
transCommand, transArgs := e.Translator(command, arg...)
return e.Executor.ExecuteCommand(transCommand, transArgs...)
}
// ExecuteCommandWithOutput starts a process and wait for its completion
func (e *TranslateCommandExecutor) ExecuteCommandWithOutput(debug bool, actionName string, command string, arg ...string) (string, error) {
transCommand, transArgs := e.Translator(debug, actionName, command, arg...)
return e.Executor.ExecuteCommandWithOutput(debug, actionName, transCommand, transArgs...)
func (e *TranslateCommandExecutor) ExecuteCommandWithOutput(command string, arg ...string) (string, error) {
transCommand, transArgs := e.Translator(command, arg...)
return e.Executor.ExecuteCommandWithOutput(transCommand, transArgs...)
}
// ExecuteCommandWithCombinedOutput starts a process and returns its stdout and stderr combined.
func (e *TranslateCommandExecutor) ExecuteCommandWithCombinedOutput(debug bool, actionName string, command string, arg ...string) (string, error) {
transCommand, transArgs := e.Translator(debug, actionName, command, arg...)
return e.Executor.ExecuteCommandWithCombinedOutput(debug, actionName, transCommand, transArgs...)
func (e *TranslateCommandExecutor) ExecuteCommandWithCombinedOutput(command string, arg ...string) (string, error) {
transCommand, transArgs := e.Translator(command, arg...)
return e.Executor.ExecuteCommandWithCombinedOutput(transCommand, transArgs...)
}
// ExecuteCommandWithOutputFile starts a process and saves output to file
func (e *TranslateCommandExecutor) ExecuteCommandWithOutputFile(debug bool, actionName string, command, outfileArg string, arg ...string) (string, error) {
transCommand, transArgs := e.Translator(debug, actionName, command, arg...)
return e.Executor.ExecuteCommandWithOutputFile(debug, actionName, transCommand, outfileArg, transArgs...)
func (e *TranslateCommandExecutor) ExecuteCommandWithOutputFile(command, outfileArg string, arg ...string) (string, error) {
transCommand, transArgs := e.Translator(command, arg...)
return e.Executor.ExecuteCommandWithOutputFile(transCommand, outfileArg, transArgs...)
}
// ExecuteCommandWithOutputFileTimeout is the same as ExecuteCommandWithOutputFile but with a timeout limit.
func (e *TranslateCommandExecutor) ExecuteCommandWithOutputFileTimeout(
debug bool, timeout time.Duration, actionName string,
timeout time.Duration,
command, outfileArg string, arg ...string) (string, error) {
transCommand, transArgs := e.Translator(debug, actionName, command, arg...)
return e.Executor.ExecuteCommandWithOutputFileTimeout(debug, timeout, actionName, transCommand, outfileArg, transArgs...)
transCommand, transArgs := e.Translator(command, arg...)
return e.Executor.ExecuteCommandWithOutputFileTimeout(timeout, transCommand, outfileArg, transArgs...)
}
// ExecuteCommandWithTimeout starts a process and wait for its completion with timeout.
func (e *TranslateCommandExecutor) ExecuteCommandWithTimeout(debug bool, timeout time.Duration, actionName string, command string, arg ...string) (string, error) {
transCommand, transArgs := e.Translator(debug, actionName, command, arg...)
return e.Executor.ExecuteCommandWithTimeout(debug, timeout, actionName, transCommand, transArgs...)
}
// ExecuteStat returns a file stat
func (e *TranslateCommandExecutor) ExecuteStat(name string) (os.FileInfo, error) {
return nil, fmt.Errorf("TODO: TranslateCommandExecutor.ExecuteStat() not implemented ... is it needed?")
func (e *TranslateCommandExecutor) ExecuteCommandWithTimeout(timeout time.Duration, command string, arg ...string) (string, error) {
transCommand, transArgs := e.Translator(command, arg...)
return e.Executor.ExecuteCommandWithTimeout(timeout, transCommand, transArgs...)
}
-32
View File
@@ -38,38 +38,6 @@ func VerifyRequiredFlags(cmd *cobra.Command, requiredFlags []string) error {
return createRequiredFlagError(cmd.Name(), missingFlags)
}
type RenamedFlag struct {
NewFlagName string
OldFlagName string
}
func VerifyRenamedFlags(cmd *cobra.Command, renamedFlags []RenamedFlag) error {
var missingFlags []string
for _, renamedFlag := range renamedFlags {
val, err := cmd.Flags().GetString(renamedFlag.NewFlagName)
if err != nil || val == "" {
val, err := cmd.Flags().GetString(renamedFlag.OldFlagName)
if err != nil || val == "" {
missingFlags = append(missingFlags, renamedFlag.NewFlagName)
}
}
}
return createRequiredFlagError(cmd.Name(), missingFlags)
}
func VerifyRequiredUint64Flags(cmd *cobra.Command, requiredFlags []string) error {
var missingFlags []string
for _, reqFlag := range requiredFlags {
val, err := cmd.Flags().GetUint64(reqFlag)
if err != nil || val == 0 {
missingFlags = append(missingFlags, reqFlag)
}
}
return createRequiredFlagError(cmd.Name(), missingFlags)
}
func createRequiredFlagError(name string, flags []string) error {
if len(flags) == 0 {
return nil
-58
View File
@@ -48,64 +48,6 @@ func TestStringFlags(t *testing.T) {
assert.Nil(t, err)
}
func TestRenamedFlags(t *testing.T) {
cmd := &cobra.Command{
Use: "test",
Short: "Creates a test arg",
}
var arg1 string
var arg2 string
cmd.Flags().StringVar(&arg1, "foo", "", "test 1")
cmd.Flags().StringVar(&arg2, "bar", "", "test 2")
// both arguments are missing
err := VerifyRenamedFlags(cmd, []RenamedFlag{{"foo", "bar"}})
assert.Equal(t, "foo is required for test", err.Error())
// old argument is missing
cmd.Flags().Set("foo", "fooval")
err = VerifyRenamedFlags(cmd, []RenamedFlag{{"foo", "bar"}})
assert.Nil(t, err)
// new argument is missing
cmd.Flags().Set("foo", "")
cmd.Flags().Set("bar", "barval")
err = VerifyRenamedFlags(cmd, []RenamedFlag{{"foo", "bar"}})
assert.Nil(t, err)
// no arguments are missing
cmd.Flags().Set("foo", "fooval")
err = VerifyRenamedFlags(cmd, []RenamedFlag{{"foo", "bar"}})
assert.Nil(t, err)
}
func TestUintFlags(t *testing.T) {
cmd := &cobra.Command{
Use: "test",
Short: "Creates a test arg",
}
var arg1 uint64
var arg2 uint64
cmd.Flags().Uint64Var(&arg1, "foo", 0, "test 1")
cmd.Flags().Uint64Var(&arg2, "bar", 0, "test 2")
// both arguments are missing
err := VerifyRequiredUint64Flags(cmd, []string{"foo", "bar"})
assert.Equal(t, "foo,bar are required for test", err.Error())
// one argument is missing
cmd.Flags().Set("foo", "1234")
err = VerifyRequiredUint64Flags(cmd, []string{"foo", "bar"})
assert.Equal(t, "bar is required for test", err.Error())
// no arguments are missing
cmd.Flags().Set("bar", "5432")
err = VerifyRequiredUint64Flags(cmd, []string{"foo", "bar"})
assert.Nil(t, err)
}
func TestGetFlagsAndValues(t *testing.T) {
cmd := &cobra.Command{
Use: "test",
-20
View File
@@ -1,20 +0,0 @@
/*
Copyright 2016 The Rook Authors. All rights reserved.
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 proc
import "github.com/coreos/pkg/capnslog"
var logger = capnslog.NewPackageLogger("github.com/rook/rook", "proc")
-152
View File
@@ -1,152 +0,0 @@
/*
Copyright 2016 The Rook Authors. All rights reserved.
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 proc
import (
"math"
"os/exec"
"syscall"
"time"
)
const (
maxDelaySeconds = 30
)
type MonitoredProc struct {
parent *ProcManager
cmd *exec.Cmd
monitor bool
retries int
totalRetries int
retrySecondsExponentBase float64
waitForExit func()
}
func newMonitoredProc(p *ProcManager, cmd *exec.Cmd) *MonitoredProc {
m := &MonitoredProc{
parent: p,
cmd: cmd,
retrySecondsExponentBase: 2,
}
m.waitForExit = m.waitForProcessExit
return m
}
func (p *MonitoredProc) Monitor(logName string) {
p.monitor = true
var err error
var lastRetryCheck time.Time
var lastStartTime time.Time
for {
// wait for the given process to complete, unless the last retry had failed immediately
if err == nil {
p.waitForExit()
}
if !p.monitor {
logger.Infof("done monitoring process %v", p.cmd.Args)
break
}
// calculate the delay
delay := calcRetryDelay(p.retrySecondsExponentBase, p.retries, time.Now(), lastStartTime, lastRetryCheck)
lastRetryCheck = time.Now()
logger.Infof("starting process %v again after %.1f seconds", p.cmd.Args, delay)
<-time.After(time.Second * time.Duration(delay))
if !p.monitor {
logger.Infof("done monitoring process %v", p.cmd.Args)
break
}
// start the process
p.cmd, err = p.parent.executor.StartExecuteCommand(false, logName, p.cmd.Args[0], p.cmd.Args[1:]...)
if err != nil {
logger.Warningf("retry %d (total %d): process %v failed to restart. %v", p.retries, p.totalRetries, p.cmd.Args, err)
p.retries++
} else {
logger.Infof("retry (total %d). started process %v", p.totalRetries, p.cmd.Args)
lastStartTime = time.Now()
p.retries = 0
}
p.totalRetries++
}
}
func (p *MonitoredProc) waitForProcessExit() {
state, err := p.cmd.Process.Wait()
if err != nil {
logger.Errorf("waiting for process %d had an error: %+v", p.cmd.Process.Pid, err)
return
}
// check the wait status of the process which has all the exit information
waitStatus, ok := state.Sys().(syscall.WaitStatus)
if !ok {
logger.Errorf("unknown waitStatus for process %d: %+v", p.cmd.Process.Pid, state.Sys())
return
}
logger.Infof("process %d completed. Exited: %t, ExitStatus: %d, Signaled: %t, Signal: %d, %+v",
p.cmd.Process.Pid, waitStatus.Exited(), waitStatus.ExitStatus(), waitStatus.Signaled(), waitStatus.Signal(), p.cmd)
}
func (p *MonitoredProc) Stop(mon bool) error {
p.monitor = mon
if p.cmd == nil || p.cmd.Process == nil {
return nil
}
pid := p.cmd.Process.Pid
logger.Infof("stopping child process %d\n", pid)
if err := p.cmd.Process.Kill(); err != nil {
logger.Errorf("failed to stop child process %d: %+v\n", pid, err)
return err
}
logger.Infof("child process %d stopped successfully\n", pid)
return nil
}
// determines the time to delay in seconds before retrying again
func calcRetryDelay(base float64, retries int, now, lastStartTime, lastRetryCheck time.Time) float64 {
if base == 0 {
return 0
}
power := 0.0
if retries > 0 {
power = float64(retries)
} else if !lastRetryCheck.IsZero() && !lastStartTime.IsZero() && now.Sub(lastStartTime).Seconds() < maxDelaySeconds {
// the process was running for shorter than the max retry delay, so it wasn't running for too long before
// it exited again. retry count is 0, so the process did start "successfully" last time, but in order to prevent
// rapid retry loops where the process starts successfully but crashes right after, let's use the number of
// seconds since the last retry check as a proxy for retry count. this will give us exponential back off
// for a process that crashes immediately after starting.
power = now.Sub(lastRetryCheck).Seconds()
}
delay := math.Pow(base, power)
if delay > float64(maxDelaySeconds) {
return float64(maxDelaySeconds)
}
return delay
}
-110
View File
@@ -1,110 +0,0 @@
/*
Copyright 2016 The Rook Authors. All rights reserved.
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 proc
import (
"errors"
"os/exec"
"testing"
"time"
"github.com/rook/rook/pkg/util/exec/test"
"github.com/stretchr/testify/assert"
)
func TestRestartDelay(t *testing.T) {
var zeroTime time.Time
// test cases for when last retry check time is not available
assert.Equal(t, float64(0), calcRetryDelay(0, 0, zeroTime, zeroTime, zeroTime))
assert.Equal(t, float64(0), calcRetryDelay(0, 2, zeroTime, zeroTime, zeroTime))
assert.Equal(t, float64(1), calcRetryDelay(2, 0, zeroTime, zeroTime, zeroTime))
assert.Equal(t, float64(2), calcRetryDelay(2, 1, zeroTime, zeroTime, zeroTime))
assert.Equal(t, float64(4), calcRetryDelay(2, 2, zeroTime, zeroTime, zeroTime))
assert.Equal(t, float64(30), calcRetryDelay(2, 5, zeroTime, zeroTime, zeroTime))
assert.Equal(t, float64(30), calcRetryDelay(2, 100, zeroTime, zeroTime, zeroTime))
// test cases for when last retry check is available and retry count is 0 and the process hasn't been
// running long at all. the time delay should be 2^(number of secs since last retry), with a max limit of 30.
now := time.Now()
lastStartTime := now
lastRetryCheck := now
assert.Equal(t, float64(1), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))
lastRetryCheck = now.Add(-1 * time.Second)
assert.Equal(t, float64(2), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))
lastRetryCheck = now.Add(-2 * time.Second)
assert.Equal(t, float64(4), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))
lastRetryCheck = now.Add(-4 * time.Second)
assert.Equal(t, float64(16), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))
lastRetryCheck = now.Add(-5 * time.Second)
assert.Equal(t, float64(30), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))
lastRetryCheck = now.Add(-999999999 * time.Second)
assert.Equal(t, float64(30), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))
// test case for when the process has been running for quite awhile. we should not delay for very long since we
// are clearly not in a rapid retry loop.
lastStartTime = now.Add(-100 * time.Second)
assert.Equal(t, float64(1), calcRetryDelay(2, 0, now, lastStartTime, lastRetryCheck))
// test cases for when both retry count and last retry check are available, retry count should be favored
lastRetryCheck = now.Add(-4 * time.Second)
assert.Equal(t, float64(2), calcRetryDelay(2, 1, now, lastStartTime, lastRetryCheck))
}
func TestMonitoredRestart(t *testing.T) {
executor := &test.MockExecutor{}
procMgr := New(executor)
cmd := &exec.Cmd{Args: []string{"/my/path", "1", "2", "3"}}
proc := newMonitoredProc(procMgr, cmd)
proc.retrySecondsExponentBase = 0.0
commands := 0
executor.MockStartExecuteCommand = func(debug bool, name string, command string, args ...string) (*exec.Cmd, error) {
commands++
cmd := &exec.Cmd{Args: append([]string{command}, args...)}
switch {
case commands == 1:
return cmd, errors.New("test failure")
case commands == 2:
assert.Equal(t, 1, proc.retries)
}
return cmd, nil
}
iter := 0
proc.waitForExit = func() {
logger.Debugf("waited for process")
assert.True(t, proc.monitor)
switch {
case iter == 0:
case iter == 1:
assert.True(t, proc.monitor)
logger.Debugf("stop monitoring")
proc.monitor = false
}
iter++
}
proc.Monitor("testproc")
assert.False(t, proc.monitor)
assert.Equal(t, proc.retries, 0)
assert.Equal(t, proc.totalRetries, 2)
}
-239
View File
@@ -1,239 +0,0 @@
/*
Copyright 2016 The Rook Authors. All rights reserved.
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 proc
import (
"fmt"
"os"
"regexp"
"strings"
"sync"
"syscall"
ps "github.com/jbw976/go-ps"
"github.com/rook/rook/pkg/util/exec"
)
type ProcStartPolicy int
const (
StartAction = "start"
RunAction = "run"
StopAction = "stop"
RestartExisting ProcStartPolicy = iota
ReuseExisting
)
type ProcManager struct {
sync.RWMutex
procs []*MonitoredProc
executor exec.Executor
}
// Create a new proc manager
func New(executor exec.Executor) *ProcManager {
return &ProcManager{executor: executor}
}
// Start a child process and wait for its completion
func (p *ProcManager) RunWithOutput(logName, command string, args ...string) (string, error) {
logger.Infof("Running process %s with args: %v", command, args)
output, err := p.executor.ExecuteCommandWithOutput(false, logName, command, args...)
if err != nil {
return "", fmt.Errorf("failed to run %s: %+v", command, err)
}
return output, nil
}
// Start a child process and wait for its completion
func (p *ProcManager) RunWithCombinedOutput(logName, command string, args ...string) (string, error) {
logger.Infof("Running process %s with args: %v", command, args)
output, err := p.executor.ExecuteCommandWithCombinedOutput(false, logName, command, args...)
if err != nil {
return "", fmt.Errorf("failed to run %s: %+v", command, err)
}
return output, nil
}
// Start a child process and wait for its completion
func (p *ProcManager) Run(logName, command string, args ...string) error {
logger.Infof("Running process %s with args: %v", command, args)
err := p.executor.ExecuteCommand(false, logName, command, args...)
if err != nil {
return fmt.Errorf("failed to run %s: %+v", command, err)
}
return nil
}
// Start the given process with the provided arguments. Handling of any matching existing process will be in accordance
// with the given ProcStartPolicy. The search pattern will be used to search through the cmdline args of existing
// processes to find any matching existing process. Therefore, it should be a regex pattern that can uniquely
// identify the process (e.g., --id=1)
func (p *ProcManager) Start(name, command, procSearchPattern string, policy ProcStartPolicy, args ...string) (*MonitoredProc, error) {
// look for an existing process first
shouldStart, err := p.checkProcessExists(command, procSearchPattern, policy)
if err != nil {
return nil, err
}
if !shouldStart {
// based on the presence of an existing process and the given policy, we should not start a process, bail out.
return nil, nil
}
logger.Infof("Starting process %s with args: %v", name, args)
cmd, err := p.executor.StartExecuteCommand(false, name, command, args...)
if err != nil {
return nil, fmt.Errorf("failed to start process %s: %+v", name, err)
}
proc := newMonitoredProc(p, cmd)
// monitor the process if it was not mocked
if cmd != nil && cmd.Process != nil {
go proc.Monitor(name)
}
p.Lock()
p.procs = append(p.procs, proc)
p.Unlock()
return proc, nil
}
func (p *ProcManager) Shutdown() error {
p.RLock()
for _, proc := range p.procs {
if err := proc.Stop(false); err != nil {
return fmt.Errorf("error while shutting down. %v", err)
}
}
p.procs = nil
p.RUnlock()
return nil
}
// Checks if a process exists. If the restart policy indicates the process should be restarted,
// the process will be stopped here.
func (p *ProcManager) checkProcessExists(binary, procSearchPattern string, policy ProcStartPolicy) (shouldStart bool, err error) {
// check if this process is already being monitored
p.RLock()
defer p.RUnlock()
if index, proc := p.findMonitoredProc(binary, procSearchPattern); proc != nil {
if policy == ReuseExisting {
// no need to start the process since the process manager is already watching to ensure it is running
logger.Infof("Process is already being managed and will not be started again. %s", procSearchPattern)
return false, nil
} else if policy == RestartExisting {
// stop the process and the process manager so a new process manager can be started.
// the caller is responsible to start a new process
logger.Infof("Policy is 'restart' for %s. Stopping its monitor.", procSearchPattern)
p.purgeManagedProc(index, proc)
return true, nil
}
}
// check if this process is currently running even though not being managed
existingProc, err := ps.FindProcessByCmdline(binary, procSearchPattern)
if err != nil {
return false, fmt.Errorf("failed to search for process %s with pattern '%s': %+v", binary, procSearchPattern, err)
}
if existingProc == nil {
logger.Infof("no existing process found for binary %s and pattern '%s'", binary, procSearchPattern)
return true, nil
}
logger.Infof("existing process found for binary %s with pid %d, cmdline '%s'.", binary, existingProc.Pid(), existingProc.Cmdline())
if policy == ReuseExisting {
logger.Infof("Policy is 'reuse', reusing existing process.")
return false, nil
}
logger.Infof("Policy is 'restart', restarting existing process.")
// double check if the process is managed and stop it
index, proc := p.findMonitoredProcByPID(existingProc.Pid())
if proc != nil {
p.purgeManagedProc(index, proc)
return true, nil
}
// we couldn't stop the existing process through our own managed process set, try to stop the process
// via a direct signal to its PID
if err := syscall.Kill(existingProc.Pid(), syscall.SIGKILL); err != nil {
return false, fmt.Errorf("failed to stop child process %d: %v", existingProc.Pid(), err)
}
return true, nil
}
// Stop and remove a process from the list of managed.
// Assumes we are inside the process lock.
func (p *ProcManager) purgeManagedProc(index int, proc *MonitoredProc) {
err := proc.Stop(false)
if err != nil {
logger.Warningf("did not stop process %+v. %+v", proc.cmd, err)
}
// remove the process from our managed list (if it's in there)
// If the process failed to stop, it is still not being managed so we will go ahead and remove it
if index >= 0 {
p.procs[index] = p.procs[len(p.procs)-1]
p.procs[len(p.procs)-1] = nil
p.procs = p.procs[:len(p.procs)-1]
}
}
// Find a monitored process.
// Assumes we are inside the process lock.
func (p *ProcManager) findMonitoredProc(binary, searchPattern string) (int, *MonitoredProc) {
// find if the process is being monitored
for index, proc := range p.procs {
if os.Args[0] != binary {
// skip processes that are not owned by rook
continue
}
cmdline := strings.Join(proc.cmd.Args, " ")
if matched, _ := regexp.MatchString(searchPattern, cmdline); matched {
logger.Infof("found monitored process for %s", searchPattern)
return index, proc
}
}
return -1, nil
}
// Find a monitored process by its pid.
// Assumes we are inside the process lock.
func (p *ProcManager) findMonitoredProcByPID(pid int) (int, *MonitoredProc) {
// find if the process is being monitored
for index, proc := range p.procs {
if proc.cmd.Process != nil && proc.cmd.Process.Pid == pid {
return index, proc
}
}
return -1, nil
}
-83
View File
@@ -1,83 +0,0 @@
/*
Copyright 2016 The Rook Authors. All rights reserved.
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 proc
import (
"os"
"os/exec"
"testing"
exectest "github.com/rook/rook/pkg/util/exec/test"
"github.com/stretchr/testify/assert"
)
func TestCheckProcessExists(t *testing.T) {
executor := &exectest.MockExecutor{}
p := &ProcManager{executor: executor}
// no managed process to find
shouldStart, err := p.checkProcessExists(os.Args[0], "bar=2", ReuseExisting)
assert.Nil(t, err)
assert.True(t, shouldStart)
// create a couple managed processes
cmd1 := exec.Command("mycmd1", []string{os.Args[0], "foo=1", "bar=2"}...)
cmd2 := exec.Command("mycmd2", []string{os.Args[0], "foo=3", "bar=4"}...)
monitored1 := &MonitoredProc{parent: p, cmd: cmd1, waitForExit: func() {}}
monitored2 := &MonitoredProc{parent: p, cmd: cmd2, waitForExit: func() {}}
p.procs = []*MonitoredProc{monitored1, monitored2}
// find the managed process
shouldStart, err = p.checkProcessExists(os.Args[0], "bar=2", ReuseExisting)
assert.Nil(t, err)
assert.False(t, shouldStart)
// can't find another process
index, proc := p.findMonitoredProcByPID(0)
assert.Equal(t, -1, index)
assert.Nil(t, proc)
// purge the managed process
p.purgeManagedProc(0, monitored1)
assert.Equal(t, 1, len(p.procs))
assert.Equal(t, "mycmd2", p.procs[0].cmd.Args[0])
p.Shutdown()
assert.Equal(t, 0, len(p.procs))
}
func TestRunProcesses(t *testing.T) {
executor := &exectest.MockExecutor{}
p := &ProcManager{executor: executor}
proc, err := p.Start("mylog", "mydaemon", "mysearch", ReuseExisting, "arg1", "arg2")
assert.Nil(t, err)
assert.Equal(t, "mydaemon", proc.cmd.Args[0])
assert.Equal(t, "arg1", proc.cmd.Args[1])
assert.Equal(t, "arg2", proc.cmd.Args[2])
run := false
executor.MockExecuteCommand = func(debug bool, name string, command string, args ...string) error {
assert.Equal(t, "arg1", args[0])
assert.Equal(t, "arg2", args[1])
run = true
return nil
}
err = p.Run("mylog", "mytool", "arg1", "arg2")
assert.Nil(t, err)
assert.True(t, run)
}
+10 -21
View File
@@ -110,8 +110,7 @@ type LocalDisk struct {
// ListDevices list all devices available on a machine
func ListDevices(executor exec.Executor) ([]string, error) {
cmd := "lsblk all"
devices, err := executor.ExecuteCommandWithOutput(false, cmd, "lsblk", "--all", "--noheadings", "--list", "--output", "KNAME")
devices, err := executor.ExecuteCommandWithOutput("lsblk", "--all", "--noheadings", "--list", "--output", "KNAME")
if err != nil {
return nil, fmt.Errorf("failed to list all devices: %+v", err)
}
@@ -130,8 +129,7 @@ func GetDevicePartitions(device string, executor exec.Executor) (partitions []Pa
devicePath = device //use the exact device path (like /mnt/<pvc-name>) in case of PVC block device
}
cmd := fmt.Sprintf("lsblk %s", devicePath)
output, err := executor.ExecuteCommandWithOutput(false, cmd, "lsblk", devicePath,
output, err := executor.ExecuteCommandWithOutput("lsblk", devicePath,
"--bytes", "--pairs", "--output", "NAME,SIZE,TYPE,PKNAME")
logger.Infof("Output: %+v", output)
if err != nil {
@@ -200,8 +198,7 @@ func GetDeviceProperties(device string, executor exec.Executor) (map[string]stri
// GetDevicePropertiesFromPath gets a device property from a path
func GetDevicePropertiesFromPath(devicePath string, executor exec.Executor) (map[string]string, error) {
cmd := fmt.Sprintf("lsblk %s", devicePath)
output, err := executor.ExecuteCommandWithOutput(false, cmd, "lsblk", devicePath,
output, err := executor.ExecuteCommandWithOutput("lsblk", devicePath,
"--bytes", "--nodeps", "--pairs", "--output", "SIZE,ROTA,RO,TYPE,PKNAME,NAME")
if err != nil {
// The "not a block device" error also returns code 32 so the ExitStatus() check hides this error
@@ -210,8 +207,7 @@ func GetDevicePropertiesFromPath(devicePath string, executor exec.Executor) (map
}
// try to get more information about the command error
cmdErr, ok := err.(*exec.CommandError)
if ok && cmdErr.ExitStatus() == 32 {
if code, ok := exec.ExitStatus(err); ok && code == 32 {
// certain device types (such as loop) return exit status 32 when probed further,
// ignore and continue without logging
return map[string]string{}, nil
@@ -238,8 +234,7 @@ func IsLV(devicePath string, executor exec.Executor) (bool, error) {
// GetUdevInfo gets udev information
func GetUdevInfo(device string, executor exec.Executor) (map[string]string, error) {
cmd := fmt.Sprintf("udevadm info %s", device)
output, err := executor.ExecuteCommandWithOutput(false, cmd, "udevadm", "info", "--query=property", fmt.Sprintf("/dev/%s", device))
output, err := executor.ExecuteCommandWithOutput("udevadm", "info", "--query=property", fmt.Sprintf("/dev/%s", device))
if err != nil {
return nil, err
}
@@ -253,10 +248,9 @@ func GetDeviceFilesystems(device string, executor exec.Executor) (string, error)
if len(devicePath) == 1 {
device = fmt.Sprintf("/dev/%s", device)
}
cmd := fmt.Sprintf("get filesystem type for %s", device)
output, err := executor.ExecuteCommandWithOutput(false, cmd, "udevadm", "info", "--query=property", device)
output, err := executor.ExecuteCommandWithOutput("udevadm", "info", "--query=property", device)
if err != nil {
return "", fmt.Errorf("command %s failed: %+v", cmd, err)
return "", err
}
return parseFS(output), nil
@@ -274,10 +268,7 @@ func GetDiskUUID(device string, executor exec.Executor) (string, error) {
device = fmt.Sprintf("/dev/%s", device)
}
cmd := fmt.Sprintf("get disk %s uuid", device)
output, err := executor.ExecuteCommandWithOutput(false, cmd,
sgdisk, "--print", device)
output, err := executor.ExecuteCommandWithOutput(sgdisk, "--print", device)
if err != nil {
return "", err
}
@@ -378,7 +369,7 @@ func inventoryDevice(executor exec.Executor, dev string) (CephVolumeInventory, e
device := path.Join("/dev", dev)
args := []string{"inventory", "--format", "json", device}
inventory, err := executor.ExecuteCommandWithOutput(false, "", "ceph-volume", args...)
inventory, err := executor.ExecuteCommandWithOutput("ceph-volume", args...)
if err != nil {
return CVInventory, fmt.Errorf("failed to execute ceph-volume inventory on disk %q. %v", err, device)
}
@@ -394,9 +385,7 @@ func inventoryDevice(executor exec.Executor, dev string) (CephVolumeInventory, e
// ListDevicesChild list all child available on a device
func ListDevicesChild(executor exec.Executor, device string) ([]string, error) {
cmd := "lsblk for child"
childListRaw, err := executor.ExecuteCommandWithOutput(false, cmd, "lsblk", "--noheadings", "--pairs", path.Join("/dev", device))
childListRaw, err := executor.ExecuteCommandWithOutput("lsblk", "--noheadings", "--pairs", path.Join("/dev", device))
if err != nil {
return []string{}, fmt.Errorf("failed to list child devices of %q. %v", device, err)
}
+4 -4
View File
@@ -116,9 +116,9 @@ func TestParseFileSystem(t *testing.T) {
func TestGetPartitions(t *testing.T) {
run := 0
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, arg ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, arg ...string) (string, error) {
run++
logger.Infof("run %d action %s command %s", run, actionName, command)
logger.Infof("run %d command %s", run, command)
switch {
case run == 1:
return `NAME="sdc" SIZE="100000" TYPE="disk" PKNAME=""`, nil
@@ -185,8 +185,8 @@ func TestParseUdevInfo(t *testing.T) {
func TestListDevicesChildListDevicesChild(t *testing.T) {
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, actionName string, command string, arg ...string) (string, error) {
logger.Infof("action %s command %s", actionName, command)
MockExecuteCommandWithOutput: func(command string, arg ...string) (string, error) {
logger.Infof("command %s", command)
return lsblkChildOutput, nil
},
}
+3 -3
View File
@@ -42,7 +42,7 @@ func IsBuiltinKernelModule(name string, executor pkgexec.Executor) (bool, error)
}
kv = fmt.Sprintf("/lib/modules/%s/modules.builtin", kv)
out, err := executor.ExecuteCommandWithCombinedOutput(false, "check builtin kmod", "cat", kv)
out, err := executor.ExecuteCommandWithCombinedOutput("cat", kv)
if err != nil {
return false, fmt.Errorf("failed to cat %s: %+v", kv, err)
}
@@ -58,7 +58,7 @@ func LoadKernelModule(name string, options []string, executor pkgexec.Executor)
args := append([]string{name}, options...)
if err := executor.ExecuteCommand(false, fmt.Sprintf("modprobe %s", name), "modprobe", args[:]...); err != nil {
if err := executor.ExecuteCommand("modprobe", args[:]...); err != nil {
return fmt.Errorf("failed to load kernel module %s: %+v", name, err)
}
@@ -66,7 +66,7 @@ func LoadKernelModule(name string, options []string, executor pkgexec.Executor)
}
func CheckKernelModuleParam(name, param string, executor pkgexec.Executor) (bool, error) {
out, err := executor.ExecuteCommandWithOutput(false, "check kmod param", "modinfo", "-F", "parm", name)
out, err := executor.ExecuteCommandWithOutput("modinfo", "-F", "parm", name)
if err != nil {
return false, fmt.Errorf("failed to check for %s module %s param: %+v", name, param, err)
}
+1 -1
View File
@@ -63,7 +63,7 @@ func CreateTestClient(k8sHelper *utils.K8sHelper, manifests installer.CephManife
// Status returns rook status details
func (c TestClient) Status(namespace string) (client.CephStatus, error) {
context := c.k8sh.MakeContext()
status, err := client.Status(context, namespace, false)
status, err := client.Status(context, namespace)
if err != nil {
return client.CephStatus{}, fmt.Errorf("failed to get status: %+v", err)
}
+8 -12
View File
@@ -20,12 +20,11 @@ import (
"bufio"
"bytes"
"fmt"
"log"
"os/exec"
"strings"
"syscall"
"github.com/coreos/pkg/capnslog"
utilexec "github.com/rook/rook/pkg/util/exec"
)
var logger = capnslog.NewPackageLogger("github.com/rook/rook", "testutil")
@@ -111,17 +110,14 @@ func ExecuteCommand(cmdStruct CommandArgs) CommandOut {
stdin.Close()
}
if err := cmd.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
// The program has exited with an exit code != 0
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
log.Printf("Exit Status: %d", status.ExitStatus())
return CommandOut{StdErr: errBuffer.String(), StdOut: outBuffer.String(), ExitCode: status.ExitStatus(), Err: exiterr}
}
} else {
return CommandOut{StdErr: errBuffer.String(), StdOut: outBuffer.String(), Err: nil}
err = cmd.Wait()
out := CommandOut{StdErr: errBuffer.String(), StdOut: outBuffer.String()}
if err != nil {
out.Err = err
if code, ok := utilexec.ExitStatus(err); ok {
out.ExitCode = code
}
}
return CommandOut{StdErr: errBuffer.String(), StdOut: outBuffer.String(), Err: err}
return out
}
+1 -1
View File
@@ -39,7 +39,7 @@ func NewHelmHelper(helmPath string) *HelmHelper {
// Execute is wrapper for executing helm commands
func (h *HelmHelper) Execute(args ...string) (string, error) {
result, err := h.executor.ExecuteCommandWithOutput(false, "", h.HelmPath, args...)
result, err := h.executor.ExecuteCommandWithOutput(h.HelmPath, args...)
if err != nil {
logger.Errorf("Errors Encountered while executing helm command %v: %v", result, err)
return result, fmt.Errorf("Failed to run helm commands on args %v : %v , err -> %v", args, result, err)
+4 -4
View File
@@ -117,7 +117,7 @@ func (k8sh *K8sHelper) GetDockerImage(image string) error {
if dockercmd == "" {
dockercmd = "docker"
}
return k8sh.executor.ExecuteCommand(false, "", dockercmd, "pull", image)
return k8sh.executor.ExecuteCommand(dockercmd, "pull", image)
}
// SetDeploymentVersion sets the container version on the deployment. It is assumed to be the rook/ceph image.
@@ -128,7 +128,7 @@ func (k8sh *K8sHelper) SetDeploymentVersion(namespace, deploymentName, container
// Kubectl is wrapper for executing kubectl commands
func (k8sh *K8sHelper) Kubectl(args ...string) (string, error) {
result, err := k8sh.executor.ExecuteCommandWithTimeout(false, 15*time.Second, "kubectl", "kubectl", args...)
result, err := k8sh.executor.ExecuteCommandWithTimeout(15*time.Second, "kubectl", args...)
if err != nil {
k8slogger.Errorf("Failed to execute: kubectl %+v : %+v. %s", args, err, result)
if args[0] == "delete" {
@@ -162,9 +162,9 @@ func (k8sh *K8sHelper) KubectlWithStdin(stdin string, args ...string) (string, e
}
func getKubeConfig(executor exec.Executor) (*rest.Config, error) {
context, err := executor.ExecuteCommandWithOutput(false, "", "kubectl", "config", "view", "-o", "json")
context, err := executor.ExecuteCommandWithOutput("kubectl", "config", "view", "-o", "json")
if err != nil {
k8slogger.Errorf("Errors Encountered while executing kubectl command : %v", err)
k8slogger.Errorf("failed to execute kubectl command. %v", err)
}
// Parse the kubectl context to get the settings for client connections
+3 -3
View File
@@ -96,7 +96,7 @@ var rawContext = `{
func TestLoadMinikubeContext(t *testing.T) {
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, command string, subcommand string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, subcommand string, args ...string) (string, error) {
return fmt.Sprintf(rawContext, "minikube"), nil
}}
config, err := getKubeConfig(executor)
@@ -111,7 +111,7 @@ func TestLoadMinikubeContext(t *testing.T) {
func TestLoadDindContext(t *testing.T) {
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, command string, subcommand string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, subcommand string, args ...string) (string, error) {
return fmt.Sprintf(rawContext, "dind"), nil
}}
config, err := getKubeConfig(executor)
@@ -126,7 +126,7 @@ func TestLoadDindContext(t *testing.T) {
func TestLoadVagrantContext(t *testing.T) {
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(debug bool, command string, subcommand string, args ...string) (string, error) {
MockExecuteCommandWithOutput: func(command string, subcommand string, args ...string) (string, error) {
return fmt.Sprintf(rawContext, "vagrant-single"), nil
}}
config, err := getKubeConfig(executor)