Skip to content

Commit bde8bb0

Browse files
committed
Implement ConfigFetcher interface for UI component configuration retrieval
1 parent a7d4d1b commit bde8bb0

File tree

2 files changed

+204
-151
lines changed

2 files changed

+204
-151
lines changed

internal/controller/scalityuicomponent_controller.go

Lines changed: 130 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,50 @@ type ConfigSpec struct {
5959
Version string `json:"version"`
6060
}
6161

62+
// ConfigFetcher defines an interface for fetching UI component configurations
63+
type ConfigFetcher interface {
64+
FetchConfig(ctx context.Context, namespace, serviceName string, port int) (string, error)
65+
}
66+
67+
// K8sServiceProxyFetcher implements ConfigFetcher using Kubernetes service proxy
68+
type K8sServiceProxyFetcher struct {
69+
Config *rest.Config
70+
}
71+
72+
// FetchConfig retrieves the micro-app configuration from the specified service
73+
func (f *K8sServiceProxyFetcher) FetchConfig(ctx context.Context, namespace, serviceName string, port int) (string, error) {
74+
logger := log.FromContext(ctx)
75+
76+
clientset, err := kubernetes.NewForConfig(f.Config)
77+
if err != nil {
78+
logger.Error(err, "Failed to create clientset")
79+
return "", err
80+
}
81+
82+
restClient := clientset.CoreV1().RESTClient()
83+
req := restClient.Get().
84+
Namespace(namespace).
85+
Resource("services").
86+
Name(fmt.Sprintf("%s:%d", serviceName, port)).
87+
SubResource("proxy").
88+
Suffix("/.well-known/micro-app-configuration")
89+
90+
// Execute the request
91+
result := req.Do(ctx)
92+
raw, err := result.Raw()
93+
if err != nil {
94+
logger.Error(err, "Failed to get configuration")
95+
return "", err
96+
}
97+
98+
return string(raw), nil
99+
}
100+
62101
type ScalityUIComponentReconciler struct {
63102
client.Client
64-
Scheme *runtime.Scheme
65-
Config *rest.Config
103+
Scheme *runtime.Scheme
104+
Config *rest.Config
105+
ConfigFetcher ConfigFetcher
66106
}
67107

68108
// +kubebuilder:rbac:groups=ui.scality.com,resources=scalityuicomponents,verbs=get;list;watch;create;update;patch;delete
@@ -158,114 +198,124 @@ func (r *ScalityUIComponentReconciler) Reconcile(ctx context.Context, req ctrl.R
158198
}
159199

160200
if deployment.Status.ReadyReplicas > 0 {
161-
// Fetch configuration
162-
configContent, err := r.fetchMicroAppConfig(ctx, scalityUIComponent.Namespace, scalityUIComponent.Name)
163-
164-
if err != nil {
165-
logger.Error(err, "Failed to fetch micro-app-configuration")
166-
167-
// Add a failure condition
168-
meta.SetStatusCondition(&scalityUIComponent.Status.Conditions, metav1.Condition{
169-
Type: "ConfigurationRetrieved",
170-
Status: metav1.ConditionFalse,
171-
Reason: "FetchFailed",
172-
Message: fmt.Sprintf("Failed to fetch configuration: %v", err),
173-
LastTransitionTime: metav1.Now(),
174-
})
175-
176-
if updateErr := r.Status().Update(ctx, scalityUIComponent); updateErr != nil {
177-
logger.Error(updateErr, "Failed to update status with failure condition")
178-
}
179-
180-
return ctrl.Result{RequeueAfter: time.Second * 10}, nil
181-
}
201+
// Fetch and process configuration
202+
return r.processUIComponentConfig(ctx, scalityUIComponent)
203+
} else {
204+
logger.Info("Deployment not ready yet, waiting for pods to start")
205+
return ctrl.Result{RequeueAfter: time.Second * 10}, nil
206+
}
207+
}
182208

183-
// Parse JSON to extract information
184-
var config MicroAppConfig
185-
if err := json.Unmarshal([]byte(configContent), &config); err != nil {
186-
logger.Error(err, "Failed to parse micro-app-configuration")
187-
188-
// Add a failure condition
189-
meta.SetStatusCondition(&scalityUIComponent.Status.Conditions, metav1.Condition{
190-
Type: "ConfigurationRetrieved",
191-
Status: metav1.ConditionFalse,
192-
Reason: "ParseFailed",
193-
Message: fmt.Sprintf("Failed to parse configuration: %v", err),
194-
LastTransitionTime: metav1.Now(),
195-
})
196-
197-
if updateErr := r.Status().Update(ctx, scalityUIComponent); updateErr != nil {
198-
logger.Error(updateErr, "Failed to update status with failure condition")
199-
}
200-
201-
return ctrl.Result{RequeueAfter: time.Second * 10}, nil
202-
}
209+
// processUIComponentConfig fetches and processes UI component configuration,
210+
// updates the status and returns the reconcile result
211+
func (r *ScalityUIComponentReconciler) processUIComponentConfig(ctx context.Context, scalityUIComponent *uiv1alpha1.ScalityUIComponent) (ctrl.Result, error) {
212+
logger := log.FromContext(ctx)
203213

204-
// Update status with retrieved information
205-
scalityUIComponent.Status.Kind = config.Metadata.Kind
206-
scalityUIComponent.Status.PublicPath = config.Spec.PublicPath
207-
scalityUIComponent.Status.Version = config.Spec.Version
214+
// Fetch configuration
215+
configContent, err := r.fetchMicroAppConfig(ctx, scalityUIComponent.Namespace, scalityUIComponent.Name)
216+
217+
if err != nil {
218+
logger.Error(err, "Failed to fetch micro-app-configuration")
208219

209-
// Add a success condition
220+
// Add a failure condition
210221
meta.SetStatusCondition(&scalityUIComponent.Status.Conditions, metav1.Condition{
211222
Type: "ConfigurationRetrieved",
212-
Status: metav1.ConditionTrue,
213-
Reason: "FetchSucceeded",
214-
Message: "Successfully fetched and applied UI component configuration",
223+
Status: metav1.ConditionFalse,
224+
Reason: "FetchFailed",
225+
Message: fmt.Sprintf("Failed to fetch configuration: %v", err),
215226
LastTransitionTime: metav1.Now(),
216227
})
217228

218-
// Update the status
219-
if err := r.Status().Update(ctx, scalityUIComponent); err != nil {
220-
logger.Error(err, "Failed to update status with configuration")
221-
return ctrl.Result{}, err
229+
if updateErr := r.Status().Update(ctx, scalityUIComponent); updateErr != nil {
230+
logger.Error(updateErr, "Failed to update status with failure condition")
222231
}
223232

224-
logger.Info("ScalityUIComponent status updated with configuration",
225-
"kind", scalityUIComponent.Status.Kind,
226-
"publicPath", scalityUIComponent.Status.PublicPath,
227-
"version", scalityUIComponent.Status.Version)
228-
} else {
229-
logger.Info("Deployment not ready yet, waiting for pods to start")
230233
return ctrl.Result{RequeueAfter: time.Second * 10}, nil
231234
}
232235

236+
// Parse and apply configuration
237+
result, err := r.parseAndApplyConfig(ctx, scalityUIComponent, configContent)
238+
if err != nil {
239+
return result, nil // Error handling is done in parseAndApplyConfig
240+
}
241+
242+
logger.Info("ScalityUIComponent status updated with configuration",
243+
"kind", scalityUIComponent.Status.Kind,
244+
"publicPath", scalityUIComponent.Status.PublicPath,
245+
"version", scalityUIComponent.Status.Version)
246+
233247
return ctrl.Result{}, nil
234248
}
235249

236-
func (r *ScalityUIComponentReconciler) fetchMicroAppConfig(ctx context.Context, namespace, serviceName string) (string, error) {
250+
// parseAndApplyConfig parses the configuration content and updates the ScalityUIComponent status
251+
func (r *ScalityUIComponentReconciler) parseAndApplyConfig(ctx context.Context,
252+
scalityUIComponent *uiv1alpha1.ScalityUIComponent, configContent string) (ctrl.Result, error) {
237253
logger := log.FromContext(ctx)
238254

239-
clientset, err := kubernetes.NewForConfig(r.Config)
240-
if err != nil {
241-
logger.Error(err, "Failed to create clientset")
242-
return "", err
255+
var config MicroAppConfig
256+
if err := json.Unmarshal([]byte(configContent), &config); err != nil {
257+
logger.Error(err, "Failed to parse micro-app-configuration")
258+
259+
// Add a failure condition
260+
meta.SetStatusCondition(&scalityUIComponent.Status.Conditions, metav1.Condition{
261+
Type: "ConfigurationRetrieved",
262+
Status: metav1.ConditionFalse,
263+
Reason: "ParseFailed",
264+
Message: fmt.Sprintf("Failed to parse configuration: %v", err),
265+
LastTransitionTime: metav1.Now(),
266+
})
267+
268+
if updateErr := r.Status().Update(ctx, scalityUIComponent); updateErr != nil {
269+
logger.Error(updateErr, "Failed to update status with failure condition")
270+
}
271+
272+
return ctrl.Result{RequeueAfter: time.Second * 10}, err
243273
}
244274

245-
restClient := clientset.CoreV1().RESTClient()
246-
req := restClient.Get().
247-
Namespace(namespace).
248-
Resource("services").
249-
Name(fmt.Sprintf("%s:%d", serviceName, DefaultServicePort)).
250-
SubResource("proxy").
251-
Suffix("/.well-known/micro-app-configuration")
275+
// Update status with retrieved information
276+
scalityUIComponent.Status.Kind = config.Metadata.Kind
277+
scalityUIComponent.Status.PublicPath = config.Spec.PublicPath
278+
scalityUIComponent.Status.Version = config.Spec.Version
279+
280+
// Add a success condition
281+
meta.SetStatusCondition(&scalityUIComponent.Status.Conditions, metav1.Condition{
282+
Type: "ConfigurationRetrieved",
283+
Status: metav1.ConditionTrue,
284+
Reason: "FetchSucceeded",
285+
Message: "Successfully fetched and applied UI component configuration",
286+
LastTransitionTime: metav1.Now(),
287+
})
252288

253-
// Execute the request
254-
result := req.Do(ctx)
255-
raw, err := result.Raw()
256-
if err != nil {
257-
logger.Error(err, "Failed to get configuration")
258-
return "", err
289+
// Update the status
290+
if err := r.Status().Update(ctx, scalityUIComponent); err != nil {
291+
logger.Error(err, "Failed to update status with configuration")
292+
return ctrl.Result{}, err
259293
}
260294

261-
return string(raw), nil
295+
return ctrl.Result{}, nil
296+
}
297+
298+
func (r *ScalityUIComponentReconciler) fetchMicroAppConfig(ctx context.Context, namespace, serviceName string) (string, error) {
299+
// Use the ConfigFetcher if available, otherwise use the default K8sServiceProxyFetcher
300+
if r.ConfigFetcher != nil {
301+
return r.ConfigFetcher.FetchConfig(ctx, namespace, serviceName, DefaultServicePort)
302+
}
303+
304+
// Default implementation using K8s service proxy
305+
fetcher := &K8sServiceProxyFetcher{Config: r.Config}
306+
return fetcher.FetchConfig(ctx, namespace, serviceName, DefaultServicePort)
262307
}
263308

264309
// SetupWithManager sets up the controller with the Manager.
265310
func (r *ScalityUIComponentReconciler) SetupWithManager(mgr ctrl.Manager) error {
266311
// Store the Kubernetes REST configuration for later use in API requests
267312
r.Config = mgr.GetConfig()
268313

314+
// Initialize the default config fetcher if not explicitly provided
315+
if r.ConfigFetcher == nil {
316+
r.ConfigFetcher = &K8sServiceProxyFetcher{Config: r.Config}
317+
}
318+
269319
return ctrl.NewControllerManagedBy(mgr).
270320
For(&uiv1alpha1.ScalityUIComponent{}).
271321
Owns(&appsv1.Deployment{}).

0 commit comments

Comments
 (0)