work on graceful shutdown + clean
This commit is contained in:
		
							parent
							
								
									a266685a29
								
							
						
					
					
						commit
						541a671bc2
					
				| @ -1,12 +1,10 @@ | |||||||
| package deployers | package deployers | ||||||
| 
 | 
 | ||||||
| import "context" |  | ||||||
| 
 |  | ||||||
| var ErrContextDone = "unable to execute, context done" | var ErrContextDone = "unable to execute, context done" | ||||||
| 
 | 
 | ||||||
| type IDeployer interface { | type IDeployer interface { | ||||||
| 	Deploy(ctx context.Context) error | 	Deploy() error | ||||||
| 	Build(ctx context.Context) error | 	Build() error | ||||||
| 	Clear(ctx context.Context) error | 	Clear() error | ||||||
| 	Done() <-chan struct{} | 	Done() <-chan struct{} | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,15 +12,17 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type NginxDeployer struct { | type NginxDeployer struct { | ||||||
|  | 	ctx context.Context | ||||||
|  | 
 | ||||||
| 	conn    connection.IConnection | 	conn    connection.IConnection | ||||||
| 	project *models.Project | 	project *models.Project | ||||||
| 	archivePath string | 
 | ||||||
| 	chDone chan struct{} | 	chDone chan struct{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var _ IDeployer = (*NginxDeployer)(nil) | var _ IDeployer = (*NginxDeployer)(nil) | ||||||
| 
 | 
 | ||||||
| func NewNginxDeployer(netInfo *models.HMNetInfo, project *models.Project) (NginxDeployer, error) { | func NewNginxDeployer(ctx context.Context, netInfo *models.HMNetInfo, project *models.Project) (NginxDeployer, error) { | ||||||
| 	var nd NginxDeployer | 	var nd NginxDeployer | ||||||
| 
 | 
 | ||||||
| 	conn, err := connection.NewSSHConn(netInfo.IP.String(), netInfo.SSH.User, netInfo.SSH.Port, netInfo.SSH.PrivKey) | 	conn, err := connection.NewSSHConn(netInfo.IP.String(), netInfo.SSH.User, netInfo.SSH.Port, netInfo.SSH.PrivKey) | ||||||
| @ -30,16 +32,17 @@ func NewNginxDeployer(netInfo *models.HMNetInfo, project *models.Project) (Nginx | |||||||
| 
 | 
 | ||||||
| 	nd.conn = &conn | 	nd.conn = &conn | ||||||
| 	nd.project = project | 	nd.project = project | ||||||
| 	nd.chDone = make(chan struct{}, 1) | 	nd.chDone = make(chan struct{}, 5) | ||||||
|  | 	nd.ctx = ctx | ||||||
| 
 | 
 | ||||||
| 	return nd, nil | 	return nd, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (nd *NginxDeployer) close(ctx context.Context) error { | func (nd *NginxDeployer) close() error { | ||||||
| 	return nd.conn.Close() | 	return nd.conn.Close() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (nd *NginxDeployer) clean(ctx context.Context) (err error) { | func (nd *NginxDeployer) clean() (err error) { | ||||||
| 	_, err = nd.conn.Execute("rm -f " + nd.project.Name + ".conf") | 	_, err = nd.conn.Execute("rm -f " + nd.project.Name + ".conf") | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| @ -49,17 +52,30 @@ func (nd *NginxDeployer) setDone() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (nd *NginxDeployer) Done() <-chan struct{} { | func (nd *NginxDeployer) Done() <-chan struct{} { | ||||||
| 	return nd.chDone | 	chDone := make(chan struct{}) | ||||||
|  | 	go func() { | ||||||
|  | 		for { | ||||||
|  | 			select { | ||||||
|  | 			case <-nd.chDone: | ||||||
|  | 				chDone <- struct{}{} | ||||||
|  | 				return | ||||||
|  | 			case <-nd.ctx.Done(): | ||||||
|  | 				chDone <- struct{}{} | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | 	return chDone | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (nd *NginxDeployer) Clear(ctx context.Context) error { | func (nd *NginxDeployer) Clear() error { | ||||||
| 	log.Debug().Msg("clearing nginx deployment...") | 	log.Debug().Msg("clearing nginx deployment...") | ||||||
| 
 | 
 | ||||||
| 	if err := nd.clean(ctx); err != nil { | 	if err := nd.clean(); err != nil { | ||||||
| 		log.Err(err).Msg("unable to clean nginx conf remotly") | 		log.Err(err).Msg("unable to clean nginx conf remotly") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := nd.close(ctx); err != nil { | 	if err := nd.close(); err != nil { | ||||||
| 		log.Err(err).Msg("unable to close nginx conn") | 		log.Err(err).Msg("unable to close nginx conn") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -67,11 +83,11 @@ func (nd *NginxDeployer) Clear(ctx context.Context) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (nd *NginxDeployer) Build(ctx context.Context) error { | func (nd *NginxDeployer) Build() error { | ||||||
| 	select { | 	select { | ||||||
| 	case <-ctx.Done(): | 	case <-nd.ctx.Done(): | ||||||
| 		nd.setDone() | 		nd.setDone() | ||||||
| 		return fmt.Errorf("%w, nginx close ssh conn skipped", ErrContextDone) | 		return fmt.Errorf("%w, build nginx archive skipped", ErrContextDone) | ||||||
| 	default: | 	default: | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -89,12 +105,12 @@ func (nd *NginxDeployer) Build(ctx context.Context) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (nd *NginxDeployer) Deploy(ctx context.Context) (err error) { | func (nd *NginxDeployer) Deploy() (err error) { | ||||||
| 	defer nd.setDone() | 	defer nd.setDone() | ||||||
| 
 | 
 | ||||||
| 	select { | 	select { | ||||||
| 	case <-ctx.Done(): | 	case <-nd.ctx.Done(): | ||||||
| 		return fmt.Errorf("%w, nginx close ssh conn skipped", ErrContextDone) | 		return fmt.Errorf("%w, nginx deployment skipped", ErrContextDone) | ||||||
| 	default: | 	default: | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,6 +15,8 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type SwarmDeployer struct { | type SwarmDeployer struct { | ||||||
|  | 	ctx context.Context | ||||||
|  | 
 | ||||||
| 	conn connection.IConnection | 	conn connection.IConnection | ||||||
| 	dcli docker.IClient | 	dcli docker.IClient | ||||||
| 
 | 
 | ||||||
| @ -26,7 +28,7 @@ type SwarmDeployer struct { | |||||||
| 
 | 
 | ||||||
| var _ IDeployer = (*SwarmDeployer)(nil) | var _ IDeployer = (*SwarmDeployer)(nil) | ||||||
| 
 | 
 | ||||||
| func NewSwarmDeployer(dockerClient docker.IClient, netInfo *models.HMNetInfo, project *models.Project) (SwarmDeployer, error) { | func NewSwarmDeployer(ctx context.Context, dockerClient docker.IClient, netInfo *models.HMNetInfo, project *models.Project) (SwarmDeployer, error) { | ||||||
| 	var sd SwarmDeployer | 	var sd SwarmDeployer | ||||||
| 
 | 
 | ||||||
| 	conn, err := connection.NewSSHConn(netInfo.IP.String(), netInfo.SSH.User, netInfo.SSH.Port, netInfo.SSH.PrivKey) | 	conn, err := connection.NewSSHConn(netInfo.IP.String(), netInfo.SSH.User, netInfo.SSH.Port, netInfo.SSH.PrivKey) | ||||||
| @ -34,19 +36,20 @@ func NewSwarmDeployer(dockerClient docker.IClient, netInfo *models.HMNetInfo, pr | |||||||
| 		return sd, err | 		return sd, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	sd.ctx = ctx | ||||||
| 	sd.conn = &conn | 	sd.conn = &conn | ||||||
| 	sd.dcli = dockerClient | 	sd.dcli = dockerClient | ||||||
| 	sd.project = project | 	sd.project = project | ||||||
| 	sd.chDone = make(chan struct{}, 1) | 	sd.chDone = make(chan struct{}, 5) | ||||||
| 
 | 
 | ||||||
| 	return sd, nil | 	return sd, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (sd *SwarmDeployer) close(ctx context.Context) error { | func (sd *SwarmDeployer) close() error { | ||||||
| 	return sd.conn.Close() | 	return sd.conn.Close() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (sd *SwarmDeployer) clean(ctx context.Context) (err error) { | func (sd *SwarmDeployer) clean() (err error) { | ||||||
| 	defer os.Remove(sd.archivePath) | 	defer os.Remove(sd.archivePath) | ||||||
| 	_, err = sd.conn.Execute(fmt.Sprintf("rm -f %s %s *.tar.gz *.tar", models.ComposeFile, models.EnvFile)) | 	_, err = sd.conn.Execute(fmt.Sprintf("rm -f %s %s *.tar.gz *.tar", models.ComposeFile, models.EnvFile)) | ||||||
| 	return | 	return | ||||||
| @ -57,17 +60,30 @@ func (sd *SwarmDeployer) setDone() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (sd *SwarmDeployer) Done() <-chan struct{} { | func (sd *SwarmDeployer) Done() <-chan struct{} { | ||||||
| 	return sd.chDone | 	chDone := make(chan struct{}) | ||||||
|  | 	go func() { | ||||||
|  | 		for { | ||||||
|  | 			select { | ||||||
|  | 			case <-sd.chDone: | ||||||
|  | 				chDone <- struct{}{} | ||||||
|  | 				return | ||||||
|  | 			case <-sd.ctx.Done(): | ||||||
|  | 				chDone <- struct{}{} | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | 	return chDone | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (sd *SwarmDeployer) Clear(ctx context.Context) error { | func (sd *SwarmDeployer) Clear() error { | ||||||
| 	log.Debug().Msg("clearing swarm deployment...") | 	log.Debug().Msg("clearing swarm deployment...") | ||||||
| 
 | 
 | ||||||
| 	if err := sd.clean(ctx); err != nil { | 	if err := sd.clean(); err != nil { | ||||||
| 		log.Err(err).Msg("unable to clean swarm conf remotly") | 		log.Err(err).Msg("unable to clean swarm conf remotly") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := sd.close(ctx); err != nil { | 	if err := sd.close(); err != nil { | ||||||
| 		log.Err(err).Msg("unable to close swarm conn") | 		log.Err(err).Msg("unable to close swarm conn") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -76,9 +92,9 @@ func (sd *SwarmDeployer) Clear(ctx context.Context) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (sd *SwarmDeployer) Build(ctx context.Context) error { | func (sd *SwarmDeployer) Build() error { | ||||||
| 	select { | 	select { | ||||||
| 	case <-ctx.Done(): | 	case <-sd.ctx.Done(): | ||||||
| 		sd.setDone() | 		sd.setDone() | ||||||
| 		return fmt.Errorf("%w, swarm project build skipped", ErrContextDone) | 		return fmt.Errorf("%w, swarm project build skipped", ErrContextDone) | ||||||
| 	default: | 	default: | ||||||
| @ -121,12 +137,12 @@ func (sd *SwarmDeployer) Build(ctx context.Context) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (sd *SwarmDeployer) Deploy(ctx context.Context) error { | func (sd *SwarmDeployer) Deploy() error { | ||||||
| 	defer sd.setDone() | 	defer sd.setDone() | ||||||
| 
 | 
 | ||||||
| 	select { | 	select { | ||||||
| 	case <-ctx.Done(): | 	case <-sd.ctx.Done(): | ||||||
| 		return fmt.Errorf("%w, nginx close ssh conn skipped", ErrContextDone) | 		return fmt.Errorf("%w, swarm deployment skipped", ErrContextDone) | ||||||
| 	default: | 	default: | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								main.go
									
									
									
									
									
								
							| @ -65,7 +65,7 @@ func main() { | |||||||
| 	} | 	} | ||||||
| 	dcli := docker.NewClient() | 	dcli := docker.NewClient() | ||||||
| 
 | 
 | ||||||
| 	sd, err := deployers.NewSwarmDeployer(&dcli, swarmNet, &project) | 	sd, err := deployers.NewSwarmDeployer(ctx, &dcli, swarmNet, &project) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal().Err(err).Msg("unable to init swarm deployer") | 		log.Fatal().Err(err).Msg("unable to init swarm deployer") | ||||||
| 	} | 	} | ||||||
| @ -78,7 +78,7 @@ func main() { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		d, err := deployers.NewNginxDeployer(nginxNet, &project) | 		d, err := deployers.NewNginxDeployer(ctx, nginxNet, &project) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Err(err).Msg("unable to nginx conf") | 			log.Err(err).Msg("unable to nginx conf") | ||||||
| 			return | 			return | ||||||
| @ -90,7 +90,7 @@ func main() { | |||||||
| 	deployNginx := scheduler.NewTask("nginx-deploy", nd.Deploy) | 	deployNginx := scheduler.NewTask("nginx-deploy", nd.Deploy) | ||||||
| 	deploySwarm := scheduler.NewTask("swarm-deploy", sd.Deploy, deployNginx) | 	deploySwarm := scheduler.NewTask("swarm-deploy", sd.Deploy, deployNginx) | ||||||
| 
 | 
 | ||||||
| 	s := scheduler.NewScheduler(ctx, 30, 4) | 	s := scheduler.NewScheduler(context.Background(), 30, 4) | ||||||
| 	s.Submit(scheduler.NewTask("swarm-build", sd.Build, deploySwarm)) | 	s.Submit(scheduler.NewTask("swarm-build", sd.Build, deploySwarm)) | ||||||
| 	s.Submit(scheduler.NewTask("nginx-build", nd.Build)) | 	s.Submit(scheduler.NewTask("nginx-build", nd.Build)) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,7 +9,10 @@ import ( | |||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ErrSchedulerMaxCapacityReached = errors.New("unable to add new task, max capacity reached") | var ( | ||||||
|  | 	ErrSchedulerMaxCapacityReached = errors.New("unable to add new task, max capacity reached") | ||||||
|  | 	ErrSchedulerContextDone        = errors.New("context done, scheduler stopped") | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| type TaskStatus string | type TaskStatus string | ||||||
| 
 | 
 | ||||||
| @ -20,7 +23,7 @@ const ( | |||||||
| 	Failed             = "failed" | 	Failed             = "failed" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type FnJob func(context.Context) error | type FnJob func() error | ||||||
| 
 | 
 | ||||||
| type taskStore struct { | type taskStore struct { | ||||||
| 	l     sync.RWMutex | 	l     sync.RWMutex | ||||||
| @ -114,7 +117,7 @@ func (s *Scheduler) run() { | |||||||
| 				case t := <-s.chTasks: | 				case t := <-s.chTasks: | ||||||
| 					s.tasks.setStatus(t, Running) | 					s.tasks.setStatus(t, Running) | ||||||
| 
 | 
 | ||||||
| 					if err := t.Job(s.ctx); err != nil { | 					if err := t.Job(); err != nil { | ||||||
| 						log.Err(err).Str("task", t.Name).Msg("error executing task") | 						log.Err(err).Str("task", t.Name).Msg("error executing task") | ||||||
| 						s.tasks.setStatus(t, Failed) | 						s.tasks.setStatus(t, Failed) | ||||||
| 						continue | 						continue | ||||||
| @ -139,6 +142,13 @@ func (s *Scheduler) Stop() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scheduler) Submit(task *Task) error { | func (s *Scheduler) Submit(task *Task) error { | ||||||
|  | 	select { | ||||||
|  | 	case <-s.ctx.Done(): | ||||||
|  | 		log.Error().Msg("unable to submit new task, scheduler is stopping...") | ||||||
|  | 		return ErrSchedulerContextDone | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	cap := s.capacity.Load() | 	cap := s.capacity.Load() | ||||||
| 	if s.tasks.len() >= int(cap) { | 	if s.tasks.len() >= int(cap) { | ||||||
| 		return ErrSchedulerMaxCapacityReached | 		return ErrSchedulerMaxCapacityReached | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 rmanach
						rmanach