diff --git a/api/projects/tasks.go b/api/projects/tasks.go index 919e540f..41d8be7f 100644 --- a/api/projects/tasks.go +++ b/api/projects/tasks.go @@ -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) diff --git a/api/router.go b/api/router.go index c1dc49c3..3b438ca9 100644 --- a/api/router.go +++ b/api/router.go @@ -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 diff --git a/db_lib/TerraformApp.go b/db_lib/TerraformApp.go index d24e7766..8d286501 100644 --- a/db_lib/TerraformApp.go +++ b/db_lib/TerraformApp.go @@ -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 } diff --git a/pkg/task_logger/task_logger.go b/pkg/task_logger/task_logger.go index cb4ce8ae..5349a1be 100644 --- a/pkg/task_logger/task_logger.go +++ b/pkg/task_logger/task_logger.go @@ -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) } diff --git a/services/tasks/TaskPool.go b/services/tasks/TaskPool.go index e4fa228f..e3083614 100644 --- a/services/tasks/TaskPool.go +++ b/services/tasks/TaskPool.go @@ -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 diff --git a/web/src/components/TaskLogView.vue b/web/src/components/TaskLogView.vue index b148a3bc..cbd65e7a 100644 --- a/web/src/components/TaskLogView.vue +++ b/web/src/components/TaskLogView.vue @@ -87,30 +87,22 @@ -
- Please confirm this task. -
- - {{ $t('confirmTask') }} + mdi-check + + + + mdi-close