Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog/fragments/ansible-successful-condition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
entries:
- description: >
For Ansible-based Operators, adds documented `Successful` condition, and no longer
removes conditions from the status in updates. Users can now wait for a successful
reconciliation by waiting for the `Successful` type condition to be `True`.

kind: "bugfix"
breaking: false
6 changes: 2 additions & 4 deletions hack/generate/samples/internal/ansible/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ const moleculeTaskFragment = `- name: Load CR
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"

- name: Wait 2 minutes for memcached deployment
Expand Down Expand Up @@ -259,8 +258,7 @@ const originaMemcachedMoleculeTask = `- name: Create the cache.example.com/v1alp
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"
vars:
cr_file: 'cache_v1alpha1_memcached.yaml'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"
register: args_test

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"
register: case_test

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"
vars:
cr_file: 'test_v1alpha1_clusterannotationtest.yaml'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"

- name: Assert ConfigMap has been created by collection Role
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"
vars:
custom_resource: "{{ lookup('template', '/'.join([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"
register: reconciliation_test
- name: Retreive the number of iterations on the ConfigMap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"
register: selector_test

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"
register: subresources_test

Expand Down
62 changes: 47 additions & 15 deletions internal/ansible/controller/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@ func (r *AnsibleOperatorReconciler) markRunning(ctx context.Context, nn types.Na
errCond.Status = v1.ConditionFalse
ansiblestatus.SetCondition(&crStatus, *errCond)
}
successCond := ansiblestatus.GetCondition(crStatus, ansiblestatus.SuccessfulConditionType)
if successCond != nil {
successCond.Status = v1.ConditionFalse
ansiblestatus.SetCondition(&crStatus, *successCond)
}
// If the condition is currently running, making sure that the values are correct.
// If they are the same a no-op, if they are different then it is a good thing we
// are updating it.
Expand Down Expand Up @@ -338,7 +343,12 @@ func (r *AnsibleOperatorReconciler) markError(ctx context.Context, nn types.Name
}
crStatus := getStatus(u)

sc := ansiblestatus.GetCondition(crStatus, ansiblestatus.RunningConditionType)
rc := ansiblestatus.GetCondition(crStatus, ansiblestatus.RunningConditionType)
if rc != nil {
rc.Status = v1.ConditionFalse
ansiblestatus.SetCondition(&crStatus, *rc)
}
sc := ansiblestatus.GetCondition(crStatus, ansiblestatus.SuccessfulConditionType)
if sc != nil {
sc.Status = v1.ConditionFalse
ansiblestatus.SetCondition(&crStatus, *sc)
Expand Down Expand Up @@ -375,33 +385,55 @@ func (r *AnsibleOperatorReconciler) markDone(ctx context.Context, nn types.Names
runSuccessful := len(failureMessages) == 0
ansibleStatus := ansiblestatus.NewAnsibleResultFromStatusJobEvent(statusEvent)

if !runSuccessful {
if runSuccessful {
metrics.ReconcileSucceeded(r.GVK.String())
deprecatedRunningCondition := ansiblestatus.NewCondition(
ansiblestatus.RunningConditionType,
v1.ConditionTrue,
ansibleStatus,
ansiblestatus.SuccessfulReason,
ansiblestatus.AwaitingMessage,
)
failureCondition := ansiblestatus.NewCondition(
ansiblestatus.FailureConditionType,
v1.ConditionFalse,
nil,
"",
"",
)
successfulCondition := ansiblestatus.NewCondition(
ansiblestatus.SuccessfulConditionType,
v1.ConditionTrue,
nil,
ansiblestatus.SuccessfulReason,
ansiblestatus.SuccessfulMessage,
)
ansiblestatus.SetCondition(&crStatus, *deprecatedRunningCondition)
ansiblestatus.SetCondition(&crStatus, *successfulCondition)
ansiblestatus.SetCondition(&crStatus, *failureCondition)
} else {
metrics.ReconcileFailed(r.GVK.String())
sc := ansiblestatus.GetCondition(crStatus, ansiblestatus.RunningConditionType)
if sc != nil {
sc.Status = v1.ConditionFalse
ansiblestatus.SetCondition(&crStatus, *sc)
}
c := ansiblestatus.NewCondition(
failureCondition := ansiblestatus.NewCondition(
ansiblestatus.FailureConditionType,
v1.ConditionTrue,
ansibleStatus,
ansiblestatus.FailedReason,
strings.Join(failureMessages, "\n"),
)
ansiblestatus.SetCondition(&crStatus, *c)
} else {
metrics.ReconcileSucceeded(r.GVK.String())
c := ansiblestatus.NewCondition(
ansiblestatus.RunningConditionType,
v1.ConditionTrue,
ansibleStatus,
ansiblestatus.SuccessfulReason,
ansiblestatus.SuccessfulMessage,
successfulCondition := ansiblestatus.NewCondition(
ansiblestatus.SuccessfulConditionType,
v1.ConditionFalse,
nil,
"",
"",
)
// Remove the failure condition if set, because this completed successfully.
ansiblestatus.RemoveCondition(&crStatus, ansiblestatus.FailureConditionType)
ansiblestatus.SetCondition(&crStatus, *c)
ansiblestatus.SetCondition(&crStatus, *failureCondition)
ansiblestatus.SetCondition(&crStatus, *successfulCondition)
}
// This needs the status subresource to be enabled by default.
u.Object["status"] = crStatus.GetJSONMap()
Expand Down
30 changes: 27 additions & 3 deletions internal/ansible/controller/reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ func TestReconcile(t *testing.T) {
"message": "Awaiting next reconciliation",
"reason": "Successful",
},
map[string]interface{}{
"status": "True",
"type": "Successful",
"message": "Last reconciliation succeeded",
"reason": "Successful",
},
map[string]interface{}{
"status": "False",
"type": "Failure",
},
},
},
},
Expand Down Expand Up @@ -200,6 +210,10 @@ func TestReconcile(t *testing.T) {
"message": "new failure message",
"reason": "Failed",
},
map[string]interface{}{
"status": "False",
"type": "Successful",
},
},
},
},
Expand Down Expand Up @@ -313,6 +327,16 @@ func TestReconcile(t *testing.T) {
"message": "Awaiting next reconciliation",
"reason": "Successful",
},
map[string]interface{}{
"status": "True",
"type": "Successful",
"message": "Last reconciliation succeeded",
"reason": "Successful",
},
map[string]interface{}{
"status": "False",
"type": "Failure",
},
},
},
},
Expand Down Expand Up @@ -555,15 +579,15 @@ func TestReconcile(t *testing.T) {
actualCond := ansiblestatus.GetCondition(actualStatus, c.Type)
if c.Reason != actualCond.Reason || c.Message != actualCond.Message || c.Status !=
actualCond.Status {
t.Fatalf("Message or reason did not match\nexpected: %v\nactual: %v", c, actualCond)
t.Fatalf("Message or reason did not match\nexpected: %+v\nactual: %+v", c, actualCond)
}
if c.AnsibleResult == nil && actualCond.AnsibleResult != nil {
t.Fatalf("Ansible result did not match expected: %v\nactual: %v", c.AnsibleResult,
t.Fatalf("Ansible result did not match\nexpected: %+v\nactual: %+v", c.AnsibleResult,
actualCond.AnsibleResult)
}
if c.AnsibleResult != nil {
if !reflect.DeepEqual(c.AnsibleResult, actualCond.AnsibleResult) {
t.Fatalf("Ansible result did not match expected: %v\nactual: %v", c.AnsibleResult,
t.Fatalf("Ansible result did not match\nexpected: %+v\nactual: %+v", c.AnsibleResult,
actualCond.AnsibleResult)
}
}
Expand Down
6 changes: 4 additions & 2 deletions internal/ansible/controller/status/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ const (
RunningConditionType ConditionType = "Running"
// FailureConditionType - condition type of failure.
FailureConditionType ConditionType = "Failure"
// SuccessfulConditionType - condition type of success.
SuccessfulConditionType ConditionType = "Successful"
)

// Condition - the condition for the ansible operator.
Expand All @@ -118,11 +120,11 @@ func createConditionFromMap(cm map[string]interface{}) Condition {
}
reason, ok := cm["reason"].(string)
if !ok {
reason = RunningReason
reason = ""
}
message, ok := cm["message"].(string)
if !ok {
message = RunningMessage
message = ""
}
asm, ok := cm["ansibleResult"].(map[string]interface{})
var ansibleResult *AnsibleResult
Expand Down
4 changes: 3 additions & 1 deletion internal/ansible/controller/status/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ const (
// RunningMessage - message for running reason.
RunningMessage = "Running reconciliation"
// SuccessfulMessage - message for successful reason.
SuccessfulMessage = "Awaiting next reconciliation"
AwaitingMessage = "Awaiting next reconciliation"
// SuccessfulMessage - message for successful condition.
SuccessfulMessage = "Last reconciliation succeeded"
)

// NewCondition - condition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ const resourceTestTemplate = `---
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"
vars:
cr_file: '{{ .SampleFile }}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
wait: yes
wait_timeout: 300
wait_condition:
type: Running
reason: Successful
type: Successful
status: "True"

- name: Wait 2 minutes for memcached deployment
Expand Down