2016-04-07 14:49:34 +02:00
|
|
|
package tasks
|
|
|
|
|
|
|
|
import (
|
2018-03-16 02:26:25 +01:00
|
|
|
"bytes"
|
2016-04-08 21:41:20 +02:00
|
|
|
"encoding/json"
|
2016-04-07 14:49:34 +02:00
|
|
|
"fmt"
|
2021-09-06 13:05:10 +02:00
|
|
|
log "github.com/Sirupsen/logrus"
|
|
|
|
"github.com/ansible-semaphore/semaphore/api/helpers"
|
2021-08-25 17:37:19 +02:00
|
|
|
"github.com/ansible-semaphore/semaphore/api/sockets"
|
2021-09-06 13:05:10 +02:00
|
|
|
"github.com/ansible-semaphore/semaphore/db"
|
|
|
|
"github.com/ansible-semaphore/semaphore/util"
|
2016-04-07 14:49:34 +02:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2017-08-19 10:45:01 +02:00
|
|
|
"regexp"
|
2016-04-07 14:49:34 +02:00
|
|
|
"strconv"
|
2016-11-22 02:01:57 +01:00
|
|
|
"strings"
|
2016-05-17 21:12:54 +02:00
|
|
|
"time"
|
2016-04-07 14:49:34 +02:00
|
|
|
)
|
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
const (
|
2021-08-25 17:37:19 +02:00
|
|
|
taskRunningStatus = "running"
|
|
|
|
taskWaitingStatus = "waiting"
|
|
|
|
taskStoppingStatus = "stopping"
|
|
|
|
taskStoppedStatus = "stopped"
|
|
|
|
taskSuccessStatus = "success"
|
|
|
|
taskFailStatus = "error"
|
2021-09-02 15:57:46 +02:00
|
|
|
gitURLFilePrefix = "file://"
|
2018-03-27 22:12:47 +02:00
|
|
|
)
|
|
|
|
|
2016-04-07 14:49:34 +02:00
|
|
|
type task struct {
|
2021-09-02 15:57:46 +02:00
|
|
|
store db.Store
|
|
|
|
task db.Task
|
|
|
|
template db.Template
|
|
|
|
inventory db.Inventory
|
|
|
|
repository db.Repository
|
|
|
|
environment db.Environment
|
|
|
|
users []int
|
|
|
|
projectID int
|
|
|
|
hosts []string
|
|
|
|
alertChat string
|
|
|
|
alert bool
|
|
|
|
prepared bool
|
|
|
|
process *os.Process
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 14:03:52 +02:00
|
|
|
func (t *task) getRepoName() string {
|
|
|
|
return "repository_" + strconv.Itoa(t.repository.ID) + "_" + strconv.Itoa(t.template.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *task) getRepoPath() string {
|
|
|
|
return util.Config.TmpPath + "/" + t.getRepoName()
|
|
|
|
}
|
|
|
|
|
2021-10-12 23:17:11 +02:00
|
|
|
func (t *task) validateRepo() error {
|
|
|
|
path := t.getRepoPath()
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-08-25 17:37:19 +02:00
|
|
|
func (t *task) setStatus(status string) {
|
|
|
|
if t.task.Status == taskStoppingStatus {
|
|
|
|
switch status {
|
|
|
|
case taskFailStatus:
|
|
|
|
status = taskStoppedStatus
|
|
|
|
case taskStoppedStatus:
|
|
|
|
default:
|
|
|
|
panic("stopping task cannot be " + status)
|
|
|
|
}
|
|
|
|
}
|
2021-10-26 18:54:19 +02:00
|
|
|
|
2021-08-25 17:37:19 +02:00
|
|
|
t.task.Status = status
|
2021-10-26 18:54:19 +02:00
|
|
|
|
2016-05-17 21:12:54 +02:00
|
|
|
t.updateStatus()
|
2021-10-26 18:54:19 +02:00
|
|
|
|
2021-10-26 19:18:31 +02:00
|
|
|
if status == taskFailStatus {
|
2021-10-26 18:54:19 +02:00
|
|
|
t.sendMailAlert()
|
2021-10-26 19:18:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if status == taskSuccessStatus || status == taskFailStatus {
|
2021-10-26 18:54:19 +02:00
|
|
|
t.sendTelegramAlert()
|
|
|
|
}
|
2021-08-25 17:37:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *task) updateStatus() {
|
|
|
|
for _, user := range t.users {
|
|
|
|
b, err := json.Marshal(&map[string]interface{}{
|
2021-10-25 11:42:34 +02:00
|
|
|
"type": "update",
|
|
|
|
"start": t.task.Start,
|
|
|
|
"end": t.task.End,
|
|
|
|
"status": t.task.Status,
|
|
|
|
"task_id": t.task.ID,
|
|
|
|
"template_id": t.task.TemplateID,
|
|
|
|
"project_id": t.projectID,
|
|
|
|
"version": t.task.Version,
|
2021-08-25 17:37:19 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
util.LogPanic(err)
|
|
|
|
|
|
|
|
sockets.Message(user, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := t.store.UpdateTask(t.task); err != nil {
|
|
|
|
t.panicOnError(err, "Failed to update task status")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *task) fail() {
|
|
|
|
t.setStatus(taskFailStatus)
|
2016-04-17 02:20:23 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 00:12:33 +02:00
|
|
|
func (t *task) destroyKeys() {
|
|
|
|
err := t.destroyKey(t.repository.SSHKey)
|
|
|
|
if err != nil {
|
2021-09-12 00:18:26 +02:00
|
|
|
t.log("Can't destroy repository key, error: " + err.Error())
|
2021-08-31 00:12:33 +02:00
|
|
|
}
|
2021-09-12 00:18:26 +02:00
|
|
|
|
2021-08-31 00:12:33 +02:00
|
|
|
err = t.destroyKey(t.inventory.SSHKey)
|
|
|
|
if err != nil {
|
2021-09-12 00:18:26 +02:00
|
|
|
t.log("Can't destroy inventory user key, error: " + err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
err = t.destroyKey(t.inventory.BecomeKey)
|
|
|
|
if err != nil {
|
|
|
|
t.log("Can't destroy inventory become user key, error: " + err.Error())
|
|
|
|
}
|
|
|
|
|
2021-09-16 23:20:59 +02:00
|
|
|
err = t.destroyKey(t.template.VaultKey)
|
2021-09-12 00:18:26 +02:00
|
|
|
if err != nil {
|
|
|
|
t.log("Can't destroy inventory vault password file, error: " + err.Error())
|
2021-08-31 00:12:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-30 21:42:11 +02:00
|
|
|
func (t *task) createTaskEvent() {
|
2021-10-13 16:07:22 +02:00
|
|
|
objType := db.EventTask
|
2021-08-30 21:42:11 +02:00
|
|
|
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Alias + ")" + " finished - " + strings.ToUpper(t.task.Status)
|
|
|
|
|
|
|
|
_, err := t.store.CreateEvent(db.Event{
|
|
|
|
UserID: t.task.UserID,
|
|
|
|
ProjectID: &t.projectID,
|
|
|
|
ObjectType: &objType,
|
|
|
|
ObjectID: &t.task.ID,
|
|
|
|
Description: &desc,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
t.panicOnError(err, "Fatal error inserting an event")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
func (t *task) prepareRun() {
|
|
|
|
t.prepared = false
|
2016-04-07 14:49:34 +02:00
|
|
|
|
|
|
|
defer func() {
|
2018-06-08 03:58:08 +02:00
|
|
|
log.Info("Stopped preparing task " + strconv.Itoa(t.task.ID))
|
2021-03-12 21:20:18 +01:00
|
|
|
log.Info("Release resource locker with task " + strconv.Itoa(t.task.ID))
|
2018-06-07 09:29:55 +02:00
|
|
|
resourceLocker <- &resourceLock{lock: false, holder: t}
|
2016-04-17 20:01:51 +02:00
|
|
|
|
2021-08-30 21:42:11 +02:00
|
|
|
t.createTaskEvent()
|
2016-04-07 14:49:34 +02:00
|
|
|
}()
|
|
|
|
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
t.log("Preparing: " + strconv.Itoa(t.task.ID))
|
|
|
|
|
2018-02-15 21:29:03 +01:00
|
|
|
err := checkTmpDir(util.Config.TmpPath)
|
|
|
|
if err != nil {
|
|
|
|
t.log("Creating tmp dir failed: " + err.Error())
|
|
|
|
t.fail()
|
|
|
|
return
|
|
|
|
}
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
|
2016-05-17 21:12:54 +02:00
|
|
|
if err := t.populateDetails(); err != nil {
|
|
|
|
t.log("Error: " + err.Error())
|
|
|
|
t.fail()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-10-13 16:07:22 +02:00
|
|
|
objType := db.EventTask
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Alias + ")" + " is preparing"
|
2020-12-20 19:00:59 +01:00
|
|
|
_, err = t.store.CreateEvent(db.Event{
|
2021-08-25 17:37:19 +02:00
|
|
|
UserID: t.task.UserID,
|
2016-04-17 20:01:51 +02:00
|
|
|
ProjectID: &t.projectID,
|
|
|
|
ObjectType: &objType,
|
|
|
|
ObjectID: &t.task.ID,
|
|
|
|
Description: &desc,
|
2020-12-01 20:06:49 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
2016-04-17 20:01:51 +02:00
|
|
|
t.log("Fatal error inserting an event")
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
t.log("Prepare task with template: " + t.template.Alias + "\n")
|
2021-10-25 11:42:34 +02:00
|
|
|
|
|
|
|
t.updateStatus()
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2021-09-12 00:18:26 +02:00
|
|
|
if err := t.installKey(t.repository.SSHKey, db.AccessKeyUsagePrivateKey); err != nil {
|
2016-04-07 14:49:34 +02:00
|
|
|
t.log("Failed installing ssh key for repository access: " + err.Error())
|
2016-04-17 02:20:23 +02:00
|
|
|
t.fail()
|
2016-04-07 14:49:34 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-02 15:57:46 +02:00
|
|
|
if strings.HasPrefix(t.repository.GitURL, gitURLFilePrefix) {
|
|
|
|
repositoryPath := strings.TrimPrefix(gitURLFilePrefix, t.repository.GitURL)
|
|
|
|
if _, err := os.Stat(repositoryPath); err != nil {
|
|
|
|
t.log("Failed in finding static repository at " + repositoryPath + ": " + err.Error())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := t.updateRepository(); err != nil {
|
|
|
|
t.log("Failed updating repository: " + err.Error())
|
|
|
|
t.fail()
|
|
|
|
return
|
|
|
|
}
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
2021-10-12 22:44:10 +02:00
|
|
|
if err := t.checkoutRepository(); err != nil {
|
|
|
|
t.log("Failed to checkout repository to required commit: " + err.Error())
|
|
|
|
t.fail()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
if err := t.installInventory(); err != nil {
|
|
|
|
t.log("Failed to install inventory: " + err.Error())
|
2016-04-17 02:20:23 +02:00
|
|
|
t.fail()
|
2016-04-08 21:41:20 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:06:22 +02:00
|
|
|
if err := t.installRequirements(); err != nil {
|
2017-11-22 02:31:29 +01:00
|
|
|
t.log("Running galaxy failed: " + err.Error())
|
|
|
|
t.fail()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-16 23:20:59 +02:00
|
|
|
if err := t.installVaultKeyFile(); err != nil {
|
2021-09-01 23:14:32 +02:00
|
|
|
t.log("Failed to install vault password file: " + err.Error())
|
|
|
|
t.fail()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
// todo: write environment
|
|
|
|
|
2018-03-16 03:00:14 +01:00
|
|
|
if stderr, err := t.listPlaybookHosts(); err != nil {
|
2018-03-16 02:26:25 +01:00
|
|
|
t.log("Listing playbook hosts failed: " + err.Error() + "\n" + stderr)
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
t.fail()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
t.prepared = true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *task) run() {
|
|
|
|
defer func() {
|
2018-06-08 03:58:08 +02:00
|
|
|
log.Info("Stopped running task " + strconv.Itoa(t.task.ID))
|
2020-12-01 20:06:49 +01:00
|
|
|
log.Info("Release resource locker with task " + strconv.Itoa(t.task.ID))
|
2017-08-19 10:45:01 +02:00
|
|
|
resourceLocker <- &resourceLock{lock: false, holder: t}
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
t.task.End = &now
|
|
|
|
t.updateStatus()
|
2021-08-31 00:12:33 +02:00
|
|
|
t.createTaskEvent()
|
|
|
|
t.destroyKeys()
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
}()
|
|
|
|
|
2021-08-25 17:37:19 +02:00
|
|
|
if t.task.Status == taskStoppingStatus {
|
|
|
|
t.setStatus(taskStoppedStatus)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-10-25 11:42:34 +02:00
|
|
|
now := time.Now()
|
|
|
|
t.task.Start = &now
|
|
|
|
t.setStatus(taskRunningStatus)
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
|
2021-10-13 16:07:22 +02:00
|
|
|
objType := db.EventTask
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Alias + ")" + " is running"
|
2020-12-01 20:06:49 +01:00
|
|
|
|
2020-12-20 19:00:59 +01:00
|
|
|
_, err := t.store.CreateEvent(db.Event{
|
2021-08-25 17:37:19 +02:00
|
|
|
UserID: t.task.UserID,
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
ProjectID: &t.projectID,
|
|
|
|
ObjectType: &objType,
|
|
|
|
ObjectID: &t.task.ID,
|
|
|
|
Description: &desc,
|
2020-12-01 20:06:49 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
t.log("Fatal error inserting an event")
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
t.log("Started: " + strconv.Itoa(t.task.ID))
|
|
|
|
t.log("Run task with template: " + t.template.Alias + "\n")
|
|
|
|
|
2021-08-25 17:37:19 +02:00
|
|
|
if t.task.Status == taskStoppingStatus {
|
|
|
|
t.setStatus(taskStoppedStatus)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
if err := t.runPlaybook(); err != nil {
|
|
|
|
t.log("Running playbook failed: " + err.Error())
|
2016-04-17 02:20:23 +02:00
|
|
|
t.fail()
|
2016-04-08 21:41:20 +02:00
|
|
|
return
|
|
|
|
}
|
2016-04-17 02:20:23 +02:00
|
|
|
|
2021-08-25 17:37:19 +02:00
|
|
|
t.setStatus(taskSuccessStatus)
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
2020-12-20 19:00:59 +01:00
|
|
|
func (t *task) prepareError(err error, errMsg string) error {
|
2021-08-30 18:04:18 +02:00
|
|
|
if err == db.ErrNotFound {
|
2020-12-20 19:00:59 +01:00
|
|
|
t.log(errMsg)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
t.fail()
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
//nolint: gocyclo
|
2016-04-07 14:49:34 +02:00
|
|
|
func (t *task) populateDetails() error {
|
|
|
|
// get template
|
2020-12-20 19:00:59 +01:00
|
|
|
var err error
|
|
|
|
|
|
|
|
t.template, err = t.store.GetTemplate(t.projectID, t.task.TemplateID)
|
|
|
|
if err != nil {
|
|
|
|
return t.prepareError(err, "Template not found!")
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
2017-04-18 16:36:09 +02:00
|
|
|
// get project alert setting
|
2020-12-20 19:00:59 +01:00
|
|
|
project, err := t.store.GetProject(t.template.ProjectID)
|
|
|
|
if err != nil {
|
|
|
|
return t.prepareError(err, "Project not found!")
|
2017-03-10 07:25:42 +01:00
|
|
|
}
|
2020-12-20 19:00:59 +01:00
|
|
|
|
2017-05-03 06:27:58 +02:00
|
|
|
t.alert = project.Alert
|
2018-03-27 22:12:47 +02:00
|
|
|
t.alertChat = project.AlertChat
|
2017-03-10 01:12:55 +01:00
|
|
|
|
2016-04-17 12:41:36 +02:00
|
|
|
// get project users
|
2020-12-20 19:00:59 +01:00
|
|
|
users, err := t.store.GetProjectUsers(t.template.ProjectID, db.RetrieveQueryParams{})
|
2021-03-12 21:30:17 +01:00
|
|
|
if err != nil {
|
|
|
|
return t.prepareError(err, "Users not found!")
|
|
|
|
}
|
2016-04-17 12:41:36 +02:00
|
|
|
|
|
|
|
t.users = []int{}
|
|
|
|
for _, user := range users {
|
|
|
|
t.users = append(t.users, user.ID)
|
|
|
|
}
|
|
|
|
|
2016-04-07 14:49:34 +02:00
|
|
|
// get inventory
|
2020-12-20 19:00:59 +01:00
|
|
|
t.inventory, err = t.store.GetInventory(t.template.ProjectID, t.template.InventoryID)
|
|
|
|
if err != nil {
|
|
|
|
return t.prepareError(err, "Template Inventory not found!")
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// get repository
|
2021-05-06 10:32:13 +02:00
|
|
|
t.repository, err = t.store.GetRepository(t.template.ProjectID, t.template.RepositoryID)
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2021-05-06 10:32:13 +02:00
|
|
|
if err != nil {
|
2016-04-07 14:49:34 +02:00
|
|
|
return err
|
|
|
|
}
|
2021-05-06 10:32:13 +02:00
|
|
|
|
2016-04-07 14:49:34 +02:00
|
|
|
// get environment
|
2021-11-02 18:37:31 +01:00
|
|
|
if t.template.EnvironmentID != nil {
|
2021-05-06 10:32:13 +02:00
|
|
|
t.environment, err = t.store.GetEnvironment(t.template.ProjectID, *t.template.EnvironmentID)
|
2016-04-07 14:49:34 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-11-02 18:37:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if t.task.Environment != "" {
|
|
|
|
environment := make(map[string]interface{})
|
|
|
|
if t.environment.JSON != "" {
|
|
|
|
err = json.Unmarshal([]byte(t.task.Environment), &environment)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
taskEnvironment := make(map[string]interface{})
|
|
|
|
err = json.Unmarshal([]byte(t.environment.JSON), &taskEnvironment)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range taskEnvironment {
|
|
|
|
environment[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
var ev []byte
|
|
|
|
ev, err = json.Marshal(environment)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
t.environment.JSON = string(ev)
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-08-31 00:12:33 +02:00
|
|
|
func (t *task) destroyKey(key db.AccessKey) error {
|
|
|
|
path := key.GetPath()
|
|
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return os.Remove(path)
|
|
|
|
}
|
|
|
|
|
2021-09-16 23:20:59 +02:00
|
|
|
func (t *task) installVaultKeyFile() error {
|
|
|
|
if t.template.VaultKeyID == nil {
|
2021-09-01 23:14:32 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-09-16 23:20:59 +02:00
|
|
|
return t.template.VaultKey.Install(db.AccessKeyUsageVault)
|
2021-09-01 23:14:32 +02:00
|
|
|
}
|
|
|
|
|
2021-09-12 00:18:26 +02:00
|
|
|
func (t *task) installKey(key db.AccessKey, accessKeyUsage int) error {
|
2021-09-01 20:11:24 +02:00
|
|
|
if key.Type != db.AccessKeySSH {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
t.log("access key " + key.Name + " installed")
|
2017-04-18 16:21:20 +02:00
|
|
|
|
|
|
|
path := key.GetPath()
|
|
|
|
|
2021-09-10 00:41:36 +02:00
|
|
|
err := key.DeserializeSecret()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-09-01 16:38:28 +02:00
|
|
|
if key.SshKey.Passphrase != "" {
|
|
|
|
return fmt.Errorf("ssh key with passphrase not supported")
|
2021-08-31 01:02:41 +02:00
|
|
|
}
|
|
|
|
|
2021-10-14 21:14:21 +02:00
|
|
|
return ioutil.WriteFile(path, []byte(key.SshKey.PrivateKey+"\n"), 0600)
|
2016-04-08 21:41:20 +02:00
|
|
|
}
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2021-10-12 22:44:10 +02:00
|
|
|
func (t *task) checkoutRepository() error {
|
2021-10-12 22:58:18 +02:00
|
|
|
if t.task.CommitHash != nil { // checkout to commit if it is provided for task
|
2021-10-12 23:17:11 +02:00
|
|
|
err := t.validateRepo()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-10-12 22:44:10 +02:00
|
|
|
|
2021-10-12 23:17:11 +02:00
|
|
|
cmd := exec.Command("git")
|
|
|
|
cmd.Dir = t.getRepoPath()
|
2021-10-12 22:44:10 +02:00
|
|
|
t.log("Checkout repository to commit " + *t.task.CommitHash)
|
|
|
|
cmd.Args = append(cmd.Args, "checkout", *t.task.CommitHash)
|
|
|
|
t.logCmd(cmd)
|
|
|
|
return cmd.Run()
|
|
|
|
}
|
|
|
|
|
2021-10-12 22:58:18 +02:00
|
|
|
// store commit to task table
|
|
|
|
|
|
|
|
commitHash, err := t.getCommitHash()
|
2021-10-12 22:44:10 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-10-14 12:35:42 +02:00
|
|
|
commitMessage, _ := t.getCommitMessage()
|
2021-10-12 22:58:18 +02:00
|
|
|
t.task.CommitHash = &commitHash
|
2021-10-14 17:13:21 +02:00
|
|
|
t.task.CommitMessage = commitMessage
|
2021-10-14 12:35:42 +02:00
|
|
|
|
2021-10-12 22:58:18 +02:00
|
|
|
return t.store.UpdateTask(t.task)
|
2021-10-12 22:44:10 +02:00
|
|
|
}
|
|
|
|
|
2021-10-12 22:58:18 +02:00
|
|
|
// getCommitHash retrieves current commit hash from task repository
|
2021-10-14 12:35:42 +02:00
|
|
|
func (t *task) getCommitHash() (res string, err error) {
|
2021-10-12 23:17:11 +02:00
|
|
|
err = t.validateRepo()
|
2021-10-12 22:44:10 +02:00
|
|
|
if err != nil {
|
2021-10-12 23:17:11 +02:00
|
|
|
return
|
2021-10-12 22:44:10 +02:00
|
|
|
}
|
|
|
|
|
2021-10-12 23:17:11 +02:00
|
|
|
cmd := exec.Command("git")
|
|
|
|
cmd.Dir = t.getRepoPath()
|
2021-10-14 12:35:42 +02:00
|
|
|
t.log("Get current commit hash")
|
2021-10-12 22:44:10 +02:00
|
|
|
cmd.Args = append(cmd.Args, "rev-parse", "HEAD")
|
2021-10-12 22:58:18 +02:00
|
|
|
out, err := cmd.Output()
|
2021-10-12 22:44:10 +02:00
|
|
|
if err != nil {
|
2021-10-12 23:17:11 +02:00
|
|
|
return
|
2021-10-12 22:44:10 +02:00
|
|
|
}
|
2021-10-14 12:35:42 +02:00
|
|
|
res = strings.Trim(string(out), " \n")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// getCommitMessage retrieves current commit message from task repository
|
|
|
|
func (t *task) getCommitMessage() (res string, err error) {
|
|
|
|
err = t.validateRepo()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := exec.Command("git")
|
|
|
|
cmd.Dir = t.getRepoPath()
|
|
|
|
t.log("Get current commit message")
|
|
|
|
cmd.Args = append(cmd.Args, "show-branch", "--no-name", "HEAD")
|
|
|
|
out, err := cmd.Output()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
res = strings.Trim(string(out), " \n")
|
|
|
|
|
|
|
|
if len(res) > 100 {
|
|
|
|
res = res[0:100]
|
|
|
|
}
|
|
|
|
|
2021-10-12 23:17:11 +02:00
|
|
|
return
|
2016-04-08 21:41:20 +02:00
|
|
|
}
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
func (t *task) updateRepository() error {
|
2021-09-12 00:18:26 +02:00
|
|
|
var gitSSHCommand string
|
|
|
|
if t.repository.SSHKey.Type == db.AccessKeySSH {
|
|
|
|
gitSSHCommand = t.repository.SSHKey.GetSshCommand()
|
|
|
|
}
|
2016-04-08 21:41:20 +02:00
|
|
|
|
2018-06-07 09:29:55 +02:00
|
|
|
cmd := exec.Command("git") //nolint: gas
|
2016-05-17 21:12:54 +02:00
|
|
|
cmd.Dir = util.Config.TmpPath
|
2021-09-12 00:18:26 +02:00
|
|
|
t.setCmdEnvironment(cmd, gitSSHCommand)
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
repoURL, repoTag := t.repository.GitURL, "master"
|
2016-11-22 02:07:00 +01:00
|
|
|
if split := strings.Split(repoURL, "#"); len(split) > 1 {
|
|
|
|
repoURL, repoTag = split[0], split[1]
|
|
|
|
}
|
|
|
|
|
2021-10-12 23:17:11 +02:00
|
|
|
err := t.validateRepo()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-11-22 02:07:00 +01:00
|
|
|
t.log("Cloning repository " + repoURL)
|
2021-10-12 23:17:11 +02:00
|
|
|
cmd.Args = append(cmd.Args, "clone", "--recursive", "--branch", repoTag, repoURL, t.getRepoName())
|
2016-04-08 21:41:20 +02:00
|
|
|
} else {
|
2016-11-22 02:07:00 +01:00
|
|
|
t.log("Updating repository " + repoURL)
|
2021-10-13 20:51:35 +02:00
|
|
|
cmd.Dir = t.getRepoPath()
|
2016-11-22 02:07:00 +01:00
|
|
|
cmd.Args = append(cmd.Args, "pull", "origin", repoTag)
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
t.logCmd(cmd)
|
|
|
|
return cmd.Run()
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
2021-04-15 18:06:22 +02:00
|
|
|
func (t *task) installRequirements() error {
|
2021-09-01 18:18:49 +02:00
|
|
|
requirementsFilePath := fmt.Sprintf("%s/roles/requirements.yml", t.getRepoPath())
|
|
|
|
requirementsHashFilePath := fmt.Sprintf("%s/requirements.md5", t.getRepoPath())
|
2021-04-15 18:40:16 +02:00
|
|
|
|
2021-04-15 18:07:49 +02:00
|
|
|
if _, err := os.Stat(requirementsFilePath); err != nil {
|
2021-04-15 18:51:44 +02:00
|
|
|
t.log("No roles/requirements.yml file found. Skip galaxy install process.\n")
|
2021-04-15 18:07:49 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:40:16 +02:00
|
|
|
if hasRequirementsChanges(requirementsFilePath, requirementsHashFilePath) {
|
|
|
|
if err := t.runGalaxy([]string{
|
|
|
|
"install",
|
|
|
|
"-r",
|
|
|
|
"roles/requirements.yml",
|
|
|
|
"--force",
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := writeMD5Hash(requirementsFilePath, requirementsHashFilePath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.log("roles/requirements.yml has no changes. Skip galaxy install process.\n")
|
2021-04-15 18:06:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-20 21:48:25 +01:00
|
|
|
func (t *task) runGalaxy(args []string) error {
|
2018-06-07 09:29:55 +02:00
|
|
|
cmd := exec.Command("ansible-galaxy", args...) //nolint: gas
|
2021-08-31 14:03:52 +02:00
|
|
|
cmd.Dir = t.getRepoPath()
|
2017-02-08 13:26:15 +01:00
|
|
|
|
2021-09-12 00:18:26 +02:00
|
|
|
t.setCmdEnvironment(cmd, t.repository.SSHKey.GetSshCommand())
|
2016-06-30 17:34:35 +02:00
|
|
|
|
|
|
|
t.logCmd(cmd)
|
|
|
|
return cmd.Run()
|
|
|
|
}
|
|
|
|
|
2018-03-16 03:00:14 +01:00
|
|
|
func (t *task) listPlaybookHosts() (string, error) {
|
2018-06-07 09:29:55 +02:00
|
|
|
|
|
|
|
if util.Config.ConcurrencyMode == "project" {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
args, err := t.getPlaybookArgs()
|
|
|
|
if err != nil {
|
2018-03-16 03:00:14 +01:00
|
|
|
return "", err
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
}
|
|
|
|
args = append(args, "--list-hosts")
|
|
|
|
|
2018-06-07 09:29:55 +02:00
|
|
|
cmd := exec.Command("ansible-playbook", args...) //nolint: gas
|
2021-08-31 14:03:52 +02:00
|
|
|
cmd.Dir = t.getRepoPath()
|
2021-09-12 00:18:26 +02:00
|
|
|
t.setCmdEnvironment(cmd, "")
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
|
2018-03-16 02:26:25 +01:00
|
|
|
var errb bytes.Buffer
|
|
|
|
cmd.Stderr = &errb
|
|
|
|
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
out, err := cmd.Output()
|
2018-03-16 02:26:25 +01:00
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
re := regexp.MustCompile(`(?m)^\\s{6}(.*)$`)
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
matches := re.FindAllSubmatch(out, 20)
|
|
|
|
hosts := make([]string, len(matches))
|
2018-03-27 22:12:47 +02:00
|
|
|
for i := range matches {
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
hosts[i] = string(matches[i][1])
|
|
|
|
}
|
|
|
|
t.hosts = hosts
|
2018-03-20 01:12:43 +01:00
|
|
|
return errb.String(), err
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
}
|
|
|
|
|
2021-08-25 17:37:19 +02:00
|
|
|
func (t *task) runPlaybook() (err error) {
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
args, err := t.getPlaybookArgs()
|
|
|
|
if err != nil {
|
2021-08-25 17:37:19 +02:00
|
|
|
return
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
}
|
2018-06-07 09:29:55 +02:00
|
|
|
cmd := exec.Command("ansible-playbook", args...) //nolint: gas
|
2021-08-31 14:03:52 +02:00
|
|
|
cmd.Dir = t.getRepoPath()
|
2021-09-12 00:18:26 +02:00
|
|
|
t.setCmdEnvironment(cmd, "")
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
|
|
|
|
t.logCmd(cmd)
|
2018-02-05 19:51:14 +01:00
|
|
|
cmd.Stdin = strings.NewReader("")
|
2021-08-25 17:37:19 +02:00
|
|
|
err = cmd.Start()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t.process = cmd.Process
|
|
|
|
err = cmd.Wait()
|
|
|
|
return
|
Allow concurrency for tasks that does not collide
Two different concurrency modes are implemented, and is enabled by
setting "concurrency_mode" in the config file to either "project" or "node".
When "project" concurrency is enabled, tasks will run in parallel if and
only if they do not share the same project id, with no regard to the
nodes/hosts that are affected.
When "node" concurrency is enabled, a task will run in parallel if and
only if the hosts affected by tasks already running does not intersect
with the hosts that would be affected by the task in question.
If "concurrency_mode" is not specified, no task will start before the
previous one has finished.
The collision check is based on the output from the "--list-hosts"
argument to ansible, which uses the hosts specified in the inventory.
Thus, if two different hostnames are used that points to the same node,
such as "127.0.0.1" and "localhost", there will be no collision and two
tasks may connect to the same node concurrently. If this behaviour is
not desired, one should make sure to not include aliases for their hosts
in their inventories when enabling concurrency mode.
To restrict the amount of parallel tasks that runs at the same time, one
can add the "max_parallel_tasks" to the config file. This defaults to a
humble 10 if not specified.
2017-05-29 17:27:56 +02:00
|
|
|
}
|
|
|
|
|
2021-09-12 00:18:26 +02:00
|
|
|
func (t *task) getExtraVars() (str string, err error) {
|
2021-09-01 23:14:32 +02:00
|
|
|
extraVars := make(map[string]interface{})
|
|
|
|
|
|
|
|
if t.environment.JSON != "" {
|
2021-09-12 00:18:26 +02:00
|
|
|
err = json.Unmarshal([]byte(t.environment.JSON), &extraVars)
|
2021-09-01 23:14:32 +02:00
|
|
|
if err != nil {
|
2021-09-12 00:18:26 +02:00
|
|
|
return
|
2021-09-01 23:14:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-01 23:22:31 +02:00
|
|
|
delete(extraVars, "ENV")
|
2021-09-01 23:14:32 +02:00
|
|
|
|
2021-10-14 22:25:12 +02:00
|
|
|
if t.template.Type != db.TemplateTask &&
|
|
|
|
(util.Config.VariablesPassingMethod == util.VariablesPassingBoth ||
|
|
|
|
util.Config.VariablesPassingMethod == util.VariablesPassingExtra) {
|
2021-10-14 21:14:21 +02:00
|
|
|
extraVars["semaphore_task_type"] = t.template.Type
|
2021-11-02 08:16:20 +01:00
|
|
|
|
|
|
|
var version string
|
|
|
|
switch t.template.Type {
|
|
|
|
case db.TemplateBuild:
|
|
|
|
version = *t.task.Version
|
|
|
|
case db.TemplateDeploy:
|
|
|
|
buildTask, err := t.store.GetTask(t.task.ProjectID, *t.task.BuildTaskID)
|
|
|
|
if err != nil {
|
|
|
|
panic("Deploy task has no build task")
|
|
|
|
}
|
|
|
|
version = *buildTask.Version
|
|
|
|
}
|
|
|
|
extraVars["semaphore_task_version"] = version
|
2021-10-14 21:14:21 +02:00
|
|
|
}
|
|
|
|
|
2021-09-01 23:14:32 +02:00
|
|
|
ev, err := json.Marshal(extraVars)
|
|
|
|
if err != nil {
|
2021-09-12 00:18:26 +02:00
|
|
|
return
|
2021-09-01 23:14:32 +02:00
|
|
|
}
|
|
|
|
|
2021-09-12 00:18:26 +02:00
|
|
|
str = string(ev)
|
|
|
|
|
|
|
|
return
|
2021-09-01 23:14:32 +02:00
|
|
|
}
|
|
|
|
|
2018-03-27 22:12:47 +02:00
|
|
|
//nolint: gocyclo
|
2021-09-12 00:18:26 +02:00
|
|
|
func (t *task) getPlaybookArgs() (args []string, err error) {
|
2016-04-08 21:41:20 +02:00
|
|
|
playbookName := t.task.Playbook
|
2021-09-13 15:22:08 +02:00
|
|
|
if playbookName == "" {
|
2016-04-08 21:41:20 +02:00
|
|
|
playbookName = t.template.Playbook
|
|
|
|
}
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2018-03-16 02:26:25 +01:00
|
|
|
var inventory string
|
2017-10-26 09:00:48 +02:00
|
|
|
switch t.inventory.Type {
|
2021-09-12 00:18:26 +02:00
|
|
|
case db.InventoryFile:
|
2017-10-26 09:00:48 +02:00
|
|
|
inventory = t.inventory.Inventory
|
|
|
|
default:
|
|
|
|
inventory = util.Config.TmpPath + "/inventory_" + strconv.Itoa(t.task.ID)
|
|
|
|
}
|
|
|
|
|
2021-09-12 00:18:26 +02:00
|
|
|
args = []string{
|
2017-10-26 09:00:48 +02:00
|
|
|
"-i", inventory,
|
2016-04-08 21:41:20 +02:00
|
|
|
}
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2021-09-12 00:18:26 +02:00
|
|
|
if t.inventory.SSHKeyID != nil {
|
|
|
|
switch t.inventory.SSHKey.Type {
|
|
|
|
case db.AccessKeySSH:
|
|
|
|
args = append(args, "--private-key="+t.inventory.SSHKey.GetPath())
|
|
|
|
case db.AccessKeyLoginPassword:
|
|
|
|
args = append(args, "--extra-vars=@"+t.inventory.SSHKey.GetPath())
|
|
|
|
case db.AccessKeyNone:
|
|
|
|
default:
|
2021-09-13 15:22:08 +02:00
|
|
|
err = fmt.Errorf("access key does not suite for inventory's User Access Key")
|
2021-09-12 00:18:26 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-13 15:22:08 +02:00
|
|
|
if t.inventory.BecomeKeyID != nil {
|
|
|
|
switch t.inventory.BecomeKey.Type {
|
2021-09-12 00:18:26 +02:00
|
|
|
case db.AccessKeyLoginPassword:
|
2021-09-13 15:22:08 +02:00
|
|
|
args = append(args, "--extra-vars=@"+t.inventory.BecomeKey.GetPath())
|
2021-09-12 00:18:26 +02:00
|
|
|
case db.AccessKeyNone:
|
|
|
|
default:
|
2021-09-13 15:22:08 +02:00
|
|
|
err = fmt.Errorf("access key does not suite for inventory's Become User Access Key")
|
2021-09-12 00:18:26 +02:00
|
|
|
return
|
|
|
|
}
|
2016-04-08 21:41:20 +02:00
|
|
|
}
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
if t.task.Debug {
|
|
|
|
args = append(args, "-vvvv")
|
|
|
|
}
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2016-06-30 16:57:45 +02:00
|
|
|
if t.task.DryRun {
|
|
|
|
args = append(args, "--check")
|
|
|
|
}
|
|
|
|
|
2021-09-16 23:20:59 +02:00
|
|
|
if t.template.VaultKeyID != nil {
|
|
|
|
args = append(args, "--vault-password-file", t.template.VaultKey.GetPath())
|
2021-09-01 23:14:32 +02:00
|
|
|
}
|
2018-02-14 15:54:04 +01:00
|
|
|
|
2021-09-01 23:14:32 +02:00
|
|
|
extraVars, err := t.getExtraVars()
|
|
|
|
if err != nil {
|
|
|
|
t.log(err.Error())
|
|
|
|
t.log("Could not remove command environment, if existant it will be passed to --extra-vars. This is not fatal but be aware of side effects")
|
|
|
|
} else if extraVars != "" {
|
|
|
|
args = append(args, "--extra-vars", extraVars)
|
2016-04-08 21:41:20 +02:00
|
|
|
}
|
|
|
|
|
2018-09-11 13:49:03 +02:00
|
|
|
var templateExtraArgs []string
|
2016-04-08 21:41:20 +02:00
|
|
|
if t.template.Arguments != nil {
|
2021-09-12 00:18:26 +02:00
|
|
|
err = json.Unmarshal([]byte(*t.template.Arguments), &templateExtraArgs)
|
2018-09-11 13:49:03 +02:00
|
|
|
if err != nil {
|
|
|
|
t.log("Could not unmarshal arguments to []string")
|
2021-09-12 00:18:26 +02:00
|
|
|
return
|
2016-04-08 21:41:20 +02:00
|
|
|
}
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
if t.template.OverrideArguments {
|
2018-09-11 13:49:03 +02:00
|
|
|
args = templateExtraArgs
|
2016-04-08 21:41:20 +02:00
|
|
|
} else {
|
2018-09-11 13:49:03 +02:00
|
|
|
args = append(args, templateExtraArgs...)
|
2016-04-08 21:41:20 +02:00
|
|
|
args = append(args, playbookName)
|
|
|
|
}
|
2021-09-12 00:18:26 +02:00
|
|
|
|
|
|
|
return
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
2017-02-08 13:26:15 +01:00
|
|
|
|
2021-09-12 00:18:26 +02:00
|
|
|
func (t *task) setCmdEnvironment(cmd *exec.Cmd, gitSSHCommand string) {
|
2017-02-08 13:26:15 +01:00
|
|
|
env := os.Environ()
|
2021-09-12 00:18:26 +02:00
|
|
|
env = append(env, fmt.Sprintf("HOME=%s", util.Config.TmpPath))
|
|
|
|
env = append(env, fmt.Sprintf("PWD=%s", cmd.Dir))
|
2017-02-08 13:26:15 +01:00
|
|
|
env = append(env, fmt.Sprintln("PYTHONUNBUFFERED=1"))
|
2018-02-14 15:54:04 +01:00
|
|
|
env = append(env, extractCommandEnvironment(t.environment.JSON)...)
|
|
|
|
|
2021-10-14 22:25:12 +02:00
|
|
|
if t.template.Type != db.TemplateTask &&
|
|
|
|
(util.Config.VariablesPassingMethod == util.VariablesPassingBoth ||
|
|
|
|
util.Config.VariablesPassingMethod == util.VariablesPassingEnv) {
|
|
|
|
env = append(env, "SEMAPHORE_TASK_TYPE="+string(t.template.Type))
|
2021-10-20 13:56:29 +02:00
|
|
|
var version string
|
2021-10-25 20:09:46 +02:00
|
|
|
switch t.template.Type {
|
|
|
|
case db.TemplateBuild:
|
2021-10-20 13:56:29 +02:00
|
|
|
version = *t.task.Version
|
2021-10-25 20:09:46 +02:00
|
|
|
case db.TemplateDeploy:
|
|
|
|
buildTask, err := t.store.GetTask(t.task.ProjectID, *t.task.BuildTaskID)
|
|
|
|
if err != nil {
|
|
|
|
panic("Deploy task has no build task")
|
|
|
|
}
|
|
|
|
version = *buildTask.Version
|
2021-10-20 13:56:29 +02:00
|
|
|
}
|
|
|
|
env = append(env, "SEMAPHORE_TASK_VERSION="+version)
|
2017-02-08 13:26:15 +01:00
|
|
|
}
|
|
|
|
|
2021-09-12 00:18:26 +02:00
|
|
|
if gitSSHCommand != "" {
|
|
|
|
env = append(env, fmt.Sprintf("GIT_SSH_COMMAND=%s", gitSSHCommand))
|
2017-02-08 13:26:15 +01:00
|
|
|
}
|
2021-09-12 00:18:26 +02:00
|
|
|
cmd.Env = env
|
2017-02-08 13:26:15 +01:00
|
|
|
}
|
2018-02-14 15:54:04 +01:00
|
|
|
|
2021-04-15 18:39:19 +02:00
|
|
|
func hasRequirementsChanges(requirementsFilePath string, requirementsHashFilePath string) bool {
|
|
|
|
oldFileMD5HashBytes, err := ioutil.ReadFile(requirementsHashFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
newFileMD5Hash, err := helpers.GetMD5Hash(requirementsFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(oldFileMD5HashBytes) != newFileMD5Hash
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:39:36 +02:00
|
|
|
func writeMD5Hash(requirementsFile string, requirementsHashFile string) error {
|
|
|
|
newFileMD5Hash, err := helpers.GetMD5Hash(requirementsFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ioutil.WriteFile(requirementsHashFile, []byte(newFileMD5Hash), 0644)
|
|
|
|
}
|
|
|
|
|
2018-02-14 15:54:04 +01:00
|
|
|
// extractCommandEnvironment unmarshalls a json string, extracts the ENV key from it and returns it as
|
|
|
|
// []string where strings are in key=value format
|
2018-03-15 00:52:37 +01:00
|
|
|
func extractCommandEnvironment(envJSON string) []string {
|
2018-02-14 15:54:04 +01:00
|
|
|
env := make([]string, 0)
|
|
|
|
var js map[string]interface{}
|
2018-03-15 00:52:37 +01:00
|
|
|
err := json.Unmarshal([]byte(envJSON), &js)
|
2018-02-14 15:54:04 +01:00
|
|
|
if err == nil {
|
|
|
|
if cfg, ok := js["ENV"]; ok {
|
|
|
|
switch v := cfg.(type) {
|
|
|
|
case map[string]interface{}:
|
|
|
|
for key, val := range v {
|
|
|
|
env = append(env, fmt.Sprintf("%s=%s", key, val))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return env
|
|
|
|
}
|
|
|
|
|
2018-02-15 21:29:03 +01:00
|
|
|
// checkTmpDir checks to see if the temporary directory exists
|
|
|
|
// and if it does not attempts to create it
|
|
|
|
func checkTmpDir(path string) error {
|
2018-03-27 22:12:47 +02:00
|
|
|
var err error
|
|
|
|
if _, err = os.Stat(path); err != nil {
|
2018-02-15 21:29:03 +01:00
|
|
|
if os.IsNotExist(err) {
|
2018-02-28 10:02:54 +01:00
|
|
|
return os.MkdirAll(path, 0700)
|
2018-02-15 21:29:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
2021-05-06 10:32:13 +02:00
|
|
|
}
|