-
Notifications
You must be signed in to change notification settings - Fork 24
/
workspace_controller_runs.go
165 lines (138 loc) · 5.53 KB
/
workspace_controller_runs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package controllers
import (
"context"
"fmt"
tfc "github.com/hashicorp/go-tfe"
appv1alpha2 "github.com/hashicorp/terraform-cloud-operator/api/v1alpha2"
)
func (r *WorkspaceReconciler) reconcileRuns(ctx context.Context, w *workspaceInstance, workspace *tfc.Workspace) error {
w.log.Info("Reconcile Runs", "msg", "new reconciliation event")
if runNew, ok := w.instance.Annotations[workspaceAnnotationRunNew]; ok && runNew == annotationTrue {
w.log.Info("Reconcile Runs", "msg", "trigger a new run")
runType := runTypeDefault
if rt, ok := w.instance.Annotations[workspaceAnnotationRunType]; ok {
runType = rt
}
options := tfc.RunCreateOptions{
Message: tfc.String(runMessage),
Workspace: workspace,
}
switch runType {
case runTypePlan:
options.PlanOnly = tfc.Bool(true)
if t, ok := w.instance.Annotations[workspaceAnnotationRunTerraformVersion]; ok {
options.TerraformVersion = tfc.String(t)
}
return r.triggerPlanRun(ctx, w, options)
case runTypeApply:
options.PlanOnly = tfc.Bool(false)
return r.triggerApplyRun(ctx, w, options)
case runTypeRefresh:
options.RefreshOnly = tfc.Bool(true)
return r.triggerApplyRun(ctx, w, options)
default:
// Throw an error message here but don't return.
w.log.Error(fmt.Errorf("run type %q is not valid", runType), "Reconcile Runs", "msg", "no new run will be triggered")
}
}
if err := r.reconcileCurrentRun(ctx, w, workspace); err != nil {
return err
}
if err := r.reconcilePlanRun(ctx, w); err != nil {
return err
}
return nil
}
func (r *WorkspaceReconciler) reconcileCurrentRun(ctx context.Context, w *workspaceInstance, workspace *tfc.Workspace) error {
if workspace.CurrentRun == nil {
w.log.Info("Reconcile Runs", "msg", "there are no ongoing non-speculative runs")
return nil
}
if w.instance.Status.Run != nil {
if workspace.CurrentRun.ID == w.instance.Status.Run.ID && w.instance.Status.Run.RunCompleted() {
w.log.Info("Reconcile Runs", "msg", fmt.Sprintf("the run %s status is synchronized no actions is required", workspace.CurrentRun.ID))
return nil
}
}
w.log.Info("Reconcile Runs", "msg", "get the ongoing non-speculative run status")
run, err := w.tfClient.Client.Runs.Read(ctx, workspace.CurrentRun.ID)
if err != nil {
w.log.Error(err, "Reconcile Runs", "msg", "failed to get the ongoing non-speculative run status")
return err
}
w.log.Info("Reconcile Runs", "msg", fmt.Sprintf("successfully got the ongoing non-speculative run status %s", run.Status))
if w.instance.Status.Run == nil {
w.instance.Status.Run = &appv1alpha2.RunStatus{}
}
w.instance.Status.Run.ID = run.ID
w.instance.Status.Run.Status = string(run.Status)
w.instance.Status.Run.ConfigurationVersion = run.ConfigurationVersion.ID
return nil
}
func (r *WorkspaceReconciler) reconcilePlanRun(ctx context.Context, w *workspaceInstance) error {
if w.instance.Status.Plan == nil {
w.log.Info("Reconcile Runs", "msg", "there are no ongoing speculative runs")
return nil
}
if !w.instance.Status.Plan.RunCompleted() {
w.log.Info("Reconcile Runs", "msg", "get the speculative run status")
run, err := w.tfClient.Client.Runs.Read(ctx, w.instance.Status.Plan.ID)
if err != nil {
w.log.Error(err, "Reconcile Runs", "msg", "failed to get the speculative run status")
return err
}
w.log.Info("Reconcile Runs", "msg", fmt.Sprintf("successfully got the speculative run status %s", run.Status))
w.instance.Status.Plan.ID = run.ID
w.instance.Status.Plan.Status = string(run.Status)
}
return nil
}
func (r *WorkspaceReconciler) triggerApplyRun(ctx context.Context, w *workspaceInstance, options tfc.RunCreateOptions) error {
w.log.Info("Reconcile Runs", "msg", "create a new apply run")
run, err := w.tfClient.Client.Runs.Create(ctx, options)
if err != nil {
w.log.Error(err, "Reconcile Runs", "msg", "failed to create a apply new run")
return err
}
w.log.Info("Reconcile Runs", "msg", fmt.Sprintf("successfully created a new apply run %s", run.ID))
// Set annotation `workspace.app.terraform.io/run-new` to false if a new run was successfully triggered.
w.instance.Annotations[workspaceAnnotationRunNew] = annotationFalse
err = r.Update(ctx, &w.instance)
if err != nil {
w.log.Error(err, "Reconcile Runs", "msg", "failed to update instance")
return err
}
// Update status
if w.instance.Status.Run == nil {
w.instance.Status.Run = &appv1alpha2.RunStatus{}
}
w.instance.Status.Run.ID = run.ID
w.instance.Status.Run.Status = string(run.Status)
w.instance.Status.Run.ConfigurationVersion = run.ConfigurationVersion.ID
return nil
}
func (r *WorkspaceReconciler) triggerPlanRun(ctx context.Context, w *workspaceInstance, options tfc.RunCreateOptions) error {
w.log.Info("Reconcile Runs", "msg", "create a new plan run")
run, err := w.tfClient.Client.Runs.Create(ctx, options)
if err != nil {
w.log.Error(err, "Reconcile Runs", "msg", "failed to create a new plan run")
return err
}
w.log.Info("Reconcile Runs", "msg", fmt.Sprintf("successfully created a new plan run %s", run.ID))
// Set annotation `workspace.app.terraform.io/run-new` to false if a new run was successfully triggered.
w.instance.Annotations[workspaceAnnotationRunNew] = annotationFalse
err = r.Update(ctx, &w.instance)
if err != nil {
w.log.Error(err, "Reconcile Runs", "msg", "failed to update instance")
return err
}
// Update status
w.instance.Status.Plan = &appv1alpha2.PlanStatus{
ID: run.ID,
Status: string(run.Status),
TerraformVersion: run.TerraformVersion,
}
return nil
}