mirror of
				https://github.com/appleboy/drone-ssh.git
				synced 2025-10-29 00:51:15 +08:00 
			
		
		
		
	Add sync mode.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
		
							parent
							
								
									6f1ace35bf
								
							
						
					
					
						commit
						0402162a26
					
				
							
								
								
									
										6
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								main.go
									
									
									
									
									
								
							| @ -66,6 +66,11 @@ func main() { | |||||||
| 			EnvVar: "PLUGIN_PORT,SSH_PORT", | 			EnvVar: "PLUGIN_PORT,SSH_PORT", | ||||||
| 			Value:  22, | 			Value:  22, | ||||||
| 		}, | 		}, | ||||||
|  | 		cli.BoolFlag{ | ||||||
|  | 			Name:   "sync", | ||||||
|  | 			Usage:  "sync mode", | ||||||
|  | 			EnvVar: "PLUGIN_SYNC", | ||||||
|  | 		}, | ||||||
| 		cli.DurationFlag{ | 		cli.DurationFlag{ | ||||||
| 			Name:   "timeout,t", | 			Name:   "timeout,t", | ||||||
| 			Usage:  "connection timeout", | 			Usage:  "connection timeout", | ||||||
| @ -195,6 +200,7 @@ func run(c *cli.Context) error { | |||||||
| 			Secrets:        c.StringSlice("secrets"), | 			Secrets:        c.StringSlice("secrets"), | ||||||
| 			Envs:           c.StringSlice("envs"), | 			Envs:           c.StringSlice("envs"), | ||||||
| 			Debug:          c.Bool("debug"), | 			Debug:          c.Bool("debug"), | ||||||
|  | 			Sync:           c.Bool("sync"), | ||||||
| 			Proxy: easyssh.DefaultConfig{ | 			Proxy: easyssh.DefaultConfig{ | ||||||
| 				Key:      c.String("proxy.ssh-key"), | 				Key:      c.String("proxy.ssh-key"), | ||||||
| 				KeyPath:  c.String("proxy.key-path"), | 				KeyPath:  c.String("proxy.key-path"), | ||||||
|  | |||||||
							
								
								
									
										163
									
								
								plugin.go
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								plugin.go
									
									
									
									
									
								
							| @ -34,6 +34,7 @@ type ( | |||||||
| 		Envs           []string | 		Envs           []string | ||||||
| 		Proxy          easyssh.DefaultConfig | 		Proxy          easyssh.DefaultConfig | ||||||
| 		Debug          bool | 		Debug          bool | ||||||
|  | 		Sync           bool | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Plugin structure
 | 	// Plugin structure
 | ||||||
| @ -42,6 +43,80 @@ type ( | |||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | func (p Plugin) exec(host string, wg *sync.WaitGroup, errChannel chan error) { | ||||||
|  | 	// Create MakeConfig instance with remote username, server address and path to private key.
 | ||||||
|  | 	ssh := &easyssh.MakeConfig{ | ||||||
|  | 		Server:   host, | ||||||
|  | 		User:     p.Config.UserName, | ||||||
|  | 		Password: p.Config.Password, | ||||||
|  | 		Port:     strconv.Itoa(p.Config.Port), | ||||||
|  | 		Key:      p.Config.Key, | ||||||
|  | 		KeyPath:  p.Config.KeyPath, | ||||||
|  | 		Timeout:  p.Config.Timeout, | ||||||
|  | 		Proxy: easyssh.DefaultConfig{ | ||||||
|  | 			Server:   p.Config.Proxy.Server, | ||||||
|  | 			User:     p.Config.Proxy.User, | ||||||
|  | 			Password: p.Config.Proxy.Password, | ||||||
|  | 			Port:     p.Config.Proxy.Port, | ||||||
|  | 			Key:      p.Config.Proxy.Key, | ||||||
|  | 			KeyPath:  p.Config.Proxy.KeyPath, | ||||||
|  | 			Timeout:  p.Config.Proxy.Timeout, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	p.log(host, "======CMD======") | ||||||
|  | 	p.log(host, strings.Join(p.Config.Script, "\n")) | ||||||
|  | 	p.log(host, "======END======") | ||||||
|  | 
 | ||||||
|  | 	env := []string{} | ||||||
|  | 	for _, key := range p.Config.Envs { | ||||||
|  | 		key = strings.ToUpper(key) | ||||||
|  | 		val := os.Getenv(key) | ||||||
|  | 		val = strings.Replace(val, " ", "", -1) | ||||||
|  | 		env = append(env, key+"="+val) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	p.Config.Script = append(env, p.Config.Script...) | ||||||
|  | 
 | ||||||
|  | 	if p.Config.Debug { | ||||||
|  | 		p.log(host, "======ENV======") | ||||||
|  | 		p.log(host, strings.Join(env, "\n")) | ||||||
|  | 		p.log(host, "======END======") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	stdoutChan, stderrChan, doneChan, errChan, err := ssh.Stream(strings.Join(p.Config.Script, "\n"), p.Config.CommandTimeout) | ||||||
|  | 	if err != nil { | ||||||
|  | 		errChannel <- err | ||||||
|  | 	} else { | ||||||
|  | 		// read from the output channel until the done signal is passed
 | ||||||
|  | 		isTimeout := true | ||||||
|  | 	loop: | ||||||
|  | 		for { | ||||||
|  | 			select { | ||||||
|  | 			case isTimeout = <-doneChan: | ||||||
|  | 				break loop | ||||||
|  | 			case outline := <-stdoutChan: | ||||||
|  | 				p.log(host, "out:", outline) | ||||||
|  | 			case errline := <-stderrChan: | ||||||
|  | 				p.log(host, "err:", errline) | ||||||
|  | 			case err = <-errChan: | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// get exit code or command error.
 | ||||||
|  | 		if err != nil { | ||||||
|  | 			errChannel <- err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// command time out
 | ||||||
|  | 		if !isTimeout { | ||||||
|  | 			errChannel <- fmt.Errorf(commandTimeOut) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	wg.Done() | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (p Plugin) log(host string, message ...interface{}) { | func (p Plugin) log(host string, message ...interface{}) { | ||||||
| 	if count := len(p.Config.Host); count == 1 { | 	if count := len(p.Config.Host); count == 1 { | ||||||
| 		fmt.Printf("%s", fmt.Sprintln(message...)) | 		fmt.Printf("%s", fmt.Sprintln(message...)) | ||||||
| @ -69,85 +144,19 @@ func (p Plugin) Exec() error { | |||||||
| 	errChannel := make(chan error, 1) | 	errChannel := make(chan error, 1) | ||||||
| 	finished := make(chan bool, 1) | 	finished := make(chan bool, 1) | ||||||
| 	for _, host := range p.Config.Host { | 	for _, host := range p.Config.Host { | ||||||
| 		go func(host string) { | 		if p.Config.Sync { | ||||||
| 			// Create MakeConfig instance with remote username, server address and path to private key.
 | 			p.exec(host, &wg, errChannel) | ||||||
| 			ssh := &easyssh.MakeConfig{ | 		} else { | ||||||
| 				Server:   host, | 			go p.exec(host, &wg, errChannel) | ||||||
| 				User:     p.Config.UserName, | 		} | ||||||
| 				Password: p.Config.Password, |  | ||||||
| 				Port:     strconv.Itoa(p.Config.Port), |  | ||||||
| 				Key:      p.Config.Key, |  | ||||||
| 				KeyPath:  p.Config.KeyPath, |  | ||||||
| 				Timeout:  p.Config.Timeout, |  | ||||||
| 				Proxy: easyssh.DefaultConfig{ |  | ||||||
| 					Server:   p.Config.Proxy.Server, |  | ||||||
| 					User:     p.Config.Proxy.User, |  | ||||||
| 					Password: p.Config.Proxy.Password, |  | ||||||
| 					Port:     p.Config.Proxy.Port, |  | ||||||
| 					Key:      p.Config.Proxy.Key, |  | ||||||
| 					KeyPath:  p.Config.Proxy.KeyPath, |  | ||||||
| 					Timeout:  p.Config.Proxy.Timeout, |  | ||||||
| 				}, |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			p.log(host, "======CMD======") |  | ||||||
| 			p.log(host, strings.Join(p.Config.Script, "\n")) |  | ||||||
| 			p.log(host, "======END======") |  | ||||||
| 
 |  | ||||||
| 			env := []string{} |  | ||||||
| 			for _, key := range p.Config.Envs { |  | ||||||
| 				key = strings.ToUpper(key) |  | ||||||
| 				val := os.Getenv(key) |  | ||||||
| 				val = strings.Replace(val, " ", "", -1) |  | ||||||
| 				env = append(env, key+"="+val) |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			p.Config.Script = append(env, p.Config.Script...) |  | ||||||
| 
 |  | ||||||
| 			if p.Config.Debug { |  | ||||||
| 				p.log(host, "======ENV======") |  | ||||||
| 				p.log(host, strings.Join(env, "\n")) |  | ||||||
| 				p.log(host, "======END======") |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			stdoutChan, stderrChan, doneChan, errChan, err := ssh.Stream(strings.Join(p.Config.Script, "\n"), p.Config.CommandTimeout) |  | ||||||
| 			if err != nil { |  | ||||||
| 				errChannel <- err |  | ||||||
| 			} else { |  | ||||||
| 				// read from the output channel until the done signal is passed
 |  | ||||||
| 				isTimeout := true |  | ||||||
| 			loop: |  | ||||||
| 				for { |  | ||||||
| 					select { |  | ||||||
| 					case isTimeout = <-doneChan: |  | ||||||
| 						break loop |  | ||||||
| 					case outline := <-stdoutChan: |  | ||||||
| 						p.log(host, "out:", outline) |  | ||||||
| 					case errline := <-stderrChan: |  | ||||||
| 						p.log(host, "err:", errline) |  | ||||||
| 					case err = <-errChan: |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				// get exit code or command error.
 |  | ||||||
| 				if err != nil { |  | ||||||
| 					errChannel <- err |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				// command time out
 |  | ||||||
| 				if !isTimeout { |  | ||||||
| 					errChannel <- fmt.Errorf(commandTimeOut) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			wg.Done() |  | ||||||
| 		}(host) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	go func() { | 	if !p.Config.Sync { | ||||||
| 		wg.Wait() | 		go func() { | ||||||
| 		close(finished) | 			wg.Wait() | ||||||
| 	}() | 			close(finished) | ||||||
|  | 		}() | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	select { | 	select { | ||||||
| 	case <-finished: | 	case <-finished: | ||||||
|  | |||||||
| @ -256,3 +256,20 @@ func TestSetENV(t *testing.T) { | |||||||
| 	err := plugin.Exec() | 	err := plugin.Exec() | ||||||
| 	assert.Nil(t, err) | 	assert.Nil(t, err) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestSyncMode(t *testing.T) { | ||||||
|  | 	plugin := Plugin{ | ||||||
|  | 		Config: Config{ | ||||||
|  | 			Host:           []string{"localhost", "127.0.0.1"}, | ||||||
|  | 			UserName:       "drone-scp", | ||||||
|  | 			Port:           22, | ||||||
|  | 			KeyPath:        "./tests/.ssh/id_rsa", | ||||||
|  | 			Script:         []string{"whoami", "for i in {1..3}; do echo ${i}; sleep 1; done", "echo 'done'"}, | ||||||
|  | 			CommandTimeout: 60, | ||||||
|  | 			Sync:           true, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err := plugin.Exec() | ||||||
|  | 	assert.Nil(t, err) | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Bo-Yi Wu
						Bo-Yi Wu