mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-21 18:37:45 +08:00
Merge pull request #2822 from ipfs/feature/stdin-stable
Change logic of stdin input to be last and only if required
This commit is contained in:
commit
e5e2f2c19c
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -33,7 +34,9 @@ environment variable:
|
||||
export IPFS_PATH=/path/to/ipfsrepo
|
||||
`,
|
||||
},
|
||||
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.FileArg("default-config", false, false, "Initialize with the given configuration.").EnableStdin(),
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
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),
|
||||
@ -76,7 +79,24 @@ environment variable:
|
||||
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)
|
||||
return
|
||||
}
|
||||
@ -88,10 +108,10 @@ Reinitializing would overwrite your keys.
|
||||
`)
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -104,9 +124,12 @@ func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int) err
|
||||
return errRepoExists
|
||||
}
|
||||
|
||||
conf, err := config.Init(out, nBitsForKeypair)
|
||||
if err != nil {
|
||||
return err
|
||||
if conf == nil {
|
||||
var err error
|
||||
conf, err = config.Init(out, nBitsForKeypair)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := fsrepo.Init(repoRoot, conf); err != nil {
|
||||
|
||||
@ -244,15 +244,6 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
|
||||
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
|
||||
numRequired := 0
|
||||
for _, argDef := range argDefs {
|
||||
@ -293,9 +284,18 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
|
||||
numRequired--
|
||||
}
|
||||
|
||||
fillingVariadic := argDefIndex+1 > len(argDefs)
|
||||
|
||||
var err error
|
||||
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
|
||||
stringArgs, inputs = appendString(stringArgs, inputs)
|
||||
} else if !argDef.SupportsStdin {
|
||||
@ -307,35 +307,39 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
|
||||
|
||||
stringArgs, inputs = appendString(stringArgs, inputs)
|
||||
} else {
|
||||
if len(inputs) > 0 {
|
||||
// don't use stdin if we have inputs
|
||||
stdin = nil
|
||||
} else {
|
||||
if stdin != nil && argDef.Required && !fillingVariadic {
|
||||
// if we have a stdin, read it in and use the data as a string value
|
||||
stringArgs, stdin, err = appendStdinAsString(stringArgs, stdin)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if argDef.Type == cmds.ArgFile {
|
||||
if stdin == nil || !argDef.SupportsStdin {
|
||||
if len(inputs) > 0 {
|
||||
// treat stringArg values as file paths
|
||||
fpath := inputs[0]
|
||||
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 {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
fileArgs[fpath] = file
|
||||
} else {
|
||||
if len(inputs) > 0 {
|
||||
// don't use stdin if we have inputs
|
||||
stdin = nil
|
||||
} else {
|
||||
// if we have a stdin, create a file from it
|
||||
if stdin != nil && argDef.SupportsStdin &&
|
||||
argDef.Required && !fillingVariadic {
|
||||
fileArgs[""] = files.NewReaderFile("", "", stdin, nil)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -431,16 +435,3 @@ func appendFile(fpath string, argDef *cmds.Argument, recursive, hidden bool) (fi
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
1
test/bin/.gitignore
vendored
1
test/bin/.gitignore
vendored
@ -8,3 +8,4 @@
|
||||
!checkflags
|
||||
!continueyn
|
||||
!verify-go-fmt.sh
|
||||
!time-out
|
||||
|
||||
83
test/bin/time-out
Executable file
83
test/bin/time-out
Executable file
@ -0,0 +1,83 @@
|
||||
#!/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 "$@"
|
||||
55
test/sharness/t0022-init-default.sh
Executable file
55
test/sharness/t0022-init-default.sh
Executable file
@ -0,0 +1,55 @@
|
||||
#!/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
|
||||
|
||||
# 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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user