diff --git a/CHANGELOG.md b/CHANGELOG.md index c17ade00d..8e94301b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - (Feature) Add Reason in OOM Metric - (Feature) PersistentVolume Inspector - (Bugfix) Discover Arango image during ID phase +- (Feature) PV Unschedulable condition ## [1.2.27](https://github.com/arangodb/kube-arangodb/tree/1.2.27) (2023-04-27) - (Feature) Add InSync Cache diff --git a/pkg/apis/deployment/v1/conditions.go b/pkg/apis/deployment/v1/conditions.go index 2189eca74..7bf83b069 100644 --- a/pkg/apis/deployment/v1/conditions.go +++ b/pkg/apis/deployment/v1/conditions.go @@ -72,6 +72,8 @@ const ( ConditionTypeSpecAccepted ConditionType = "SpecAccepted" // ConditionTypeSpecPropagated indicates that the deployment has been at least once UpToDate after spec acceptance. ConditionTypeSpecPropagated ConditionType = "SpecPropagated" + // ConditionTypeMemberVolumeUnschedulable indicates that the member cannot schedued due to volume issue. + ConditionTypeMemberVolumeUnschedulable ConditionType = "MemberVolumeUnschedulable" // ConditionTypeMarkedToRemove indicates that the member is marked to be removed. ConditionTypeMarkedToRemove ConditionType = "MarkedToRemove" // ConditionTypeScaleDownCandidate indicates that the member will be picked in ScaleDown operaion. diff --git a/pkg/apis/deployment/v2alpha1/conditions.go b/pkg/apis/deployment/v2alpha1/conditions.go index 8e7a23635..eedf5019f 100644 --- a/pkg/apis/deployment/v2alpha1/conditions.go +++ b/pkg/apis/deployment/v2alpha1/conditions.go @@ -72,6 +72,8 @@ const ( ConditionTypeSpecAccepted ConditionType = "SpecAccepted" // ConditionTypeSpecPropagated indicates that the deployment has been at least once UpToDate after spec acceptance. ConditionTypeSpecPropagated ConditionType = "SpecPropagated" + // ConditionTypeMemberVolumeUnschedulable indicates that the member cannot schedued due to volume issue. + ConditionTypeMemberVolumeUnschedulable ConditionType = "MemberVolumeUnschedulable" // ConditionTypeMarkedToRemove indicates that the member is marked to be removed. ConditionTypeMarkedToRemove ConditionType = "MarkedToRemove" // ConditionTypeScaleDownCandidate indicates that the member will be picked in ScaleDown operaion. diff --git a/pkg/deployment/member/phase_updates.go b/pkg/deployment/member/phase_updates.go index 80511b692..bc2d871bc 100644 --- a/pkg/deployment/member/phase_updates.go +++ b/pkg/deployment/member/phase_updates.go @@ -104,6 +104,7 @@ func removeMemberConditionsMapFunc(m *api.MemberStatus) { m.Conditions.Remove(api.ConditionTypePVCResizePending) m.Conditions.Remove(api.ConditionTypeArchitectureMismatch) m.Conditions.Remove(api.ConditionTypeArchitectureChangeCannotBeApplied) + m.Conditions.Remove(api.ConditionTypeMemberVolumeUnschedulable) m.RemoveTerminationsBefore(time.Now().Add(-1 * recentTerminationsKeepPeriod)) diff --git a/pkg/deployment/reconcile/plan_builder_high.go b/pkg/deployment/reconcile/plan_builder_high.go index 903521250..8c32a18ed 100644 --- a/pkg/deployment/reconcile/plan_builder_high.go +++ b/pkg/deployment/reconcile/plan_builder_high.go @@ -59,6 +59,7 @@ func (r *Reconciler) createHighPlan(ctx context.Context, apiObject k8sutil.APIOb ApplyIfEmpty(r.createTopologyMemberUpdatePlan). ApplyIfEmptyWithBackOff(LicenseCheck, 30*time.Second, r.updateClusterLicense). ApplyIfEmpty(r.createTopologyMemberConditionPlan). + ApplyIfEmpty(r.updateMemberConditionTypeMemberVolumeUnschedulableCondition). ApplyIfEmpty(r.createRebalancerCheckPlan). ApplyIfEmpty(r.createMemberFailedRestoreHighPlan). ApplyIfEmpty(r.scaleDownCandidate). diff --git a/pkg/deployment/reconcile/plan_builder_volume.go b/pkg/deployment/reconcile/plan_builder_volume.go new file mode 100644 index 000000000..ff72b24d8 --- /dev/null +++ b/pkg/deployment/reconcile/plan_builder_volume.go @@ -0,0 +1,84 @@ +// +// DISCLAIMER +// +// Copyright 2023 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +package reconcile + +import ( + "context" + + core "k8s.io/api/core/v1" + + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + "github.com/arangodb/kube-arangodb/pkg/deployment/reconcile/shared" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector" +) + +// updateMemberPhasePlan creates plan to update member phase +func (r *Reconciler) updateMemberConditionTypeMemberVolumeUnschedulableCondition(ctx context.Context, apiObject k8sutil.APIObject, + spec api.DeploymentSpec, status api.DeploymentStatus, + context PlanBuilderContext) api.Plan { + var plan api.Plan + + cache := context.ACS().CurrentClusterCache() + + volumeClient, err := cache.PersistentVolume().V1() + if err != nil { + // We cant fetch volumes, continue + return nil + } + + for _, e := range status.Members.AsList() { + if pvcStatus := e.Member.PersistentVolumeClaim; pvcStatus != nil { + if pvc, ok := context.ACS().CurrentClusterCache().PersistentVolumeClaim().V1().GetSimple(pvcStatus.GetName()); ok { + if volumeName := pvc.Spec.VolumeName; volumeName != "" { + if pv, ok := volumeClient.GetSimple(volumeName); ok { + // We have volume and volumeclaim, lets calculate condition + unschedulable := memberConditionTypeMemberVolumeUnschedulableCalculate(cache, pv, pvc) + + if unschedulable == e.Member.Conditions.IsTrue(api.ConditionTypeMemberVolumeUnschedulable) { + continue + } else if unschedulable && !e.Member.Conditions.IsTrue(api.ConditionTypeMemberVolumeUnschedulable) { + plan = append(plan, shared.UpdateMemberConditionActionV2("PV Unschedulable", api.ConditionTypeMemberVolumeUnschedulable, e.Group, e.Member.ID, true, + "PV Unschedulable", "PV Unschedulable", "")) + } else if !unschedulable && e.Member.Conditions.IsTrue(api.ConditionTypeMemberVolumeUnschedulable) { + plan = append(plan, shared.RemoveMemberConditionActionV2("PV Schedulable", api.ConditionTypeMemberVolumeUnschedulable, e.Group, e.Member.ID)) + } + + } + } + } + } + } + + return plan +} + +type memberConditionTypeMemberVolumeUnschedulableCalculateFunc func(cache inspectorInterface.Inspector, pv *core.PersistentVolume, pvc *core.PersistentVolumeClaim) bool + +func memberConditionTypeMemberVolumeUnschedulableCalculate(cache inspectorInterface.Inspector, pv *core.PersistentVolume, pvc *core.PersistentVolumeClaim, funcs ...memberConditionTypeMemberVolumeUnschedulableCalculateFunc) bool { + for _, f := range funcs { + if f(cache, pv, pvc) { + return true + } + } + + return false +}