diff --git a/charts/yurt-manager/crds/apps.openyurt.io_yurtappdaemons.yaml b/charts/yurt-manager/crds/apps.openyurt.io_yurtappdaemons.yaml index 4fa7d8e6dfa..23f898517e9 100644 --- a/charts/yurt-manager/crds/apps.openyurt.io_yurtappdaemons.yaml +++ b/charts/yurt-manager/crds/apps.openyurt.io_yurtappdaemons.yaml @@ -30,6 +30,10 @@ spec: jsonPath: .metadata.creationTimestamp name: AGE type: date + - description: The name of overrider bound to this yurtappdaemon + jsonPath: .status.overriderRef + name: OverriderRef + type: string name: v1alpha1 schema: openAPIV3Schema: @@ -225,9 +229,32 @@ spec: which is updated on mutation by the API Server. format: int64 type: integer + overriderRef: + type: string templateType: description: TemplateType indicates the type of PoolTemplate type: string + workloadSummary: + description: Records the topology detailed information of each workload. + items: + properties: + availableCondition: + type: string + readyReplicas: + format: int32 + type: integer + replicas: + format: int32 + type: integer + workloadName: + type: string + required: + - availableCondition + - readyReplicas + - replicas + - workloadName + type: object + type: array required: - currentRevision - templateType diff --git a/charts/yurt-manager/crds/apps.openyurt.io_yurtappsets.yaml b/charts/yurt-manager/crds/apps.openyurt.io_yurtappsets.yaml index 93ef1d936c2..559f8967049 100644 --- a/charts/yurt-manager/crds/apps.openyurt.io_yurtappsets.yaml +++ b/charts/yurt-manager/crds/apps.openyurt.io_yurtappsets.yaml @@ -32,6 +32,10 @@ spec: jsonPath: .metadata.creationTimestamp name: AGE type: date + - description: The name of overrider bound to this yurtappset + jsonPath: .status.overriderRef + name: OverriderRef + type: string name: v1alpha1 schema: openAPIV3Schema: @@ -329,6 +333,8 @@ spec: which is updated on mutation by the API Server. format: int64 type: integer + overriderRef: + type: string poolReplicas: additionalProperties: format: int32 @@ -347,6 +353,27 @@ spec: templateType: description: TemplateType indicates the type of PoolTemplate type: string + workloadSummary: + description: Records the topology detailed information of each workload. + items: + properties: + availableCondition: + type: string + readyReplicas: + format: int32 + type: integer + replicas: + format: int32 + type: integer + workloadName: + type: string + required: + - availableCondition + - readyReplicas + - replicas + - workloadName + type: object + type: array required: - currentRevision - replicas diff --git a/go.mod b/go.mod index bda596c041b..7adaca6b76b 100644 --- a/go.mod +++ b/go.mod @@ -28,8 +28,8 @@ require ( go.etcd.io/etcd/api/v3 v3.5.0 go.etcd.io/etcd/client/pkg/v3 v3.5.0 go.etcd.io/etcd/client/v3 v3.5.0 - golang.org/x/net v0.14.0 - golang.org/x/sys v0.11.0 + golang.org/x/net v0.15.0 + golang.org/x/sys v0.12.0 google.golang.org/grpc v1.57.0 gopkg.in/cheggaaa/pb.v1 v1.0.28 gopkg.in/square/go-jose.v2 v2.6.0 @@ -150,11 +150,11 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/term v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/term v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 1a6aba74e96..e92e2355ed8 100644 --- a/go.sum +++ b/go.sum @@ -773,8 +773,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -855,8 +855,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -873,8 +873,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -939,13 +939,13 @@ golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -954,8 +954,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/apis/apps/v1alpha1/yurtappdaemon_types.go b/pkg/apis/apps/v1alpha1/yurtappdaemon_types.go index 084ee6fa42e..a695d7c8d94 100644 --- a/pkg/apis/apps/v1alpha1/yurtappdaemon_types.go +++ b/pkg/apis/apps/v1alpha1/yurtappdaemon_types.go @@ -73,6 +73,12 @@ type YurtAppDaemonStatus struct { // +optional Conditions []YurtAppDaemonCondition `json:"conditions,omitempty"` + OverriderRef string `json:"overriderRef,omitempty"` + + // Records the topology detailed information of each workload. + // +optional + WorkloadSummaries []WorkloadSummary `json:"workloadSummary,omitempty"` + // TemplateType indicates the type of PoolTemplate TemplateType TemplateType `json:"templateType"` @@ -105,6 +111,7 @@ type YurtAppDaemonCondition struct { // +kubebuilder:resource:scope=Namespaced,path=yurtappdaemons,shortName=yad,categories=all // +kubebuilder:printcolumn:name="WorkloadTemplate",type="string",JSONPath=".status.templateType",description="The WorkloadTemplate Type." // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp",description="CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC." +// +kubebuilder:printcolumn:name="OverriderRef",type="string",JSONPath=".status.overriderRef",description="The name of overrider bound to this yurtappdaemon" // YurtAppDaemon is the Schema for the samples API type YurtAppDaemon struct { diff --git a/pkg/apis/apps/v1alpha1/yurtappset_types.go b/pkg/apis/apps/v1alpha1/yurtappset_types.go index 5af4f460801..4f333d203d7 100644 --- a/pkg/apis/apps/v1alpha1/yurtappset_types.go +++ b/pkg/apis/apps/v1alpha1/yurtappset_types.go @@ -163,6 +163,12 @@ type YurtAppSetStatus struct { // +optional Conditions []YurtAppSetCondition `json:"conditions,omitempty"` + // Records the topology detailed information of each workload. + // +optional + WorkloadSummaries []WorkloadSummary `json:"workloadSummary,omitempty"` + + OverriderRef string `json:"overriderRef,omitempty"` + // Records the topology detail information of the replicas of each pool. // +optional PoolReplicas map[string]int32 `json:"poolReplicas,omitempty"` @@ -178,6 +184,13 @@ type YurtAppSetStatus struct { TemplateType TemplateType `json:"templateType"` } +type WorkloadSummary struct { + AvailableCondition corev1.ConditionStatus `json:"availableCondition"` + Replicas int32 `json:"replicas"` + ReadyReplicas int32 `json:"readyReplicas"` + WorkloadName string `json:"workloadName"` +} + // YurtAppSetCondition describes current state of a YurtAppSet. type YurtAppSetCondition struct { // Type of in place set condition. @@ -203,6 +216,7 @@ type YurtAppSetCondition struct { // +kubebuilder:printcolumn:name="READY",type="integer",JSONPath=".status.readyReplicas",description="The number of pods ready." // +kubebuilder:printcolumn:name="WorkloadTemplate",type="string",JSONPath=".status.templateType",description="The WorkloadTemplate Type." // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp",description="CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC." +// +kubebuilder:printcolumn:name="OverriderRef",type="string",JSONPath=".status.overriderRef",description="The name of overrider bound to this yurtappset" // YurtAppSet is the Schema for the yurtAppSets API type YurtAppSet struct { diff --git a/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go index bbd03f00dfb..061cbcc12c0 100644 --- a/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/apps/v1alpha1/zz_generated.deepcopy.go @@ -343,6 +343,21 @@ func (in *Topology) DeepCopy() *Topology { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkloadSummary) DeepCopyInto(out *WorkloadSummary) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadSummary. +func (in *WorkloadSummary) DeepCopy() *WorkloadSummary { + if in == nil { + return nil + } + out := new(WorkloadSummary) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkloadTemplate) DeepCopyInto(out *WorkloadTemplate) { *out = *in @@ -489,6 +504,11 @@ func (in *YurtAppDaemonStatus) DeepCopyInto(out *YurtAppDaemonStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.WorkloadSummaries != nil { + in, out := &in.WorkloadSummaries, &out.WorkloadSummaries + *out = make([]WorkloadSummary, len(*in)) + copy(*out, *in) + } if in.NodePools != nil { in, out := &in.NodePools, &out.NodePools *out = make([]string, len(*in)) @@ -688,6 +708,11 @@ func (in *YurtAppSetStatus) DeepCopyInto(out *YurtAppSetStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.WorkloadSummaries != nil { + in, out := &in.WorkloadSummaries, &out.WorkloadSummaries + *out = make([]WorkloadSummary, len(*in)) + copy(*out, *in) + } if in.PoolReplicas != nil { in, out := &in.PoolReplicas, &out.PoolReplicas *out = make(map[string]int32, len(*in)) diff --git a/pkg/yurtmanager/controller/yurtappdaemon/util.go b/pkg/yurtmanager/controller/yurtappdaemon/util.go index 503f3d7c73e..5b094c7b43b 100644 --- a/pkg/yurtmanager/controller/yurtappdaemon/util.go +++ b/pkg/yurtmanager/controller/yurtappdaemon/util.go @@ -57,6 +57,11 @@ func GetYurtAppDaemonCondition(status unitv1alpha1.YurtAppDaemonStatus, condType return nil } +// RemoveYurtAppDaemonCondition removes the YurtAppDaemon condition with the provided type. +func RemoveYurtAppDaemonCondition(status *unitv1alpha1.YurtAppDaemonStatus, condType unitv1alpha1.YurtAppDaemonConditionType) { + status.Conditions = filterOutCondition(status.Conditions, condType) +} + // SetYurtAppDaemonCondition updates the YurtAppDaemon to include the provided condition. If the condition that // we are about to add already exists and has the same status, reason and message then we are not going to update. func SetYurtAppDaemonCondition(status *unitv1alpha1.YurtAppDaemonStatus, condition *unitv1alpha1.YurtAppDaemonCondition) { diff --git a/pkg/yurtmanager/controller/yurtappdaemon/workloadcontroller/deployment_controller.go b/pkg/yurtmanager/controller/yurtappdaemon/workloadcontroller/deployment_controller.go index a70ad98f03b..d61b2194dab 100644 --- a/pkg/yurtmanager/controller/yurtappdaemon/workloadcontroller/deployment_controller.go +++ b/pkg/yurtmanager/controller/yurtappdaemon/workloadcontroller/deployment_controller.go @@ -21,6 +21,7 @@ import ( "errors" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -185,6 +186,13 @@ func (d *DeploymentControllor) GetAllWorkloads(yad *v1alpha1.YurtAppDaemon) ([]* for i, o := range objs { deploy := o.(*appsv1.Deployment) spec := deploy.Spec + var availableCondition corev1.ConditionStatus + for _, condition := range deploy.Status.Conditions { + if condition.Type == appsv1.DeploymentAvailable { + availableCondition = condition.Status + break + } + } w := &Workload{ Name: o.GetName(), Namespace: o.GetNamespace(), @@ -194,6 +202,11 @@ func (d *DeploymentControllor) GetAllWorkloads(yad *v1alpha1.YurtAppDaemon) ([]* NodeSelector: spec.Template.Spec.NodeSelector, Tolerations: spec.Template.Spec.Tolerations, }, + Status: WorkloadStatus{ + Replicas: deploy.Status.Replicas, + ReadyReplicas: deploy.Status.ReadyReplicas, + AvailableCondition: availableCondition, + }, } workloads = append(workloads, w) } diff --git a/pkg/yurtmanager/controller/yurtappdaemon/workloadcontroller/workload.go b/pkg/yurtmanager/controller/yurtappdaemon/workloadcontroller/workload.go index 0270f9f0bb0..3eec35289a0 100644 --- a/pkg/yurtmanager/controller/yurtappdaemon/workloadcontroller/workload.go +++ b/pkg/yurtmanager/controller/yurtappdaemon/workloadcontroller/workload.go @@ -40,6 +40,9 @@ type WorkloadSpec struct { // WorkloadStatus stores the observed state of the Workload. type WorkloadStatus struct { + Replicas int32 + ReadyReplicas int32 + AvailableCondition corev1.ConditionStatus } func (w *Workload) GetRevision() string { diff --git a/pkg/yurtmanager/controller/yurtappdaemon/yurtappdaemon_controller.go b/pkg/yurtmanager/controller/yurtappdaemon/yurtappdaemon_controller.go index 05df1435db7..d36bb905cb5 100644 --- a/pkg/yurtmanager/controller/yurtappdaemon/yurtappdaemon_controller.go +++ b/pkg/yurtmanager/controller/yurtappdaemon/yurtappdaemon_controller.go @@ -202,13 +202,13 @@ func (r *ReconcileYurtAppDaemon) Reconcile(_ context.Context, request reconcile. return reconcile.Result{}, err } - return r.updateStatus(instance, newStatus, oldStatus, currentRevision, collisionCount, templateType) + return r.updateStatus(instance, newStatus, oldStatus, currentRevision, collisionCount, templateType, currentNPToWorkload) } func (r *ReconcileYurtAppDaemon) updateStatus(instance *unitv1alpha1.YurtAppDaemon, newStatus, oldStatus *unitv1alpha1.YurtAppDaemonStatus, - currentRevision *appsv1.ControllerRevision, collisionCount int32, templateType unitv1alpha1.TemplateType) (reconcile.Result, error) { + currentRevision *appsv1.ControllerRevision, collisionCount int32, templateType unitv1alpha1.TemplateType, currentNodepoolToWorkload map[string]*workloadcontroller.Workload) (reconcile.Result, error) { - newStatus = r.calculateStatus(instance, newStatus, currentRevision, collisionCount, templateType) + newStatus = r.calculateStatus(instance, newStatus, currentRevision, collisionCount, templateType, currentNodepoolToWorkload) _, err := r.updateYurtAppDaemon(instance, oldStatus, newStatus) return reconcile.Result{}, err @@ -254,17 +254,42 @@ func (r *ReconcileYurtAppDaemon) updateYurtAppDaemon(yad *unitv1alpha1.YurtAppDa } func (r *ReconcileYurtAppDaemon) calculateStatus(instance *unitv1alpha1.YurtAppDaemon, newStatus *unitv1alpha1.YurtAppDaemonStatus, - currentRevision *appsv1.ControllerRevision, collisionCount int32, templateType unitv1alpha1.TemplateType) *unitv1alpha1.YurtAppDaemonStatus { + currentRevision *appsv1.ControllerRevision, collisionCount int32, templateType unitv1alpha1.TemplateType, currentNodepoolToWorkload map[string]*workloadcontroller.Workload) *unitv1alpha1.YurtAppDaemonStatus { newStatus.CollisionCount = &collisionCount + var workloadFailure string + overriderList := unitv1alpha1.YurtAppOverriderList{} + if err := r.List(context.TODO(), &overriderList); err != nil { + workloadFailure = fmt.Sprintf("unable to list yurtappoverrider: %v", err) + } + for _, overrider := range overriderList.Items { + if overrider.Subject.Kind == "YurtAppDaemon" && overrider.Subject.Name == instance.Name { + newStatus.OverriderRef = overrider.Name + break + } + } + + newStatus.WorkloadSummaries = make([]unitv1alpha1.WorkloadSummary, 0) + for _, workload := range currentNodepoolToWorkload { + newStatus.WorkloadSummaries = append(newStatus.WorkloadSummaries, unitv1alpha1.WorkloadSummary{ + AvailableCondition: workload.Status.AvailableCondition, + Replicas: workload.Status.Replicas, + ReadyReplicas: workload.Status.ReadyReplicas, + WorkloadName: workload.Name, + }) + } if newStatus.CurrentRevision == "" { // init with current revision newStatus.CurrentRevision = currentRevision.Name } - newStatus.TemplateType = templateType + if workloadFailure == "" { + RemoveYurtAppDaemonCondition(newStatus, unitv1alpha1.WorkLoadFailure) + } else { + SetYurtAppDaemonCondition(newStatus, NewYurtAppDaemonCondition(unitv1alpha1.WorkLoadFailure, corev1.ConditionFalse, "Error", workloadFailure)) + } return newStatus } diff --git a/pkg/yurtmanager/controller/yurtappdaemon/yurtappdaemon_controller_test.go b/pkg/yurtmanager/controller/yurtappdaemon/yurtappdaemon_controller_test.go index 5611e911552..da0b909d944 100644 --- a/pkg/yurtmanager/controller/yurtappdaemon/yurtappdaemon_controller_test.go +++ b/pkg/yurtmanager/controller/yurtappdaemon/yurtappdaemon_controller_test.go @@ -23,6 +23,7 @@ import ( appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/openyurtio/openyurt/cmd/yurt-manager/names" @@ -166,9 +167,11 @@ func TestUpdateStatus(t *testing.T) { t.Parallel() t.Logf("\tTestCase: %s", st.name) { - rc := &ReconcileYurtAppDaemon{} + rc := &ReconcileYurtAppDaemon{ + Client: fakeclient.NewClientBuilder().Build(), + } get, _ := rc.updateStatus( - st.instance, st.newStatus, st.oldStatus, st.currentRevision, st.collisionCount, st.templateType) + st.instance, st.newStatus, st.oldStatus, st.currentRevision, st.collisionCount, st.templateType, make(map[string]*workloadcontroller.Workload)) if !reflect.DeepEqual(get, st.expect) { t.Fatalf("\t%s\texpect %v, but get %v", failed, st.expect, get) } @@ -256,13 +259,14 @@ func TestCalculateStatus(t *testing.T) { var cr appsv1.ControllerRevision cr.Name = "a" tests := []struct { - name string - instance *unitv1alpha1.YurtAppDaemon - newStatus *unitv1alpha1.YurtAppDaemonStatus - currentRevision *appsv1.ControllerRevision - collisionCount int32 - templateType unitv1alpha1.TemplateType - expect unitv1alpha1.YurtAppDaemonStatus + name string + instance *unitv1alpha1.YurtAppDaemon + newStatus *unitv1alpha1.YurtAppDaemonStatus + currentNodepoolToWorkload map[string]*workloadcontroller.Workload + currentRevision *appsv1.ControllerRevision + collisionCount int32 + templateType unitv1alpha1.TemplateType + expect unitv1alpha1.YurtAppDaemonStatus }{ { "normal", @@ -281,6 +285,7 @@ func TestCalculateStatus(t *testing.T) { }, }, }, + map[string]*workloadcontroller.Workload{}, &cr, 1, "StatefulSet", @@ -307,8 +312,10 @@ func TestCalculateStatus(t *testing.T) { t.Parallel() t.Logf("\tTestCase: %s", st.name) { - rc := &ReconcileYurtAppDaemon{} - get := rc.calculateStatus(st.instance, st.newStatus, st.currentRevision, st.collisionCount, st.templateType) + rc := &ReconcileYurtAppDaemon{ + Client: fakeclient.NewClientBuilder().Build(), + } + get := rc.calculateStatus(st.instance, st.newStatus, st.currentRevision, st.collisionCount, st.templateType, st.currentNodepoolToWorkload) if !reflect.DeepEqual(get.CurrentRevision, st.expect.CurrentRevision) { t.Fatalf("\t%s\texpect %v, but get %v", failed, st.expect.CurrentRevision, get.CurrentRevision) } @@ -351,7 +358,9 @@ func TestManageWorkloads(t *testing.T) { t.Parallel() t.Logf("\tTestCase: %s", st.name) { - rc := &ReconcileYurtAppDaemon{} + rc := &ReconcileYurtAppDaemon{ + Client: fakeclient.NewClientBuilder().Build(), + } rc.manageWorkloads(st.instance, st.currentNodepoolToWorkload, st.allNameToNodePools, st.expectedRevision, st.templateType) get := st.expect if !reflect.DeepEqual(get, st.expect) { diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go index 0fd375a3ce8..e8328f5a31e 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller.go @@ -20,10 +20,13 @@ import ( "context" "flag" "fmt" + "reflect" "time" v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/client-go/tools/record" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -33,6 +36,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" appconfig "github.com/openyurtio/openyurt/cmd/yurt-manager/app/config" + "github.com/openyurtio/openyurt/cmd/yurt-manager/names" appsv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" "github.com/openyurtio/openyurt/pkg/yurtmanager/controller/yurtappoverrider/config" ) @@ -71,14 +75,18 @@ var _ reconcile.Reconciler = &ReconcileYurtAppOverrider{} // ReconcileYurtAppOverrider reconciles a YurtAppOverrider object type ReconcileYurtAppOverrider struct { client.Client - Configuration config.YurtAppOverriderControllerConfiguration + Configuration config.YurtAppOverriderControllerConfiguration + CacheOverriderMap map[string]*appsv1alpha1.YurtAppOverrider + recorder record.EventRecorder } // newReconciler returns a new reconcile.Reconciler func newReconciler(c *appconfig.CompletedConfig, mgr manager.Manager) reconcile.Reconciler { return &ReconcileYurtAppOverrider{ - Client: mgr.GetClient(), - Configuration: c.ComponentConfig.YurtAppOverriderController, + Client: mgr.GetClient(), + Configuration: c.ComponentConfig.YurtAppOverriderController, + CacheOverriderMap: make(map[string]*appsv1alpha1.YurtAppOverrider), + recorder: mgr.GetEventRecorderFor(names.YurtAppOverriderController), } } @@ -128,8 +136,38 @@ func (r *ReconcileYurtAppOverrider) Reconcile(_ context.Context, request reconci return reconcile.Result{}, nil } - deployments := v1.DeploymentList{} + switch instance.Subject.Kind { + case "YurtAppSet": + appSet := &appsv1alpha1.YurtAppSet{} + if err := r.Get(context.TODO(), client.ObjectKey{Namespace: instance.Namespace, Name: instance.Subject.Name}, appSet); err != nil { + return reconcile.Result{}, err + } + if appSet.Spec.WorkloadTemplate.StatefulSetTemplate != nil { + r.recorder.Event(instance.DeepCopy(), corev1.EventTypeWarning, fmt.Sprintf("unable to override statefulset workload of %s", appSet.Name), "It is not supported to overrider statefulset now") + return reconcile.Result{}, nil + } + case "YurtAppDaemon": + appDaemon := &appsv1alpha1.YurtAppDaemon{} + if err := r.Get(context.TODO(), client.ObjectKey{Namespace: instance.Namespace, Name: instance.Subject.Name}, appDaemon); err != nil { + return reconcile.Result{}, err + } + if appDaemon.Spec.WorkloadTemplate.StatefulSetTemplate != nil { + r.recorder.Event(instance.DeepCopy(), corev1.EventTypeWarning, fmt.Sprintf("unable to override statefulset workload of %s", appDaemon.Name), "It is not supported to overrider statefulset now") + return reconcile.Result{}, nil + } + default: + return reconcile.Result{}, fmt.Errorf("unsupported kind: %s", instance.Subject.Kind) + } + + if cacheOverrider, ok := r.CacheOverriderMap[instance.Namespace+"/"+instance.Name]; ok { + if reflect.DeepEqual(cacheOverrider.Entries, instance.Entries) { + return reconcile.Result{}, nil + } + } else { + r.CacheOverriderMap[instance.Namespace+"/"+instance.Name] = instance.DeepCopy() + } + deployments := v1.DeploymentList{} if err := r.List(context.TODO(), &deployments); err != nil { return reconcile.Result{}, err } diff --git a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go index 646ce4f5d89..92d3b005a3c 100644 --- a/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go +++ b/pkg/yurtmanager/controller/yurtappoverrider/yurtappoverrider_controller_test.go @@ -35,6 +35,25 @@ var ( replica int32 = 3 ) +var yurtAppDaemon = &v1alpha1.YurtAppDaemon{ + ObjectMeta: metav1.ObjectMeta{ + Name: "yurtappdaemon", + Namespace: "default", + }, + Spec: v1alpha1.YurtAppDaemonSpec{ + NodePoolSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "test", + }, + }, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "test", + }, + }, + }, +} + var daemonDeployment = &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "test", @@ -104,7 +123,8 @@ func TestReconcile(t *testing.T) { return } reconciler := ReconcileYurtAppOverrider{ - Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(daemonDeployment, overrider).Build(), + Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(daemonDeployment, overrider, yurtAppDaemon).Build(), + CacheOverriderMap: make(map[string]*v1alpha1.YurtAppOverrider), } _, err := reconciler.Reconcile(context.Background(), reconcile.Request{ NamespacedName: types.NamespacedName{ diff --git a/pkg/yurtmanager/controller/yurtappset/adapter/adapter.go b/pkg/yurtmanager/controller/yurtappset/adapter/adapter.go index f460a731314..9ff1110c16a 100644 --- a/pkg/yurtmanager/controller/yurtappset/adapter/adapter.go +++ b/pkg/yurtmanager/controller/yurtappset/adapter/adapter.go @@ -21,6 +21,7 @@ change Adapter interface package adapter import ( + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -36,6 +37,8 @@ type Adapter interface { GetStatusObservedGeneration(pool metav1.Object) int64 // GetDetails returns the replicas information of the pool status. GetDetails(pool metav1.Object) (replicasInfo ReplicasInfo, err error) + // GetAvailableStatus returns the available condition status of the workload + GetAvailableStatus(set metav1.Object) (conditionStatus corev1.ConditionStatus, err error) // GetPoolFailure returns failure information of the pool. GetPoolFailure() *string // ApplyPoolTemplate updates the pool to the latest revision. diff --git a/pkg/yurtmanager/controller/yurtappset/adapter/deployment_adapter.go b/pkg/yurtmanager/controller/yurtappset/adapter/deployment_adapter.go index 72b7971b6cb..4d7ec32a7ff 100644 --- a/pkg/yurtmanager/controller/yurtappset/adapter/deployment_adapter.go +++ b/pkg/yurtmanager/controller/yurtappset/adapter/deployment_adapter.go @@ -20,6 +20,7 @@ import ( "fmt" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/v2" @@ -68,6 +69,17 @@ func (a *DeploymentAdapter) GetDetails(obj metav1.Object) (ReplicasInfo, error) return replicasInfo, nil } +// GetAvailableStatus returns the available condition status of the workload +func (a *DeploymentAdapter) GetAvailableStatus(obj metav1.Object) (conditionStatus corev1.ConditionStatus, err error) { + dp := obj.(*appsv1.Deployment) + for _, condition := range dp.Status.Conditions { + if condition.Type == appsv1.DeploymentAvailable { + return condition.Status, nil + } + } + return corev1.ConditionUnknown, nil +} + // GetPoolFailure returns the failure information of the pool. // Deployment has no condition. func (a *DeploymentAdapter) GetPoolFailure() *string { diff --git a/pkg/yurtmanager/controller/yurtappset/adapter/statefulset_adapter.go b/pkg/yurtmanager/controller/yurtappset/adapter/statefulset_adapter.go index be8a2710d4c..555d21be578 100644 --- a/pkg/yurtmanager/controller/yurtappset/adapter/statefulset_adapter.go +++ b/pkg/yurtmanager/controller/yurtappset/adapter/statefulset_adapter.go @@ -25,6 +25,7 @@ import ( "fmt" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/v2" @@ -74,6 +75,16 @@ func (a *StatefulSetAdapter) GetDetails(obj metav1.Object) (ReplicasInfo, error) return replicasInfo, nil } +// GetAvailableStatus returns the available condition status of the workload +func (a *StatefulSetAdapter) GetAvailableStatus(obj metav1.Object) (conditionStatus corev1.ConditionStatus, err error) { + set := obj.(*appsv1.StatefulSet) + + if set.Status.AvailableReplicas != set.Status.Replicas { + return corev1.ConditionFalse, nil + } + return corev1.ConditionTrue, nil +} + // GetPoolFailure returns the failure information of the pool. // StatefulSet has no condition. func (a *StatefulSetAdapter) GetPoolFailure() *string { diff --git a/pkg/yurtmanager/controller/yurtappset/pool.go b/pkg/yurtmanager/controller/yurtappset/pool.go index a579a8f3a2f..5960d8afab4 100644 --- a/pkg/yurtmanager/controller/yurtappset/pool.go +++ b/pkg/yurtmanager/controller/yurtappset/pool.go @@ -18,6 +18,7 @@ limitations under the License. package yurtappset import ( + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" unitv1alpha1 "github.com/openyurtio/openyurt/pkg/apis/apps/v1alpha1" @@ -41,7 +42,8 @@ type PoolSpec struct { type PoolStatus struct { ObservedGeneration int64 adapter.ReplicasInfo - PatchInfo string + PatchInfo string + AvailableCondition v1.ConditionStatus } // ResourceRef stores the Pool resource it represents. diff --git a/pkg/yurtmanager/controller/yurtappset/pool_control.go b/pkg/yurtmanager/controller/yurtappset/pool_control.go index 45f07095fb7..6a6ba35d536 100644 --- a/pkg/yurtmanager/controller/yurtappset/pool_control.go +++ b/pkg/yurtmanager/controller/yurtappset/pool_control.go @@ -158,6 +158,10 @@ func (m *PoolControl) convertToPool(set metav1.Object) (*Pool, error) { if err != nil { return nil, err } + conditionStatus, err := m.adapter.GetAvailableStatus(set) + if err != nil { + return nil, err + } pool := &Pool{ Name: poolName, Namespace: set.GetNamespace(), @@ -167,6 +171,7 @@ func (m *PoolControl) convertToPool(set metav1.Object) (*Pool, error) { Status: PoolStatus{ ObservedGeneration: m.adapter.GetStatusObservedGeneration(set), ReplicasInfo: specReplicas, + AvailableCondition: conditionStatus, }, } if data, ok := set.GetAnnotations()[apps.AnnotationPatchKey]; ok { diff --git a/pkg/yurtmanager/controller/yurtappset/yurtappset_controller.go b/pkg/yurtmanager/controller/yurtappset/yurtappset_controller.go index 750e57a8f05..8180aad3db7 100644 --- a/pkg/yurtmanager/controller/yurtappset/yurtappset_controller.go +++ b/pkg/yurtmanager/controller/yurtappset/yurtappset_controller.go @@ -299,19 +299,38 @@ func (r *ReconcileYurtAppSet) calculateStatus(instance *unitv1alpha1.YurtAppSet, newStatus.CurrentRevision = currentRevision.Name } + // update OverriderRef + var poolFailure *string + overriderList := unitv1alpha1.YurtAppOverriderList{} + if err := r.List(context.TODO(), &overriderList); err != nil { + message := fmt.Sprintf("fail to list yurtappoverrider: %v", err) + poolFailure = &message + } + for _, overrider := range overriderList.Items { + if overrider.Subject.Kind == "YurtAppSet" && overrider.Subject.Name == instance.Name { + newStatus.OverriderRef = overrider.Name + break + } + } // sync from status + newStatus.WorkloadSummaries = make([]unitv1alpha1.WorkloadSummary, 0) newStatus.PoolReplicas = make(map[string]int32) newStatus.ReadyReplicas = 0 newStatus.Replicas = 0 for _, pool := range nameToPool { newStatus.PoolReplicas[pool.Name] = pool.Status.Replicas + newStatus.WorkloadSummaries = append(newStatus.WorkloadSummaries, unitv1alpha1.WorkloadSummary{ + AvailableCondition: pool.Status.AvailableCondition, + Replicas: pool.Status.Replicas, + ReadyReplicas: pool.Status.ReadyReplicas, + WorkloadName: pool.Spec.PoolRef.GetName(), + }) newStatus.Replicas += pool.Status.Replicas newStatus.ReadyReplicas += pool.Status.ReadyReplicas } newStatus.TemplateType = getPoolTemplateType(instance) - var poolFailure *string for _, pool := range nameToPool { failureMessage := control.GetPoolFailure(pool) if failureMessage != nil { @@ -348,7 +367,7 @@ func (r *ReconcileYurtAppSet) updateYurtAppSet(yas *unitv1alpha1.YurtAppSet, old oldStatus.Replicas == newStatus.Replicas && oldStatus.ReadyReplicas == newStatus.ReadyReplicas && yas.Generation == newStatus.ObservedGeneration && - reflect.DeepEqual(oldStatus.PoolReplicas, newStatus.PoolReplicas) && + reflect.DeepEqual(oldStatus.WorkloadSummaries, newStatus.WorkloadSummaries) && reflect.DeepEqual(oldStatus.Conditions, newStatus.Conditions) { return yas, nil }