add destroy command
This commit is contained in:
parent
43a78573f5
commit
cfdaa9538b
2
Makefile
2
Makefile
@ -5,7 +5,7 @@ run: lint
|
|||||||
|
|
||||||
build: lint
|
build: lint
|
||||||
@echo "building binary..."
|
@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:
|
install:
|
||||||
@$(shell whereis $(BIN_NAME) | cut -d ' ' -f2 | xargs rm -f)
|
@$(shell whereis $(BIN_NAME) | cut -d ' ' -f2 | xargs rm -f)
|
||||||
|
|||||||
34
README.md
34
README.md
@ -11,19 +11,19 @@ For production environment, use it **with caution**.
|
|||||||
|
|
||||||
You have an Nginx instance in front of a Docker Swarm instance (ip are here for demonstration):
|
You have an Nginx instance in front of a Docker Swarm instance (ip are here for demonstration):
|
||||||
```ascii
|
```ascii
|
||||||
+-------------------------+ +-------------------------+
|
+-------------------------+ +-------------------------+
|
||||||
|docker swarm | |nginx |
|
|docker swarm | |nginx |
|
||||||
| | | |
|
| | | |
|
||||||
|ip: 10.0.0.2 | |ip: 10.0.0.1 |
|
|ip: 10.0.0.2 | |ip: 10.0.0.1 |
|
||||||
| | | | HTTP Request
|
| | | |
|
||||||
| +----------+ |<-----------
|
| +------+ +------+ +----------+ |<-----------
|
||||||
| | | |
|
| |app1 | |app2 | | | | HTTP request
|
||||||
| | | |
|
| | | | | | | |
|
||||||
| | | |
|
| |:8080 | |:8081 | | | |
|
||||||
| | | |
|
| +------+ +------+ | | |
|
||||||
| | | |
|
| | | |
|
||||||
| | | |
|
| | | |
|
||||||
+-------------------------+ +-------------------------+
|
+-------------------------+ +-------------------------+
|
||||||
```
|
```
|
||||||
|
|
||||||
You want to deploy a service and its Nginx conf easyly with a simple CLI ? This tool is for you.
|
You want to deploy a service and its Nginx conf easyly with a simple CLI ? This tool is for you.
|
||||||
@ -120,14 +120,18 @@ The binary is then installed in your **$GOPATH/bin**.
|
|||||||
# you can then launch the program directly
|
# you can then launch the program directly
|
||||||
hmdeploy
|
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
|
# use --path to point the project dir where `.homeserver` is located
|
||||||
hmdeploy --path /path/my-project
|
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
|
## Next steps
|
||||||
* Improve the CLI arguments
|
* Improve the CLI arguments
|
||||||
* Destroy
|
* ~~Destroy~~
|
||||||
* post-install script
|
* post-install script
|
||||||
* Deals with bugs
|
* Deals with bugs
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@ type IDeployer interface {
|
|||||||
Type() DeployerType
|
Type() DeployerType
|
||||||
Deploy() error
|
Deploy() error
|
||||||
Build() error
|
Build() error
|
||||||
|
Destroy() error
|
||||||
Clear() error
|
Clear() error
|
||||||
Error() error
|
Error() error
|
||||||
Done() <-chan struct{}
|
Done() <-chan struct{}
|
||||||
@ -65,7 +66,7 @@ func newDeployer(ctx context.Context, type_ DeployerType, project *models.Projec
|
|||||||
|
|
||||||
func (d *deployer) setDone(err error) {
|
func (d *deployer) setDone(err error) {
|
||||||
d.chDone <- struct{}{}
|
d.chDone <- struct{}{}
|
||||||
d.errFlag = err
|
d.errFlag = errors.Join(d.ctx.Err(), err)
|
||||||
if err != nil && d.fnCancel != nil {
|
if err != nil && d.fnCancel != nil {
|
||||||
d.fnCancel()
|
d.fnCancel()
|
||||||
}
|
}
|
||||||
@ -110,6 +111,10 @@ func (d *deployer) Done() <-chan struct{} {
|
|||||||
|
|
||||||
timeout := time.NewTicker(GracefulTimeout)
|
timeout := time.NewTicker(GracefulTimeout)
|
||||||
tick := time.NewTicker(time.Second)
|
tick := time.NewTicker(time.Second)
|
||||||
|
|
||||||
|
defer tick.Stop()
|
||||||
|
defer timeout.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-timeout.C:
|
case <-timeout.C:
|
||||||
|
|||||||
@ -70,7 +70,7 @@ func (nd *NginxDeployer) Build() error {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case <-nd.ctx.Done():
|
case <-nd.ctx.Done():
|
||||||
nd.errFlag = ErrContextDone
|
nd.setDone(nil)
|
||||||
return fmt.Errorf("%w, build nginx archive skipped", ErrContextDone)
|
return fmt.Errorf("%w, build nginx archive skipped", ErrContextDone)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ func (nd *NginxDeployer) Deploy() (err error) {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case <-nd.ctx.Done():
|
case <-nd.ctx.Done():
|
||||||
nd.errFlag = ErrContextDone
|
nd.setDone(nil)
|
||||||
return fmt.Errorf("%w, nginx deployment skipped", ErrContextDone)
|
return fmt.Errorf("%w, nginx deployment skipped", ErrContextDone)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@ -119,3 +119,33 @@ func (nd *NginxDeployer) Deploy() (err error) {
|
|||||||
|
|
||||||
return err
|
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
|
||||||
|
}
|
||||||
|
|||||||
@ -56,7 +56,9 @@ func (sd *SwarmDeployer) close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sd *SwarmDeployer) clean() (err 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(
|
_, err = sd.conn.Execute(
|
||||||
fmt.Sprintf("rm -f %s %s *.tar.gz *.tar", models.ComposeFile, models.EnvFile),
|
fmt.Sprintf("rm -f %s %s *.tar.gz *.tar", models.ComposeFile, models.EnvFile),
|
||||||
)
|
)
|
||||||
@ -89,7 +91,7 @@ func (sd *SwarmDeployer) Build() error {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case <-sd.ctx.Done():
|
case <-sd.ctx.Done():
|
||||||
sd.errFlag = ErrContextDone
|
sd.setDone(nil)
|
||||||
return fmt.Errorf("%w, swarm project build skipped", ErrContextDone)
|
return fmt.Errorf("%w, swarm project build skipped", ErrContextDone)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@ -148,7 +150,7 @@ func (sd *SwarmDeployer) Deploy() error {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case <-sd.ctx.Done():
|
case <-sd.ctx.Done():
|
||||||
sd.errFlag = ErrContextDone
|
sd.setDone(nil)
|
||||||
return fmt.Errorf("%w, swarm deployment skipped", ErrContextDone)
|
return fmt.Errorf("%w, swarm deployment skipped", ErrContextDone)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@ -186,3 +188,19 @@ func (sd *SwarmDeployer) Deploy() error {
|
|||||||
sd.setDone(nil)
|
sd.setDone(nil)
|
||||||
return 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
|
||||||
|
}
|
||||||
|
|||||||
24
main.go
24
main.go
@ -136,7 +136,7 @@ func initDeployers(
|
|||||||
// generateTasksTree returns a list of linked `Task` to submit.
|
// generateTasksTree returns a list of linked `Task` to submit.
|
||||||
//
|
//
|
||||||
// It's here that all tasks are linked each other to provide the deployment ordering.
|
// 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 {
|
if len(deployers) != MaxDeployers {
|
||||||
return nil, fmt.Errorf("%w, deployers len should be equals to 2", ErrGenerateTasksTree)
|
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{}
|
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
|
var swarmTask *scheduler.Task
|
||||||
if nd != nil {
|
if nd != nil {
|
||||||
deployNginx := scheduler.NewTask("nginx-deploy", nd.Deploy)
|
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.
|
// After the completion, deployers `Clear` methods are executed to clean all ressources.
|
||||||
// Then the scheduler is stopped to terminate the engine.
|
// 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
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
for idx := range deployers {
|
for idx := range deployers {
|
||||||
@ -183,7 +194,9 @@ func waitForCompletion(deployers []deployers.IDeployer, s *scheduler.Scheduler)
|
|||||||
for idx := range deployers {
|
for idx := range deployers {
|
||||||
if d := deployers[idx]; d != nil {
|
if d := deployers[idx]; d != nil {
|
||||||
errs = append(errs, d.Error())
|
errs = append(errs, d.Error())
|
||||||
s.Submit(scheduler.NewTask(string(d.Type()), d.Clear)) //nolint: errcheck // TODO
|
if !destroy {
|
||||||
|
s.Submit(scheduler.NewTask(string(d.Type()), d.Clear)) //nolint: errcheck // TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +217,7 @@ func main() {
|
|||||||
log.Info().Msg("hmdeploy started")
|
log.Info().Msg("hmdeploy started")
|
||||||
|
|
||||||
projectDir := flag.String("path", ".", "define the .homeserver project root dir")
|
projectDir := flag.String("path", ".", "define the .homeserver project root dir")
|
||||||
|
destroy := flag.Bool("destroy", false, "delete the deployed project")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
hmmap, err := loadHMMap()
|
hmmap, err := loadHMMap()
|
||||||
@ -225,7 +239,7 @@ func main() {
|
|||||||
log.Fatal().Err(err).Msg("unable to init deployers")
|
log.Fatal().Err(err).Msg("unable to init deployers")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks, err := generateTasksTree(deployers)
|
tasks, err := generateTasksTree(deployers, *destroy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("unable to generate tasks tree")
|
log.Fatal().Err(err).Msg("unable to generate tasks tree")
|
||||||
}
|
}
|
||||||
@ -237,7 +251,7 @@ func main() {
|
|||||||
tasks...,
|
tasks...,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := waitForCompletion(deployers, s); err != nil {
|
if err := waitForCompletion(deployers, s, *destroy); err != nil {
|
||||||
log.Fatal().
|
log.Fatal().
|
||||||
Err(err).
|
Err(err).
|
||||||
Str("name", project.Name).
|
Str("name", project.Name).
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user