@@ -19,18 +19,13 @@ package controller
1919
2020import (
2121 "context"
22- "fmt"
23- "strconv"
2422
2523 "github.com/go-logr/logr"
26- "github.com/openstack-k8s-operators/lib-common/modules/common"
2724 "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
2825 "github.com/openstack-k8s-operators/lib-common/modules/common/env"
29- "github.com/openstack-k8s-operators/lib-common/modules/common/helper"
3026 testv1beta1 "github.com/openstack-k8s-operators/test-operator/api/v1beta1"
3127 "github.com/openstack-k8s-operators/test-operator/internal/ansibletest"
3228 corev1 "k8s.io/api/core/v1"
33- k8s_errors "k8s.io/apimachinery/pkg/api/errors"
3429 ctrl "sigs.k8s.io/controller-runtime"
3530 "sigs.k8s.io/controller-runtime/pkg/log"
3631)
@@ -56,222 +51,84 @@ func (r *AnsibleTestReconciler) GetLogger(ctx context.Context) logr.Logger {
5651// +kubebuilder:rbac:groups="",resources=persistentvolumeclaims,verbs=get;list;create;update;watch;patch;delete
5752
5853// Reconcile - AnsibleTest
59- func (r * AnsibleTestReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (result ctrl.Result , _err error ) {
60- Log := r .GetLogger (ctx )
61-
62- // Fetch the ansible instance
54+ func (r * AnsibleTestReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
6355 instance := & testv1beta1.AnsibleTest {}
64- err := r .Client .Get (ctx , req .NamespacedName , instance )
65- if err != nil {
66- if k8s_errors .IsNotFound (err ) {
67- return ctrl.Result {}, nil
68- }
69- return ctrl.Result {}, err
70- }
71-
72- // Create a helper
73- helper , err := helper .NewHelper (
74- instance ,
75- r .Client ,
76- r .Kclient ,
77- r .Scheme ,
78- r .Log ,
79- )
80- if err != nil {
81- return ctrl.Result {}, err
82- }
83-
84- // initialize status
85- isNewInstance := instance .Status .Conditions == nil
86- if isNewInstance {
87- instance .Status .Conditions = condition.Conditions {}
88- }
89-
90- // Save a copy of the conditions so that we can restore the LastTransitionTime
91- // when a condition's state doesn't change.
92- savedConditions := instance .Status .Conditions .DeepCopy ()
93-
94- // Always patch the instance status when exiting this function so we
95- // can persist any changes.
96- defer func () {
97- // Don't update the status, if reconciler Panics
98- if r := recover (); r != nil {
99- Log .Info (fmt .Sprintf ("panic during reconcile %v\n " , r ))
100- panic (r )
101- }
102- condition .RestoreLastTransitionTimes (& instance .Status .Conditions , savedConditions )
103- if instance .Status .Conditions .IsUnknown (condition .ReadyCondition ) {
104- instance .Status .Conditions .Set (
105- instance .Status .Conditions .Mirror (condition .ReadyCondition ))
106- }
107- err := helper .PatchInstance (ctx , instance )
108- if err != nil {
109- _err = err
110- return
111- }
112- }()
113-
114- if isNewInstance {
115- // Initialize conditions used later as Status=Unknown
116- cl := condition .CreateList (
117- condition .UnknownCondition (condition .ReadyCondition , condition .InitReason , condition .ReadyInitMessage ),
118- condition .UnknownCondition (condition .InputReadyCondition , condition .InitReason , condition .InputReadyInitMessage ),
119- condition .UnknownCondition (condition .DeploymentReadyCondition , condition .InitReason , condition .DeploymentReadyInitMessage ),
120- )
121- instance .Status .Conditions .Init (& cl )
12256
123- // Register overall status immediately to have an early feedback
124- // e.g. in the cli
125- return ctrl.Result {}, nil
57+ config := FrameworkConfig [* testv1beta1.AnsibleTest ]{
58+ ServiceName : ansibletest .ServiceName ,
59+ NeedsNetworkAttachments : false ,
60+ NeedsConfigMaps : false ,
61+ NeedsFinalizer : false ,
62+ SupportsWorkflow : true ,
63+
64+ BuildPod : func (ctx context.Context , instance * testv1beta1.AnsibleTest , labels , annotations map [string ]string , workflowStepNum int , pvcIndex int ) (* corev1.Pod , error ) {
65+ return r .buildAnsibleTestPod (ctx , instance , labels , annotations , workflowStepNum , pvcIndex )
66+ },
67+
68+ GetInitialConditions : func () []* condition.Condition {
69+ return []* condition.Condition {
70+ condition .UnknownCondition (condition .ReadyCondition , condition .InitReason , condition .ReadyInitMessage ),
71+ condition .UnknownCondition (condition .InputReadyCondition , condition .InitReason , condition .InputReadyInitMessage ),
72+ condition .UnknownCondition (condition .DeploymentReadyCondition , condition .InitReason , condition .DeploymentReadyInitMessage ),
73+ }
74+ },
75+
76+ ValidateInputs : func (ctx context.Context , instance * testv1beta1.AnsibleTest ) error {
77+ return r .ValidateOpenstackInputs (ctx , instance , instance .Spec .OpenStackConfigMap , instance .Spec .OpenStackConfigSecret )
78+ },
79+
80+ GetSpec : func (instance * testv1beta1.AnsibleTest ) interface {} {
81+ return & instance .Spec
82+ },
83+
84+ GetWorkflowStep : func (instance * testv1beta1.AnsibleTest , step int ) interface {} {
85+ return instance .Spec .Workflow [step ]
86+ },
87+
88+ GetWorkflowLength : func (instance * testv1beta1.AnsibleTest ) int {
89+ return len (instance .Spec .Workflow )
90+ },
91+
92+ GetStorageClass : func (instance * testv1beta1.AnsibleTest ) string {
93+ return instance .Spec .StorageClass
94+ },
95+
96+ SetObservedGeneration : func (instance * testv1beta1.AnsibleTest ) {
97+ instance .Status .ObservedGeneration = instance .Generation
98+ },
12699 }
127- instance .Status .ObservedGeneration = instance .Generation
128-
129- workflowLength := len (instance .Spec .Workflow )
130- nextAction , nextWorkflowStep , err := r .NextAction (ctx , instance , workflowLength )
131- if nextWorkflowStep < workflowLength {
132- MergeSections (& instance .Spec , instance .Spec .Workflow [nextWorkflowStep ])
133- }
134-
135- switch nextAction {
136- case Failure :
137- return ctrl.Result {}, err
138-
139- case Wait :
140- Log .Info (InfoWaitingOnPod )
141- return ctrl.Result {RequeueAfter : RequeueAfterValue }, nil
142-
143- case EndTesting :
144- // All pods created by the instance were completed. Release the lock
145- // so that other instances can spawn their pods.
146- if lockReleased , err := r .ReleaseLock (ctx , instance ); ! lockReleased {
147- Log .Info (fmt .Sprintf (InfoCanNotReleaseLock , testOperatorLockName ))
148- return ctrl.Result {RequeueAfter : RequeueAfterValue }, err
149- }
150-
151- instance .Status .Conditions .MarkTrue (
152- condition .DeploymentReadyCondition ,
153- condition .DeploymentReadyMessage )
154-
155- if instance .Status .Conditions .AllSubConditionIsTrue () {
156- instance .Status .Conditions .MarkTrue (condition .ReadyCondition , condition .ReadyMessage )
157- }
158-
159- Log .Info (InfoTestingCompleted )
160- return ctrl.Result {}, nil
161-
162- case CreateFirstPod :
163- lockAcquired , err := r .AcquireLock (ctx , instance , helper , false )
164- if ! lockAcquired {
165- Log .Info (fmt .Sprintf (InfoCanNotAcquireLock , testOperatorLockName ))
166- return ctrl.Result {RequeueAfter : RequeueAfterValue }, err
167- }
168-
169- Log .Info (fmt .Sprintf (InfoCreatingFirstPod , nextWorkflowStep ))
170-
171- case CreateNextPod :
172- // Confirm that we still hold the lock. This is useful to check if for
173- // example somebody / something deleted the lock and it got claimed by
174- // another instance. This is considered to be an error state.
175- lockAcquired , err := r .AcquireLock (ctx , instance , helper , false )
176- if ! lockAcquired {
177- Log .Error (err , ErrConfirmLockOwnership , testOperatorLockName )
178- return ctrl.Result {RequeueAfter : RequeueAfterValue }, err
179- }
180100
181- Log .Info (fmt .Sprintf (InfoCreatingNextPod , nextWorkflowStep ))
182-
183- default :
184- return ctrl.Result {}, ErrReceivedUnexpectedAction
185- }
186-
187- serviceLabels := map [string ]string {
188- common .AppSelector : ansibletest .ServiceName ,
189- workflowStepLabel : strconv .Itoa (nextWorkflowStep ),
190- instanceNameLabel : instance .Name ,
191- operatorNameLabel : "test-operator" ,
192- }
193-
194- err = r .ValidateOpenstackInputs (ctx , instance , instance .Spec .OpenStackConfigMap , instance .Spec .OpenStackConfigSecret )
195- if err != nil {
196- instance .Status .Conditions .Set (condition .FalseCondition (
197- condition .InputReadyCondition ,
198- condition .ErrorReason ,
199- condition .SeverityError ,
200- condition .InputReadyErrorMessage ,
201- err .Error ()))
202- return ctrl.Result {RequeueAfter : RequeueAfterValue }, err
203- }
204- instance .Status .Conditions .MarkTrue (condition .InputReadyCondition , condition .InputReadyMessage )
205-
206- // Create PersistentVolumeClaim
207- ctrlResult , err := r .EnsureLogsPVCExists (
208- ctx ,
209- instance ,
210- helper ,
211- serviceLabels ,
212- instance .Spec .StorageClass ,
213- 0 ,
214- )
215- if err != nil {
216- return ctrlResult , err
217- } else if (ctrlResult != ctrl.Result {}) {
218- return ctrlResult , nil
219- }
220- // Create PersistentVolumeClaim - end
101+ return CommonReconcile (ctx , & r .Reconciler , req , instance , config , r .GetLogger (ctx ))
102+ }
221103
222- // Create a new pod
104+ func (r * AnsibleTestReconciler ) buildAnsibleTestPod (
105+ ctx context.Context ,
106+ instance * testv1beta1.AnsibleTest ,
107+ labels , _ map [string ]string ,
108+ workflowStepNum int ,
109+ pvcIndex int ,
110+ ) (* corev1.Pod , error ) {
223111 mountCerts := r .CheckSecretExists (ctx , instance , "combined-ca-bundle" )
224- podName := r .GetPodName (instance , nextWorkflowStep )
225112 envVars := r .PrepareAnsibleEnv (instance )
226- logsPVCName := r .GetPVCLogsName (instance , 0 )
113+
114+ podName := r .GetPodName (instance , workflowStepNum )
115+ logsPVCName := r .GetPVCLogsName (instance , pvcIndex )
116+
227117 containerImage , err := r .GetContainerImage (ctx , instance )
228118 if err != nil {
229- return ctrl. Result {} , err
119+ return nil , err
230120 }
231121
232- podDef := ansibletest .Pod (
122+ return ansibletest .Pod (
233123 instance ,
234- serviceLabels ,
124+ labels ,
235125 podName ,
236126 logsPVCName ,
237127 mountCerts ,
238128 envVars ,
239- nextWorkflowStep ,
129+ workflowStepNum ,
240130 containerImage ,
241- )
242-
243- ctrlResult , err = r .CreatePod (ctx , * helper , podDef )
244- if err != nil {
245- // Creation of the ansibleTests pod was not successful.
246- // Release the lock and allow other controllers to spawn
247- // a pod.
248- if lockReleased , err := r .ReleaseLock (ctx , instance ); ! lockReleased {
249- return ctrl.Result {}, err
250- }
251-
252- instance .Status .Conditions .Set (condition .FalseCondition (
253- condition .DeploymentReadyCondition ,
254- condition .ErrorReason ,
255- condition .SeverityWarning ,
256- condition .DeploymentReadyErrorMessage ,
257- err .Error ()))
258- return ctrlResult , err
259- } else if (ctrlResult != ctrl.Result {}) {
260- instance .Status .Conditions .Set (condition .FalseCondition (
261- condition .DeploymentReadyCondition ,
262- condition .RequestedReason ,
263- condition .SeverityInfo ,
264- condition .DeploymentReadyRunningMessage ))
265- return ctrlResult , nil
266- }
267- // Create a new pod - end
268-
269- if instance .Status .Conditions .AllSubConditionIsTrue () {
270- instance .Status .Conditions .MarkTrue (condition .ReadyCondition , condition .ReadyMessage )
271- }
272-
273- Log .Info ("Reconciled Service successfully" )
274- return ctrl.Result {}, nil
131+ ), nil
275132}
276133
277134// SetupWithManager sets up the controller with the Manager.
0 commit comments