restructure json conf + deploy nginx assets
This commit is contained in:
parent
0dae6ae400
commit
07f096a0a5
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
map.json
|
map.json
|
||||||
|
|
||||||
hmdeploy
|
hmdeploy
|
||||||
|
hmdeploy-linux*
|
||||||
@ -10,7 +10,10 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrContextDone = errors.New("unable to execute, context done")
|
var (
|
||||||
|
ErrContextDone = errors.New("unable to execute, context done")
|
||||||
|
ErrEmptyArchive = errors.New("no file to add to the archive")
|
||||||
|
)
|
||||||
|
|
||||||
type IDeployer interface {
|
type IDeployer interface {
|
||||||
Type() DeployerType
|
Type() DeployerType
|
||||||
@ -48,7 +51,8 @@ type deployer struct { //nolint:govet // ll
|
|||||||
type_ DeployerType
|
type_ DeployerType
|
||||||
errFlag error
|
errFlag error
|
||||||
|
|
||||||
project *models.Project
|
project *models.Project
|
||||||
|
archivePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDeployer(ctx context.Context, type_ DeployerType, project *models.Project) *deployer {
|
func newDeployer(ctx context.Context, type_ DeployerType, project *models.Project) *deployer {
|
||||||
|
|||||||
@ -3,9 +3,12 @@ package deployers
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"gitea.thegux.fr/hmdeploy/connection"
|
"gitea.thegux.fr/hmdeploy/connection"
|
||||||
"gitea.thegux.fr/hmdeploy/models"
|
"gitea.thegux.fr/hmdeploy/models"
|
||||||
|
"gitea.thegux.fr/hmdeploy/utils"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,12 +43,28 @@ func NewNginxDeployer(
|
|||||||
return nd, nil
|
return nd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nd NginxDeployer) getConfPath() string {
|
||||||
|
return nd.project.GetNginxConfPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nd NginxDeployer) getAssetsPath() string {
|
||||||
|
return nd.project.GetNginxAssetsPath()
|
||||||
|
}
|
||||||
|
|
||||||
func (nd *NginxDeployer) close() error {
|
func (nd *NginxDeployer) close() error {
|
||||||
return nd.conn.Close()
|
return nd.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nd *NginxDeployer) clean() (err error) {
|
func (nd *NginxDeployer) clean() (err error) {
|
||||||
_, err = nd.conn.Execute("rm -f " + nd.project.Name + ".conf")
|
if err = os.Remove(nd.archivePath); err != nil {
|
||||||
|
log.Err(err).Str("archive", nd.archivePath).Msg("unable to clean local nginx archive file")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := "rm -rf nginx.conf build/ *.tar.gz"
|
||||||
|
if ap := nd.getAssetsPath(); ap != "" {
|
||||||
|
cmd += " " + filepath.Base(nd.getAssetsPath())
|
||||||
|
}
|
||||||
|
_, err = nd.conn.Execute(cmd)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,20 +94,36 @@ func (nd *NginxDeployer) Build() error {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
nginxConf := nd.project.Name + ".conf"
|
log.Info().Msg("building nginx archive for deployment...")
|
||||||
|
|
||||||
log.Info().Str("nginx", nginxConf).Msg("transferring nginx conf...")
|
filesToArchive := []string{}
|
||||||
|
if ap := nd.getAssetsPath(); ap != "" {
|
||||||
|
filesToArchive = append(filesToArchive, ap)
|
||||||
|
}
|
||||||
|
if cp := nd.getConfPath(); cp != "" {
|
||||||
|
filesToArchive = append(filesToArchive, cp)
|
||||||
|
}
|
||||||
|
|
||||||
if err := nd.conn.CopyFile(nd.project.Deps.NginxFile, nginxConf); err != nil {
|
if len(filesToArchive) == 0 {
|
||||||
nd.setDone(err)
|
return ErrEmptyArchive
|
||||||
|
}
|
||||||
|
|
||||||
|
archivePath, err := utils.CreateArchive(
|
||||||
|
nd.project.Dir,
|
||||||
|
fmt.Sprintf("%s-%s", nd.project.Name, "nginx"),
|
||||||
|
filesToArchive...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Str("nginx", nginxConf).Msg("nginx conf transferred with success")
|
nd.archivePath = archivePath
|
||||||
|
|
||||||
|
log.Info().Str("archive", archivePath).Msg("nginx archive built")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nd *NginxDeployer) Deploy() (err error) {
|
func (nd *NginxDeployer) Deploy() error {
|
||||||
nd.processing.Store(true)
|
nd.processing.Store(true)
|
||||||
defer nd.processing.Store(false)
|
defer nd.processing.Store(false)
|
||||||
|
|
||||||
@ -99,25 +134,55 @@ func (nd *NginxDeployer) Deploy() (err error) {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
nginxConf := nd.project.Name + ".conf"
|
archiveDestPath := filepath.Base(nd.archivePath)
|
||||||
|
if err := nd.conn.CopyFile(nd.archivePath, archiveDestPath); err != nil {
|
||||||
log.Info().Str("nginx", nginxConf).Msg("deploying nginx conf...")
|
nd.setDone(err)
|
||||||
|
return err
|
||||||
_, err = nd.conn.Execute(
|
|
||||||
fmt.Sprintf(
|
|
||||||
"cp %s /etc/nginx/sites-available && ln -sf /etc/nginx/sites-available/%s /etc/nginx/sites-enabled/%s",
|
|
||||||
nginxConf,
|
|
||||||
nginxConf,
|
|
||||||
nginxConf,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
nd.setDone(err)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
log.Info().Str("nginx", nginxConf).Msg("nginx conf successfully deployed")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
if _, err := nd.conn.Execute(fmt.Sprintf("tar xzvf %s", archiveDestPath)); err != nil {
|
||||||
|
nd.setDone(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ap := nd.getAssetsPath(); ap != "" {
|
||||||
|
log.Info().Msg("deploying nginx assets...")
|
||||||
|
|
||||||
|
if _, err := nd.conn.Execute(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"rm -rf /var/www/static/%s/* && mkdir -p /var/www/static/%s && mv %s/* /var/www/static/%s",
|
||||||
|
nd.project.Name,
|
||||||
|
nd.project.Name,
|
||||||
|
filepath.Base(nd.getAssetsPath()),
|
||||||
|
nd.project.Name,
|
||||||
|
),
|
||||||
|
); err != nil {
|
||||||
|
nd.setDone(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cp := nd.getConfPath(); cp != "" {
|
||||||
|
nginxConf := nd.project.Name + ".conf"
|
||||||
|
log.Info().Str("nginx", nginxConf).Msg("deploying nginx conf...")
|
||||||
|
|
||||||
|
if _, err := nd.conn.Execute(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"mv %s /etc/nginx/sites-available/%s && ln -sf /etc/nginx/sites-available/%s /etc/nginx/sites-enabled/%s",
|
||||||
|
filepath.Base(cp),
|
||||||
|
nginxConf,
|
||||||
|
nginxConf,
|
||||||
|
nginxConf,
|
||||||
|
),
|
||||||
|
); err != nil {
|
||||||
|
nd.setDone(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Msg("nginx project successfully deployed")
|
||||||
|
nd.setDone(nil)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nd *NginxDeployer) Destroy() (err error) {
|
func (nd *NginxDeployer) Destroy() (err error) {
|
||||||
|
|||||||
@ -19,10 +19,9 @@ var ErrSwarmDeployerNoArchive = errors.New("no archive found to be deployed")
|
|||||||
// SwarmDeployer handles the deployment of a Docker service on the swarm instance.
|
// SwarmDeployer handles the deployment of a Docker service on the swarm instance.
|
||||||
type SwarmDeployer struct {
|
type SwarmDeployer struct {
|
||||||
*deployer
|
*deployer
|
||||||
conn connection.IConnection
|
conn connection.IConnection
|
||||||
dloc docker.IClient
|
dloc docker.IClient
|
||||||
drem *docker.RemoteClient
|
drem *docker.RemoteClient
|
||||||
archivePath string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ IDeployer = (*SwarmDeployer)(nil)
|
var _ IDeployer = (*SwarmDeployer)(nil)
|
||||||
@ -54,6 +53,14 @@ func NewSwarmDeployer(
|
|||||||
return sd, nil
|
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 {
|
func (sd *SwarmDeployer) close() error {
|
||||||
return sd.conn.Close()
|
return sd.conn.Close()
|
||||||
}
|
}
|
||||||
@ -119,7 +126,7 @@ func (sd *SwarmDeployer) Build() error {
|
|||||||
log.Info().Str("image", tarFile).Msg("image transferred with success")
|
log.Info().Str("image", tarFile).Msg("image transferred with success")
|
||||||
}
|
}
|
||||||
|
|
||||||
if envFilePath := sd.project.Deps.EnvFile; envFilePath != "" {
|
if envFilePath := sd.getEnvPath(); envFilePath != "" {
|
||||||
filesToArchive = append(
|
filesToArchive = append(
|
||||||
filesToArchive,
|
filesToArchive,
|
||||||
envFilePath,
|
envFilePath,
|
||||||
@ -129,7 +136,7 @@ func (sd *SwarmDeployer) Build() error {
|
|||||||
|
|
||||||
filesToArchive = append(
|
filesToArchive = append(
|
||||||
filesToArchive,
|
filesToArchive,
|
||||||
sd.project.Deps.ComposeFile,
|
sd.getComposePath(),
|
||||||
)
|
)
|
||||||
|
|
||||||
archivePath, err := utils.CreateArchive(
|
archivePath, err := utils.CreateArchive(
|
||||||
@ -171,9 +178,6 @@ func (sd *SwarmDeployer) Deploy() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
archiveDestPath := filepath.Base(sd.archivePath)
|
archiveDestPath := filepath.Base(sd.archivePath)
|
||||||
log.Info().
|
|
||||||
Str("archive", sd.archivePath).
|
|
||||||
Msg("archive built with success, tranferring to swarm for deployment...")
|
|
||||||
if err := sd.conn.CopyFile(sd.archivePath, archiveDestPath); err != nil {
|
if err := sd.conn.CopyFile(sd.archivePath, archiveDestPath); err != nil {
|
||||||
sd.setDone(err)
|
sd.setDone(err)
|
||||||
return err
|
return err
|
||||||
@ -185,7 +189,7 @@ func (sd *SwarmDeployer) Deploy() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Str("project", sd.project.Name).Msg("deploying swarm project...")
|
log.Info().Str("project", sd.project.Name).Msg("deploying swarm project...")
|
||||||
composeFileBase := filepath.Base(sd.project.Deps.ComposeFile)
|
composeFileBase := filepath.Base(sd.getComposePath())
|
||||||
if err := sd.drem.DeployStack(sd.ctx, sd.project.Name, composeFileBase, docker.WithCheckState()); err != nil {
|
if err := sd.drem.DeployStack(sd.ctx, sd.project.Name, composeFileBase, docker.WithCheckState()); err != nil {
|
||||||
sd.setDone(err)
|
sd.setDone(err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
stateTickDuration = 4 * time.Second
|
stateTickDuration = 4 * time.Second
|
||||||
defaultStateTimeout = 30 * time.Second
|
defaultStateTimeout = 10 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
13
hmdeploy.example.json
Normal file
13
hmdeploy.example.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "my-api",
|
||||||
|
"dependencies": {
|
||||||
|
"swarm": {
|
||||||
|
"env": ".env",
|
||||||
|
"compose": "docker-compose.deploy.yml"
|
||||||
|
},
|
||||||
|
"nginx": {
|
||||||
|
"conf": "nginx.conf",
|
||||||
|
"assets": "assets/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
main.go
66
main.go
@ -136,10 +136,13 @@ func (d *Deployers) generateTasksTree() scheduler.Tasks {
|
|||||||
func (d *Deployers) waitForCompletion(s *scheduler.Scheduler) error {
|
func (d *Deployers) waitForCompletion(s *scheduler.Scheduler) error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
deps := []deployers.IDeployer{d.sd}
|
deps := []deployers.IDeployer{}
|
||||||
if d.nd != nil {
|
if d.nd != nil {
|
||||||
deps = append(deps, d.nd)
|
deps = append(deps, d.nd)
|
||||||
}
|
}
|
||||||
|
if d.sd != nil {
|
||||||
|
deps = append(deps, d.sd)
|
||||||
|
}
|
||||||
|
|
||||||
for idx := range deps {
|
for idx := range deps {
|
||||||
if d := deps[idx]; d != nil {
|
if d := deps[idx]; d != nil {
|
||||||
@ -238,14 +241,40 @@ func loadHMMap() (models.HMMap, error) {
|
|||||||
return hmmap, nil
|
return hmmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initSwarmDeployer(
|
||||||
|
ctx context.Context,
|
||||||
|
project *models.Project,
|
||||||
|
swarmNet *models.HMNetInfo,
|
||||||
|
fnCancel context.CancelFunc,
|
||||||
|
) (deployers.SwarmDeployer, error) {
|
||||||
|
dloc := docker.NewLocalClient()
|
||||||
|
drem, err := docker.NewRemoteClient(swarmNet)
|
||||||
|
if err != nil {
|
||||||
|
return deployers.SwarmDeployer{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sd, err := deployers.NewSwarmDeployer(ctx, project, swarmNet, &dloc, &drem)
|
||||||
|
if err != nil {
|
||||||
|
return deployers.SwarmDeployer{}, fmt.Errorf(
|
||||||
|
"%w, unable to init swarm deployer, err=%v",
|
||||||
|
ErrDeployerInit,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fnCancel != nil {
|
||||||
|
sd.SetCancellationFunc(fnCancel)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd, nil
|
||||||
|
}
|
||||||
|
|
||||||
// initDeployers instanciates from `Project` and `HMMap` needed deployers and returns them.
|
// initDeployers instanciates from `Project` and `HMMap` needed deployers and returns them.
|
||||||
//
|
//
|
||||||
// You can provide as an optional arg:
|
// You can provide as an optional arg:
|
||||||
// - WithGlobalCancellation(fnCancel context.CancelFunc): close the global context, notifying all deployers to stop
|
// - WithGlobalCancellation(fnCancel context.CancelFunc): close the global context, notifying all deployers to stop
|
||||||
// - WithNoSwarm(): disable Swarm deployment
|
// - WithNoSwarm(): disable Swarm deployment
|
||||||
// - WithNoNginx(): disable Nginx deployment
|
// - WithNoNginx(): disable Nginx deployment
|
||||||
//
|
|
||||||
//nolint:funlen // not that so much...
|
|
||||||
func initDeployers(
|
func initDeployers(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
hmmap *models.HMMap,
|
hmmap *models.HMMap,
|
||||||
@ -261,34 +290,21 @@ func initDeployers(
|
|||||||
destroy: opt.destroy,
|
destroy: opt.destroy,
|
||||||
}
|
}
|
||||||
|
|
||||||
swarmNet := hmmap.GetSwarmNetInfo()
|
if !opt.noSwarm && project.GetComposePath() != "" {
|
||||||
if swarmNet == nil {
|
swarmNet := hmmap.GetSwarmNetInfo()
|
||||||
return deps, fmt.Errorf("%w, swarm net info does not exist", ErrNetInfoNotFound)
|
if swarmNet == nil {
|
||||||
}
|
return deps, fmt.Errorf("%w, swarm net info does not exist", ErrNetInfoNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
dloc := docker.NewLocalClient()
|
sd, err := initSwarmDeployer(ctx, project, swarmNet, opt.fnCancel)
|
||||||
drem, err := docker.NewRemoteClient(swarmNet)
|
|
||||||
if err != nil {
|
|
||||||
return deps, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !opt.noSwarm {
|
|
||||||
sd, err := deployers.NewSwarmDeployer(ctx, project, swarmNet, &dloc, &drem)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return deps, fmt.Errorf(
|
return deps, err
|
||||||
"%w, unable to init swarm deployer, err=%v",
|
|
||||||
ErrDeployerInit,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
deps.sd = &sd
|
|
||||||
|
|
||||||
if opt.fnCancel != nil {
|
deps.sd = &sd
|
||||||
sd.SetCancellationFunc(opt.fnCancel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opt.noNginx && project.Deps.NginxFile != "" {
|
if !opt.noNginx && project.GetNginxConfPath() != "" {
|
||||||
nginxNet := hmmap.GetNginxNetInfo()
|
nginxNet := hmmap.GetNginxNetInfo()
|
||||||
if nginxNet == nil {
|
if nginxNet == nil {
|
||||||
return deps, fmt.Errorf("%w, nginx net info does not exist", ErrNetInfoNotFound)
|
return deps, fmt.Errorf("%w, nginx net info does not exist", ErrNetInfoNotFound)
|
||||||
|
|||||||
@ -22,10 +22,26 @@ const (
|
|||||||
|
|
||||||
var ErrProjectConfFile = errors.New("project error")
|
var ErrProjectConfFile = errors.New("project error")
|
||||||
|
|
||||||
func getFilepath(baseDir, filePath string) (string, error) {
|
type filepathOption struct {
|
||||||
filePath = filepath.Join(baseDir, filePath)
|
isDir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type fnFilepathOption func(*filepathOption)
|
||||||
|
|
||||||
|
func IsDir() fnFilepathOption {
|
||||||
|
return func(fo *filepathOption) {
|
||||||
|
fo.isDir = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFilepath(baseDir, filePath string, options ...fnFilepathOption) (string, error) {
|
||||||
|
var opts filepathOption
|
||||||
|
for _, opt := range options {
|
||||||
|
opt(&opts)
|
||||||
|
}
|
||||||
|
|
||||||
if !filepath.IsAbs(filePath) {
|
if !filepath.IsAbs(filePath) {
|
||||||
|
filePath = filepath.Join(baseDir, filePath)
|
||||||
filePath, err := filepath.Abs(filePath) //nolint: govet
|
filePath, err := filepath.Abs(filePath) //nolint: govet
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return filePath, fmt.Errorf(
|
return filePath, fmt.Errorf(
|
||||||
@ -47,7 +63,7 @@ func getFilepath(baseDir, filePath string) (string, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileInfo.IsDir() {
|
if fileInfo.IsDir() && !opts.isDir {
|
||||||
return filePath, fmt.Errorf(
|
return filePath, fmt.Errorf(
|
||||||
"%w, file=%s, err=%s",
|
"%w, file=%s, err=%s",
|
||||||
ErrProjectConfFile,
|
ErrProjectConfFile,
|
||||||
@ -56,6 +72,15 @@ func getFilepath(baseDir, filePath string) (string, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !fileInfo.IsDir() && opts.isDir {
|
||||||
|
return filePath, fmt.Errorf(
|
||||||
|
"%w, file=%s, err=%s",
|
||||||
|
ErrProjectConfFile,
|
||||||
|
filePath,
|
||||||
|
"must be a dir",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return filePath, nil
|
return filePath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,43 +89,73 @@ type Project struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Dir string
|
Dir string
|
||||||
Deps struct {
|
Deps struct {
|
||||||
EnvFile string `json:"env"`
|
Swarm struct {
|
||||||
ComposeFile string `json:"compose"`
|
EnvFile string `json:"env"`
|
||||||
NginxFile string `json:"nginx"`
|
ComposeFile string `json:"compose"`
|
||||||
|
} `json:"swarm"`
|
||||||
|
Nginx struct {
|
||||||
|
Conf string `json:"conf"`
|
||||||
|
Assets string `json:"assets"`
|
||||||
|
}
|
||||||
} `json:"dependencies"`
|
} `json:"dependencies"`
|
||||||
ImageNames []string `json:"images"`
|
ImageNames []string `json:"images"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Project) validate() error {
|
func (p *Project) validate() error {
|
||||||
cpath, err := getFilepath(p.Dir, p.Deps.ComposeFile)
|
if compf := p.Deps.Swarm.ComposeFile; compf != "" {
|
||||||
if err != nil {
|
cpath, err := getFilepath(p.Dir, compf)
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.Deps.ComposeFile = cpath
|
|
||||||
|
|
||||||
if p.Deps.EnvFile != "" {
|
|
||||||
epath, err := getFilepath(p.Dir, p.Deps.EnvFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.Deps.EnvFile = epath
|
|
||||||
|
p.Deps.Swarm.ComposeFile = cpath
|
||||||
} else {
|
} else {
|
||||||
log.Warn().Msg("no .env file provided, hoping one it's set elsewhere...")
|
log.Warn().Msg("no docker-compose file provided, Swarm deployment discards")
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Deps.NginxFile != "" {
|
if env := p.Deps.Swarm.EnvFile; env != "" {
|
||||||
npath, err := getFilepath(p.Dir, p.Deps.NginxFile)
|
epath, err := getFilepath(p.Dir, env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.Deps.NginxFile = npath
|
p.Deps.Swarm.EnvFile = epath
|
||||||
} else {
|
}
|
||||||
log.Warn().Msg("no Nginx conf file provided, Nginx deployment discarded")
|
|
||||||
|
if conf := p.Deps.Nginx.Conf; conf != "" {
|
||||||
|
npath, err := getFilepath(p.Dir, conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Deps.Nginx.Conf = npath
|
||||||
|
}
|
||||||
|
|
||||||
|
if assets := p.Deps.Nginx.Assets; assets != "" {
|
||||||
|
apath, err := getFilepath(p.Dir, assets, IsDir())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Deps.Nginx.Assets = apath
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Project) GetComposePath() string {
|
||||||
|
return p.Deps.Swarm.ComposeFile
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Project) GetEnvPath() string {
|
||||||
|
return p.Deps.Swarm.EnvFile
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Project) GetNginxConfPath() string {
|
||||||
|
return p.Deps.Nginx.Conf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Project) GetNginxAssetsPath() string {
|
||||||
|
return p.Deps.Nginx.Assets
|
||||||
|
}
|
||||||
|
|
||||||
// ProjectFromDir instantiates a new project from a directory path.
|
// ProjectFromDir instantiates a new project from a directory path.
|
||||||
//
|
//
|
||||||
// The directory path must refers to the path including the `.homeserver` dir not
|
// The directory path must refers to the path including the `.homeserver` dir not
|
||||||
|
|||||||
@ -17,25 +17,75 @@ import (
|
|||||||
|
|
||||||
const confirmChar = "Y"
|
const confirmChar = "Y"
|
||||||
|
|
||||||
func addToArchive(tw *tar.Writer, filename string) error {
|
// addToArchive adds a file or directory (recursively) to the tar writer.
|
||||||
file, err := os.Open(filename)
|
// The baseName is the desired root name in the archive.
|
||||||
|
//
|
||||||
|
//nolint:funlen // it's ok
|
||||||
|
func addToArchive(tw *tar.Writer, filename, baseName, basePath string) error {
|
||||||
|
// resolve the absolute path to eliminate relative components like ../
|
||||||
|
absPath, err := filepath.Abs(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to resolve absolute path for %s: %v", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(absPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close() //nolint: errcheck // defered
|
defer file.Close() //nolint: errcheck // deferred
|
||||||
|
|
||||||
info, err := file.Stat()
|
info, err := file.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
header, err := tar.FileInfoHeader(info, info.Name())
|
// compute the relative path within the archive
|
||||||
|
// start with baseName and append the relative path from basePath
|
||||||
|
relPath, err := filepath.Rel(basePath, absPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to compute relative path for %s: %v", absPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// combine baseName with the relative path
|
||||||
|
if relPath == "." {
|
||||||
|
relPath = baseName
|
||||||
|
} else {
|
||||||
|
relPath = filepath.Join(baseName, relPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
relPath = filepath.ToSlash(relPath)
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
header, err := tar.FileInfoHeader(info, "") //nolint:govet // shadow ok
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
header.Name = relPath
|
||||||
|
if err := tw.WriteHeader(header); err != nil { //nolint:govet // shadow ok
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
entryPath := filepath.Join(absPath, entry.Name())
|
||||||
|
if err := addToArchive(tw, entryPath, baseName, basePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := tar.FileInfoHeader(info, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
header.Name = filepath.Base(file.Name())
|
header.Name = relPath
|
||||||
|
if err := tw.WriteHeader(header); err != nil { //nolint:govet // shadow ok
|
||||||
if err = tw.WriteHeader(header); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +93,8 @@ func addToArchive(tw *tar.Writer, filename string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateArchive creates a gzip tar archive in the `destDir` path including `files`.
|
// CreateArchive creates a gzip tar archive in the `destDir` path including `files` and returns
|
||||||
|
// the generated archive path.
|
||||||
func CreateArchive(destDir, name string, files ...string) (string, error) {
|
func CreateArchive(destDir, name string, files ...string) (string, error) {
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
archivePath := filepath.Join(
|
archivePath := filepath.Join(
|
||||||
@ -55,16 +106,22 @@ func CreateArchive(destDir, name string, files ...string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to create archive=%s, err=%v", archivePath, err)
|
return "", fmt.Errorf("unable to create archive=%s, err=%v", archivePath, err)
|
||||||
}
|
}
|
||||||
defer file.Close() //nolint: errcheck // defered
|
defer file.Close() //nolint: errcheck // deferred
|
||||||
|
|
||||||
gw := gzip.NewWriter(file)
|
gw := gzip.NewWriter(file)
|
||||||
defer gw.Close() //nolint: errcheck // defered
|
defer gw.Close() //nolint: errcheck // deferred
|
||||||
|
|
||||||
tw := tar.NewWriter(gw)
|
tw := tar.NewWriter(gw)
|
||||||
defer tw.Close() //nolint: errcheck // defered
|
defer tw.Close() //nolint: errcheck // deferred
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if err := addToArchive(tw, f); err != nil {
|
absPath, err := filepath.Abs(f)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to resolve absolute path for %s: %v", f, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
baseName := filepath.Base(f)
|
||||||
|
if err := addToArchive(tw, f, baseName, absPath); err != nil {
|
||||||
return "", fmt.Errorf(
|
return "", fmt.Errorf(
|
||||||
"unable to add file=%s to archive=%s, err=%v",
|
"unable to add file=%s to archive=%s, err=%v",
|
||||||
f,
|
f,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user