comments fixes
This commit is contained in:
		| @@ -221,23 +221,23 @@ func (r *NodeTainterConfigReconciler) SetupWithManager(mgr ctrl.Manager) error { | ||||
|  | ||||
| // --- UTILS FUNCTIONS --- | ||||
|  | ||||
| // TaintToString конвертирует тейнт в строку для статуса/логов | ||||
| // Converts Taint to string for status/logs | ||||
| func TaintToString(taint *corev1.Taint) string { | ||||
| 	return fmt.Sprintf("%s=%s:%s", taint.Key, taint.Value, taint.Effect) | ||||
| } | ||||
|  | ||||
| // TaintsToString конвертирует слайс тейнтов в слайс строк | ||||
| // Converts Taints slice to string slice | ||||
| func TaintsToStrings(taints []corev1.Taint) []string { | ||||
| 	res := make([]string, len(taints)) | ||||
| 	for i, t := range taints { | ||||
| 		res[i] = TaintToString(&t) | ||||
| 	} | ||||
| 	sort.Strings(res) // Сортируем для консистентности статуса | ||||
| 	sort.Strings(res) | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| // parseLabelRulesFromSpec парсит правила из CRD Spec | ||||
| // Возвращает map["labelKey=labelValue"]corev1.Taint и ошибки | ||||
| // Parses rules from CRD Spec | ||||
| // returns map["labelKey=labelValue"]corev1.Taint and errors | ||||
| func parseLabelRulesFromSpec(specLabelRules map[string]string) (map[string]corev1.Taint, []error) { | ||||
| 	parsed := make(map[string]corev1.Taint) | ||||
| 	var errs []error | ||||
| @@ -251,25 +251,24 @@ func parseLabelRulesFromSpec(specLabelRules map[string]string) (map[string]corev | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Парсим селектор "key=value" | ||||
| 		partsSelector := strings.SplitN(ruleSelector, "=", 2) | ||||
| 		if len(partsSelector) != 2 { // Должен быть знак = | ||||
| 		if len(partsSelector) != 2 { | ||||
| 			errs = append(errs, fmt.Errorf("invalid rule selector format '%s': missing '='", ruleSelector)) | ||||
| 			continue | ||||
| 		} | ||||
| 		labelKey := strings.TrimSpace(partsSelector[0]) | ||||
| 		labelValue := strings.TrimSpace(partsSelector[1]) // Может быть пустым! | ||||
| 		labelValue := strings.TrimSpace(partsSelector[1]) | ||||
|  | ||||
| 		if labelKey == "" { | ||||
| 			errs = append(errs, fmt.Errorf("invalid rule selector format '%s': empty label key", ruleSelector)) | ||||
| 			continue | ||||
| 		} | ||||
| 		// Валидируем ключ лейбла | ||||
|  | ||||
| 		if msgs := apivalidation.IsQualifiedName(labelKey); len(msgs) > 0 { | ||||
| 			errs = append(errs, fmt.Errorf("invalid label key in selector '%s': %v", ruleSelector, msgs)) | ||||
| 			continue | ||||
| 		} | ||||
| 		// Валидируем значение лейбла (если не пустое) | ||||
|  | ||||
| 		if labelValue != "" { | ||||
| 			if msgs := apivalidation.IsValidLabelValue(labelValue); len(msgs) > 0 { | ||||
| 				errs = append(errs, fmt.Errorf("invalid label value in selector '%s': %v", ruleSelector, msgs)) | ||||
| @@ -277,7 +276,6 @@ func parseLabelRulesFromSpec(specLabelRules map[string]string) (map[string]corev | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Парсим строку тейнта "key=value:Effect" (используем улучшенную логику из прошлого ответа) | ||||
| 		partsEffect := strings.SplitN(taintString, ":", 2) | ||||
| 		if len(partsEffect) != 2 || partsEffect[1] == "" { | ||||
| 			errs = append(errs, fmt.Errorf("invalid taint format for rule '%s': '%s' (missing effect)", ruleSelector, taintString)) | ||||
| @@ -311,35 +309,31 @@ func parseLabelRulesFromSpec(specLabelRules map[string]string) (map[string]corev | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Все ок | ||||
| 		taint := corev1.Taint{Key: taintKey, Value: taintValue, Effect: effect} | ||||
| 		parsed[ruleSelector] = taint // Ключ = "labelKey=labelValue" | ||||
| 		parsed[ruleSelector] = taint // Key = "labelKey=labelValue" | ||||
| 	} | ||||
| 	return parsed, errs | ||||
| } | ||||
|  | ||||
| // calculateDesiredTaints определяет тейнты на основе лейблов ноды и правил | ||||
| // Defines Taints depending on node labels and rules | ||||
| func calculateDesiredTaints(nodeLabels map[string]string, parsedLabelRules map[string]corev1.Taint) []corev1.Taint { | ||||
| 	desired := []corev1.Taint{} | ||||
| 	foundTaints := make(map[string]bool) // Для уникальности Key:Effect | ||||
| 	foundTaints := make(map[string]bool) | ||||
|  | ||||
| 	if nodeLabels == nil { | ||||
| 		nodeLabels = make(map[string]string) // Безопасность | ||||
| 		nodeLabels = make(map[string]string) | ||||
| 	} | ||||
|  | ||||
| 	for ruleSelector, taint := range parsedLabelRules { | ||||
| 		parts := strings.SplitN(ruleSelector, "=", 2) | ||||
| 		if len(parts) != 2 { | ||||
| 			continue | ||||
| 		} // Уже должно быть отва лидировано | ||||
| 		} | ||||
| 		ruleKey := parts[0] | ||||
| 		ruleValue := parts[1] // Может быть пустой | ||||
| 		ruleValue := parts[1] | ||||
|  | ||||
| 		actualValue, exists := nodeLabels[ruleKey] | ||||
|  | ||||
| 		// Логика сравнения: | ||||
| 		// 1. Ключ лейбла должен существовать на ноде. | ||||
| 		// 2. Значение лейбла на ноде должно ТОЧНО совпадать со значением в правиле (включая пустую строку). | ||||
| 		if exists && actualValue == ruleValue { | ||||
| 			taintKeyEffect := fmt.Sprintf("%s:%s", taint.Key, taint.Effect) | ||||
| 			if !foundTaints[taintKeyEffect] { | ||||
| @@ -351,22 +345,19 @@ func calculateDesiredTaints(nodeLabels map[string]string, parsedLabelRules map[s | ||||
| 	return desired | ||||
| } | ||||
|  | ||||
| // TaintKeyEffect создает уникальную строку для тейнта (Key:Effect) | ||||
| // Creates unique string for Taint (Key:Effect) | ||||
| func TaintKeyEffect(taint *corev1.Taint) string { | ||||
| 	return fmt.Sprintf("%s:%s", taint.Key, taint.Effect) | ||||
| } | ||||
|  | ||||
| // mergeAndCheckTaints сравнивает текущие и желаемые тейнты, управляемые оператором. | ||||
| // parsedLabelRules: map["labelKey=labelValue"]corev1.Taint - содержит ВСЕ валидные правила из конфига. | ||||
| // Compares current and desired controlled Taints | ||||
| func mergeAndCheckTaints(currentTaints []corev1.Taint, desiredTaints []corev1.Taint, parsedLabelRules map[string]corev1.Taint) (bool, []corev1.Taint) { | ||||
| 	// 1. Определяем, какие типы тейнтов (Key:Effect) управляются нами по всем правилам | ||||
| 	managedTaintTypes := sets.NewString() | ||||
| 	for _, ruleTaint := range parsedLabelRules { // Итерируем по значениям (Taint объектам) | ||||
| 	for _, ruleTaint := range parsedLabelRules { | ||||
| 		managedTaintTypes.Insert(TaintKeyEffect(&ruleTaint)) | ||||
| 	} | ||||
|  | ||||
| 	// 2. Разделяем текущие тейнты на управляемые и неуправляемые | ||||
| 	currentManagedTaints := make(map[string]corev1.Taint) // key:Effect -> Taint | ||||
| 	currentManagedTaints := make(map[string]corev1.Taint) | ||||
| 	unmanagedTaints := []corev1.Taint{} | ||||
| 	for _, taint := range currentTaints { | ||||
| 		ke := TaintKeyEffect(&taint) | ||||
| @@ -377,31 +368,27 @@ func mergeAndCheckTaints(currentTaints []corev1.Taint, desiredTaints []corev1.Ta | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 3. Создаем map желаемых тейнтов для быстрого поиска | ||||
| 	desiredTaintsMap := make(map[string]corev1.Taint) // key:Effect -> Taint | ||||
| 	for _, taint := range desiredTaints { | ||||
| 		// Проверка, что желаемый тейнт действительно определен в правилах (на всякий случай) | ||||
| 		ke := TaintKeyEffect(&taint) | ||||
| 		if managedTaintTypes.Has(ke) { | ||||
| 			desiredTaintsMap[ke] = taint | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 4. Сравниваем управляемые текущие и желаемые | ||||
| 	needsUpdate := false | ||||
| 	if len(currentManagedTaints) != len(desiredTaintsMap) { | ||||
| 		needsUpdate = true | ||||
| 	} else { | ||||
| 		for ke, desiredTaint := range desiredTaintsMap { | ||||
| 			currentTaint, exists := currentManagedTaints[ke] | ||||
| 			if !exists || currentTaint.Value != desiredTaint.Value { // Сравниваем и значения | ||||
| 			if !exists || currentTaint.Value != desiredTaint.Value { | ||||
| 				needsUpdate = true | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// 5. Собираем новый список тейнтов, если нужно обновление | ||||
| 	if needsUpdate { | ||||
| 		newTaints := make([]corev1.Taint, 0, len(unmanagedTaints)+len(desiredTaintsMap)) | ||||
| 		newTaints = append(newTaints, unmanagedTaints...) | ||||
| @@ -409,7 +396,7 @@ func mergeAndCheckTaints(currentTaints []corev1.Taint, desiredTaints []corev1.Ta | ||||
| 		for ke := range desiredTaintsMap { | ||||
| 			desiredKeys = append(desiredKeys, ke) | ||||
| 		} | ||||
| 		sort.Strings(desiredKeys) // Сортируем для консистентности | ||||
| 		sort.Strings(desiredKeys) | ||||
| 		for _, ke := range desiredKeys { | ||||
| 			newTaints = append(newTaints, desiredTaintsMap[ke]) | ||||
| 		} | ||||
| @@ -419,16 +406,14 @@ func mergeAndCheckTaints(currentTaints []corev1.Taint, desiredTaints []corev1.Ta | ||||
| 	return false, currentTaints | ||||
| } | ||||
|  | ||||
| // updateCRDStatus обновляет статус ресурса NodeTainterConfig | ||||
| // TODO: Вызывать эту функцию при изменении CRD или при старте/ошибках контроллера. | ||||
| // Updates NodeTainterConfig status | ||||
| // TODO: Call this function on CRD updates or controller start/errors | ||||
| func (r *NodeTainterConfigReconciler) updateCRDStatus(ctx context.Context, config *configv1alpha1.NodeTainterConfig, status metav1.ConditionStatus, reason, message string) error { | ||||
| 	log := log.FromContext(ctx).WithValues("config", config.Name) | ||||
| 	configCopy := config.DeepCopy() | ||||
|  | ||||
| 	// Устанавливаем observedGeneration | ||||
| 	configCopy.Status.ObservedGeneration = config.Generation | ||||
|  | ||||
| 	// Обновляем Condition | ||||
| 	newCondition := metav1.Condition{ | ||||
| 		Type:               ConditionTypeReady, | ||||
| 		Status:             status, | ||||
| @@ -436,16 +421,12 @@ func (r *NodeTainterConfigReconciler) updateCRDStatus(ctx context.Context, confi | ||||
| 		Message:            message, | ||||
| 		LastTransitionTime: metav1.Now(), | ||||
| 	} | ||||
| 	// TODO: Использовать 'meta.SetStatusCondition' из 'k8s.io/apimachinery/pkg/api/meta' для правильного обновления conditions | ||||
| 	// Примерно так: | ||||
| 	// meta.SetStatusCondition(&configCopy.Status.Conditions, newCondition) | ||||
| 	// Пока просто заменяем для простоты | ||||
| 	// TODO: Use 'meta.SetStatusCondition' from 'k8s.io/apimachinery/pkg/api/meta' for correct conditions updates | ||||
| 	configCopy.Status.Conditions = []metav1.Condition{newCondition} | ||||
|  | ||||
| 	// TODO: Обновить NodeTaintStatus на основе данных со всех нод (может быть сложно и затратно) | ||||
| 	// TODO: Update NodeTaintStatus based on data from all nodes | ||||
| 	// configCopy.Status.NodeTaintStatus = ... | ||||
|  | ||||
| 	// Используем Patch для обновления статуса | ||||
| 	if err := r.Status().Patch(ctx, configCopy, client.MergeFrom(config)); err != nil { | ||||
| 		log.Error(err, "Failed to patch NodeTainterConfig status") | ||||
| 		return err | ||||
| @@ -454,26 +435,19 @@ func (r *NodeTainterConfigReconciler) updateCRDStatus(ctx context.Context, confi | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // updateNodeTaintStatus обновляет информацию о тейнтах для конкретной ноды в статусе CRD | ||||
| // TODO: Эта функция в текущем виде будет вызывать конфликты, т.к. каждый Reconcile ноды | ||||
| // будет пытаться перезаписать весь Status.NodeTaintStatus. | ||||
| // Правильный подход: читать текущий статус CRD, обновлять только запись для текущей ноды, патчить. | ||||
| // Это усложняет код, пока оставим так для демонстрации, но ЭТО НУЖНО ИСПРАВИТЬ для production. | ||||
| // Updates info about Taints for correct Node in CRD status | ||||
| func (r *NodeTainterConfigReconciler) updateNodeTaintStatus(ctx context.Context, node *corev1.Node, appliedTaints []corev1.Taint, errorMsg string) error { | ||||
| 	log := log.FromContext(ctx).WithValues("node", node.Name) | ||||
|  | ||||
| 	var config configv1alpha1.NodeTainterConfig | ||||
| 	configKey := types.NamespacedName{Name: GlobalTaintConfigName} | ||||
| 	// Получаем CRD еще раз, чтобы обновить его статус | ||||
| 	if err := r.Get(ctx, configKey, &config); err != nil { | ||||
| 		log.Error(err, "Failed to get NodeTainterConfig for status update", "configName", GlobalTaintConfigName) | ||||
| 		// Не можем обновить статус, если не получили CRD | ||||
| 		return fmt.Errorf("failed to get config %s for status update: %w", GlobalTaintConfigName, err) | ||||
| 	} | ||||
|  | ||||
| 	configCopy := config.DeepCopy() | ||||
|  | ||||
| 	// Ищем статус для текущей ноды | ||||
| 	found := false | ||||
| 	nodeStatus := configv1alpha1.NodeTaintInfo{ | ||||
| 		NodeName:      node.Name, | ||||
| @@ -492,12 +466,10 @@ func (r *NodeTainterConfigReconciler) updateNodeTaintStatus(ctx context.Context, | ||||
| 		configCopy.Status.NodeTaintStatus = append(configCopy.Status.NodeTaintStatus, nodeStatus) | ||||
| 	} | ||||
|  | ||||
| 	// Сортируем для консистентности | ||||
| 	sort.Slice(configCopy.Status.NodeTaintStatus, func(i, j int) bool { | ||||
| 		return configCopy.Status.NodeTaintStatus[i].NodeName < configCopy.Status.NodeTaintStatus[j].NodeName | ||||
| 	}) | ||||
|  | ||||
| 	// Патчим статус | ||||
| 	if err := r.Status().Patch(ctx, configCopy, client.MergeFrom(&config)); err != nil { | ||||
| 		log.Error(err, "Failed to patch NodeTainterConfig status with node info", "node", node.Name) | ||||
| 		return err | ||||
|   | ||||
		Reference in New Issue
	
	Block a user