2016-04-07 14:49:34 +02:00
|
|
|
package tasks
|
|
|
|
|
|
|
|
import (
|
2018-03-16 02:26:25 +01:00
|
|
|
"bytes"
|
2016-04-07 14:49:34 +02:00
|
|
|
"database/sql"
|
2016-04-08 21:41:20 +02:00
|
|
|
"encoding/json"
|
2016-04-07 14:49:34 +02:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"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
|
|
|
|
2017-02-23 06:12:16 +01:00
|
|
|
"github.com/ansible-semaphore/semaphore/db"
|
2016-04-07 14:49:34 +02:00
|
|
|
"github.com/ansible-semaphore/semaphore/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
type task struct {
|
2017-02-23 06:12:16 +01:00
|
|
|
task db.Task
|
|
|
|
template db.Template
|
|
|
|
sshKey db.AccessKey
|
|
|
|
inventory db.Inventory
|
|
|
|
repository db.Repository
|
|
|
|
environment db.Environment
|
2016-04-17 12:41:36 +02:00
|
|
|
users []int
|
2016-04-17 20:01:51 +02:00
|
|
|
projectID int
|
2017-03-10 01:12:55 +01:00
|
|
|
alert bool
|
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 []string
|
|
|
|
prepared bool
|
2017-05-03 06:27:58 +02:00
|
|
|
alert_chat string
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
2016-04-17 02:20:23 +02:00
|
|
|
func (t *task) fail() {
|
|
|
|
t.task.Status = "error"
|
2016-05-17 21:12:54 +02:00
|
|
|
t.updateStatus()
|
2017-02-22 09:46:42 +01:00
|
|
|
t.sendMailAlert()
|
2017-03-22 08:22:09 +01:00
|
|
|
t.sendTelegramAlert()
|
2016-04-17 02:20:23 +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
|
|
|
func (t *task) prepareRun() {
|
|
|
|
t.prepared = false
|
2016-04-07 14:49:34 +02:00
|
|
|
|
|
|
|
defer func() {
|
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
|
|
|
fmt.Println("Stopped preparing task")
|
2016-04-17 20:01:51 +02:00
|
|
|
|
|
|
|
objType := "task"
|
2017-03-21 03:40:00 +01:00
|
|
|
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Alias + ")" + " finished - " + strings.ToUpper(t.task.Status)
|
2017-02-23 06:12:16 +01:00
|
|
|
if err := (db.Event{
|
2016-04-17 20:01:51 +02:00
|
|
|
ProjectID: &t.projectID,
|
|
|
|
ObjectType: &objType,
|
|
|
|
ObjectID: &t.task.ID,
|
|
|
|
Description: &desc,
|
|
|
|
}.Insert()); err != nil {
|
|
|
|
t.log("Fatal error inserting an event")
|
|
|
|
panic(err)
|
|
|
|
}
|
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:53:36 +01:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-04-17 20:01:51 +02:00
|
|
|
objType := "task"
|
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"
|
2017-02-23 06:12:16 +01:00
|
|
|
if err := (db.Event{
|
2016-04-17 20:01:51 +02:00
|
|
|
ProjectID: &t.projectID,
|
|
|
|
ObjectType: &objType,
|
|
|
|
ObjectID: &t.task.ID,
|
|
|
|
Description: &desc,
|
|
|
|
}.Insert()); err != nil {
|
|
|
|
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")
|
2016-04-07 14:49:34 +02:00
|
|
|
|
|
|
|
if err := t.installKey(t.repository.SshKey); err != nil {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := t.updateRepository(); err != nil {
|
|
|
|
t.log("Failed updating repository: " + err.Error())
|
2016-04-17 02:20:23 +02:00
|
|
|
t.fail()
|
2016-04-07 14:49:34 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-11-22 02:31:29 +01:00
|
|
|
if err := t.runGalaxy(); err != nil {
|
|
|
|
t.log("Running galaxy failed: " + err.Error())
|
|
|
|
t.fail()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
// todo: write environment
|
|
|
|
|
2018-03-16 02:26:25 +01:00
|
|
|
if err, stderr := t.listPlaybookHosts(); err != nil {
|
|
|
|
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() {
|
|
|
|
fmt.Println("Stopped running tasks")
|
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()
|
|
|
|
|
|
|
|
objType := "task"
|
|
|
|
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Alias + ")" + " finished - " + strings.ToUpper(t.task.Status)
|
|
|
|
if err := (db.Event{
|
|
|
|
ProjectID: &t.projectID,
|
|
|
|
ObjectType: &objType,
|
|
|
|
ObjectID: &t.task.ID,
|
|
|
|
Description: &desc,
|
|
|
|
}.Insert()); err != nil {
|
|
|
|
t.log("Fatal error inserting an event")
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
{
|
|
|
|
now := time.Now()
|
|
|
|
t.task.Status = "running"
|
|
|
|
t.task.Start = &now
|
|
|
|
|
|
|
|
t.updateStatus()
|
|
|
|
}
|
|
|
|
|
|
|
|
objType := "task"
|
|
|
|
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Alias + ")" + " is running"
|
|
|
|
if err := (db.Event{
|
|
|
|
ProjectID: &t.projectID,
|
|
|
|
ObjectType: &objType,
|
|
|
|
ObjectID: &t.task.ID,
|
|
|
|
Description: &desc,
|
|
|
|
}.Insert()); err != nil {
|
|
|
|
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")
|
|
|
|
|
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
|
|
|
|
|
|
|
t.task.Status = "success"
|
2016-05-17 21:12:54 +02:00
|
|
|
t.updateStatus()
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *task) fetch(errMsg string, ptr interface{}, query string, args ...interface{}) error {
|
2017-02-23 06:12:16 +01:00
|
|
|
err := db.Mysql.SelectOne(ptr, query, args...)
|
2016-04-07 14:49:34 +02:00
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
t.log(errMsg)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
2016-04-17 02:20:23 +02:00
|
|
|
t.fail()
|
2016-04-07 14:49:34 +02:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *task) populateDetails() error {
|
|
|
|
// get template
|
|
|
|
if err := t.fetch("Template not found!", &t.template, "select * from project__template where id=?", t.task.TemplateID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-05-03 06:27:58 +02:00
|
|
|
type AlertSettings struct {
|
|
|
|
Alert bool `db:"alert"`
|
|
|
|
AlertChat string `db:"alert_chat"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var project db.Project
|
2017-04-18 16:36:09 +02:00
|
|
|
// get project alert setting
|
2017-05-03 06:27:58 +02:00
|
|
|
if err := t.fetch("Alert setting not found!", &project, "select alert, alert_chat from project where id=?", t.template.ProjectID); err != nil {
|
2017-03-10 07:25:42 +01:00
|
|
|
return err
|
|
|
|
}
|
2017-05-03 06:27:58 +02:00
|
|
|
t.alert = project.Alert
|
|
|
|
t.alert_chat = project.AlertChat
|
2017-03-10 01:12:55 +01:00
|
|
|
|
2016-04-17 12:41:36 +02:00
|
|
|
// get project users
|
|
|
|
var users []struct {
|
|
|
|
ID int `db:"id"`
|
|
|
|
}
|
2017-02-23 06:12:16 +01:00
|
|
|
if _, err := db.Mysql.Select(&users, "select user_id as id from project__user where project_id=?", t.template.ProjectID); err != nil {
|
2016-04-17 12:41:36 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
t.users = []int{}
|
|
|
|
for _, user := range users {
|
|
|
|
t.users = append(t.users, user.ID)
|
|
|
|
}
|
|
|
|
|
2016-04-07 14:49:34 +02:00
|
|
|
// get access key
|
|
|
|
if err := t.fetch("Template Access Key not found!", &t.sshKey, "select * from access_key where id=?", t.template.SshKeyID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.sshKey.Type != "ssh" {
|
|
|
|
t.log("Non ssh-type keys are currently not supported: " + t.sshKey.Type)
|
|
|
|
return errors.New("Unsupported SSH Key")
|
|
|
|
}
|
|
|
|
|
|
|
|
// get inventory
|
|
|
|
if err := t.fetch("Template Inventory not found!", &t.inventory, "select * from project__inventory where id=?", t.template.InventoryID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
// get inventory services key
|
|
|
|
if t.inventory.KeyID != nil {
|
|
|
|
if err := t.fetch("Inventory AccessKey not found!", &t.inventory.Key, "select * from access_key where id=?", *t.inventory.KeyID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get inventory ssh key
|
|
|
|
if t.inventory.SshKeyID != nil {
|
|
|
|
if err := t.fetch("Inventory Ssh Key not found!", &t.inventory.SshKey, "select * from access_key where id=?", *t.inventory.SshKeyID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-07 14:49:34 +02:00
|
|
|
// get repository
|
|
|
|
if err := t.fetch("Repository not found!", &t.repository, "select * from project__repository where id=?", t.template.RepositoryID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// get repository access key
|
|
|
|
if err := t.fetch("Repository Access Key not found!", &t.repository.SshKey, "select * from access_key where id=?", t.repository.SshKeyID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if t.repository.SshKey.Type != "ssh" {
|
|
|
|
t.log("Repository Access Key is not 'SSH': " + t.repository.SshKey.Type)
|
|
|
|
return errors.New("Unsupported SSH Key")
|
|
|
|
}
|
|
|
|
|
|
|
|
// get environment
|
|
|
|
if len(t.task.Environment) == 0 && t.template.EnvironmentID != nil {
|
|
|
|
err := t.fetch("Environment not found", &t.environment, "select * from project__environment where id=?", *t.template.EnvironmentID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if len(t.task.Environment) > 0 {
|
|
|
|
t.environment.JSON = t.task.Environment
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-23 06:12:16 +01:00
|
|
|
func (t *task) installKey(key db.AccessKey) error {
|
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()
|
2017-02-23 12:21:18 +01:00
|
|
|
if key.Key != nil {
|
2017-04-18 16:21:20 +02:00
|
|
|
if err := ioutil.WriteFile(path+"-cert.pub", []byte(*key.Key), 0600); err != nil {
|
|
|
|
return err
|
2017-02-23 12:21:18 +01:00
|
|
|
}
|
|
|
|
}
|
2017-04-18 16:21:20 +02:00
|
|
|
|
|
|
|
return ioutil.WriteFile(path, []byte(*key.Secret), 0600)
|
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 {
|
|
|
|
repoName := "repository_" + strconv.Itoa(t.repository.ID)
|
|
|
|
_, err := os.Stat(util.Config.TmpPath + "/" + repoName)
|
|
|
|
|
|
|
|
cmd := exec.Command("git")
|
2016-05-17 21:12:54 +02:00
|
|
|
cmd.Dir = util.Config.TmpPath
|
2017-02-08 13:26:15 +01:00
|
|
|
|
2017-04-18 16:21:20 +02:00
|
|
|
gitSSHCommand := "ssh -o StrictHostKeyChecking=no -i " + t.repository.SshKey.GetPath()
|
|
|
|
cmd.Env = t.envVars(util.Config.TmpPath, util.Config.TmpPath, &gitSSHCommand)
|
2016-04-07 14:49:34 +02:00
|
|
|
|
2016-11-22 02:07:00 +01:00
|
|
|
repoURL, repoTag := t.repository.GitUrl, "master"
|
|
|
|
if split := strings.Split(repoURL, "#"); len(split) > 1 {
|
|
|
|
repoURL, repoTag = split[0], split[1]
|
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
if err != nil && os.IsNotExist(err) {
|
2016-11-22 02:07:00 +01:00
|
|
|
t.log("Cloning repository " + repoURL)
|
|
|
|
cmd.Args = append(cmd.Args, "clone", "--recursive", "--branch", repoTag, repoURL, repoName)
|
2016-04-08 21:41:20 +02:00
|
|
|
} else if err != nil {
|
2016-04-07 14:49:34 +02:00
|
|
|
return err
|
2016-04-08 21:41:20 +02:00
|
|
|
} else {
|
2016-11-22 02:07:00 +01:00
|
|
|
t.log("Updating repository " + repoURL)
|
2016-05-17 21:12:54 +02:00
|
|
|
cmd.Dir += "/" + repoName
|
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
|
|
|
}
|
|
|
|
|
2016-06-30 17:34:35 +02:00
|
|
|
func (t *task) runGalaxy() error {
|
|
|
|
args := []string{
|
|
|
|
"install",
|
|
|
|
"-r",
|
|
|
|
"roles/requirements.yml",
|
|
|
|
"-p",
|
|
|
|
"./roles/",
|
|
|
|
"--force",
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := exec.Command("ansible-galaxy", args...)
|
|
|
|
cmd.Dir = util.Config.TmpPath + "/repository_" + strconv.Itoa(t.repository.ID)
|
2017-02-08 13:26:15 +01:00
|
|
|
|
2017-04-18 16:21:20 +02:00
|
|
|
gitSSHCommand := "ssh -o StrictHostKeyChecking=no -i " + t.repository.SshKey.GetPath()
|
|
|
|
cmd.Env = t.envVars(util.Config.TmpPath, cmd.Dir, &gitSSHCommand)
|
2016-06-30 17:34:35 +02:00
|
|
|
|
|
|
|
if _, err := os.Stat(cmd.Dir + "/roles/requirements.yml"); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
t.logCmd(cmd)
|
|
|
|
return cmd.Run()
|
|
|
|
}
|
|
|
|
|
2018-03-16 02:26:25 +01:00
|
|
|
func (t *task) listPlaybookHosts() (error, string) {
|
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 02:26:25 +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")
|
|
|
|
|
|
|
|
cmd := exec.Command("ansible-playbook", args...)
|
|
|
|
cmd.Dir = util.Config.TmpPath + "/repository_" + strconv.Itoa(t.repository.ID)
|
|
|
|
cmd.Env = t.envVars(util.Config.TmpPath, cmd.Dir, nil)
|
|
|
|
|
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
|
|
|
stderr := errb.String()
|
|
|
|
|
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
|
|
|
re := regexp.MustCompile("(?m)^\\s{6}(.*)$")
|
|
|
|
matches := re.FindAllSubmatch(out, 20)
|
|
|
|
hosts := make([]string, len(matches))
|
|
|
|
for i, _ := range matches {
|
|
|
|
hosts[i] = string(matches[i][1])
|
|
|
|
}
|
|
|
|
t.hosts = hosts
|
2018-03-16 02:26:25 +01:00
|
|
|
return err, 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
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
func (t *task) runPlaybook() 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 {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cmd := exec.Command("ansible-playbook", args...)
|
|
|
|
cmd.Dir = util.Config.TmpPath + "/repository_" + strconv.Itoa(t.repository.ID)
|
|
|
|
cmd.Env = t.envVars(util.Config.TmpPath, cmd.Dir, nil)
|
|
|
|
|
|
|
|
t.logCmd(cmd)
|
2018-02-05 19:51:14 +01:00
|
|
|
cmd.Stdin = strings.NewReader("")
|
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
|
|
|
return cmd.Run()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *task) getPlaybookArgs() ([]string, error) {
|
2016-04-08 21:41:20 +02:00
|
|
|
playbookName := t.task.Playbook
|
|
|
|
if len(playbookName) == 0 {
|
|
|
|
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 {
|
|
|
|
case "file":
|
|
|
|
inventory = t.inventory.Inventory
|
|
|
|
default:
|
|
|
|
inventory = util.Config.TmpPath + "/inventory_" + strconv.Itoa(t.task.ID)
|
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +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
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
if t.inventory.SshKeyID != nil {
|
|
|
|
args = append(args, "--private-key="+t.inventory.SshKey.GetPath())
|
|
|
|
}
|
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")
|
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
if len(t.environment.JSON) > 0 {
|
2017-03-18 10:57:41 +01:00
|
|
|
var js map[string]interface{}
|
2017-04-18 15:58:48 +02:00
|
|
|
err := json.Unmarshal([]byte(t.environment.JSON), &js)
|
2017-03-18 10:57:41 +01:00
|
|
|
if err != nil {
|
|
|
|
t.log("JSON is not valid")
|
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
|
|
|
return nil, err
|
2017-03-18 10:57:41 +01:00
|
|
|
}
|
|
|
|
|
2016-04-08 21:41:20 +02:00
|
|
|
args = append(args, "--extra-vars", t.environment.JSON)
|
|
|
|
}
|
|
|
|
|
|
|
|
var extraArgs []string
|
|
|
|
if t.template.Arguments != nil {
|
|
|
|
err := json.Unmarshal([]byte(*t.template.Arguments), &extraArgs)
|
|
|
|
if err != nil {
|
|
|
|
t.log("Could not unmarshal arguments to []string")
|
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
|
|
|
return nil, err
|
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 {
|
|
|
|
args = extraArgs
|
|
|
|
} else {
|
|
|
|
args = append(args, extraArgs...)
|
|
|
|
args = append(args, playbookName)
|
|
|
|
}
|
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
|
|
|
return args, nil
|
2016-04-07 14:49:34 +02:00
|
|
|
}
|
2017-02-08 13:26:15 +01:00
|
|
|
|
2017-04-18 16:21:20 +02:00
|
|
|
func (t *task) envVars(home string, pwd string, gitSSHCommand *string) []string {
|
2017-02-08 13:26:15 +01:00
|
|
|
env := os.Environ()
|
2017-02-08 13:36:44 +01:00
|
|
|
env = append(env, fmt.Sprintf("HOME=%s", home))
|
|
|
|
env = append(env, fmt.Sprintf("PWD=%s", pwd))
|
2017-02-08 13:26:15 +01:00
|
|
|
env = append(env, fmt.Sprintln("PYTHONUNBUFFERED=1"))
|
|
|
|
//env = append(env, fmt.Sprintln("GIT_FLUSH=1"))
|
|
|
|
|
2017-04-18 16:21:20 +02:00
|
|
|
if gitSSHCommand != nil {
|
|
|
|
env = append(env, fmt.Sprintf("GIT_SSH_COMMAND=%s", *gitSSHCommand))
|
2017-02-08 13:26:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
var err error = nil
|
|
|
|
if _, err := os.Stat(path); err != nil {
|
|
|
|
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
|
|
|
|
}
|