From 4dc091bd26fb4dc3c0d2f963966ed353f47d9a01 Mon Sep 17 00:00:00 2001 From: RainbowMango Date: Mon, 25 Oct 2021 17:12:36 +0800 Subject: [PATCH] Add custom resource webhook proposal Signed-off-by: RainbowMango --- .../resource-exploring-webhook/README.md | 424 ++++++++++++++++++ .../resource-exploring-webhook.drawio | 1 + .../resource-exploring-webhook.png | Bin 0 -> 30540 bytes 3 files changed, 425 insertions(+) create mode 100644 docs/proposals/resource-exploring-webhook/README.md create mode 100644 docs/proposals/resource-exploring-webhook/resource-exploring-webhook.drawio create mode 100644 docs/proposals/resource-exploring-webhook/resource-exploring-webhook.png diff --git a/docs/proposals/resource-exploring-webhook/README.md b/docs/proposals/resource-exploring-webhook/README.md new file mode 100644 index 000000000..e1ea9e708 --- /dev/null +++ b/docs/proposals/resource-exploring-webhook/README.md @@ -0,0 +1,424 @@ +--- +title: Resource Exploring Webhook +authors: +- "@RainbowMango" +reviewers: +- "@TBD" +approvers: +- "@TBD" + +creation-date: 2021-10-18 + +--- + +# Resource Exploring Webhook + +## Summary + +In the progress of a resource(as known as `resource template`) propagating to cluster, Karmada take actions according to +the resource definition. For example, at the phase of building `ResourceBinding`, the `karmada-controller` will parse +the `replicas` from resource templates like `deployments` but do nothing for resources that don't have `replicas`. + +For the Kubernetes native resources, Karmada knows how to parse them. But for custom resource type, as lack of the +knowledge of the structure, Karmada treat the custom resource type as a general resource. + +This proposal aims to provide a solution for users to teach Karmada to learn their custom resources. + +## Motivation + +Nowadays, lots of people or projects extend Kubernetes by `Custom Resource Defination`. In order to propagate the +custom resources, Karmada has to learn the structure of the custom resource. + +### Goals + +- Provide a solution to support custom resources by teaching Karmada the resource structure. + +### Non-Goals + +## Proposal + +### User Stories + +#### As a user, I want to propagate my custom resource(workload type with replicas) to leverage the Karmada replica scheduling capabilities. + +I have a custom resource which extremely similar with `deployments`, it has a `replica` field as well, I want to `divide` +the replicas to multiple clusters by declaring a `ReplicaScheduling` rule. + +> In this scenario, as lack of knowledge of the custom resource, Karmada can't grab it's `replica`. + +#### As a user, I want to customize the retain method for my CRD resources. + +I have a custom resource which reconciling by a controller running in member clusters. The controllers would make changes +to the resource(such as update status), I wish Karmada could retain the changes make by my controller. + +> In this sceanrio, as lack of knowledge of the custom resource, Karmada might can't retain the custom resource correctly. +> Thus, the resource might be changed back and forth by Karmada and it's controller. + +### Notes/Constraints/Caveats (Optional) + +### Risks and Mitigations + +## Design Details + +Inspire of the [Kubernetes Admission webhook][1], we propose a webhook called `ResourceExploringWebhook` which contains: +- A configuration API `ResourceExploringWebhookConfiguration` to declare the enabled webhooks. +- A review API `ExploreReview` to declare the request and response between Karmada and webhooks. + +In the `ResourceExploringWebhookConfiguration` API, the `OperationType` represents the request that Karmada might call +the webhooks in the whole propagating process. + +![operations](resource-exploring-webhook.png) + +### New ResourceExploringWebhookConfiguration API + +We propose a new CR in `config.karmada.io` group. + +```golang +type ResourceExploringWebhookConfiguration struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + // Webhooks is a list of webhooks and the affected resources and operations. + // +required + Webhooks []ResourceExploringWebhook `json:"webhooks"` +} + +// ResourceExploringWebhook describes the webhook as well as the resources and operations it applies to. +type ResourceExploringWebhook struct { + // Name is the full-qualified name of the webhook. + // +required + Name string `json:"name"` + + // ClientConfig defines how to communicate with the hook. + // +required + ClientConfig admissionregistrationv1.WebhookClientConfig `json:"clientConfig"` + + // Rules describes what operations on what resources the webhook cares about. + // The webhook cares about an operation if it matches any Rule. + // +optional + Rules []RuleWithOperations `json:"rules,omitempty"` + + // FailurePolicy defines how unrecognized errors from the webhook are handled, + // allowed values are Ignore or Fail. Defaults to Fail. + // +optional + FailurePolicy *admissionregistrationv1.FailurePolicyType `json:"failurePolicy,omitempty"` + + // TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, + // the webhook call will be ignored or the API call will fail based on the + // failure policy. + // The timeout value must be between 1 and 30 seconds. + // Default to 10 seconds. + // +optional + TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty"` + + // ExploreReviewVersions is an ordered list of preferred `ExploreReview` + // versions the Webhook expects. Karmada will try to use first version in + // the list which it supports. If none of the versions specified in this list + // supported by Karmada, validation will fail for this object. + // If a persisted webhook configuration specifies allowed versions and does not + // include any versions known to the Karmada, calls to the webhook will fail + // and be subject to the failure policy. + ExploreReviewVersions []string `json:"exploreReviewVersions"` +} + +// RuleWithOperations is a tuple of Operations and Resources. It is recommended to make +// sure that all the tuple expansions are valid. +type RuleWithOperations struct { + // Operations is the operations the hook cares about. + // If '*' is present, the length of the slice must be one. + // +required + Operations []OperationType `json:"operations"` + + // Rule is embedded, it describes other criteria of the rule, like + // APIGroups, APIVersions, Resources, etc. + admissionregistrationv1.Rule `json:",inline"` +} + +// OperationType specifies an operation for a request. +type OperationType string + +const ( + // ExploreReplica indicates that karmada want to figure out the replica declaration of a specific object. + // Only necessary for those resource types that have replica declaration, like Deployment or similar custom resources. + ExploreReplica OperationType = "ExploreReplica" + + // ExploreStatus indicates that karmada want to figure out how to get the status. + // Only necessary for those resource types that define their status in a special path(not '.status'). + ExploreStatus OperationType = "ExploreStatus" + + // ExplorePacking indicates that karmada want to figure out how to package resource template to Work. + ExplorePacking OperationType = "ExplorePacking" + + // ExploreReplicaRevising indicates that karmada request webhook to modify the replica. + ExploreReplicaRevising OperationType = "ExploreReplicaRevising" + + // ExploreRetaining indicates that karmada request webhook to retain the desired resource template. + // Only necessary for those resources which specification will be updated by their controllers running in member cluster. + ExploreRetaining OperationType = "ExploreRetaining" + + // ExploreStatusAggregating indicates that karmada want to figure out how to aggregate status to resource template. + // Only necessary for those resource types that want to aggregate status to resource template. + ExploreStatusAggregating OperationType = "ExploreStatusAggregating" + + // ExploreHealthy indicates that karmada want to figure out the healthy status of a specific object. + // Only necessary for those resource types that have and want to reflect their healthy status. + ExploreHealthy OperationType = "ExploreHealthy" + + // ExploreDependencies indicates that karmada want to figure out the dependencies of a specific object. + // Only necessary for those resource types that have dependencies resources and expect the dependencies be propagated + // together, like Deployment depends on ConfigMap/Secret. + ExploreDependencies OperationType = "ExploreDependencies" +) +``` + +### New ExploreReview API + +```golang + +// ExploreReview describes an explore review request and response. +type ExploreReview struct { + metav1.TypeMeta `json:",inline"` + // Request describes the attributes for the explore request. + // +optional + Request *ExploreRequest `json:"request,omitempty"` + + // Response describes the attributes for the explore response. + // +optional + Response *ExploreResponse `json:"response,omitempty"` +} + +// ExploreRequest describes the explore.Attributes for the explore request. +type ExploreRequest struct { + // UID is an identifier for the individual request/response. + // The UID is meant to track the round trip (request/response) between the karmada and the WebHook, not the user request. + // It is suitable for correlating log entries between the webhook and karmada, for either auditing or debugging. + // +required + UID types.UID `json:"uid"` + + // Kind is the fully-qualified type of object being submitted (for example, v1.Pod or autoscaling.v1.Scale) + // +required + Kind metav1.GroupVersionKind `json:"kind"` + + // Name is the name of the object as presented in the request. + // +required + Name string `json:"name"` + + // Namespace is the namespace associated with the request (if any). + // +optional + Namespace string `json:"namespace,omitempty"` + + // Operation is the operation being performed. + // +required + Operation OperationType `json:"operation"` + + // Object is the object from the incoming request. + // +optional + Object runtime.RawExtension `json:"object,omitempty"` + + // DesiredReplicas represents the desired pods number which webhook should revise with. + // It'll be set only if OperationType is ExploreReplicaRevising. + // +optional + DesiredReplicas *int32 `json:"replicas,omitempty"` + + // AggregatedStatus represents status list of the resource running in each member cluster. + // +optional + AggregatedStatus []AggregatedStatusItem `json:"aggregatedStatus,omitempty"` +} + +// AggregatedStatusItem represents status of the resource running in a member cluster. +type AggregatedStatusItem struct { + // ClusterName represents the member cluster name which the resource deployed on. + // +required + ClusterName string `json:"clusterName"` + + // Status reflects running status of current manifest. + // +kubebuilder:pruning:PreserveUnknownFields + // +optional + Status *runtime.RawExtension `json:"status,omitempty"` + // Applied represents if the resource referencing by ResourceBinding or ClusterResourceBinding + // is successfully applied on the cluster. + // +optional + Applied bool `json:"applied,omitempty"` + + // AppliedMessage is a human readable message indicating details about the applied status. + // This is usually holds the error message in case of apply failed. + // +optional + AppliedMessage string `json:"appliedMessage,omitempty"` +} + +// ExploreResponse describes an explore response. +type ExploreResponse struct { + // UID is an identifier for the individual request/response. + // This must be copied over from the corresponding ExploreRequest. + // +required + UID types.UID `json:"uid"` + + // The patch body. We only support "JSONPatch" currently which implements RFC 6902. + // +optional + Patch []byte `json:"patch,omitempty"` + + // The type of Patch. We only allow "JSONPatch" currently. + // +optional + PatchType *PatchType `json:"patchType,omitempty" protobuf:"bytes,5,opt,name=patchType"` + + // ReplicaRequirements represents the requirements required by each replica. + // Required if OperationType is ExploreReplica. + // +optional + ReplicaRequirements *ReplicaRequirements `json:"replicaRequirements,omitempty"` + + // Replicas represents the number of desired pods. This is a pointer to distinguish between explicit + // zero and not specified. + // Required if OperationType is ExploreReplica. + // +optional + Replicas *int32 `json:"replicas,omitempty"` + + // Dependencies represents the reference of dependencies object. + // Required if OperationType is ExploreDependencies. + // +optional + Dependencies []DependentObjectReference `json:"dependencies,omitempty"` + + // Status represents the referencing object's status. + // +optional + Status *runtime.RawExtension `json:"status,omitempty"` + + // Healthy represents the referencing object's healthy status. + // +optional + Healthy *bool `json:"healthy,omitempty"` +} + +// PatchType is the type of patch being used to represent the mutated object +type PatchType string + +const ( + PatchTypeJSONPatch PatchType = "JSONPatch" +) + +// ReplicaRequirements represents the requirements required by each replica. +type ReplicaRequirements struct { + // NodeClaim represents the node claim HardNodeAffinity, NodeSelector and Tolerations required by each replica. + // +optional + NodeClaim *NodeClaim `json:"nodeClaim,omitempty"` + + // ResourceRequest represents the resources required by each replica. + // +optional + ResourceRequest corev1.ResourceList `json:"resourceRequest,omitempty"` +} + +// NodeClaim represents the node claim HardNodeAffinity, NodeSelector and Tolerations required by each replica. +type NodeClaim struct { + // A node selector represents the union of the results of one or more label queries over a set of + // nodes; that is, it represents the OR of the selectors represented by the node selector terms. + // Note that only PodSpec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution + // is included here because it has a hard limit on pod scheduling. + // +optional + HardNodeAffinity *corev1.NodeSelector `json:"hardNodeAffinity,omitempty"` + // NodeSelector is a selector which must be true for the pod to fit on a node. + // Selector which must match a node's labels for the pod to be scheduled on that node. + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // If specified, the pod's tolerations. + // +optional + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` +} + +// DependentObjectReference contains enough information to locate the referenced object inside current cluster. +type DependentObjectReference struct { + // APIVersion represents the API version of the referent. + // +required + APIVersion string `json:"apiVersion"` + + // Kind represents the Kind of the referent. + // +required + Kind string `json:"kind"` + + // Namespace represents the namespace for the referent. + // For non-namespace scoped resources(e.g. 'ClusterRole'),do not need specify Namespace, + // and for namespace scoped resources, Namespace is required. + // If Namespace is not specified, means the resource is non-namespace scoped. + // +optional + Namespace string `json:"namespace,omitempty"` + + // Name represents the name of the referent. + // +required + Name string `json:"name"` +} +``` + +### Example +#### Configuration +The example below show two webhooks configuration. +The `foo.example.com` webhook serves for `foos` under `foo.example.com` group and implemented `ExploreRetaining` and +`ExploreHealthy` operations. +The `bar.example.com` webhook serves for `bars` under `bar.example.com` group and implemented `ExploreDependencies` and +`ExploreHealthy` operations. + +```yaml +apiVersion: config.karmada.io/v1alpha1 +kind: ResourceExploringWebhookConfiguration +metadata: + name: example +webhooks: + - name: foo.example.com + rules: + - operations: ["ExploreRetaining", "ExploreHealthy"] + apiGroups: ["foo.example.com"] + apiVersions: ["*"] + resources: ["foos"] + scope: "Namespaced" + clientConfig: + url: https://xxx:443/explore-foo + caBundle: {{caBundle}} + failurePolicy: Fail + exploreReviewVersions: ["v1alpha1"] + timeoutSeconds: 3 + - name: bar.example.com + rules: + - operations: ["ExploreDependencies", "ExploreHealthy"] + apiGroups: ["bar.example.com"] + apiVersions: ["*"] + resources: ["bars"] + scope: "Cluster" + clientConfig: + url: https://xxx:443/explore-bar + caBundle: {{caBundle}} + failurePolicy: Fail + exploreReviewVersions: ["v1alpha1"] + timeoutSeconds: 3 +``` +#### Request and Response +Take `ExploreHealthy` for example, Karmada will send the request like: +```yaml +apiVersion: config.karmada.io/v1alpha1 +kind: ExploreReview +request: + - uid: xxx + - Kind: + - group: foo.example.com + version: v1alpha1 + Kind: Foo + - name: foo + - namespace: default + - operation: ExploreHealthy + - object: +``` + +And the response like: +```yaml +apiVersion: config.karmada.io/v1alpha1 +kind: ExploreReview +response: + - uid: xxx(same uid in the request) + - healthy: true +``` + +### Test Plan + +- Propose E2E test cases according the operations described above. + +## Alternatives + +The proposal [Configurable Local Value Retention][2] described a solution to retain custom resource, but the +configuration would be a little complex to users. + + +[1]: https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/ +[2]: https://github.com/karmada-io/karmada/tree/master/docs/proposals/configurable-local-value-retention \ No newline at end of file diff --git a/docs/proposals/resource-exploring-webhook/resource-exploring-webhook.drawio b/docs/proposals/resource-exploring-webhook/resource-exploring-webhook.drawio new file mode 100644 index 000000000..7ddf54dd3 --- /dev/null +++ b/docs/proposals/resource-exploring-webhook/resource-exploring-webhook.drawio @@ -0,0 +1 @@ +7VtZk9o4EP41VGUfJuUD2/AIDJNMqpLKMlub5FHYjVFGtry2uPLrV7Il8ME54TAVm4eRWldL/fXnpjW0zEGw/BCjaPqZekBahuYtW+ZjyzB0rWPzP0KyyiROx8oEfow92WkjeMG/QI2U0hn2ICl0ZJQShqOi0KVhCC4ryFAc00Wx24SS4qoR8qEieHERqUq/YY9NpVS3u5uGj4D9qVy6YzhZwxi5r35MZ6Fcr2WYk/TJmgOk5pIbTabIo4ucyBy2zEFMKctKwXIARJytOrZs3NOO1rXeMYTsmAE/PjPn19+u3dN+Bn73+dOTM//yYFudbJ45IjN5IiNI6Cx24R8IIoIYSPXZSh0ZgyVfsT9lAeECnRcTFtNXGFBC47SLaacPb5lgQnLyiSU+XI4I9kMuc7n2wBv7c4gZ5mbpyYYAe55YsL+YYgYvEXLF6gsOQi5Ljx3ExjSxBg3Zi1RPl/XcmtnD5dUTk4co1oZlTiRP8APQAFi84l1ka7strSnR3pHVRQ46yuDTHGrUMCTR6q9n3liMF6TRTjJgd6cB+zj0cOg39tttvxoY0NYqBvxG49fGarut1q6B1fSK1ZTbtQybCEuNY17yRendcyjeCBCM+aHzVxmZJfz4/2pMvNvEdg1MXH01ViwGHg8mZDUBP+AHMNyI+hB6PRGjCHsQlCTYLZoXlph9l4ctyj9E+b0la4/LXNPjSlVCvrvv+Uo2yrBUfTMuramBO60kYXtElMBQ7AM75nUkDmav3XN2tbaYVcli4CEInheDtW22lit8pZjvbg2rNYxWJZipKbK9y1H56Kk0kWMWJzK7pYmyo6lMxM2PVrlukeiQ7Fa4rRXX0TVtr17l/ka30J8XMg02frC2wW+4hvOnuEZNYFxmR1N/I4zLE1nadWCsFD4WxpZ2DRh3qi/xBsZXhLF1Lhg7V4KxdSKMnevA2Lw4jEuRn5Y+eajqRag6h6Cq/EI/wS9+I4BR4frhAEZ9HauJy9xdAGPvd4GD/UsudimXsf9wl6kJvC37QDxyLLzLE1UipAvB2zoR3uZ14H35+LyB9xvgbZRJ963w1i8U8JThWvo6ebC/fpWAp7uNvbOkWxRDAeb2fzNxwZLC9SFJb596vEP3fScS20gxpKWNExRgssqa+TgUiHyYHO3SMKHcC8rytCvfhBbQkCZpKm29okr+rdOBSjBcRtxt4CMgwqYr1cxPYlwewmXpfpS05MGbZJ1eyhqqqyaxP194ryy7NBB+nJYJGgPpry+xlC+HNIRqenHAn6cnMVuMPAwb11fdd7BBJRu5LelYTHLKGXe69vE5RVsvce+2nKJz1ZxidxsxXwS5Ij/b3gZd1XAKdnfpJhLWD9LGYnUCk5JuN1a87HQvDLFZ0vP9GHxO7OJy7Hj3a5zyAk6p18Apq1eo9+yUe3U77LG3csrGE2/sidbNPdHRtiVkG0+st+Jxiowbc8gIGMLhWV7oDbmcgVwMs37kYjTkcneK34Rc6gyHMvE9QgSciUIXwxlCqIYRL8eIRpERTfP2jLjt4rD+LtAw4n1oXgfdzrf83qzWsWz9lTPcBYPUhr6vR981CGjbDX3fneINfd8xfY8gIthFDX3fPX1f9doBm//29YW2WIajn53hszGHL8uHJtV5f4o35H3/5D2COU6aGPxGJF5h7C28fjSJty+YQuHVzW+zs/9c2vwA3hz+Dw== \ No newline at end of file diff --git a/docs/proposals/resource-exploring-webhook/resource-exploring-webhook.png b/docs/proposals/resource-exploring-webhook/resource-exploring-webhook.png new file mode 100644 index 0000000000000000000000000000000000000000..b879b81178182af4db8fdb07c3538150c5b135aa GIT binary patch literal 30540 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 literal 0 HcmV?d00001