add destroy command

This commit is contained in:
rmanach 2025-04-04 16:59:46 +02:00
parent 43a78573f5
commit cfdaa9538b
6 changed files with 98 additions and 27 deletions

View File

@ -5,7 +5,7 @@ run: lint
build: lint
@echo "building binary..."
@go build -o $(BIN_NAME) -race main.go && echo "$(BIN_NAME) built"
@go build -o $(BIN_NAME) main.go && echo "$(BIN_NAME) built"
install:
@$(shell whereis $(BIN_NAME) | cut -d ' ' -f2 | xargs rm -f)

View File

@ -15,12 +15,12 @@ You have an Nginx instance in front of a Docker Swarm instance (ip are here for
|docker swarm | |nginx |
| | | |
|ip: 10.0.0.2 | |ip: 10.0.0.1 |
| | | | HTTP Request
| +----------+ |<-----------
| | | |
| | | |
| | | |
| | | |
| +------+ +------+ +----------+ |<-----------
| |app1 | |app2 | | | | HTTP request
| | | | | | | |
| |:8080 | |:8081 | | | |
| +------+ +------+ | | |
| | | |
| | | |
+-------------------------+ +-------------------------+
@ -120,14 +120,18 @@ The binary is then installed in your **$GOPATH/bin**.
# you can then launch the program directly
hmdeploy
# if you want the deploy a specific project
# if you want to deploy a specific project
# use --path to point the project dir where `.homeserver` is located
hmdeploy --path /path/my-project
# if you want to undeploy a specific project
# do not worry, volumes are preserved
hmdeploy --path /path/my-project --destroy
```
## Next steps
* Improve the CLI arguments
* Destroy
* ~~Destroy~~
* post-install script
* Deals with bugs

View File

@ -16,6 +16,7 @@ type IDeployer interface {
Type() DeployerType
Deploy() error
Build() error
Destroy() error
Clear() error
Error() error
Done() <-chan struct{}
@ -65,7 +66,7 @@ func newDeployer(ctx context.Context, type_ DeployerType, project *models.Projec
func (d *deployer) setDone(err error) {
d.chDone <- struct{}{}
d.errFlag = err
d.errFlag = errors.Join(d.ctx.Err(), err)
if err != nil && d.fnCancel != nil {
d.fnCancel()
}
@ -110,6 +111,10 @@ func (d *deployer) Done() <-chan struct{} {
timeout := time.NewTicker(GracefulTimeout)
tick := time.NewTicker(time.Second)
defer tick.Stop()
defer timeout.Stop()
for {
select {
case <-timeout.C:

View File

@ -70,7 +70,7 @@ func (nd *NginxDeployer) Build() error {
select {
case <-nd.ctx.Done():
nd.errFlag = ErrContextDone
nd.setDone(nil)
return fmt.Errorf("%w, build nginx archive skipped", ErrContextDone)
default:
}
@ -94,7 +94,7 @@ func (nd *NginxDeployer) Deploy() (err error) {
select {
case <-nd.ctx.Done():
nd.errFlag = ErrContextDone
nd.setDone(nil)
return fmt.Errorf("%w, nginx deployment skipped", ErrContextDone)
default:
}
@ -119,3 +119,33 @@ func (nd *NginxDeployer) Deploy() (err error) {
return err
}
func (nd *NginxDeployer) Destroy() (err error) {
nd.processing.Store(true)
defer nd.processing.Store(false)
select {
case <-nd.ctx.Done():
nd.setDone(nil)
return fmt.Errorf("%w, nginx destroy skipped", ErrContextDone)
default:
}
nginxConf := nd.project.Name + ".conf"
log.Info().Str("nginx", nginxConf).Msg("destroying nginx conf...")
_, err = nd.conn.Execute(
fmt.Sprintf(
"unlink /etc/nginx/sites-enabled/%s",
nginxConf,
),
)
nd.setDone(err)
if err == nil {
log.Info().Str("nginx", nginxConf).Msg("nginx conf successfully destroyed")
}
return err
}

View File

@ -56,7 +56,9 @@ func (sd *SwarmDeployer) close() error {
}
func (sd *SwarmDeployer) clean() (err error) {
defer os.Remove(sd.archivePath) //nolint: errcheck // defered
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),
)
@ -89,7 +91,7 @@ func (sd *SwarmDeployer) Build() error {
select {
case <-sd.ctx.Done():
sd.errFlag = ErrContextDone
sd.setDone(nil)
return fmt.Errorf("%w, swarm project build skipped", ErrContextDone)
default:
}
@ -148,7 +150,7 @@ func (sd *SwarmDeployer) Deploy() error {
select {
case <-sd.ctx.Done():
sd.errFlag = ErrContextDone
sd.setDone(nil)
return fmt.Errorf("%w, swarm deployment skipped", ErrContextDone)
default:
}
@ -186,3 +188,19 @@ func (sd *SwarmDeployer) Deploy() error {
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.conn.Execute(fmt.Sprintf("docker stack rm %s", sd.project.Name)); err != nil {
sd.setDone(err)
return err
}
log.Info().Msg("swarm undeployment done with success")
sd.setDone(nil)
return nil
}

22
main.go
View File

@ -136,7 +136,7 @@ func initDeployers(
// generateTasksTree returns a list of linked `Task` to submit.
//
// It's here that all tasks are linked each other to provide the deployment ordering.
func generateTasksTree(deployers []deployers.IDeployer) ([]*scheduler.Task, error) {
func generateTasksTree(deployers []deployers.IDeployer, destroy bool) ([]*scheduler.Task, error) {
if len(deployers) != MaxDeployers {
return nil, fmt.Errorf("%w, deployers len should be equals to 2", ErrGenerateTasksTree)
}
@ -146,6 +146,13 @@ func generateTasksTree(deployers []deployers.IDeployer) ([]*scheduler.Task, erro
tasks := []*scheduler.Task{}
if destroy {
swarmDestroy := scheduler.NewTask("swarm-destroy", sd.Destroy)
destroyTask := scheduler.NewTask("nginx-destroy", nd.Destroy, swarmDestroy)
tasks = append(tasks, destroyTask)
return tasks, nil
}
var swarmTask *scheduler.Task
if nd != nil {
deployNginx := scheduler.NewTask("nginx-deploy", nd.Deploy)
@ -164,7 +171,11 @@ func generateTasksTree(deployers []deployers.IDeployer) ([]*scheduler.Task, erro
//
// After the completion, deployers `Clear` methods are executed to clean all ressources.
// Then the scheduler is stopped to terminate the engine.
func waitForCompletion(deployers []deployers.IDeployer, s *scheduler.Scheduler) error {
func waitForCompletion(
deployers []deployers.IDeployer,
s *scheduler.Scheduler,
destroy bool,
) error {
var wg sync.WaitGroup
for idx := range deployers {
@ -183,9 +194,11 @@ func waitForCompletion(deployers []deployers.IDeployer, s *scheduler.Scheduler)
for idx := range deployers {
if d := deployers[idx]; d != nil {
errs = append(errs, d.Error())
if !destroy {
s.Submit(scheduler.NewTask(string(d.Type()), d.Clear)) //nolint: errcheck // TODO
}
}
}
s.Stop()
<-s.Done()
@ -204,6 +217,7 @@ func main() {
log.Info().Msg("hmdeploy started")
projectDir := flag.String("path", ".", "define the .homeserver project root dir")
destroy := flag.Bool("destroy", false, "delete the deployed project")
flag.Parse()
hmmap, err := loadHMMap()
@ -225,7 +239,7 @@ func main() {
log.Fatal().Err(err).Msg("unable to init deployers")
}
tasks, err := generateTasksTree(deployers)
tasks, err := generateTasksTree(deployers, *destroy)
if err != nil {
log.Fatal().Err(err).Msg("unable to generate tasks tree")
}
@ -237,7 +251,7 @@ func main() {
tasks...,
)
if err := waitForCompletion(deployers, s); err != nil {
if err := waitForCompletion(deployers, s, *destroy); err != nil {
log.Fatal().
Err(err).
Str("name", project.Name).