Commit e5e2f2c1 authored by Jeromy Johnson's avatar Jeromy Johnson Committed by GitHub

Merge pull request #2822 from ipfs/feature/stdin-stable

Change logic of stdin input to be last and only if required
parents 9dbce4fa 628e458e
package main package main
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
...@@ -33,7 +34,9 @@ environment variable: ...@@ -33,7 +34,9 @@ environment variable:
export IPFS_PATH=/path/to/ipfsrepo export IPFS_PATH=/path/to/ipfsrepo
`, `,
}, },
Arguments: []cmds.Argument{
cmds.FileArg("default-config", false, false, "Initialize with the given configuration.").EnableStdin(),
},
Options: []cmds.Option{ Options: []cmds.Option{
cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key.").Default(nBitsForKeypairDefault), cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key.").Default(nBitsForKeypairDefault),
cmds.BoolOption("empty-repo", "e", "Don't add and pin help files to the local storage.").Default(false), cmds.BoolOption("empty-repo", "e", "Don't add and pin help files to the local storage.").Default(false),
...@@ -76,7 +79,24 @@ environment variable: ...@@ -76,7 +79,24 @@ environment variable:
return return
} }
if err := doInit(os.Stdout, req.InvocContext().ConfigRoot, empty, nBitsForKeypair); err != nil { var conf *config.Config
f := req.Files()
if f != nil {
confFile, err := f.NextFile()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
conf = &config.Config{}
if err := json.NewDecoder(confFile).Decode(conf); err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
}
if err := doInit(os.Stdout, req.InvocContext().ConfigRoot, empty, nBitsForKeypair, conf); err != nil {
res.SetError(err, cmds.ErrNormal) res.SetError(err, cmds.ErrNormal)
return return
} }
...@@ -88,10 +108,10 @@ Reinitializing would overwrite your keys. ...@@ -88,10 +108,10 @@ Reinitializing would overwrite your keys.
`) `)
func initWithDefaults(out io.Writer, repoRoot string) error { func initWithDefaults(out io.Writer, repoRoot string) error {
return doInit(out, repoRoot, false, nBitsForKeypairDefault) return doInit(out, repoRoot, false, nBitsForKeypairDefault, nil)
} }
func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int) error { func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, conf *config.Config) error {
if _, err := fmt.Fprintf(out, "initializing ipfs node at %s\n", repoRoot); err != nil { if _, err := fmt.Fprintf(out, "initializing ipfs node at %s\n", repoRoot); err != nil {
return err return err
} }
...@@ -104,9 +124,12 @@ func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int) err ...@@ -104,9 +124,12 @@ func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int) err
return errRepoExists return errRepoExists
} }
conf, err := config.Init(out, nBitsForKeypair) if conf == nil {
if err != nil { var err error
return err conf, err = config.Init(out, nBitsForKeypair)
if err != nil {
return err
}
} }
if err := fsrepo.Init(repoRoot, conf); err != nil { if err := fsrepo.Init(repoRoot, conf); err != nil {
......
...@@ -244,15 +244,6 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi ...@@ -244,15 +244,6 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
stdin = nil stdin = nil
} }
// check if stdin is coming from terminal or is being piped in
if stdin != nil {
if term, err := isTerminal(stdin); err != nil {
return nil, nil, err
} else if term {
stdin = nil // set to nil so we ignore it
}
}
// count required argument definitions // count required argument definitions
numRequired := 0 numRequired := 0
for _, argDef := range argDefs { for _, argDef := range argDefs {
...@@ -293,9 +284,18 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi ...@@ -293,9 +284,18 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
numRequired-- numRequired--
} }
fillingVariadic := argDefIndex+1 > len(argDefs)
var err error var err error
if argDef.Type == cmds.ArgString { if argDef.Type == cmds.ArgString {
if stdin == nil { if len(inputs) > 0 {
// If argument is "-" use stdin
if inputs[0] == "-" && argDef.SupportsStdin {
stringArgs, stdin, err = appendStdinAsString(stringArgs, stdin)
if err != nil {
return nil, nil, err
}
}
// add string values // add string values
stringArgs, inputs = appendString(stringArgs, inputs) stringArgs, inputs = appendString(stringArgs, inputs)
} else if !argDef.SupportsStdin { } else if !argDef.SupportsStdin {
...@@ -307,35 +307,39 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi ...@@ -307,35 +307,39 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
stringArgs, inputs = appendString(stringArgs, inputs) stringArgs, inputs = appendString(stringArgs, inputs)
} else { } else {
if len(inputs) > 0 { if stdin != nil && argDef.Required && !fillingVariadic {
// don't use stdin if we have inputs
stdin = nil
} else {
// if we have a stdin, read it in and use the data as a string value // if we have a stdin, read it in and use the data as a string value
stringArgs, stdin, err = appendStdinAsString(stringArgs, stdin) stringArgs, stdin, err = appendStdinAsString(stringArgs, stdin)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
} else {
break
} }
} }
} else if argDef.Type == cmds.ArgFile { } else if argDef.Type == cmds.ArgFile {
if stdin == nil || !argDef.SupportsStdin { if len(inputs) > 0 {
// treat stringArg values as file paths // treat stringArg values as file paths
fpath := inputs[0] fpath := inputs[0]
inputs = inputs[1:] inputs = inputs[1:]
file, err := appendFile(fpath, argDef, recursive, hidden) var file files.File
var err error
if fpath == "-" {
file = files.NewReaderFile("", "", stdin, nil)
} else {
file, err = appendFile(fpath, argDef, recursive, hidden)
}
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
fileArgs[fpath] = file fileArgs[fpath] = file
} else { } else {
if len(inputs) > 0 { if stdin != nil && argDef.SupportsStdin &&
// don't use stdin if we have inputs argDef.Required && !fillingVariadic {
stdin = nil
} else {
// if we have a stdin, create a file from it
fileArgs[""] = files.NewReaderFile("", "", stdin, nil) fileArgs[""] = files.NewReaderFile("", "", stdin, nil)
} else {
break
} }
} }
} }
...@@ -431,16 +435,3 @@ func appendFile(fpath string, argDef *cmds.Argument, recursive, hidden bool) (fi ...@@ -431,16 +435,3 @@ func appendFile(fpath string, argDef *cmds.Argument, recursive, hidden bool) (fi
return files.NewSerialFile(path.Base(fpath), fpath, hidden, stat) return files.NewSerialFile(path.Base(fpath), fpath, hidden, stat)
} }
// isTerminal returns true if stdin is a Stdin pipe (e.g. `cat file | ipfs`),
// and false otherwise (e.g. nothing is being piped in, so stdin is
// coming from the terminal)
func isTerminal(stdin *os.File) (bool, error) {
stat, err := stdin.Stat()
if err != nil {
return false, err
}
// if stdin is a CharDevice, return true
return ((stat.Mode() & os.ModeCharDevice) != 0), nil
}
...@@ -8,3 +8,4 @@ ...@@ -8,3 +8,4 @@
!checkflags !checkflags
!continueyn !continueyn
!verify-go-fmt.sh !verify-go-fmt.sh
!time-out
#!/bin/bash
#
# The Bash shell script executes a command with a time-out.
# Upon time-out expiration SIGTERM (15) is sent to the process. If the signal
# is blocked, then the subsequent SIGKILL (9) terminates it.
#
# Based on the Bash documentation example.
scriptName="${0##*/}"
declare -i DEFAULT_TIMEOUT=9
declare -i DEFAULT_INTERVAL=1
declare -i DEFAULT_DELAY=1
# Timeout.
declare -i timeout=DEFAULT_TIMEOUT
# Interval between checks if the process is still alive.
declare -i interval=DEFAULT_INTERVAL
# Delay between posting the SIGTERM signal and destroying the process by SIGKILL.
declare -i delay=DEFAULT_DELAY
function printUsage() {
cat <<EOF
Synopsis
$scriptName [-t timeout] [-i interval] [-d delay] command
Execute a command with a time-out.
Upon time-out expiration SIGTERM (15) is sent to the process. If SIGTERM
signal is blocked, then the subsequent SIGKILL (9) terminates it.
-t timeout
Number of seconds to wait for command completion.
Default value: $DEFAULT_TIMEOUT seconds.
-i interval
Interval between checks if the process is still alive.
Positive integer, default value: $DEFAULT_INTERVAL seconds.
-d delay
Delay between posting the SIGTERM signal and destroying the
process by SIGKILL. Default value: $DEFAULT_DELAY seconds.
As of today, Bash does not support floating point arithmetic (sleep does),
therefore all delay/time values must be integers.
EOF
}
# Options.
while getopts ":t:i:d:" option; do
case "$option" in
t) timeout=$OPTARG ;;
i) interval=$OPTARG ;;
d) delay=$OPTARG ;;
*) printUsage; exit 1 ;;
esac
done
shift $((OPTIND - 1))
# $# should be at least 1 (the command to execute), however it may be strictly
# greater than 1 if the command itself has options.
if (($# == 0 || interval <= 0)); then
printUsage
exit 1
fi
# kill -0 pid Exit code indicates if a signal may be sent to $pid process.
(
((t = timeout))
while ((t > 0)); do
sleep $interval
kill -0 $$ || exit 0
((t -= interval))
done
# Be nice, post SIGTERM first.
# The 'exit 0' below will be executed if any preceeding command fails.
kill -s SIGTERM $$ && kill -0 $$ || exit 0
sleep $delay
kill -s SIGKILL $$
) 2> /dev/null &
exec "$@"
#!/bin/sh
#
# Copyright (c) 2014 Christian Couder
# MIT Licensed; see the LICENSE file in this repository.
#
test_description="Test init command with default config"
. lib/test-lib.sh
cfg_key="Addresses.API"
cfg_val="/ip4/0.0.0.0/tcp/5001"
# test that init succeeds
test_expect_success "ipfs init succeeds" '
export IPFS_PATH="$(pwd)/.ipfs" &&
echo "IPFS_PATH: \"$IPFS_PATH\"" &&
BITS="2048" &&
ipfs init --bits="$BITS" >actual_init ||
test_fsh cat actual_init
'
test_expect_success ".ipfs/config has been created" '
test -f "$IPFS_PATH"/config ||
test_fsh ls -al .ipfs
'
test_expect_success "ipfs config succeeds" '
ipfs config $cfg_flags "$cfg_key" "$cfg_val"
'
test_expect_success "ipfs read config succeeds" '
IPFS_DEFAULT_CONFIG=$(cat "$IPFS_PATH"/config)
'
test_expect_success "clean up ipfs dir" '
rm -rf "$IPFS_PATH"
'
test_expect_success "ipfs init default config succeeds" '
echo $IPFS_DEFAULT_CONFIG | ipfs init - >actual_init ||
test_fsh cat actual_init
'
test_expect_success "ipfs config output looks good" '
echo "$cfg_val" >expected &&
ipfs config "$cfg_key" >actual &&
test_cmp expected actual
'
test_launch_ipfs_daemon
test_kill_ipfs_daemon
test_done
...@@ -8,4 +8,10 @@ test_init_ipfs ...@@ -8,4 +8,10 @@ test_init_ipfs
# Tests go here # Tests go here
test_expect_success "ipfs init with occupied input works - #2748" '
export IPFS_PATH="ipfs_path"
echo "" | time-out ipfs init &&
rm -rf ipfs_path
'
test_done test_done
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment