github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/testutil/exec.go (about)

     1  /*
     2   * Copyright 2019 Dgraph Labs, Inc. and Contributors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package testutil
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"os/exec"
    24  	"path"
    25  
    26  	"github.com/dgraph-io/dgraph/x"
    27  )
    28  
    29  // These are exported so they can also be set directly from outside this package.
    30  var (
    31  	ShowOutput  = os.Getenv("DEBUG_SHOW_OUTPUT") != ""
    32  	ShowError   = os.Getenv("DEBUG_SHOW_ERROR") != ""
    33  	ShowCommand = os.Getenv("DEBUG_SHOW_COMMAND") != ""
    34  )
    35  
    36  // CmdOpts sets the options to run a single command.
    37  type CmdOpts struct {
    38  	Dir string
    39  }
    40  
    41  // RepoPath converts a path relative to the root of the repo into an absolute path.
    42  func RepoPath(p string) string {
    43  	return path.Join(os.ExpandEnv("$GOPATH/src/github.com/dgraph-io/dgraph"), p)
    44  }
    45  
    46  // Exec runs a single external command.
    47  func Exec(argv ...string) error {
    48  	return Pipeline([][]string{argv})
    49  }
    50  
    51  // ExecWithOpts runs a single external command with the given options.
    52  func ExecWithOpts(argv []string, opts CmdOpts) error {
    53  	return pipelineInternal([][]string{argv}, []CmdOpts{opts})
    54  }
    55  
    56  // Pipeline runs several commands such that the output of one command becomes the input of the next.
    57  // The first argument should be an two-dimensional array containing the commands.
    58  // TODO: allow capturing output, sending to terminal, etc
    59  func Pipeline(cmds [][]string) error {
    60  	return pipelineInternal(cmds, nil)
    61  }
    62  
    63  // piplineInternal takes a list of commands and a list of options (one for each).
    64  // If opts is nil, all commands should be run with the default options.
    65  func pipelineInternal(cmds [][]string, opts []CmdOpts) error {
    66  	x.AssertTrue(opts == nil || len(cmds) == len(opts))
    67  
    68  	var p io.ReadCloser
    69  	var numCmds = len(cmds)
    70  
    71  	cmd := make([]*exec.Cmd, numCmds)
    72  
    73  	// Run all commands in parallel, connecting stdin of each to the stdout of the previous.
    74  	for i, c := range cmds {
    75  		lastCmd := i == numCmds-1
    76  		if ShowCommand {
    77  			fmt.Fprintf(os.Stderr, "%+v", c)
    78  		}
    79  
    80  		cmd[i] = exec.Command(c[0], c[1:]...)
    81  		cmd[i].Stdin = p
    82  
    83  		if opts != nil {
    84  			cmd[i].Dir = opts[i].Dir
    85  		}
    86  
    87  		if !lastCmd {
    88  			p, _ = cmd[i].StdoutPipe()
    89  		}
    90  
    91  		if ShowOutput {
    92  			cmd[i].Stderr = os.Stderr
    93  			if lastCmd {
    94  				cmd[i].Stdout = os.Stdout
    95  			}
    96  		} else if ShowError {
    97  			cmd[i].Stderr = os.Stderr
    98  		}
    99  
   100  		if ShowCommand {
   101  			if lastCmd {
   102  				fmt.Fprintf(os.Stderr, "\n")
   103  			} else {
   104  				fmt.Fprintf(os.Stderr, "\n| ")
   105  			}
   106  		}
   107  
   108  		err := cmd[i].Start()
   109  		x.Check(err)
   110  	}
   111  
   112  	// Make sure to properly reap all spawned processes, but only save the error from the
   113  	// earliest stage of the pipeline.
   114  	var err error
   115  	for i := range cmds {
   116  		e := cmd[i].Wait()
   117  		if e != nil && err == nil {
   118  			err = e
   119  		}
   120  	}
   121  
   122  	return err
   123  }