139 lines
3.1 KiB
Go
139 lines
3.1 KiB
Go
package deployers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"gitea.thegux.fr/hmdeploy/models"
|
|
)
|
|
|
|
var ErrContextDone = errors.New("unable to execute, context done")
|
|
|
|
type IDeployer interface {
|
|
Type() DeployerType
|
|
Deploy() error
|
|
Build() error
|
|
Clear() error
|
|
Error() error
|
|
Done() <-chan struct{}
|
|
}
|
|
|
|
type DeployerType string
|
|
|
|
const (
|
|
Nginx DeployerType = "nginx"
|
|
Swarm DeployerType = "swarm"
|
|
|
|
GracefulTimeout = 10 * time.Second
|
|
)
|
|
|
|
// Base struct of the deployers.
|
|
// It handles the main informations to build a deployer.
|
|
//
|
|
// "Inherited" deployers must implement three methods in order
|
|
// to satify the `IDeployer` contract:
|
|
// - `Deploy() error`: run shell command to deploy the archive remotly
|
|
// - `Build() error`: build the archive
|
|
// - `Clear() error`: clean all the ressources locally and remotly
|
|
type deployer struct {
|
|
ctx context.Context
|
|
fnCancel context.CancelFunc
|
|
|
|
type_ DeployerType
|
|
project *models.Project
|
|
|
|
processing atomic.Bool
|
|
chDone chan struct{}
|
|
errFlag error
|
|
}
|
|
|
|
func newDeployer(ctx context.Context, type_ DeployerType, project *models.Project) *deployer {
|
|
d := &deployer{
|
|
ctx: ctx,
|
|
type_: type_,
|
|
project: project,
|
|
processing: atomic.Bool{},
|
|
chDone: make(chan struct{}, 1),
|
|
}
|
|
|
|
d.processing.Store(false)
|
|
return d
|
|
}
|
|
|
|
func (d *deployer) setDone(err error) {
|
|
d.chDone <- struct{}{}
|
|
d.errFlag = err
|
|
if err != nil && d.fnCancel != nil {
|
|
d.fnCancel()
|
|
}
|
|
}
|
|
|
|
// SetCancellationFunc sets a context cancellation function for the deployer.
|
|
//
|
|
// If two deployers are related on the same context, one failed and you want
|
|
// to stop the execution of the others. Then, associate a cancel function
|
|
// for the deployer and the cancel func will be fired if error occurred
|
|
// during a deployment step. Stopping all the deployers.
|
|
func (d *deployer) SetCancellationFunc(fnCancel context.CancelFunc) {
|
|
d.fnCancel = fnCancel
|
|
}
|
|
|
|
func (d *deployer) Type() DeployerType {
|
|
return d.type_
|
|
}
|
|
|
|
func (d *deployer) Error() error {
|
|
return d.errFlag
|
|
}
|
|
|
|
// Done returns a channel providing the shutdown or the termination
|
|
// of the deployer.
|
|
//
|
|
// If the context is done, it will wait until all the current actions are done
|
|
// for a graceful shutdown. It has a graceful timeout (see: `GracefulTimeout`).
|
|
//
|
|
// If the deployer is done, succeed or failed, it simply returns.
|
|
func (d *deployer) Done() <-chan struct{} {
|
|
chDone := make(chan struct{})
|
|
go func() {
|
|
defer func() {
|
|
close(chDone)
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case <-d.ctx.Done():
|
|
log.Warn().Str("deployer", string(d.type_)).Msg("context done catch")
|
|
|
|
timeout := time.NewTicker(GracefulTimeout)
|
|
tick := time.NewTicker(time.Second)
|
|
for {
|
|
select {
|
|
case <-timeout.C:
|
|
log.Error().
|
|
Str("deployer", string(d.type_)).
|
|
Msg("timeout while waiting for graceful shutdown")
|
|
chDone <- struct{}{}
|
|
return
|
|
case <-tick.C:
|
|
if !d.processing.Load() {
|
|
chDone <- struct{}{}
|
|
return
|
|
}
|
|
tick.Reset(1 * time.Second)
|
|
}
|
|
}
|
|
case <-d.chDone:
|
|
log.Info().Str("deployer", string(d.type_)).Msg("terminated")
|
|
chDone <- struct{}{}
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
return chDone
|
|
}
|