oracle/controllers/exec.go (64 lines of code) (raw):
// Copyright 2021 Google LLC
//
// 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 controllers
import (
"bytes"
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/client-go/util/retry"
log "k8s.io/klog/v2"
)
// ExecCmdParams stores parameters for invoking pod/exec.
type ExecCmdParams struct {
Pod string
Ns string
Con *corev1.Container
Sch *runtime.Scheme
RestConfig *rest.Config
Client kubernetes.Interface
}
// ExecCmdFunc invokes pod/exec.
var ExecCmdFunc = func(p ExecCmdParams, cmd string) (string, error) {
var cmdOut, cmdErr bytes.Buffer
cmdShell := []string{"sh", "-c", cmd}
req := p.Client.CoreV1().RESTClient().Post().Resource("pods").Name(p.Pod).
Namespace(p.Ns).SubResource("exec")
req.VersionedParams(&corev1.PodExecOptions{
Container: p.Con.Name,
Command: cmdShell,
Stdout: true,
Stderr: true,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(p.RestConfig, "POST", req.URL())
if err != nil {
return "", fmt.Errorf("failed to init executor: %v", err)
}
// exec.Stream might return timout error, use a backoff with 4 retries
// 100ms, 500ms, 2.5s, 12.5s
var backoff = wait.Backoff{
Steps: 4,
Duration: 100 * time.Millisecond,
Factor: 5.0,
Jitter: 0.1,
}
if err := retry.OnError(backoff, func(error) bool { return true }, func() error {
e := exec.Stream(remotecommand.StreamOptions{
Stdout: &cmdOut,
Stderr: &cmdErr,
Tty: false,
})
if e != nil {
log.Error(fmt.Sprintf("exec.Stream failed, retrying, err: %v, stderr: %v, stdout: %v",
e, cmdErr.String(), cmdOut.String()))
}
return e
}); err != nil {
return "", fmt.Errorf("failed to run a command [%v], err: %v, stderr: %v, stdout: %v",
cmd, err, cmdErr.String(), cmdOut.String())
}
if cmdErr.Len() > 0 {
return "", fmt.Errorf("stderr: %v", cmdErr.String())
}
return cmdOut.String(), nil
}