Merge pull request #70031 from nrfox/requeue-on-error

Sample Controller: requeue work item on syncHandler error

Kubernetes-commit: b7b0aae4358065b98a3db04411bec65a11eb166e
This commit is contained in:
Kubernetes Publisher
2018-10-27 03:19:59 -07:00
39 changed files with 969 additions and 1637 deletions
+244 -244
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -219,7 +219,9 @@ func (c *Controller) processNextWorkItem() bool {
// Run the syncHandler, passing it the namespace/name string of the // Run the syncHandler, passing it the namespace/name string of the
// Foo resource to be synced. // Foo resource to be synced.
if err := c.syncHandler(key); err != nil { if err := c.syncHandler(key); err != nil {
return fmt.Errorf("error syncing '%s': %s", key, err.Error()) // Put the item back on the workqueue to handle any transient errors.
c.workqueue.AddRateLimited(key)
return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error())
} }
// Finally, if no error occurs we Forget this item so it does not // Finally, if no error occurs we Forget this item so it does not
// get queued again until another change happens. // get queued again until another change happens.
+3 -3
View File
@@ -261,9 +261,9 @@ message WebhookClientConfig {
// +optional // +optional
optional ServiceReference service = 1; optional ServiceReference service = 1;
// `caBundle` is a PEM encoded CA bundle which will be used to validate // `caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
// the webhook's server certificate. // If unspecified, system trust roots on the apiserver are used.
// Required. // +optional
optional bytes caBundle = 2; optional bytes caBundle = 2;
} }
+5 -5
View File
@@ -282,12 +282,12 @@ type WebhookClientConfig struct {
// Port 443 will be used if it is open, otherwise it is an error. // Port 443 will be used if it is open, otherwise it is an error.
// //
// +optional // +optional
Service *ServiceReference `json:"service" protobuf:"bytes,1,opt,name=service"` Service *ServiceReference `json:"service,omitempty" protobuf:"bytes,1,opt,name=service"`
// `caBundle` is a PEM encoded CA bundle which will be used to validate // `caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
// the webhook's server certificate. // If unspecified, system trust roots on the apiserver are used.
// Required. // +optional
CABundle []byte `json:"caBundle" protobuf:"bytes,2,opt,name=caBundle"` CABundle []byte `json:"caBundle,omitempty" protobuf:"bytes,2,opt,name=caBundle"`
} }
// ServiceReference holds a reference to Service.legacy.k8s.io // ServiceReference holds a reference to Service.legacy.k8s.io
@@ -116,7 +116,7 @@ var map_WebhookClientConfig = map[string]string{
"": "WebhookClientConfig contains the information to make a TLS connection with the webhook", "": "WebhookClientConfig contains the information to make a TLS connection with the webhook",
"url": "`url` gives the location of the webhook, in standard URL form (`[scheme://]host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.", "url": "`url` gives the location of the webhook, in standard URL form (`[scheme://]host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.",
"service": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`.\n\nPort 443 will be used if it is open, otherwise it is an error.", "service": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`.\n\nPort 443 will be used if it is open, otherwise it is an error.",
"caBundle": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. Required.", "caBundle": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used.",
} }
func (WebhookClientConfig) SwaggerDoc() map[string]string { func (WebhookClientConfig) SwaggerDoc() map[string]string {
+2 -3
View File
@@ -137,9 +137,8 @@ message WebhookClientConfig {
// +optional // +optional
optional ServiceReference service = 2; optional ServiceReference service = 2;
// `caBundle` is a PEM encoded CA bundle which will be used to validate // `caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
// the webhook's server certificate. // If unspecified, system trust roots on the apiserver are used.
// defaults to the apiservers CA bundle for the endpoint type
// +optional // +optional
optional bytes caBundle = 3; optional bytes caBundle = 3;
} }
+4 -5
View File
@@ -169,13 +169,12 @@ type WebhookClientConfig struct {
// Port 443 will be used if it is open, otherwise it is an error. // Port 443 will be used if it is open, otherwise it is an error.
// //
// +optional // +optional
Service *ServiceReference `json:"service" protobuf:"bytes,2,opt,name=service"` Service *ServiceReference `json:"service,omitempty" protobuf:"bytes,2,opt,name=service"`
// `caBundle` is a PEM encoded CA bundle which will be used to validate // `caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
// the webhook's server certificate. // If unspecified, system trust roots on the apiserver are used.
// defaults to the apiservers CA bundle for the endpoint type
// +optional // +optional
CABundle []byte `json:"caBundle" protobuf:"bytes,3,opt,name=caBundle"` CABundle []byte `json:"caBundle,omitempty" protobuf:"bytes,3,opt,name=caBundle"`
} }
// ServiceReference holds a reference to Service.legacy.k8s.io // ServiceReference holds a reference to Service.legacy.k8s.io
@@ -90,7 +90,7 @@ var map_WebhookClientConfig = map[string]string{
"": "WebhookClientConfig contains the information to make a connection with the webhook", "": "WebhookClientConfig contains the information to make a connection with the webhook",
"url": "`url` gives the location of the webhook, in standard URL form (`[scheme://]host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.", "url": "`url` gives the location of the webhook, in standard URL form (`[scheme://]host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.",
"service": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`.\n\nPort 443 will be used if it is open, otherwise it is an error.", "service": "`service` is a reference to the service for this webhook. Either `service` or `url` must be specified.\n\nIf the webhook is running within the cluster, then you should use `service`.\n\nPort 443 will be used if it is open, otherwise it is an error.",
"caBundle": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. defaults to the apiservers CA bundle for the endpoint type", "caBundle": "`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used.",
} }
func (WebhookClientConfig) SwaggerDoc() map[string]string { func (WebhookClientConfig) SwaggerDoc() map[string]string {
+19
View File
@@ -78,4 +78,23 @@ const (
// //
// Not all cloud providers support this annotation, though AWS & GCE do. // Not all cloud providers support this annotation, though AWS & GCE do.
AnnotationLoadBalancerSourceRangesKey = "service.beta.kubernetes.io/load-balancer-source-ranges" AnnotationLoadBalancerSourceRangesKey = "service.beta.kubernetes.io/load-balancer-source-ranges"
// EndpointsLastChangeTriggerTime is the annotation key, set for endpoints objects, that
// represents the timestamp (stored as RFC 3339 date-time string, e.g. '2018-10-22T19:32:52.1Z')
// of the last change, of some Pod or Service object, that triggered the endpoints object change.
// In other words, if a Pod / Service changed at time T0, that change was observed by endpoints
// controller at T1, and the Endpoints object was changed at T2, the
// EndpointsLastChangeTriggerTime would be set to T0.
//
// The "endpoints change trigger" here means any Pod or Service change that resulted in the
// Endpoints object change.
//
// Given the definition of the "endpoints change trigger", please note that this annotation will
// be set ONLY for endpoints object changes triggered by either Pod or Service change. If the
// Endpoints object changes due to other reasons, this annotation won't be set (or updated if it's
// already set).
//
// This annotation will be used to compute the in-cluster network programming latency SLI, see
// https://github.com/kubernetes/community/blob/master/sig-scalability/slos/network_programming_latency.md
EndpointsLastChangeTriggerTime = "endpoints.kubernetes.io/last-change-trigger-time"
) )
+3
View File
@@ -606,6 +606,9 @@ message Container {
// +optional // +optional
// +patchMergeKey=containerPort // +patchMergeKey=containerPort
// +patchStrategy=merge // +patchStrategy=merge
// +listType=map
// +listMapKey=containerPort
// +listMapKey=protocol
repeated ContainerPort ports = 6; repeated ContainerPort ports = 6;
// List of sources to populate environment variables in the container. // List of sources to populate environment variables in the container.
+7
View File
@@ -2060,6 +2060,9 @@ type Container struct {
// +optional // +optional
// +patchMergeKey=containerPort // +patchMergeKey=containerPort
// +patchStrategy=merge // +patchStrategy=merge
// +listType=map
// +listMapKey=containerPort
// +listMapKey=protocol
Ports []ContainerPort `json:"ports,omitempty" patchStrategy:"merge" patchMergeKey:"containerPort" protobuf:"bytes,6,rep,name=ports"` Ports []ContainerPort `json:"ports,omitempty" patchStrategy:"merge" patchMergeKey:"containerPort" protobuf:"bytes,6,rep,name=ports"`
// List of sources to populate environment variables in the container. // List of sources to populate environment variables in the container.
// The keys defined within a source must be a C_IDENTIFIER. All invalid keys // The keys defined within a source must be a C_IDENTIFIER. All invalid keys
@@ -4996,6 +4999,10 @@ const (
TLSCertKey = "tls.crt" TLSCertKey = "tls.crt"
// TLSPrivateKeyKey is the key for the private key field in a TLS secret. // TLSPrivateKeyKey is the key for the private key field in a TLS secret.
TLSPrivateKeyKey = "tls.key" TLSPrivateKeyKey = "tls.key"
// SecretTypeBootstrapToken is used during the automated bootstrap process (first
// implemented by kubeadm). It stores tokens that are used to sign well known
// ConfigMaps. They are used for authn.
SecretTypeBootstrapToken SecretType = "bootstrap.kubernetes.io/token"
) )
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+470 -1094
View File
File diff suppressed because it is too large Load Diff
-26
View File
@@ -22,7 +22,6 @@ syntax = 'proto2';
package k8s.io.api.extensions.v1beta1; package k8s.io.api.extensions.v1beta1;
import "k8s.io/api/core/v1/generated.proto"; import "k8s.io/api/core/v1/generated.proto";
import "k8s.io/apimachinery/pkg/api/resource/generated.proto";
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
@@ -56,31 +55,6 @@ message AllowedHostPath {
optional bool readOnly = 2; optional bool readOnly = 2;
} }
message CustomMetricCurrentStatus {
// Custom Metric name.
optional string name = 1;
// Custom Metric value (average).
optional k8s.io.apimachinery.pkg.api.resource.Quantity value = 2;
}
message CustomMetricCurrentStatusList {
repeated CustomMetricCurrentStatus items = 1;
}
// Alpha-level support for Custom Metrics in HPA (as annotations).
message CustomMetricTarget {
// Custom Metric name.
optional string name = 1;
// Custom Metric value (average).
optional k8s.io.apimachinery.pkg.api.resource.Quantity value = 2;
}
message CustomMetricTargetList {
repeated CustomMetricTarget items = 1;
}
// DEPRECATED - This group version of DaemonSet is deprecated by apps/v1beta2/DaemonSet. See the release notes for // DEPRECATED - This group version of DaemonSet is deprecated by apps/v1beta2/DaemonSet. See the release notes for
// more information. // more information.
// DaemonSet represents the configuration of a daemon set. // DaemonSet represents the configuration of a daemon set.
-24
View File
@@ -19,7 +19,6 @@ package v1beta1
import ( import (
appsv1beta1 "k8s.io/api/apps/v1beta1" appsv1beta1 "k8s.io/api/apps/v1beta1"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
) )
@@ -77,29 +76,6 @@ type ReplicationControllerDummy struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
} }
// Alpha-level support for Custom Metrics in HPA (as annotations).
type CustomMetricTarget struct {
// Custom Metric name.
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// Custom Metric value (average).
TargetValue resource.Quantity `json:"value" protobuf:"bytes,2,opt,name=value"`
}
type CustomMetricTargetList struct {
Items []CustomMetricTarget `json:"items" protobuf:"bytes,1,rep,name=items"`
}
type CustomMetricCurrentStatus struct {
// Custom Metric name.
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// Custom Metric value (average).
CurrentValue resource.Quantity `json:"value" protobuf:"bytes,2,opt,name=value"`
}
type CustomMetricCurrentStatusList struct {
Items []CustomMetricCurrentStatus `json:"items" protobuf:"bytes,1,rep,name=items"`
}
// +genclient // +genclient
// +genclient:method=GetScale,verb=get,subresource=scale,result=Scale // +genclient:method=GetScale,verb=get,subresource=scale,result=Scale
// +genclient:method=UpdateScale,verb=update,subresource=scale,input=Scale,result=Scale // +genclient:method=UpdateScale,verb=update,subresource=scale,input=Scale,result=Scale
-19
View File
@@ -46,25 +46,6 @@ func (AllowedHostPath) SwaggerDoc() map[string]string {
return map_AllowedHostPath return map_AllowedHostPath
} }
var map_CustomMetricCurrentStatus = map[string]string{
"name": "Custom Metric name.",
"value": "Custom Metric value (average).",
}
func (CustomMetricCurrentStatus) SwaggerDoc() map[string]string {
return map_CustomMetricCurrentStatus
}
var map_CustomMetricTarget = map[string]string{
"": "Alpha-level support for Custom Metrics in HPA (as annotations).",
"name": "Custom Metric name.",
"value": "Custom Metric value (average).",
}
func (CustomMetricTarget) SwaggerDoc() map[string]string {
return map_CustomMetricTarget
}
var map_DaemonSet = map[string]string{ var map_DaemonSet = map[string]string{
"": "DEPRECATED - This group version of DaemonSet is deprecated by apps/v1beta2/DaemonSet. See the release notes for more information. DaemonSet represents the configuration of a daemon set.", "": "DEPRECATED - This group version of DaemonSet is deprecated by apps/v1beta2/DaemonSet. See the release notes for more information. DaemonSet represents the configuration of a daemon set.",
"metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", "metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
-80
View File
@@ -59,86 +59,6 @@ func (in *AllowedHostPath) DeepCopy() *AllowedHostPath {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomMetricCurrentStatus) DeepCopyInto(out *CustomMetricCurrentStatus) {
*out = *in
out.CurrentValue = in.CurrentValue.DeepCopy()
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomMetricCurrentStatus.
func (in *CustomMetricCurrentStatus) DeepCopy() *CustomMetricCurrentStatus {
if in == nil {
return nil
}
out := new(CustomMetricCurrentStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomMetricCurrentStatusList) DeepCopyInto(out *CustomMetricCurrentStatusList) {
*out = *in
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CustomMetricCurrentStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomMetricCurrentStatusList.
func (in *CustomMetricCurrentStatusList) DeepCopy() *CustomMetricCurrentStatusList {
if in == nil {
return nil
}
out := new(CustomMetricCurrentStatusList)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomMetricTarget) DeepCopyInto(out *CustomMetricTarget) {
*out = *in
out.TargetValue = in.TargetValue.DeepCopy()
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomMetricTarget.
func (in *CustomMetricTarget) DeepCopy() *CustomMetricTarget {
if in == nil {
return nil
}
out := new(CustomMetricTarget)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomMetricTargetList) DeepCopyInto(out *CustomMetricTargetList) {
*out = *in
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CustomMetricTarget, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomMetricTargetList.
func (in *CustomMetricTargetList) DeepCopy() *CustomMetricTargetList {
if in == nil {
return nil
}
out := new(CustomMetricTargetList)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DaemonSet) DeepCopyInto(out *DaemonSet) { func (in *DaemonSet) DeepCopyInto(out *DaemonSet) {
*out = *in *out = *in
+9
View File
@@ -58,9 +58,11 @@ message AllowedHostPath {
// created by POSTing to .../pods/<pod name>/evictions. // created by POSTing to .../pods/<pod name>/evictions.
message Eviction { message Eviction {
// ObjectMeta describes the pod that is being evicted. // ObjectMeta describes the pod that is being evicted.
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// DeleteOptions may be provided // DeleteOptions may be provided
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.DeleteOptions deleteOptions = 2; optional k8s.io.apimachinery.pkg.apis.meta.v1.DeleteOptions deleteOptions = 2;
} }
@@ -97,17 +99,21 @@ message IDRange {
// PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods // PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods
message PodDisruptionBudget { message PodDisruptionBudget {
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// Specification of the desired behavior of the PodDisruptionBudget. // Specification of the desired behavior of the PodDisruptionBudget.
// +optional
optional PodDisruptionBudgetSpec spec = 2; optional PodDisruptionBudgetSpec spec = 2;
// Most recently observed status of the PodDisruptionBudget. // Most recently observed status of the PodDisruptionBudget.
// +optional
optional PodDisruptionBudgetStatus status = 3; optional PodDisruptionBudgetStatus status = 3;
} }
// PodDisruptionBudgetList is a collection of PodDisruptionBudgets. // PodDisruptionBudgetList is a collection of PodDisruptionBudgets.
message PodDisruptionBudgetList { message PodDisruptionBudgetList {
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1;
repeated PodDisruptionBudget items = 2; repeated PodDisruptionBudget items = 2;
@@ -119,16 +125,19 @@ message PodDisruptionBudgetSpec {
// "selector" will still be available after the eviction, i.e. even in the // "selector" will still be available after the eviction, i.e. even in the
// absence of the evicted pod. So for example you can prevent all voluntary // absence of the evicted pod. So for example you can prevent all voluntary
// evictions by specifying "100%". // evictions by specifying "100%".
// +optional
optional k8s.io.apimachinery.pkg.util.intstr.IntOrString minAvailable = 1; optional k8s.io.apimachinery.pkg.util.intstr.IntOrString minAvailable = 1;
// Label query over pods whose evictions are managed by the disruption // Label query over pods whose evictions are managed by the disruption
// budget. // budget.
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 2; optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 2;
// An eviction is allowed if at most "maxUnavailable" pods selected by // An eviction is allowed if at most "maxUnavailable" pods selected by
// "selector" are unavailable after the eviction, i.e. even in absence of // "selector" are unavailable after the eviction, i.e. even in absence of
// the evicted pod. For example, one can prevent all voluntary evictions // the evicted pod. For example, one can prevent all voluntary evictions
// by specifying 0. This is a mutually exclusive setting with "minAvailable". // by specifying 0. This is a mutually exclusive setting with "minAvailable".
// +optional
optional k8s.io.apimachinery.pkg.util.intstr.IntOrString maxUnavailable = 3; optional k8s.io.apimachinery.pkg.util.intstr.IntOrString maxUnavailable = 3;
} }
+13 -4
View File
@@ -28,16 +28,19 @@ type PodDisruptionBudgetSpec struct {
// "selector" will still be available after the eviction, i.e. even in the // "selector" will still be available after the eviction, i.e. even in the
// absence of the evicted pod. So for example you can prevent all voluntary // absence of the evicted pod. So for example you can prevent all voluntary
// evictions by specifying "100%". // evictions by specifying "100%".
// +optional
MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty" protobuf:"bytes,1,opt,name=minAvailable"` MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty" protobuf:"bytes,1,opt,name=minAvailable"`
// Label query over pods whose evictions are managed by the disruption // Label query over pods whose evictions are managed by the disruption
// budget. // budget.
// +optional
Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"` Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"`
// An eviction is allowed if at most "maxUnavailable" pods selected by // An eviction is allowed if at most "maxUnavailable" pods selected by
// "selector" are unavailable after the eviction, i.e. even in absence of // "selector" are unavailable after the eviction, i.e. even in absence of
// the evicted pod. For example, one can prevent all voluntary evictions // the evicted pod. For example, one can prevent all voluntary evictions
// by specifying 0. This is a mutually exclusive setting with "minAvailable". // by specifying 0. This is a mutually exclusive setting with "minAvailable".
// +optional
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty" protobuf:"bytes,3,opt,name=maxUnavailable"` MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty" protobuf:"bytes,3,opt,name=maxUnavailable"`
} }
@@ -81,12 +84,15 @@ type PodDisruptionBudgetStatus struct {
// PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods // PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods
type PodDisruptionBudget struct { type PodDisruptionBudget struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Specification of the desired behavior of the PodDisruptionBudget. // Specification of the desired behavior of the PodDisruptionBudget.
// +optional
Spec PodDisruptionBudgetSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` Spec PodDisruptionBudgetSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
// Most recently observed status of the PodDisruptionBudget. // Most recently observed status of the PodDisruptionBudget.
// +optional
Status PodDisruptionBudgetStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` Status PodDisruptionBudgetStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
} }
@@ -95,6 +101,7 @@ type PodDisruptionBudget struct {
// PodDisruptionBudgetList is a collection of PodDisruptionBudgets. // PodDisruptionBudgetList is a collection of PodDisruptionBudgets.
type PodDisruptionBudgetList struct { type PodDisruptionBudgetList struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Items []PodDisruptionBudget `json:"items" protobuf:"bytes,2,rep,name=items"` Items []PodDisruptionBudget `json:"items" protobuf:"bytes,2,rep,name=items"`
} }
@@ -110,9 +117,11 @@ type Eviction struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
// ObjectMeta describes the pod that is being evicted. // ObjectMeta describes the pod that is being evicted.
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// DeleteOptions may be provided // DeleteOptions may be provided
// +optional
DeleteOptions *metav1.DeleteOptions `json:"deleteOptions,omitempty" protobuf:"bytes,2,opt,name=deleteOptions"` DeleteOptions *metav1.DeleteOptions `json:"deleteOptions,omitempty" protobuf:"bytes,2,opt,name=deleteOptions"`
} }
@@ -250,13 +259,13 @@ type AllowedHostPath struct {
ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,2,opt,name=readOnly"` ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,2,opt,name=readOnly"`
} }
// FSType gives strong typing to different file systems that are used by volumes.
type FSType string
// AllowAllCapabilities can be used as a value for the PodSecurityPolicy.AllowAllCapabilities // AllowAllCapabilities can be used as a value for the PodSecurityPolicy.AllowAllCapabilities
// field and means that any capabilities are allowed to be requested. // field and means that any capabilities are allowed to be requested.
var AllowAllCapabilities v1.Capability = "*" var AllowAllCapabilities v1.Capability = "*"
// FSType gives strong typing to different file systems that are used by volumes.
type FSType string
var ( var (
AzureFile FSType = "azureFile" AzureFile FSType = "azureFile"
Flocker FSType = "flocker" Flocker FSType = "flocker"
+15 -1
View File
@@ -20,6 +20,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"reflect"
"strings" "strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -82,7 +83,20 @@ func (u *UnexpectedObjectError) Error() string {
func FromObject(obj runtime.Object) error { func FromObject(obj runtime.Object) error {
switch t := obj.(type) { switch t := obj.(type) {
case *metav1.Status: case *metav1.Status:
return &StatusError{*t} return &StatusError{ErrStatus: *t}
case runtime.Unstructured:
var status metav1.Status
obj := t.UnstructuredContent()
if !reflect.DeepEqual(obj["kind"], "Status") {
break
}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(t.UnstructuredContent(), &status); err != nil {
return err
}
if status.APIVersion != "v1" && status.APIVersion != "meta.k8s.io/v1" {
break
}
return &StatusError{ErrStatus: status}
} }
return &UnexpectedObjectError{obj} return &UnexpectedObjectError{obj}
} }
+11 -1
View File
@@ -18,6 +18,7 @@ package versioning
import ( import (
"io" "io"
"reflect"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@@ -90,7 +91,16 @@ func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into ru
into = versioned.Last() into = versioned.Last()
} }
obj, gvk, err := c.decoder.Decode(data, defaultGVK, into) // If the into object is unstructured and expresses an opinion about its group/version,
// create a new instance of the type so we always exercise the conversion path (skips short-circuiting on `into == obj`)
decodeInto := into
if into != nil {
if _, ok := into.(runtime.Unstructured); ok && !into.GetObjectKind().GroupVersionKind().GroupVersion().Empty() {
decodeInto = reflect.New(reflect.TypeOf(into).Elem()).Interface().(runtime.Object)
}
}
obj, gvk, err := c.decoder.Decode(data, defaultGVK, decodeInto)
if err != nil { if err != nil {
return nil, gvk, err return nil, gvk, err
} }
+13
View File
@@ -94,6 +94,8 @@ func (d *CachedDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, er
return ServerResources(d) return ServerResources(d)
} }
// ServerGroups returns the supported groups, with information like supported versions and the
// preferred version.
func (d *CachedDiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) { func (d *CachedDiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) {
filename := filepath.Join(d.cacheDirectory, "servergroups.json") filename := filepath.Join(d.cacheDirectory, "servergroups.json")
cachedBytes, err := d.getCachedFile(filename) cachedBytes, err := d.getCachedFile(filename)
@@ -202,26 +204,36 @@ func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Obj
return err return err
} }
// RESTClient returns a RESTClient that is used to communicate with API server
// by this client implementation.
func (d *CachedDiscoveryClient) RESTClient() restclient.Interface { func (d *CachedDiscoveryClient) RESTClient() restclient.Interface {
return d.delegate.RESTClient() return d.delegate.RESTClient()
} }
// ServerPreferredResources returns the supported resources with the version preferred by the
// server.
func (d *CachedDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) { func (d *CachedDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return ServerPreferredResources(d) return ServerPreferredResources(d)
} }
// ServerPreferredNamespacedResources returns the supported namespaced resources with the
// version preferred by the server.
func (d *CachedDiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) { func (d *CachedDiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
return ServerPreferredNamespacedResources(d) return ServerPreferredNamespacedResources(d)
} }
// ServerVersion retrieves and parses the server's version (git version).
func (d *CachedDiscoveryClient) ServerVersion() (*version.Info, error) { func (d *CachedDiscoveryClient) ServerVersion() (*version.Info, error) {
return d.delegate.ServerVersion() return d.delegate.ServerVersion()
} }
// OpenAPISchema retrieves and parses the swagger API schema the server supports.
func (d *CachedDiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) { func (d *CachedDiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) {
return d.delegate.OpenAPISchema() return d.delegate.OpenAPISchema()
} }
// Fresh is supposed to tell the caller whether or not to retry if the cache
// fails to find something (false = retry, true = no need to retry).
func (d *CachedDiscoveryClient) Fresh() bool { func (d *CachedDiscoveryClient) Fresh() bool {
d.mutex.Lock() d.mutex.Lock()
defer d.mutex.Unlock() defer d.mutex.Unlock()
@@ -229,6 +241,7 @@ func (d *CachedDiscoveryClient) Fresh() bool {
return d.fresh return d.fresh
} }
// Invalidate enforces that no cached data is used in the future that is older than the current time.
func (d *CachedDiscoveryClient) Invalidate() { func (d *CachedDiscoveryClient) Invalidate() {
d.mutex.Lock() d.mutex.Lock()
defer d.mutex.Unlock() defer d.mutex.Unlock()
+12 -12
View File
@@ -263,8 +263,8 @@ func ServerPreferredResources(d DiscoveryInterface) ([]*metav1.APIResourceList,
result := []*metav1.APIResourceList{} result := []*metav1.APIResourceList{}
grVersions := map[schema.GroupResource]string{} // selected version of a GroupResource grVersions := map[schema.GroupResource]string{} // selected version of a GroupResource
grApiResources := map[schema.GroupResource]*metav1.APIResource{} // selected APIResource for a GroupResource grAPIResources := map[schema.GroupResource]*metav1.APIResource{} // selected APIResource for a GroupResource
gvApiResourceLists := map[schema.GroupVersion]*metav1.APIResourceList{} // blueprint for a APIResourceList for later grouping gvAPIResourceLists := map[schema.GroupVersion]*metav1.APIResourceList{} // blueprint for a APIResourceList for later grouping
for _, apiGroup := range serverGroupList.Groups { for _, apiGroup := range serverGroupList.Groups {
for _, version := range apiGroup.Versions { for _, version := range apiGroup.Versions {
@@ -276,11 +276,11 @@ func ServerPreferredResources(d DiscoveryInterface) ([]*metav1.APIResourceList,
} }
// create empty list which is filled later in another loop // create empty list which is filled later in another loop
emptyApiResourceList := metav1.APIResourceList{ emptyAPIResourceList := metav1.APIResourceList{
GroupVersion: version.GroupVersion, GroupVersion: version.GroupVersion,
} }
gvApiResourceLists[groupVersion] = &emptyApiResourceList gvAPIResourceLists[groupVersion] = &emptyAPIResourceList
result = append(result, &emptyApiResourceList) result = append(result, &emptyAPIResourceList)
for i := range apiResourceList.APIResources { for i := range apiResourceList.APIResources {
apiResource := &apiResourceList.APIResources[i] apiResource := &apiResourceList.APIResources[i]
@@ -288,21 +288,21 @@ func ServerPreferredResources(d DiscoveryInterface) ([]*metav1.APIResourceList,
continue continue
} }
gv := schema.GroupResource{Group: apiGroup.Name, Resource: apiResource.Name} gv := schema.GroupResource{Group: apiGroup.Name, Resource: apiResource.Name}
if _, ok := grApiResources[gv]; ok && version.Version != apiGroup.PreferredVersion.Version { if _, ok := grAPIResources[gv]; ok && version.Version != apiGroup.PreferredVersion.Version {
// only override with preferred version // only override with preferred version
continue continue
} }
grVersions[gv] = version.Version grVersions[gv] = version.Version
grApiResources[gv] = apiResource grAPIResources[gv] = apiResource
} }
} }
} }
// group selected APIResources according to GroupVersion into APIResourceLists // group selected APIResources according to GroupVersion into APIResourceLists
for groupResource, apiResource := range grApiResources { for groupResource, apiResource := range grAPIResources {
version := grVersions[groupResource] version := grVersions[groupResource]
groupVersion := schema.GroupVersion{Group: groupResource.Group, Version: version} groupVersion := schema.GroupVersion{Group: groupResource.Group, Version: version}
apiResourceList := gvApiResourceLists[groupVersion] apiResourceList := gvAPIResourceLists[groupVersion]
apiResourceList.APIResources = append(apiResourceList.APIResources, *apiResource) apiResourceList.APIResources = append(apiResourceList.APIResources, *apiResource)
} }
@@ -464,9 +464,9 @@ func NewDiscoveryClient(c restclient.Interface) *DiscoveryClient {
// RESTClient returns a RESTClient that is used to communicate // RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation. // with API server by this client implementation.
func (c *DiscoveryClient) RESTClient() restclient.Interface { func (d *DiscoveryClient) RESTClient() restclient.Interface {
if c == nil { if d == nil {
return nil return nil
} }
return c.restClient return d.restClient
} }
+13
View File
@@ -36,6 +36,8 @@ type FakeDiscovery struct {
FakedServerVersion *version.Info FakedServerVersion *version.Info
} }
// ServerResourcesForGroupVersion returns the supported resources for a group
// and version.
func (c *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) { func (c *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
action := testing.ActionImpl{ action := testing.ActionImpl{
Verb: "get", Verb: "get",
@@ -50,6 +52,7 @@ func (c *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*me
return nil, fmt.Errorf("GroupVersion %q not found", groupVersion) return nil, fmt.Errorf("GroupVersion %q not found", groupVersion)
} }
// ServerResources returns the supported resources for all groups and versions.
func (c *FakeDiscovery) ServerResources() ([]*metav1.APIResourceList, error) { func (c *FakeDiscovery) ServerResources() ([]*metav1.APIResourceList, error) {
action := testing.ActionImpl{ action := testing.ActionImpl{
Verb: "get", Verb: "get",
@@ -59,14 +62,20 @@ func (c *FakeDiscovery) ServerResources() ([]*metav1.APIResourceList, error) {
return c.Resources, nil return c.Resources, nil
} }
// ServerPreferredResources returns the supported resources with the version
// preferred by the server.
func (c *FakeDiscovery) ServerPreferredResources() ([]*metav1.APIResourceList, error) { func (c *FakeDiscovery) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return nil, nil return nil, nil
} }
// ServerPreferredNamespacedResources returns the supported namespaced resources
// with the version preferred by the server.
func (c *FakeDiscovery) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) { func (c *FakeDiscovery) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
return nil, nil return nil, nil
} }
// ServerGroups returns the supported groups, with information like supported
// versions and the preferred version.
func (c *FakeDiscovery) ServerGroups() (*metav1.APIGroupList, error) { func (c *FakeDiscovery) ServerGroups() (*metav1.APIGroupList, error) {
action := testing.ActionImpl{ action := testing.ActionImpl{
Verb: "get", Verb: "get",
@@ -108,6 +117,7 @@ func (c *FakeDiscovery) ServerGroups() (*metav1.APIGroupList, error) {
} }
// ServerVersion retrieves and parses the server's version.
func (c *FakeDiscovery) ServerVersion() (*version.Info, error) { func (c *FakeDiscovery) ServerVersion() (*version.Info, error) {
action := testing.ActionImpl{} action := testing.ActionImpl{}
action.Verb = "get" action.Verb = "get"
@@ -122,10 +132,13 @@ func (c *FakeDiscovery) ServerVersion() (*version.Info, error) {
return &versionInfo, nil return &versionInfo, nil
} }
// OpenAPISchema retrieves and parses the swagger API schema the server supports.
func (c *FakeDiscovery) OpenAPISchema() (*openapi_v2.Document, error) { func (c *FakeDiscovery) OpenAPISchema() (*openapi_v2.Document, error) {
return &openapi_v2.Document{}, nil return &openapi_v2.Document{}, nil
} }
// RESTClient returns a RESTClient that is used to communicate with API server
// by this client implementation.
func (c *FakeDiscovery) RESTClient() restclient.Interface { func (c *FakeDiscovery) RESTClient() restclient.Interface {
return nil return nil
} }
+6 -2
View File
@@ -31,11 +31,11 @@ import (
func MatchesServerVersion(clientVersion apimachineryversion.Info, client DiscoveryInterface) error { func MatchesServerVersion(clientVersion apimachineryversion.Info, client DiscoveryInterface) error {
sVer, err := client.ServerVersion() sVer, err := client.ServerVersion()
if err != nil { if err != nil {
return fmt.Errorf("couldn't read version from server: %v\n", err) return fmt.Errorf("couldn't read version from server: %v", err)
} }
// GitVersion includes GitCommit and GitTreeState, but best to be safe? // GitVersion includes GitCommit and GitTreeState, but best to be safe?
if clientVersion.GitVersion != sVer.GitVersion || clientVersion.GitCommit != sVer.GitCommit || clientVersion.GitTreeState != sVer.GitTreeState { if clientVersion.GitVersion != sVer.GitVersion || clientVersion.GitCommit != sVer.GitCommit || clientVersion.GitTreeState != sVer.GitTreeState {
return fmt.Errorf("server version (%#v) differs from client version (%#v)!\n", sVer, clientVersion) return fmt.Errorf("server version (%#v) differs from client version (%#v)", sVer, clientVersion)
} }
return nil return nil
@@ -101,12 +101,15 @@ func FilteredBy(pred ResourcePredicate, rls []*metav1.APIResourceList) []*metav1
return result return result
} }
// ResourcePredicate has a method to check if a resource matches a given condition.
type ResourcePredicate interface { type ResourcePredicate interface {
Match(groupVersion string, r *metav1.APIResource) bool Match(groupVersion string, r *metav1.APIResource) bool
} }
// ResourcePredicateFunc returns true if it matches a resource based on a custom condition.
type ResourcePredicateFunc func(groupVersion string, r *metav1.APIResource) bool type ResourcePredicateFunc func(groupVersion string, r *metav1.APIResource) bool
// Match is a wrapper around ResourcePredicateFunc.
func (fn ResourcePredicateFunc) Match(groupVersion string, r *metav1.APIResource) bool { func (fn ResourcePredicateFunc) Match(groupVersion string, r *metav1.APIResource) bool {
return fn(groupVersion, r) return fn(groupVersion, r)
} }
@@ -116,6 +119,7 @@ type SupportsAllVerbs struct {
Verbs []string Verbs []string
} }
// Match checks if a resource contains all the given verbs.
func (p SupportsAllVerbs) Match(groupVersion string, r *metav1.APIResource) bool { func (p SupportsAllVerbs) Match(groupVersion string, r *metav1.APIResource) bool {
return sets.NewString([]string(r.Verbs)...).HasAll(p.Verbs...) return sets.NewString([]string(r.Verbs)...).HasAll(p.Verbs...)
} }
+1 -1
View File
@@ -179,7 +179,7 @@ func (c *ExpirationCache) Delete(obj interface{}) error {
func (c *ExpirationCache) Replace(list []interface{}, resourceVersion string) error { func (c *ExpirationCache) Replace(list []interface{}, resourceVersion string) error {
c.expirationLock.Lock() c.expirationLock.Lock()
defer c.expirationLock.Unlock() defer c.expirationLock.Unlock()
items := map[string]interface{}{} items := make(map[string]interface{}, len(list))
ts := c.clock.Now() ts := c.clock.Now()
for _, item := range list { for _, item := range list {
key, err := c.keyFunc(item) key, err := c.keyFunc(item)
+1 -1
View File
@@ -297,7 +297,7 @@ func (f *FIFO) Pop(process PopProcessFunc) (interface{}, error) {
// after calling this function. f's queue is reset, too; upon return, it // after calling this function. f's queue is reset, too; upon return, it
// will contain the items in the map, in no particular order. // will contain the items in the map, in no particular order.
func (f *FIFO) Replace(list []interface{}, resourceVersion string) error { func (f *FIFO) Replace(list []interface{}, resourceVersion string) error {
items := map[string]interface{}{} items := make(map[string]interface{}, len(list))
for _, item := range list { for _, item := range list {
key, err := f.keyFunc(item) key, err := f.keyFunc(item)
if err != nil { if err != nil {
+1 -1
View File
@@ -210,7 +210,7 @@ func (c *cache) GetByKey(key string) (item interface{}, exists bool, err error)
// 'c' takes ownership of the list, you should not reference the list again // 'c' takes ownership of the list, you should not reference the list again
// after calling this function. // after calling this function.
func (c *cache) Replace(list []interface{}, resourceVersion string) error { func (c *cache) Replace(list []interface{}, resourceVersion string) error {
items := map[string]interface{}{} items := make(map[string]interface{}, len(list))
for _, item := range list { for _, item := range list {
key, err := c.keyFunc(item) key, err := c.keyFunc(item)
if err != nil { if err != nil {
@@ -724,6 +724,10 @@ func genComment(out io.Writer, lines []string, indent string) {
lines = lines[:l-1] lines = lines[:l-1]
} }
for _, c := range lines { for _, c := range lines {
if len(c) == 0 {
fmt.Fprintf(out, "%s//\n", indent) // avoid trailing whitespace
continue
}
fmt.Fprintf(out, "%s// %s\n", indent, c) fmt.Fprintf(out, "%s// %s\n", indent, c)
} }
} }
+4 -4
View File
@@ -50,7 +50,7 @@ shift 4
# To support running this script from anywhere, we have to first cd into this directory # To support running this script from anywhere, we have to first cd into this directory
# so we can install the tools. # so we can install the tools.
cd $(dirname "${0}") cd $(dirname "${0}")
go install ./cmd/{defaulter-gen,client-gen,lister-gen,informer-gen,deepcopy-gen} go install ${GOFLAGS:-} ./cmd/{defaulter-gen,client-gen,lister-gen,informer-gen,deepcopy-gen}
) )
function codegen::join() { local IFS="$1"; shift; echo "$*"; } function codegen::join() { local IFS="$1"; shift; echo "$*"; }
@@ -72,8 +72,8 @@ if [ "${GENS}" = "all" ] || grep -qw "deepcopy" <<<"${GENS}"; then
fi fi
if [ "${GENS}" = "all" ] || grep -qw "client" <<<"${GENS}"; then if [ "${GENS}" = "all" ] || grep -qw "client" <<<"${GENS}"; then
echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/clientset" echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}"
${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_VERSIONED:-versioned} --input-base "" --input $(codegen::join , "${FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/clientset "$@" ${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_VERSIONED:-versioned} --input-base "" --input $(codegen::join , "${FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset} "$@"
fi fi
if [ "${GENS}" = "all" ] || grep -qw "lister" <<<"${GENS}"; then if [ "${GENS}" = "all" ] || grep -qw "lister" <<<"${GENS}"; then
@@ -85,7 +85,7 @@ if [ "${GENS}" = "all" ] || grep -qw "informer" <<<"${GENS}"; then
echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers" echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers"
${GOPATH}/bin/informer-gen \ ${GOPATH}/bin/informer-gen \
--input-dirs $(codegen::join , "${FQ_APIS[@]}") \ --input-dirs $(codegen::join , "${FQ_APIS[@]}") \
--versioned-clientset-package ${OUTPUT_PKG}/clientset/${CLIENTSET_NAME_VERSIONED:-versioned} \ --versioned-clientset-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_VERSIONED:-versioned} \
--listers-package ${OUTPUT_PKG}/listers \ --listers-package ${OUTPUT_PKG}/listers \
--output-package ${OUTPUT_PKG}/informers \ --output-package ${OUTPUT_PKG}/informers \
"$@" "$@"
+6 -6
View File
@@ -47,7 +47,7 @@ EXT_APIS_PKG="$4"
GROUPS_WITH_VERSIONS="$5" GROUPS_WITH_VERSIONS="$5"
shift 5 shift 5
go install ./$(dirname "${0}")/cmd/{defaulter-gen,conversion-gen,client-gen,lister-gen,informer-gen,deepcopy-gen} go install ${GOFLAGS:-} ./$(dirname "${0}")/cmd/{defaulter-gen,conversion-gen,client-gen,lister-gen,informer-gen,deepcopy-gen}
function codegen::join() { local IFS="$1"; shift; echo "$*"; } function codegen::join() { local IFS="$1"; shift; echo "$*"; }
# enumerate group versions # enumerate group versions
@@ -85,11 +85,11 @@ if [ "${GENS}" = "all" ] || grep -qw "conversion" <<<"${GENS}"; then
fi fi
if [ "${GENS}" = "all" ] || grep -qw "client" <<<"${GENS}"; then if [ "${GENS}" = "all" ] || grep -qw "client" <<<"${GENS}"; then
echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/clientset" echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}"
if [ -n "${INT_APIS_PKG}" ]; then if [ -n "${INT_APIS_PKG}" ]; then
${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_INTERNAL:-internalversion} --input-base "" --input $(codegen::join , $(printf '%s/ ' "${INT_FQ_APIS[@]}")) --output-package ${OUTPUT_PKG}/clientset "$@" ${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_INTERNAL:-internalversion} --input-base "" --input $(codegen::join , $(printf '%s/ ' "${INT_FQ_APIS[@]}")) --output-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset} "$@"
fi fi
${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_VERSIONED:-versioned} --input-base "" --input $(codegen::join , "${EXT_FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/clientset "$@" ${GOPATH}/bin/client-gen --clientset-name ${CLIENTSET_NAME_VERSIONED:-versioned} --input-base "" --input $(codegen::join , "${EXT_FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset} "$@"
fi fi
if [ "${GENS}" = "all" ] || grep -qw "lister" <<<"${GENS}"; then if [ "${GENS}" = "all" ] || grep -qw "lister" <<<"${GENS}"; then
@@ -101,8 +101,8 @@ if [ "${GENS}" = "all" ] || grep -qw "informer" <<<"${GENS}"; then
echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers" echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers"
${GOPATH}/bin/informer-gen \ ${GOPATH}/bin/informer-gen \
--input-dirs $(codegen::join , "${ALL_FQ_APIS[@]}") \ --input-dirs $(codegen::join , "${ALL_FQ_APIS[@]}") \
--versioned-clientset-package ${OUTPUT_PKG}/clientset/${CLIENTSET_NAME_VERSIONED:-versioned} \ --versioned-clientset-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_VERSIONED:-versioned} \
--internal-clientset-package ${OUTPUT_PKG}/clientset/${CLIENTSET_NAME_INTERNAL:-internalversion} \ --internal-clientset-package ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_INTERNAL:-internalversion} \
--listers-package ${OUTPUT_PKG}/listers \ --listers-package ${OUTPUT_PKG}/listers \
--output-package ${OUTPUT_PKG}/informers \ --output-package ${OUTPUT_PKG}/informers \
"$@" "$@"