-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.go
More file actions
164 lines (138 loc) · 6.38 KB
/
config.go
File metadata and controls
164 lines (138 loc) · 6.38 KB
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
package scalityuicomponent
import (
"context"
"encoding/json"
"fmt"
"github.com/go-logr/logr"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// getServiceNameFromFramework gets the service name by querying the actual service created by the framework
func getServiceNameFromFramework(ctx context.Context, kubeClient client.Client, cr ScalityUIComponent) (string, error) {
// Query the service directly from the cluster using the framework's naming convention
service := &corev1.Service{}
serviceName := fmt.Sprintf("%s-service", cr.Name)
err := kubeClient.Get(ctx, client.ObjectKey{
Name: serviceName,
Namespace: cr.Namespace,
}, service)
if err != nil {
return "", fmt.Errorf("failed to get service %s: %w", serviceName, err)
}
return service.Name, nil
}
// newConfigReducer creates a StateReducer for handling configuration fetching and processing
func newConfigReducer(r *ScalityUIComponentReconciler) StateReducer {
return StateReducer{
F: func(cr ScalityUIComponent, currentState State, log logr.Logger) (reconcile.Result, error) {
ctx := currentState.GetContext()
// Check if configuration is already successfully retrieved
existingCondition := meta.FindStatusCondition(cr.Status.Conditions, "ConfigurationRetrieved")
if existingCondition != nil && existingCondition.Status == metav1.ConditionTrue {
log.V(1).Info("Configuration already retrieved, skipping")
return reconcile.Result{}, nil
}
// Skip if there's a recent failed condition with ParseFailed reason to avoid continuous retries
if existingCondition != nil && existingCondition.Status == metav1.ConditionFalse && existingCondition.Reason == "ParseFailed" {
log.V(1).Info("Configuration parse recently failed, skipping")
return ctrl.Result{Requeue: true}, nil
}
// Re-fetch the Deployment to get its latest status, particularly ReadyReplicas
// This is crucial for determining if pods are ready before attempting to fetch the UI configuration
deployment := &appsv1.Deployment{}
deploymentName := fmt.Sprintf("%s-deployment", cr.Name)
if err := currentState.GetKubeClient().Get(ctx, client.ObjectKey{Name: deploymentName, Namespace: cr.Namespace}, deployment); err != nil {
log.Error(err, "Failed to get deployment status")
return ctrl.Result{Requeue: true}, nil
}
if deployment.Status.ReadyReplicas > 0 {
// Fetch and process configuration
return r.processUIComponentConfig(ctx, cr, currentState, log)
} else {
log.Info("Deployment not ready yet, waiting for pods to start")
return ctrl.Result{Requeue: true}, nil
}
},
N: "config",
}
}
// processUIComponentConfig fetches and processes UI component configuration,
// updates the status and returns the reconcile result
func (r *ScalityUIComponentReconciler) processUIComponentConfig(ctx context.Context, scalityUIComponent ScalityUIComponent, currentState State, logger logr.Logger) (ctrl.Result, error) {
// Fetch configuration
// Get the actual service name from the framework
serviceName, err := getServiceNameFromFramework(ctx, currentState.GetKubeClient(), scalityUIComponent)
if err != nil {
logger.Error(err, "Failed to get service name from framework")
return ctrl.Result{Requeue: true}, nil
}
configContent, err := r.fetchMicroAppConfig(ctx, scalityUIComponent.Namespace, serviceName)
if err != nil {
logger.Error(err, "Failed to fetch micro-app-configuration")
// Add a failure condition
meta.SetStatusCondition(&scalityUIComponent.Status.Conditions, metav1.Condition{
Type: "ConfigurationRetrieved",
Status: metav1.ConditionFalse,
Reason: "FetchFailed",
Message: fmt.Sprintf("Failed to fetch configuration: %v", err),
LastTransitionTime: metav1.Now(),
})
if updateErr := r.Client.Status().Update(ctx, scalityUIComponent); updateErr != nil {
logger.Error(updateErr, "Failed to update status with failure condition")
}
return ctrl.Result{Requeue: true}, nil
}
// Parse and apply configuration
result, err := r.parseAndApplyConfig(ctx, scalityUIComponent, configContent, logger)
if err != nil {
return result, nil // Error handling is done in parseAndApplyConfig
}
logger.Info("ScalityUIComponent status updated with configuration",
"kind", scalityUIComponent.Status.Kind,
"publicPath", scalityUIComponent.Status.PublicPath,
"version", scalityUIComponent.Status.Version)
return ctrl.Result{}, nil
}
// parseAndApplyConfig parses the configuration content and updates the ScalityUIComponent status
func (r *ScalityUIComponentReconciler) parseAndApplyConfig(ctx context.Context,
scalityUIComponent ScalityUIComponent, configContent string, logger logr.Logger) (ctrl.Result, error) {
var config MicroAppConfig
if err := json.Unmarshal([]byte(configContent), &config); err != nil {
logger.Error(err, "Failed to parse micro-app-configuration")
// Add a failure condition
meta.SetStatusCondition(&scalityUIComponent.Status.Conditions, metav1.Condition{
Type: "ConfigurationRetrieved",
Status: metav1.ConditionFalse,
Reason: "ParseFailed",
Message: fmt.Sprintf("Failed to parse configuration: %v", err),
LastTransitionTime: metav1.Now(),
})
if updateErr := r.Client.Status().Update(ctx, scalityUIComponent); updateErr != nil {
logger.Error(updateErr, "Failed to update status with failure condition")
}
return ctrl.Result{Requeue: true}, err
}
// Update status with retrieved information
scalityUIComponent.Status.Kind = config.Metadata.Kind
scalityUIComponent.Status.PublicPath = config.Spec.PublicPath
scalityUIComponent.Status.Version = config.Spec.Version
// Add a success condition
meta.SetStatusCondition(&scalityUIComponent.Status.Conditions, metav1.Condition{
Type: "ConfigurationRetrieved",
Status: metav1.ConditionTrue,
Reason: "FetchSucceeded",
Message: "Successfully fetched and applied UI component configuration",
LastTransitionTime: metav1.Now(),
})
// Update the status
if err := r.Client.Status().Update(ctx, scalityUIComponent); err != nil {
logger.Error(err, "Failed to update status with configuration")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}