func appendError(errors []error, err error) []error {
	if err != nil {
		return append(errors, err)
	}
	return errors
}
appendError conveniently checks for nil before appending slice
package processcreds
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/internal/sdkio"
)
const (
	ProviderName = `ProcessProvider`
	ErrCodeProcessProviderParse = "ProcessProviderParseError"
	ErrCodeProcessProviderVersion = "ProcessProviderVersionError"
	ErrCodeProcessProviderRequired = "ProcessProviderRequiredError"
	ErrCodeProcessProviderExecution = "ProcessProviderExecutionError"
	errMsgProcessProviderTimeout = "credential process timed out"
	errMsgProcessProviderProcess = "error in credential_process"
	errMsgProcessProviderParse = "parse failed of credential_process output"
	errMsgProcessProviderVersion = "wrong version in process output (not 1)"
	errMsgProcessProviderMissKey = "missing AccessKeyId in process output"
	errMsgProcessProviderMissSecret = "missing SecretAccessKey in process output"
	errMsgProcessProviderPrepareCmd = "failed to prepare command"
	errMsgProcessProviderEmptyCmd = "command must not be empty"
	errMsgProcessProviderPipe = "failed to initialize pipe"
	DefaultDuration = time.Duration(15) * time.Minute
	DefaultBufSize = int(8 * sdkio.KibiByte)
	DefaultTimeout = time.Duration(1) * time.Minute
)
type ProcessProvider struct {
	staticCreds bool
	credentials.Expiry
	originalCommand []string
	Duration time.Duration
	ExpiryWindow time.Duration
	command *exec.Cmd
	MaxBufSize int
	Timeout time.Duration
}
func NewCredentials(command string, options ...func(*ProcessProvider)) *credentials.Credentials {
	p := &ProcessProvider{
		command:    exec.Command(command),
		Duration:   DefaultDuration,
		Timeout:    DefaultTimeout,
		MaxBufSize: DefaultBufSize,
	}
	for _, option := range options {
		option(p)
	}
	return credentials.NewCredentials(p)
}
func NewCredentialsTimeout(command string, timeout time.Duration) *credentials.Credentials {
	p := NewCredentials(command, func(opt *ProcessProvider) {
		opt.Timeout = timeout
	})
	return p
}
func NewCredentialsCommand(command *exec.Cmd, options ...func(*ProcessProvider)) *credentials.Credentials {
	p := &ProcessProvider{
		command:    command,
		Duration:   DefaultDuration,
		Timeout:    DefaultTimeout,
		MaxBufSize: DefaultBufSize,
	}
	for _, option := range options {
		option(p)
	}
	return credentials.NewCredentials(p)
}
type credentialProcessResponse struct {
	Version         int
	AccessKeyID     string `json:"AccessKeyId"`
	SecretAccessKey string
	SessionToken    string
	Expiration      *time.Time
}
func (p *ProcessProvider) Retrieve() (credentials.Value, error) {
	out, err := p.executeCredentialProcess()
	if err != nil {
		return credentials.Value{ProviderName: ProviderName}, err
	}
	resp := &credentialProcessResponse{}
	if err = json.Unmarshal(out, resp); err != nil {
		return credentials.Value{ProviderName: ProviderName}, awserr.New(
			ErrCodeProcessProviderParse,
			fmt.Sprintf("%s: %s", errMsgProcessProviderParse, string(out)),
			err)
	}
	if resp.Version != 1 {
		return credentials.Value{ProviderName: ProviderName}, awserr.New(
			ErrCodeProcessProviderVersion,
			errMsgProcessProviderVersion,
			nil)
	}
	if len(resp.AccessKeyID) == 0 {
		return credentials.Value{ProviderName: ProviderName}, awserr.New(
			ErrCodeProcessProviderRequired,
			errMsgProcessProviderMissKey,
			nil)
	}
	if len(resp.SecretAccessKey) == 0 {
		return credentials.Value{ProviderName: ProviderName}, awserr.New(
			ErrCodeProcessProviderRequired,
			errMsgProcessProviderMissSecret,
			nil)
	}
	p.staticCreds = resp.Expiration == nil
	if resp.Expiration != nil {
		p.SetExpiration(*resp.Expiration, p.ExpiryWindow)
	}
	return credentials.Value{
		ProviderName:    ProviderName,
		AccessKeyID:     resp.AccessKeyID,
		SecretAccessKey: resp.SecretAccessKey,
		SessionToken:    resp.SessionToken,
	}, nil
}
func (p *ProcessProvider) IsExpired() bool {
	if p.staticCreds {
		return false
	}
	return p.Expiry.IsExpired()
}
func (p *ProcessProvider) prepareCommand() error {
	var cmdArgs []string
	if runtime.GOOS == "windows" {
		cmdArgs = []string{"cmd.exe", "/C"}
	} else {
		cmdArgs = []string{"sh", "-c"}
	}
	if len(p.originalCommand) == 0 {
		p.originalCommand = make([]string, len(p.command.Args))
		copy(p.originalCommand, p.command.Args)
		if len(strings.TrimSpace(p.originalCommand[0])) < 1 {
			return awserr.New(
				ErrCodeProcessProviderExecution,
				fmt.Sprintf(
					"%s: %s",
					errMsgProcessProviderPrepareCmd,
					errMsgProcessProviderEmptyCmd),
				nil)
		}
	}
	cmdArgs = append(cmdArgs, p.originalCommand...)
	p.command = exec.Command(cmdArgs[0], cmdArgs[1:]...)
	p.command.Env = os.Environ()
	return nil
}
func (p *ProcessProvider) executeCredentialProcess() ([]byte, error) {
	if err := p.prepareCommand(); err != nil {
		return nil, err
	}
	outReadPipe, outWritePipe, err := os.Pipe()
	if err != nil {
		return nil, awserr.New(
			ErrCodeProcessProviderExecution,
			errMsgProcessProviderPipe,
			err)
	}
	p.command.Stderr = os.Stderr     
	p.command.Stdout = outWritePipe  
	p.command.Stdin = os.Stdin       
	output := bytes.NewBuffer(make([]byte, 0, p.MaxBufSize))
	stdoutCh := make(chan error, 1)
	go readInput(
		io.LimitReader(outReadPipe, int64(p.MaxBufSize)),
		output,
		stdoutCh)
	execCh := make(chan error, 1)
	go executeCommand(*p.command, execCh)
	finished := false
	var errors []error
	for !finished {
		select {
		case readError := <-stdoutCh:
			errors = appendError(errors, readError)
			finished = true
		case execError := <-execCh:
			err := outWritePipe.Close()
			errors = appendError(errors, err)
			errors = appendError(errors, execError)
			if errors != nil {
				return output.Bytes(), awserr.NewBatchError(
					ErrCodeProcessProviderExecution,
					errMsgProcessProviderProcess,
					errors)
			}
		case <-time.After(p.Timeout):
			finished = true
			return output.Bytes(), awserr.NewBatchError(
				ErrCodeProcessProviderExecution,
				errMsgProcessProviderTimeout,
				errors)  
		}
	}
	out := output.Bytes()
	if runtime.GOOS == "windows" {
		out = []byte(strings.Replace(string(out), `\"`, `"`, -1))
	}
	return out, nil
}