|
| 1 | +package cli |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "errors" |
| 6 | + "fmt" |
| 7 | + "os" |
| 8 | + "os/exec" |
| 9 | + "strings" |
| 10 | + "time" |
| 11 | + |
| 12 | + "github.com/go-logr/logr" |
| 13 | + |
| 14 | + "github.com/openshift/cluster-version-operator/test/oc/api" |
| 15 | +) |
| 16 | + |
| 17 | +type client struct { |
| 18 | + logger logr.Logger |
| 19 | + executor executor |
| 20 | +} |
| 21 | + |
| 22 | +type executor interface { |
| 23 | + Run(args ...string) ([]byte, error) |
| 24 | +} |
| 25 | + |
| 26 | +type ocExecutor struct { |
| 27 | + // logger is used to log oc commands |
| 28 | + logger logr.Logger |
| 29 | + // oc is the path to the oc binary |
| 30 | + oc string |
| 31 | + // execute executes a command |
| 32 | + execute func(dir, command string, args ...string) ([]byte, error) |
| 33 | +} |
| 34 | + |
| 35 | +func (e *ocExecutor) Run(args ...string) ([]byte, error) { |
| 36 | + logger := e.logger.WithValues("cmd", e.oc, "args", strings.Join(args, " ")) |
| 37 | + b, err := e.execute("", e.oc, args...) |
| 38 | + if err != nil { |
| 39 | + logger.Error(err, "Running command failed", "output", string(b)) |
| 40 | + } else { |
| 41 | + logger.Info("Running command succeeded.") |
| 42 | + } |
| 43 | + return b, err |
| 44 | +} |
| 45 | + |
| 46 | +func newOCExecutor(oc string, timeout time.Duration, logger logr.Logger) (executor, error) { |
| 47 | + return &ocExecutor{ |
| 48 | + logger: logger, |
| 49 | + oc: oc, |
| 50 | + execute: func(dir, command string, args ...string) ([]byte, error) { |
| 51 | + ctx, cancel := context.WithTimeout(context.Background(), timeout) |
| 52 | + defer cancel() |
| 53 | + c := exec.CommandContext(ctx, command, args...) |
| 54 | + c.Dir = dir |
| 55 | + o, err := c.CombinedOutput() |
| 56 | + if errors.Is(ctx.Err(), context.DeadlineExceeded) { |
| 57 | + return o, fmt.Errorf("execution timed out after %s: %w", timeout.String(), ctx.Err()) |
| 58 | + } |
| 59 | + |
| 60 | + return o, err |
| 61 | + }, |
| 62 | + }, nil |
| 63 | +} |
| 64 | + |
| 65 | +// NewOCCli return a client for oc-cli. |
| 66 | +func NewOCCli(logger logr.Logger) (api.OC, error) { |
| 67 | + oc, err := exec.LookPath("oc") |
| 68 | + if err != nil { |
| 69 | + return nil, err |
| 70 | + } |
| 71 | + timeout := 30 * time.Second |
| 72 | + timeoutStr := os.Getenv("OC_CLI_TIMEOUT") |
| 73 | + if timeoutStr != "" { |
| 74 | + timeout, err = time.ParseDuration(timeoutStr) |
| 75 | + if err != nil { |
| 76 | + return nil, err |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + executor, err := newOCExecutor(oc, timeout, logger) |
| 81 | + if err != nil { |
| 82 | + return nil, err |
| 83 | + } |
| 84 | + ret := client{ |
| 85 | + logger: logger, |
| 86 | + executor: executor, |
| 87 | + } |
| 88 | + return &ret, nil |
| 89 | +} |
| 90 | + |
| 91 | +func (c *client) AdmReleaseExtract(o api.ReleaseExtractOptions) error { |
| 92 | + args := []string{"adm", "release", "extract", fmt.Sprintf("--to=%s", o.To)} |
| 93 | + _, err := c.executor.Run(args...) |
| 94 | + if err != nil { |
| 95 | + return err |
| 96 | + } |
| 97 | + return nil |
| 98 | +} |
| 99 | + |
| 100 | +func (c *client) Version(o api.VersionOptions) (string, error) { |
| 101 | + args := []string{"version", fmt.Sprintf("--client=%t", o.Client)} |
| 102 | + output, err := c.executor.Run(args...) |
| 103 | + if err != nil { |
| 104 | + return "", err |
| 105 | + } |
| 106 | + return string(output), nil |
| 107 | +} |
0 commit comments