blob: 461680400bba2b7d87433f0787de71e72681cf36 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 command
import (
"bytes"
"context"
"io"
)
import (
"github.com/apache/dubbo-kubernetes/pkg/bufman/pkg/app"
)
// Process represents a background process.
type Process interface {
// Wait blocks to wait for the process to exit. It will attempt to kill the
// process if the passed context expires.
Wait(ctx context.Context) error
}
// Runner runs external commands.
//
// A Runner will limit the number of concurrent commands, as well as explicitly
// set stdin, stdout, stderr, and env to nil/empty values if not set with options.
//
// All external commands in buf MUST use command.Runner instead of
// exec.Command, exec.CommandContext.
type Runner interface {
// Run runs the external command. It blocks until the command exits.
//
// This should be used instead of exec.CommandContext(...).Run().
Run(ctx context.Context, name string, options ...RunOption) error
// Start runs the external command, returning a [Process] handle to track
// its progress.
//
// This should be used instead of exec.Command(...).Start().
Start(name string, options ...StartOption) (Process, error)
}
// RunOption is an option for Run.
type RunOption func(*execOptions)
// RunWithArgs returns a new RunOption that sets the arguments other
// than the name.
//
// The default is no arguments.
func RunWithArgs(args ...string) RunOption {
return func(execOptions *execOptions) {
execOptions.args = args
}
}
// RunWithEnv returns a new RunOption that sets the environment variables.
//
// The default is to use the single environment variable __EMPTY_ENV__=1 as we
// cannot explicitly set an empty environment with the exec package.
func RunWithEnv(env map[string]string) RunOption {
return func(execOptions *execOptions) {
execOptions.env = env
}
}
// RunWithStdin returns a new RunOption that sets the stdin.
//
// The default is ioextended.DiscardReader.
func RunWithStdin(stdin io.Reader) RunOption {
return func(execOptions *execOptions) {
execOptions.stdin = stdin
}
}
// RunWithStdout returns a new RunOption that sets the stdout.
//
// The default is the null device (os.DevNull).
func RunWithStdout(stdout io.Writer) RunOption {
return func(execOptions *execOptions) {
execOptions.stdout = stdout
}
}
// RunWithStderr returns a new RunOption that sets the stderr.
//
// The default is the null device (os.DevNull).
func RunWithStderr(stderr io.Writer) RunOption {
return func(execOptions *execOptions) {
execOptions.stderr = stderr
}
}
// RunWithDir returns a new RunOption that sets the working directory.
//
// The default is the current working directory.
func RunWithDir(dir string) RunOption {
return func(execOptions *execOptions) {
execOptions.dir = dir
}
}
// StartOption is an option for Start.
type StartOption func(*execOptions)
// StartWithArgs returns a new RunOption that sets the arguments other
// than the name.
//
// The default is no arguments.
func StartWithArgs(args ...string) StartOption {
return func(execOptions *execOptions) {
execOptions.args = args
}
}
// StartWithEnv returns a new RunOption that sets the environment variables.
//
// The default is to use the single environment variable __EMPTY_ENV__=1 as we
// cannot explicitly set an empty environment with the exec package.
func StartWithEnv(env map[string]string) StartOption {
return func(execOptions *execOptions) {
execOptions.env = env
}
}
// StartWithStdin returns a new RunOption that sets the stdin.
//
// The default is ioextended.DiscardReader.
func StartWithStdin(stdin io.Reader) StartOption {
return func(execOptions *execOptions) {
execOptions.stdin = stdin
}
}
// StartWithStdout returns a new RunOption that sets the stdout.
//
// The default is the null device (os.DevNull).
func StartWithStdout(stdout io.Writer) StartOption {
return func(execOptions *execOptions) {
execOptions.stdout = stdout
}
}
// StartWithStderr returns a new RunOption that sets the stderr.
//
// The default is the null device (os.DevNull).
func StartWithStderr(stderr io.Writer) StartOption {
return func(execOptions *execOptions) {
execOptions.stderr = stderr
}
}
// StartWithDir returns a new RunOption that sets the working directory.
//
// The default is the current working directory.
func StartWithDir(dir string) StartOption {
return func(execOptions *execOptions) {
execOptions.dir = dir
}
}
// NewRunner returns a new Runner.
func NewRunner(options ...RunnerOption) Runner {
return newRunner(options...)
}
// RunnerOption is an option for a new Runner.
type RunnerOption func(*runner)
// RunnerWithParallelism returns a new Runner that sets the number of
// external commands that can be run concurrently.
//
// The default is thread.Parallelism().
func RunnerWithParallelism(parallelism int) RunnerOption {
if parallelism < 1 {
parallelism = 1
}
return func(runner *runner) {
runner.parallelism = parallelism
}
}
// RunStdout is a convenience function that attaches the container environment,
// stdin, and stderr, and returns the stdout as a byte slice.
func RunStdout(
ctx context.Context,
container app.EnvStdioContainer,
runner Runner,
name string,
args ...string,
) ([]byte, error) {
buffer := bytes.NewBuffer(nil)
if err := runner.Run(
ctx,
name,
RunWithArgs(args...),
RunWithEnv(app.EnvironMap(container)),
RunWithStdin(container.Stdin()),
RunWithStdout(buffer),
RunWithStderr(container.Stderr()),
); err != nil {
return nil, err
}
return buffer.Bytes(), nil
}