Merge pull request #5530 from a7i/skip-interpret-health

skip interpret health of resources without a hook
This commit is contained in:
karmada-bot 2024-10-08 16:41:20 +08:00 committed by GitHub
commit f656d9a2a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 14 deletions

View File

@ -38,6 +38,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
"github.com/karmada-io/karmada/pkg/events"
@ -365,20 +366,7 @@ func (c *WorkStatusController) reflectStatus(ctx context.Context, work *workv1al
}
c.EventRecorder.Eventf(work, corev1.EventTypeNormal, events.EventReasonReflectStatusSucceed, "Reflect status for object(%s/%s/%s) succeed.", clusterObj.GetKind(), clusterObj.GetNamespace(), clusterObj.GetName())
var resourceHealth workv1alpha1.ResourceHealth
// When an unregistered resource kind is requested with the ResourceInterpreter,
// the interpreter will return an error, we treat its health status as Unknown.
healthy, err := c.ResourceInterpreter.InterpretHealth(clusterObj)
if err != nil {
resourceHealth = workv1alpha1.ResourceUnknown
c.EventRecorder.Eventf(work, corev1.EventTypeWarning, events.EventReasonInterpretHealthFailed, "Interpret health of object(%s/%s/%s) failed, err: %s.", clusterObj.GetKind(), clusterObj.GetNamespace(), clusterObj.GetName(), err.Error())
} else if healthy {
resourceHealth = workv1alpha1.ResourceHealthy
c.EventRecorder.Eventf(work, corev1.EventTypeNormal, events.EventReasonInterpretHealthSucceed, "Interpret health of object(%s/%s/%s) as healthy.", clusterObj.GetKind(), clusterObj.GetNamespace(), clusterObj.GetName())
} else {
resourceHealth = workv1alpha1.ResourceUnhealthy
c.EventRecorder.Eventf(work, corev1.EventTypeNormal, events.EventReasonInterpretHealthSucceed, "Interpret health of object(%s/%s/%s) as unhealthy.", clusterObj.GetKind(), clusterObj.GetNamespace(), clusterObj.GetName())
}
resourceHealth := c.interpretHealth(clusterObj, work)
identifier, err := c.buildStatusIdentifier(work, clusterObj)
if err != nil {
@ -400,6 +388,28 @@ func (c *WorkStatusController) reflectStatus(ctx context.Context, work *workv1al
})
}
func (c *WorkStatusController) interpretHealth(clusterObj *unstructured.Unstructured, work *workv1alpha1.Work) workv1alpha1.ResourceHealth {
// For kind that doesn't have health check, we treat it as healthy.
if !c.ResourceInterpreter.HookEnabled(clusterObj.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretHealth) {
klog.V(5).Infof("skipping health assessment for object: %v %s/%s as missing customization and will treat it as healthy.", clusterObj.GroupVersionKind(), clusterObj.GetNamespace(), clusterObj.GetName())
return workv1alpha1.ResourceHealthy
}
var resourceHealth workv1alpha1.ResourceHealth
healthy, err := c.ResourceInterpreter.InterpretHealth(clusterObj)
if err != nil {
resourceHealth = workv1alpha1.ResourceUnknown
c.EventRecorder.Eventf(work, corev1.EventTypeWarning, events.EventReasonInterpretHealthFailed, "Interpret health of object(%s/%s/%s) failed, err: %s.", clusterObj.GetKind(), clusterObj.GetNamespace(), clusterObj.GetName(), err.Error())
} else if healthy {
resourceHealth = workv1alpha1.ResourceHealthy
c.EventRecorder.Eventf(work, corev1.EventTypeNormal, events.EventReasonInterpretHealthSucceed, "Interpret health of object(%s/%s/%s) as healthy.", clusterObj.GetKind(), clusterObj.GetNamespace(), clusterObj.GetName())
} else {
resourceHealth = workv1alpha1.ResourceUnhealthy
c.EventRecorder.Eventf(work, corev1.EventTypeNormal, events.EventReasonInterpretHealthSucceed, "Interpret health of object(%s/%s/%s) as unhealthy.", clusterObj.GetKind(), clusterObj.GetNamespace(), clusterObj.GetName())
}
return resourceHealth
}
func (c *WorkStatusController) buildStatusIdentifier(work *workv1alpha1.Work, clusterObj *unstructured.Unstructured) (*workv1alpha1.ResourceIdentifier, error) {
manifestRef := helper.ManifestReference{APIVersion: clusterObj.GetAPIVersion(), Kind: clusterObj.GetKind(),
Namespace: clusterObj.GetNamespace(), Name: clusterObj.GetName()}

View File

@ -25,22 +25,26 @@ import (
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/uuid"
dynamicfake "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
"k8s.io/utils/ptr"
controllerruntime "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
"github.com/karmada-io/karmada/pkg/events"
"github.com/karmada-io/karmada/pkg/resourceinterpreter/default/native"
"github.com/karmada-io/karmada/pkg/sharedcli/ratelimiterflag"
"github.com/karmada-io/karmada/pkg/util"
@ -723,6 +727,9 @@ func newWorkStatusController(cluster *clusterv1alpha1.Cluster, dynamicClientSets
m.Add(corev1.SchemeGroupVersion.WithKind("Pod"), meta.RESTScopeNamespace)
return m
}(),
ResourceInterpreter: FakeResourceInterpreter{
DefaultInterpreter: native.NewDefaultInterpreter(),
},
}
if len(dynamicClientSets) > 0 {
@ -1021,3 +1028,47 @@ func TestWorkStatusController_registerInformersAndStart(t *testing.T) {
assert.NotEmpty(t, err)
})
}
func TestWorkStatusController_interpretHealth(t *testing.T) {
tests := []struct {
name string
clusterObj client.Object
expectedResourceHealth workv1alpha1.ResourceHealth
expectedEventReason string
}{
{
name: "deployment without status is interpreted as unhealthy",
clusterObj: testhelper.NewDeployment("foo", "bar"),
expectedResourceHealth: workv1alpha1.ResourceUnhealthy,
expectedEventReason: events.EventReasonInterpretHealthSucceed,
},
{
name: "cluster role without status is interpreted as healthy",
clusterObj: testhelper.NewClusterRole("foo", []rbacv1.PolicyRule{}),
expectedResourceHealth: workv1alpha1.ResourceHealthy,
},
}
cluster := newCluster("cluster", clusterv1alpha1.ClusterConditionReady, metav1.ConditionTrue)
c := newWorkStatusController(cluster)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
work := testhelper.NewWork(tt.clusterObj.GetName(), tt.clusterObj.GetNamespace(), string(uuid.NewUUID()), []byte{})
obj, err := helper.ToUnstructured(tt.clusterObj)
assert.NoError(t, err)
resourceHealth := c.interpretHealth(obj, work)
assert.Equalf(t, tt.expectedResourceHealth, resourceHealth, "expected resource health %v, got %v", tt.expectedResourceHealth, resourceHealth)
eventRecorder := c.EventRecorder.(*record.FakeRecorder)
if tt.expectedEventReason == "" {
assert.Empty(t, eventRecorder.Events, "expected no events to get recorded")
} else {
assert.Equal(t, 1, len(eventRecorder.Events))
e := <-eventRecorder.Events
assert.Containsf(t, e, tt.expectedEventReason, "expected event reason %v, got %v", tt.expectedEventReason, e)
}
})
}
}