mirror of
https://github.com/semaphoreui/semaphore.git
synced 2025-01-20 07:19:20 +01:00
fix(tf): checking output
feat(tf): refactor stdin
This commit is contained in:
parent
7e1fd3ce0f
commit
2de9a5bdfc
@ -168,6 +168,24 @@ func ConfirmTask(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func RejectTask(w http.ResponseWriter, r *http.Request) {
|
||||
targetTask := context.Get(r, "task").(db.Task)
|
||||
project := context.Get(r, "project").(db.Project)
|
||||
|
||||
if targetTask.ProjectID != project.ID {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := helpers.TaskPool(r).RejectTask(targetTask)
|
||||
if err != nil {
|
||||
helpers.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func StopTask(w http.ResponseWriter, r *http.Request) {
|
||||
targetTask := context.Get(r, "task").(db.Task)
|
||||
project := context.Get(r, "project").(db.Project)
|
||||
|
@ -186,6 +186,7 @@ func Route() *mux.Router {
|
||||
projectTaskStop.Use(projects.ProjectMiddleware, projects.GetTaskMiddleware, projects.GetMustCanMiddleware(db.CanRunProjectTasks))
|
||||
projectTaskStop.HandleFunc("/tasks/{task_id}/stop", projects.StopTask).Methods("POST")
|
||||
projectTaskStop.HandleFunc("/tasks/{task_id}/confirm", projects.ConfirmTask).Methods("POST")
|
||||
projectTaskStop.HandleFunc("/tasks/{task_id}/reject", projects.RejectTask).Methods("POST")
|
||||
|
||||
//
|
||||
// Project resources CRUD
|
||||
|
@ -2,6 +2,7 @@ package db_lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@ -14,24 +15,55 @@ import (
|
||||
)
|
||||
|
||||
type TerraformApp struct {
|
||||
Logger task_logger.Logger
|
||||
Template db.Template
|
||||
Repository db.Repository
|
||||
Inventory db.Inventory
|
||||
reader terraformReader
|
||||
Name string
|
||||
noChanges bool
|
||||
Logger task_logger.Logger
|
||||
Template db.Template
|
||||
Repository db.Repository
|
||||
Inventory db.Inventory
|
||||
reader terraformReader // reader
|
||||
Name string // Name is the name of the terraform binary
|
||||
PlanHasNoChanges bool // PlanHasNoChanges is true if terraform plan has no changes
|
||||
}
|
||||
|
||||
type terraformReaderResult int
|
||||
|
||||
const (
|
||||
terraformReaderConfirmed terraformReaderResult = iota
|
||||
terraformReaderFailed
|
||||
)
|
||||
|
||||
type terraformReader struct {
|
||||
result *terraformReaderResult
|
||||
EOF bool
|
||||
status task_logger.TaskStatus
|
||||
logger task_logger.Logger
|
||||
}
|
||||
|
||||
func (r *terraformReader) Read(p []byte) (n int, err error) {
|
||||
if r.EOF {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if r.status != task_logger.TaskWaitingConfirmation {
|
||||
time.Sleep(time.Second * 3)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
for {
|
||||
time.Sleep(time.Second * 3)
|
||||
if r.status.IsFinished() ||
|
||||
r.status == task_logger.TaskConfirmed ||
|
||||
r.status == task_logger.TaskRejected {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
r.EOF = true
|
||||
|
||||
switch r.status {
|
||||
case task_logger.TaskConfirmed:
|
||||
copy(p, "yes\n")
|
||||
r.logger.SetStatus(task_logger.TaskRunningStatus)
|
||||
return 4, nil
|
||||
case task_logger.TaskRejected:
|
||||
copy(p, "no\n")
|
||||
r.logger.SetStatus(task_logger.TaskRunningStatus)
|
||||
return 3, nil
|
||||
default:
|
||||
copy(p, "\n")
|
||||
return 1, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TerraformApp) makeCmd(command string, args []string, environmentVars []string) *exec.Cmd {
|
||||
@ -60,27 +92,12 @@ func (t *TerraformApp) GetFullPath() string {
|
||||
}
|
||||
|
||||
func (t *TerraformApp) SetLogger(logger task_logger.Logger) task_logger.Logger {
|
||||
logger.AddStatusListener(func(status task_logger.TaskStatus) {
|
||||
t.reader.status = status
|
||||
})
|
||||
|
||||
t.reader.logger = logger
|
||||
t.Logger = logger
|
||||
|
||||
t.Logger.AddLogListener(func(new time.Time, msg string) {
|
||||
if strings.Contains(msg, "No changes.") {
|
||||
t.noChanges = true
|
||||
}
|
||||
})
|
||||
|
||||
t.Logger.AddStatusListener(func(status task_logger.TaskStatus) {
|
||||
var result terraformReaderResult
|
||||
|
||||
switch status {
|
||||
case task_logger.TaskConfirmed:
|
||||
result = terraformReaderConfirmed
|
||||
t.reader.result = &result
|
||||
case task_logger.TaskFailStatus, task_logger.TaskStoppedStatus:
|
||||
result = terraformReaderFailed
|
||||
t.reader.result = &result
|
||||
}
|
||||
})
|
||||
|
||||
return logger
|
||||
}
|
||||
|
||||
@ -92,7 +109,7 @@ func (t *TerraformApp) init(environmentVars []string, params *db.TerraformTaskPa
|
||||
}
|
||||
defer keyInstallation.Destroy() //nolint: errcheck
|
||||
|
||||
args := []string{"init"}
|
||||
args := []string{"init", "-lock=false"}
|
||||
|
||||
if params.Upgrade {
|
||||
args = append(args, "-upgrade")
|
||||
@ -106,6 +123,16 @@ func (t *TerraformApp) init(environmentVars []string, params *db.TerraformTaskPa
|
||||
|
||||
cmd := t.makeCmd(t.Name, args, environmentVars)
|
||||
t.Logger.LogCmd(cmd)
|
||||
|
||||
t.Logger.AddLogListener(func(new time.Time, msg string) {
|
||||
if strings.Contains(msg, "Do you want to copy existing state to the new backend?") {
|
||||
t.Logger.SetStatus(task_logger.TaskWaitingConfirmation)
|
||||
} else if strings.Contains(msg, "has been successfully initialized!") {
|
||||
t.reader.EOF = true
|
||||
}
|
||||
})
|
||||
|
||||
cmd.Stdin = &t.reader
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -115,7 +142,7 @@ func (t *TerraformApp) init(environmentVars []string, params *db.TerraformTaskPa
|
||||
}
|
||||
|
||||
func (t *TerraformApp) isWorkspacesSupported(environmentVars []string) bool {
|
||||
cmd := t.makeCmd(string(t.Name), []string{"workspace", "list"}, environmentVars)
|
||||
cmd := t.makeCmd(t.Name, []string{"workspace", "list"}, environmentVars)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return false
|
||||
@ -159,9 +186,16 @@ func (t *TerraformApp) InstallRequirements(environmentVars []string, params inte
|
||||
}
|
||||
|
||||
func (t *TerraformApp) Plan(args []string, environmentVars []string, inputs map[string]string, cb func(*os.Process)) error {
|
||||
args = append([]string{"plan"}, args...)
|
||||
args = append([]string{"plan", "-lock=false"}, args...)
|
||||
cmd := t.makeCmd(t.Name, args, environmentVars)
|
||||
t.Logger.LogCmd(cmd)
|
||||
|
||||
t.reader.logger.AddLogListener(func(new time.Time, msg string) {
|
||||
if strings.Contains(msg, "No changes.") {
|
||||
t.PlanHasNoChanges = true
|
||||
}
|
||||
})
|
||||
|
||||
cmd.Stdin = strings.NewReader("")
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
@ -172,7 +206,7 @@ func (t *TerraformApp) Plan(args []string, environmentVars []string, inputs map[
|
||||
}
|
||||
|
||||
func (t *TerraformApp) Apply(args []string, environmentVars []string, inputs map[string]string, cb func(*os.Process)) error {
|
||||
args = append([]string{"apply", "-auto-approve"}, args...)
|
||||
args = append([]string{"apply", "-auto-approve", "-lock=false"}, args...)
|
||||
cmd := t.makeCmd(t.Name, args, environmentVars)
|
||||
t.Logger.LogCmd(cmd)
|
||||
cmd.Stdin = strings.NewReader("")
|
||||
@ -192,7 +226,7 @@ func (t *TerraformApp) Run(args LocalAppRunningArgs) error {
|
||||
|
||||
params := args.TaskParams.(*db.TerraformTaskParams)
|
||||
|
||||
if t.noChanges || params.Plan {
|
||||
if t.PlanHasNoChanges || params.Plan {
|
||||
t.Logger.SetStatus(task_logger.TaskSuccessStatus)
|
||||
return nil
|
||||
}
|
||||
@ -206,18 +240,20 @@ func (t *TerraformApp) Run(args LocalAppRunningArgs) error {
|
||||
|
||||
for {
|
||||
time.Sleep(time.Second * 3)
|
||||
if t.reader.result != nil {
|
||||
if t.reader.status.IsFinished() ||
|
||||
t.reader.status == task_logger.TaskConfirmed ||
|
||||
t.reader.status == task_logger.TaskRejected {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch *t.reader.result {
|
||||
case terraformReaderFailed:
|
||||
return nil
|
||||
case terraformReaderConfirmed:
|
||||
switch t.reader.status {
|
||||
case task_logger.TaskRejected:
|
||||
t.Logger.SetStatus(task_logger.TaskFailStatus)
|
||||
case task_logger.TaskConfirmed:
|
||||
t.Logger.SetStatus(task_logger.TaskRunningStatus)
|
||||
return t.Apply(args.CliArgs, args.EnvironmentVars, args.Inputs, args.Callback)
|
||||
default:
|
||||
return fmt.Errorf("unknown plan result")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ const (
|
||||
TaskStartingStatus TaskStatus = "starting"
|
||||
TaskWaitingConfirmation TaskStatus = "waiting_confirmation"
|
||||
TaskConfirmed TaskStatus = "confirmed"
|
||||
TaskRejected TaskStatus = "rejected"
|
||||
TaskRunningStatus TaskStatus = "running"
|
||||
TaskStoppingStatus TaskStatus = "stopping"
|
||||
TaskStoppedStatus TaskStatus = "stopped"
|
||||
@ -57,6 +58,6 @@ type Logger interface {
|
||||
SetStatus(status TaskStatus)
|
||||
AddStatusListener(l StatusListener)
|
||||
AddLogListener(l LogListener)
|
||||
|
||||
|
||||
SetCommit(hash, message string)
|
||||
}
|
||||
|
@ -245,6 +245,18 @@ func (p *TaskPool) ConfirmTask(targetTask db.Task) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TaskPool) RejectTask(targetTask db.Task) error {
|
||||
tsk := p.GetTask(targetTask.ID)
|
||||
|
||||
if tsk == nil { // task not active, but exists in database
|
||||
return fmt.Errorf("task is not active")
|
||||
}
|
||||
|
||||
tsk.SetStatus(task_logger.TaskRejected)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TaskPool) StopTask(targetTask db.Task, forceStop bool) error {
|
||||
tsk := p.GetTask(targetTask.ID)
|
||||
if tsk == nil { // task not active, but exists in database
|
||||
|
@ -87,30 +87,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="item.status === 'waiting_confirmation'"
|
||||
class="pl-4"
|
||||
style="
|
||||
background: white;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 55px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
Please confirm this task.
|
||||
</div>
|
||||
|
||||
<v-btn
|
||||
color="warning"
|
||||
style="position: absolute; bottom: 10px; right: 170px; width: 150px;"
|
||||
color="success"
|
||||
style="position: absolute; bottom: 10px; right: 250px; width: 70px;"
|
||||
v-if="item.status === 'waiting_confirmation'"
|
||||
@click="confirmTask()"
|
||||
>
|
||||
{{ $t('confirmTask') }}
|
||||
<v-icon>mdi-check</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
color="warning"
|
||||
style="position: absolute; bottom: 10px; right: 170px; width: 70px;"
|
||||
v-if="item.status === 'waiting_confirmation'"
|
||||
@click="rejectTask()"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
@ -216,7 +208,15 @@ export default {
|
||||
|
||||
computed: {
|
||||
canStop() {
|
||||
return ['running', 'stopping', 'waiting', 'starting', 'waiting_confirmation', 'confirmed'].includes(this.item.status);
|
||||
return [
|
||||
'running',
|
||||
'stopping',
|
||||
'waiting',
|
||||
'starting',
|
||||
'waiting_confirmation',
|
||||
'confirmed',
|
||||
'rejected',
|
||||
].includes(this.item.status);
|
||||
},
|
||||
},
|
||||
|
||||
@ -235,6 +235,15 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
async rejectTask() {
|
||||
await axios({
|
||||
method: 'post',
|
||||
url: `/api/project/${this.projectId}/tasks/${this.itemId}/reject`,
|
||||
responseType: 'json',
|
||||
data: {},
|
||||
});
|
||||
},
|
||||
|
||||
async stopTask(force) {
|
||||
await axios({
|
||||
method: 'post',
|
||||
|
@ -13,6 +13,7 @@ const TaskStatus = Object.freeze({
|
||||
STARTING: 'starting',
|
||||
WAITING_CONFIRMATION: 'waiting_confirmation',
|
||||
CONFIRMED: 'confirmed',
|
||||
REJECTED: 'rejected',
|
||||
RUNNING: 'running',
|
||||
SUCCESS: 'success',
|
||||
ERROR: 'error',
|
||||
|
Loading…
Reference in New Issue
Block a user