2025-05-05 15:24:30 +02:00

176 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")
// 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
sd.dloc = dloc
sd.drem = drem
deployer, err := newDeployer(ctx, Swarm, project, netInfo)
if err != nil {
return sd, err
}
sd.deployer = deployer
return sd, nil
}
func (sd SwarmDeployer) getComposePath() string {
return sd.project.GetComposePath()
}
func (sd SwarmDeployer) getEnvPath() string {
return sd.project.GetEnvPath()
}
// 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
}
if err := sd.copyUntarArchive(); err != nil {
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(), docker.WithBaseDir(sd.deploymentDir)); 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
}