219 lines
5.0 KiB
Go
219 lines
5.0 KiB
Go
package deployers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gitea.thegux.fr/hmdeploy/connection"
|
|
"gitea.thegux.fr/hmdeploy/docker"
|
|
"gitea.thegux.fr/hmdeploy/models"
|
|
"gitea.thegux.fr/hmdeploy/utils"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var ErrSwarmDeployerNoArchive = errors.New("no archive found to be deployed")
|
|
|
|
// SwarmDeployer handles the deployment of a Docker service on the swarm instance.
|
|
type SwarmDeployer struct {
|
|
*deployer
|
|
conn connection.IConnection
|
|
dloc docker.IClient
|
|
drem *docker.RemoteClient
|
|
}
|
|
|
|
var _ IDeployer = (*SwarmDeployer)(nil)
|
|
|
|
func NewSwarmDeployer(
|
|
ctx context.Context,
|
|
project *models.Project,
|
|
netInfo *models.HMNetInfo,
|
|
dloc docker.IClient,
|
|
drem *docker.RemoteClient,
|
|
) (SwarmDeployer, error) {
|
|
var sd SwarmDeployer
|
|
|
|
conn, err := connection.NewSSHConn(
|
|
netInfo.IP.String(),
|
|
netInfo.SSH.User,
|
|
netInfo.SSH.Port,
|
|
netInfo.SSH.PrivKey,
|
|
)
|
|
if err != nil {
|
|
return sd, nil
|
|
}
|
|
|
|
sd.conn = &conn
|
|
sd.dloc = dloc
|
|
sd.drem = drem
|
|
sd.deployer = newDeployer(ctx, Swarm, project)
|
|
|
|
return sd, nil
|
|
}
|
|
|
|
func (sd SwarmDeployer) getComposePath() string {
|
|
return sd.project.GetComposePath()
|
|
}
|
|
|
|
func (sd SwarmDeployer) getEnvPath() string {
|
|
return sd.project.GetEnvPath()
|
|
}
|
|
|
|
func (sd *SwarmDeployer) close() error {
|
|
return sd.conn.Close()
|
|
}
|
|
|
|
func (sd *SwarmDeployer) clean() (err error) {
|
|
if err = os.Remove(sd.archivePath); err != nil {
|
|
log.Err(err).Str("archive", sd.archivePath).Msg("unable to clean local swarm archive file")
|
|
}
|
|
_, err = sd.conn.Execute(
|
|
fmt.Sprintf("rm -f %s %s *.tar.gz *.tar", models.ComposeFile, models.EnvFile),
|
|
)
|
|
return
|
|
}
|
|
|
|
func (sd *SwarmDeployer) Clear() error {
|
|
log.Debug().Msg("clearing swarm deployment...")
|
|
|
|
if err := sd.clean(); err != nil {
|
|
log.Err(err).Msg("unable to clean swarm conf remotly")
|
|
}
|
|
|
|
if err := sd.close(); err != nil {
|
|
log.Err(err).Msg("unable to close swarm conn")
|
|
}
|
|
|
|
log.Debug().Msg("clear swarm deployment done")
|
|
|
|
return nil
|
|
}
|
|
|
|
// Build builds the archive with mandatory files to deploy a swarm service.
|
|
//
|
|
// After the build, the path of the local archive built is set in
|
|
// the `archivePath` field.
|
|
func (sd *SwarmDeployer) Build() error {
|
|
sd.processing.Store(true)
|
|
defer sd.processing.Store(false)
|
|
|
|
select {
|
|
case <-sd.ctx.Done():
|
|
sd.setDone(nil)
|
|
return fmt.Errorf("%w, swarm project build skipped", ErrContextDone)
|
|
default:
|
|
}
|
|
|
|
log.Info().Msg("building swarm archive for deployment...")
|
|
|
|
filesToArchive := []string{}
|
|
for idx := range sd.project.Deps.Swarm.ImageNames {
|
|
tarFile, err := sd.dloc.Save(sd.project.Deps.Swarm.ImageNames[idx], sd.project.Dir)
|
|
if err != nil {
|
|
sd.setDone(err)
|
|
return err
|
|
}
|
|
|
|
defer os.Remove(tarFile) //nolint: errcheck // defered
|
|
|
|
// copy the file directly instead of adding it in the tar archive
|
|
log.Info().Str("image", tarFile).Msg("Transferring image...")
|
|
if err := sd.conn.CopyFile(tarFile, filepath.Base(tarFile)); err != nil {
|
|
return err
|
|
}
|
|
log.Info().Str("image", tarFile).Msg("image transferred with success")
|
|
}
|
|
|
|
if envFilePath := sd.getEnvPath(); envFilePath != "" {
|
|
filesToArchive = append(
|
|
filesToArchive,
|
|
envFilePath,
|
|
)
|
|
log.Info().Msg(".env file added to the archive for deployment")
|
|
}
|
|
|
|
filesToArchive = append(
|
|
filesToArchive,
|
|
sd.getComposePath(),
|
|
)
|
|
|
|
archivePath, err := utils.CreateArchive(
|
|
sd.project.Dir,
|
|
fmt.Sprintf("%s-%s", sd.project.Name, "swarm"),
|
|
filesToArchive...)
|
|
if err != nil {
|
|
sd.setDone(err)
|
|
return err
|
|
}
|
|
|
|
sd.archivePath = archivePath
|
|
|
|
log.Info().Str("archive", archivePath).Msg("swarm archive built")
|
|
return nil
|
|
}
|
|
|
|
func (sd *SwarmDeployer) Deploy() error {
|
|
sd.processing.Store(true)
|
|
defer sd.processing.Store(false)
|
|
|
|
select {
|
|
case <-sd.ctx.Done():
|
|
sd.setDone(nil)
|
|
return fmt.Errorf("%w, swarm deployment skipped", ErrContextDone)
|
|
default:
|
|
}
|
|
|
|
if sd.archivePath == "" {
|
|
sd.setDone(ErrSwarmDeployerNoArchive)
|
|
return ErrSwarmDeployerNoArchive
|
|
}
|
|
|
|
log.Info().Str("archive", sd.archivePath).Msg("deploying archive to swarm...")
|
|
|
|
if err := sd.drem.LoadImages(sd.project.Deps.Swarm.ImageNames...); err != nil {
|
|
sd.setDone(err)
|
|
return err
|
|
}
|
|
|
|
archiveDestPath := filepath.Base(sd.archivePath)
|
|
if err := sd.conn.CopyFile(sd.archivePath, archiveDestPath); err != nil {
|
|
sd.setDone(err)
|
|
return err
|
|
}
|
|
|
|
if _, err := sd.conn.Execute(fmt.Sprintf("tar xzvf %s", archiveDestPath)); err != nil {
|
|
sd.setDone(err)
|
|
return err
|
|
}
|
|
|
|
log.Info().Str("project", sd.project.Name).Msg("deploying swarm project...")
|
|
composeFileBase := filepath.Base(sd.getComposePath())
|
|
if err := sd.drem.DeployStack(sd.ctx, sd.project.Name, composeFileBase, docker.WithCheckState()); err != nil {
|
|
sd.setDone(err)
|
|
return err
|
|
}
|
|
|
|
log.Info().Msg("swarm deployment done with success")
|
|
|
|
sd.setDone(nil)
|
|
return nil
|
|
}
|
|
|
|
func (sd *SwarmDeployer) Destroy() error {
|
|
sd.processing.Store(true)
|
|
defer sd.processing.Store(false)
|
|
|
|
log.Info().Str("project", sd.project.Name).Msg("destroying swarm project...")
|
|
if err := sd.drem.DestroyStack(sd.ctx, sd.project.Name, docker.WithCheckState()); err != nil {
|
|
sd.setDone(err)
|
|
return err
|
|
}
|
|
|
|
log.Info().Msg("swarm undeployment done with success")
|
|
|
|
sd.setDone(nil)
|
|
return nil
|
|
}
|