@@ -19,14 +19,17 @@ import (
19
19
"encoding/json"
20
20
"errors"
21
21
"os"
22
+ "strings"
22
23
"time"
23
24
25
+ ansiblestatus "github.com/operator-framework/operator-sdk/pkg/ansible/controller/status"
24
26
"github.com/operator-framework/operator-sdk/pkg/ansible/events"
25
27
"github.com/operator-framework/operator-sdk/pkg/ansible/proxy/kubeconfig"
26
28
"github.com/operator-framework/operator-sdk/pkg/ansible/runner"
27
29
"github.com/operator-framework/operator-sdk/pkg/ansible/runner/eventapi"
28
30
29
31
"github.com/sirupsen/logrus"
32
+ "k8s.io/api/core/v1"
30
33
apierrors "k8s.io/apimachinery/pkg/api/errors"
31
34
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32
35
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -80,7 +83,9 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
80
83
finalizers := append (pendingFinalizers , finalizer )
81
84
u .SetFinalizers (finalizers )
82
85
err := r .Client .Update (context .TODO (), u )
83
- return reconcileResult , err
86
+ if err != nil {
87
+ return reconcileResult , err
88
+ }
84
89
}
85
90
if ! contains (pendingFinalizers , finalizer ) && deleted {
86
91
logrus .Info ("Resource is terminated, skipping reconcilation" )
@@ -96,34 +101,32 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
96
101
if err != nil {
97
102
return reconcileResult , err
98
103
}
99
- reconcileResult .Requeue = true
100
- return reconcileResult , nil
101
104
}
102
- status := u .Object ["status" ]
103
- _ , ok = status .(map [string ]interface {})
104
- if ! ok {
105
- logrus .Debugf ("status was not found" )
106
- u .Object ["status" ] = map [string ]interface {}{}
105
+ statusInterface := u .Object ["status" ]
106
+ statusMap , _ := statusInterface .(map [string ]interface {})
107
+ crStatus := ansiblestatus .CreateFromMap (statusMap )
108
+
109
+ // If there is no current status add that we are working on this resource.
110
+ errCond := ansiblestatus .GetCondition (crStatus , ansiblestatus .FailureConditionType )
111
+ succCond := ansiblestatus .GetCondition (crStatus , ansiblestatus .RunningConditionType )
112
+
113
+ // If the condition is currently running, making sure that the values are correct.
114
+ // If they are the same a no-op, if they are different then it is a good thing we
115
+ // are updating it.
116
+ if (errCond == nil && succCond == nil ) || (succCond != nil && succCond .Reason != ansiblestatus .SuccessfulReason ) {
117
+ c := ansiblestatus .NewCondition (
118
+ ansiblestatus .RunningConditionType ,
119
+ v1 .ConditionTrue ,
120
+ nil ,
121
+ ansiblestatus .RunningReason ,
122
+ ansiblestatus .RunningMessage ,
123
+ )
124
+ ansiblestatus .SetCondition (& crStatus , * c )
125
+ u .Object ["status" ] = crStatus
107
126
err = r .Client .Update (context .TODO (), u )
108
127
if err != nil {
109
128
return reconcileResult , err
110
129
}
111
- reconcileResult .Requeue = true
112
- return reconcileResult , nil
113
- }
114
-
115
- // If status is an empty map we can assume CR was just created
116
- if len (u .Object ["status" ].(map [string ]interface {})) == 0 {
117
- logrus .Debugf ("Setting phase status to %v" , StatusPhaseCreating )
118
- u .Object ["status" ] = ResourceStatus {
119
- Phase : StatusPhaseCreating ,
120
- }
121
- err = r .Client .Update (context .TODO (), u )
122
- if err != nil {
123
- return reconcileResult , err
124
- }
125
- reconcileResult .Requeue = true
126
- return reconcileResult , nil
127
130
}
128
131
129
132
ownerRef := metav1.OwnerReference {
@@ -145,11 +148,12 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
145
148
146
149
// iterate events from ansible, looking for the final one
147
150
statusEvent := eventapi.StatusJobEvent {}
151
+ failureMessages := eventapi.FailureMessages {}
148
152
for event := range eventChan {
149
153
for _ , eHandler := range r .EventHandlers {
150
154
go eHandler .Handle (u , event )
151
155
}
152
- if event .Event == "playbook_on_stats" {
156
+ if event .Event == eventapi . EventPlaybookOnStats {
153
157
// convert to StatusJobEvent; would love a better way to do this
154
158
data , err := json .Marshal (event )
155
159
if err != nil {
@@ -160,6 +164,9 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
160
164
return reconcile.Result {}, err
161
165
}
162
166
}
167
+ if event .Event == eventapi .EventRunnerOnFailed {
168
+ failureMessages = append (failureMessages , event .GetFailedPlaybookMessage ())
169
+ }
163
170
}
164
171
if statusEvent .Event == "" {
165
172
err := errors .New ("did not receive playbook_on_stats event" )
@@ -168,14 +175,7 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
168
175
}
169
176
170
177
// We only want to update the CustomResource once, so we'll track changes and do it at the end
171
- var needsUpdate bool
172
- runSuccessful := true
173
- for _ , count := range statusEvent .EventData .Failures {
174
- if count > 0 {
175
- runSuccessful = false
176
- break
177
- }
178
- }
178
+ runSuccessful := len (failureMessages ) == 0
179
179
// The finalizer has run successfully, time to remove it
180
180
if deleted && finalizerExists && runSuccessful {
181
181
finalizers := []string {}
@@ -185,31 +185,42 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
185
185
}
186
186
}
187
187
u .SetFinalizers (finalizers )
188
- needsUpdate = true
189
- }
190
-
191
- statusMap , ok := u .Object ["status" ].(map [string ]interface {})
192
- if ! ok {
193
- u .Object ["status" ] = ResourceStatus {
194
- Status : NewStatusFromStatusJobEvent (statusEvent ),
195
- }
196
- logrus .Infof ("adding status for the first time" )
197
- needsUpdate = true
198
- } else {
199
- // Need to conver the map[string]interface into a resource status.
200
- if update , status := UpdateResourceStatus (statusMap , statusEvent ); update {
201
- u .Object ["status" ] = status
202
- needsUpdate = true
188
+ err := r .Client .Update (context .TODO (), u )
189
+ if err != nil {
190
+ return reconcileResult , err
203
191
}
204
192
}
205
- if needsUpdate {
206
- err = r .Client .Update (context .TODO (), u )
207
- }
193
+ ansibleStatus := ansiblestatus .NewAnsibleResultFromStatusJobEvent (statusEvent )
194
+
208
195
if ! runSuccessful {
209
- reconcileResult .Requeue = true
210
- return reconcileResult , err
211
- }
196
+ sc := ansiblestatus .GetCondition (crStatus , ansiblestatus .RunningConditionType )
197
+ sc .Status = v1 .ConditionFalse
198
+ ansiblestatus .SetCondition (& crStatus , * sc )
199
+ c := ansiblestatus .NewCondition (
200
+ ansiblestatus .FailureConditionType ,
201
+ v1 .ConditionTrue ,
202
+ ansibleStatus ,
203
+ ansiblestatus .FailedReason ,
204
+ strings .Join (failureMessages , "\n " ),
205
+ )
206
+ ansiblestatus .SetCondition (& crStatus , * c )
207
+ } else {
208
+ c := ansiblestatus .NewCondition (
209
+ ansiblestatus .RunningConditionType ,
210
+ v1 .ConditionTrue ,
211
+ ansibleStatus ,
212
+ ansiblestatus .SuccessfulReason ,
213
+ ansiblestatus .SuccessfulMessage ,
214
+ )
215
+ // Remove the failure condition if set, because this completed successfully.
216
+ ansiblestatus .RemoveCondition (& crStatus , ansiblestatus .FailureConditionType )
217
+ ansiblestatus .SetCondition (& crStatus , * c )
218
+ }
219
+ // This needs the status subresource to be enabled by default.
220
+ u .Object ["status" ] = crStatus
221
+ err = r .Client .Update (context .TODO (), u )
212
222
return reconcileResult , err
223
+
213
224
}
214
225
215
226
func contains (l []string , s string ) bool {
0 commit comments