mirror of
				https://github.com/appleboy/drone-ssh.git
				synced 2025-10-29 00:51:15 +08:00 
			
		
		
		
	Merge b40bfc56ce into 6c0b475c15
				
					
				
			This commit is contained in:
		
						commit
						d8eac07970
					
				
							
								
								
									
										6
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								main.go
									
									
									
									
									
								
							| @ -71,6 +71,11 @@ func main() { | |||||||
| 			EnvVar: "PLUGIN_PORT,SSH_PORT", | 			EnvVar: "PLUGIN_PORT,SSH_PORT", | ||||||
| 			Value:  22, | 			Value:  22, | ||||||
| 		}, | 		}, | ||||||
|  | 		cli.DurationFlag{ | ||||||
|  | 			Name:   "retry-timeout", | ||||||
|  | 			Usage:  "connection retry timeout", | ||||||
|  | 			EnvVar: "PLUGIN_RETRY_TIMEOUT", | ||||||
|  | 		}, | ||||||
| 		cli.BoolFlag{ | 		cli.BoolFlag{ | ||||||
| 			Name:   "sync", | 			Name:   "sync", | ||||||
| 			Usage:  "sync mode", | 			Usage:  "sync mode", | ||||||
| @ -194,6 +199,7 @@ func run(c *cli.Context) error { | |||||||
| 			Password:       c.String("password"), | 			Password:       c.String("password"), | ||||||
| 			Host:           c.StringSlice("host"), | 			Host:           c.StringSlice("host"), | ||||||
| 			Port:           c.Int("port"), | 			Port:           c.Int("port"), | ||||||
|  | 			RetryTimeout:   c.Duration("retry-timeout"), | ||||||
| 			Timeout:        c.Duration("timeout"), | 			Timeout:        c.Duration("timeout"), | ||||||
| 			CommandTimeout: c.Int("command.timeout"), | 			CommandTimeout: c.Int("command.timeout"), | ||||||
| 			Script:         c.StringSlice("script"), | 			Script:         c.StringSlice("script"), | ||||||
|  | |||||||
							
								
								
									
										35
									
								
								plugin.go
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								plugin.go
									
									
									
									
									
								
							| @ -10,6 +10,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"github.com/appleboy/easyssh-proxy" | 	"github.com/appleboy/easyssh-proxy" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"net" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| @ -29,6 +30,7 @@ type ( | |||||||
| 		Host           []string | 		Host           []string | ||||||
| 		Port           int | 		Port           int | ||||||
| 		Timeout        time.Duration | 		Timeout        time.Duration | ||||||
|  | 		RetryTimeout   time.Duration | ||||||
| 		CommandTimeout int | 		CommandTimeout int | ||||||
| 		Script         []string | 		Script         []string | ||||||
| 		Secrets        []string | 		Secrets        []string | ||||||
| @ -90,7 +92,7 @@ func (p Plugin) exec(host string, wg *sync.WaitGroup, errChannel chan error) { | |||||||
| 		p.log(host, "======END======") | 		p.log(host, "======END======") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	stdoutChan, stderrChan, doneChan, errChan, err := ssh.Stream(strings.Join(p.Config.Script, "\n"), p.Config.CommandTimeout) | 	stdoutChan, stderrChan, doneChan, errChan, err := retryStream(ssh, p) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		errChannel <- err | 		errChannel <- err | ||||||
| 	} else { | 	} else { | ||||||
| @ -179,3 +181,34 @@ func (p Plugin) Exec() error { | |||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func retryStream(ssh *easyssh.MakeConfig, p Plugin) (<-chan string, <-chan string, <-chan bool, <-chan error, error) { | ||||||
|  | 	var ( | ||||||
|  | 		timeout = time.After(p.Config.RetryTimeout) | ||||||
|  | 		wait    = time.Second | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	for { | ||||||
|  | 		stdoutChan, stderrChan, doneChan, errChan, err := ssh.Stream(strings.Join(p.Config.Script, "\n"), p.Config.CommandTimeout) | ||||||
|  | 
 | ||||||
|  | 		// If there was no error, return all channels
 | ||||||
|  | 		if err == nil { | ||||||
|  | 			return stdoutChan, stderrChan, doneChan, errChan, nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// If the error was not a net.OpError, return that error
 | ||||||
|  | 		if _, ok := err.(*net.OpError); !ok { | ||||||
|  | 			return nil, nil, nil, nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		select { | ||||||
|  | 		case <-timeout: | ||||||
|  | 			return nil, nil, nil, nil, err | ||||||
|  | 		case <-time.After(wait): | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Double our back-off time
 | ||||||
|  | 		wait *= 2 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ import ( | |||||||
| 	"github.com/appleboy/easyssh-proxy" | 	"github.com/appleboy/easyssh-proxy" | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMissingHostOrUser(t *testing.T) { | func TestMissingHostOrUser(t *testing.T) { | ||||||
| @ -457,6 +459,56 @@ func TestEnvOutput(t *testing.T) { | |||||||
| 	assert.Equal(t, unindent(expected), unindent(buffer.String())) | 	assert.Equal(t, unindent(expected), unindent(buffer.String())) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // TestConnectTimeoutRetry tests that when a network error occurs, the connect
 | ||||||
|  | // is retried until it either succeeds or the configured timeout is hit.
 | ||||||
|  | func TestConnectTimeoutRetry(t *testing.T) { | ||||||
|  | 	start := time.Now() | ||||||
|  | 
 | ||||||
|  | 	plugin := Plugin{ | ||||||
|  | 		Config: Config{ | ||||||
|  | 			Host:         []string{"localhost"}, | ||||||
|  | 			UserName:     "drone-scp", | ||||||
|  | 			Port:         2200, | ||||||
|  | 			KeyPath:      "./tests/.ssh/id_rsa", | ||||||
|  | 			Script:       []string{"exit"}, | ||||||
|  | 			RetryTimeout: 15 * time.Second, | ||||||
|  | 			Sync:         true, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err := plugin.Exec() | ||||||
|  | 	assert.NotNil(t, err) | ||||||
|  | 
 | ||||||
|  | 	end := time.Now() | ||||||
|  | 	assert.WithinDuration(t, start.Local().Add(15*time.Second), end, 2*time.Second) | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestConnectTimeoutRetry tests that when a non-network error occurs, the connect
 | ||||||
|  | // is not retried and instead returns the error immediately.
 | ||||||
|  | func TestConnectTimeoutImmediate(t *testing.T) { | ||||||
|  | 	start := time.Now() | ||||||
|  | 
 | ||||||
|  | 	plugin := Plugin{ | ||||||
|  | 		Config: Config{ | ||||||
|  | 			Host:         []string{"localhost"}, | ||||||
|  | 			UserName:     "drone-scp", | ||||||
|  | 			Port:         22, | ||||||
|  | 			Key:          "", | ||||||
|  | 			Script:       []string{"exit"}, | ||||||
|  | 			RetryTimeout: 60 * time.Second, | ||||||
|  | 			Sync:         true, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err := plugin.Exec() | ||||||
|  | 	assert.NotNil(t, err) | ||||||
|  | 
 | ||||||
|  | 	end := time.Now() | ||||||
|  | 	assert.WithinDuration(t, start.Local().Add(time.Second), end, 2*time.Second) | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func unindent(text string) string { | func unindent(text string) string { | ||||||
| 	return strings.TrimSpace(strings.Replace(text, "\t", "", -1)) | 	return strings.TrimSpace(strings.Replace(text, "\t", "", -1)) | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Josh Komoroske
						Josh Komoroske