hmdeploy/deployers/swarm.go
2025-04-03 18:12:06 +02:00

179 lines
4.1 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")
type SwarmDeployer struct {
*deployer
conn connection.IConnection
dcli docker.IClient
archivePath string
}
var _ IDeployer = (*SwarmDeployer)(nil)
func NewSwarmDeployer(
ctx context.Context,
project *models.Project,
netInfo *models.HMNetInfo,
dockerClient docker.IClient,
) (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, err
}
sd.conn = &conn
sd.dcli = dockerClient
sd.deployer = newDeployer(ctx, Swarm, project)
return sd, nil
}
func (sd *SwarmDeployer) close() error {
return sd.conn.Close()
}
func (sd *SwarmDeployer) clean() (err error) {
defer os.Remove(sd.archivePath) //nolint: errcheck // defered
_, 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
}
func (sd *SwarmDeployer) Build() error {
sd.processing.Store(true)
defer sd.processing.Store(false)
select {
case <-sd.ctx.Done():
sd.errFlag = ErrContextDone
return fmt.Errorf("%w, swarm project build skipped", ErrContextDone)
default:
}
log.Info().Msg("building swarm archive for deployment...")
filesToArchive := []string{}
if imageName := sd.project.ImageName; imageName != "" {
tarFile, err := sd.dcli.Save(imageName, sd.project.Dir)
if err != nil {
sd.setDone(err)
return err
}
defer os.Remove(tarFile) //nolint: errcheck // defered
filesToArchive = append(filesToArchive, tarFile)
log.Info().Str("image", imageName).Msg("image added to archive")
}
if envFilePath := sd.project.Deps.EnvFile; envFilePath != "" {
filesToArchive = append(
filesToArchive,
filepath.Join(sd.project.Dir, filepath.Base(envFilePath)),
)
log.Info().Msg(".env file added to the archive for deployment")
}
composeFileBase := filepath.Base(sd.project.Deps.ComposeFile)
filesToArchive = append(filesToArchive, filepath.Join(sd.project.Dir, composeFileBase))
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.errFlag = ErrContextDone
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...")
archiveDestPath := filepath.Base(sd.archivePath)
log.Info().
Str("archive", sd.archivePath).
Msg("archive built with success, tranfering to swarm for deployment...")
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.project.Deps.ComposeFile)
if _, err := sd.conn.Execute(fmt.Sprintf("docker stack deploy -c %s %s", composeFileBase, sd.project.Name)); err != nil {
sd.setDone(err)
return err
}
log.Info().Msg("swarm deployment done with success")
sd.setDone(nil)
return nil
}