vendor: update buildkit to 2943a0838

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2020-09-19 22:02:46 -07:00
parent 92fb995505
commit c41b006be1
644 changed files with 62146 additions and 21194 deletions

View File

@@ -17,10 +17,7 @@ reviewers:
- saad-ali
- janetkuo
- tallclair
- eparis
- dims
- hongchaodeng
- krousey
- cjcullen
- david-mcmahon
- goltermann

View File

@@ -18,6 +18,7 @@ package errors
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"reflect"
@@ -29,14 +30,6 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
)
const (
// StatusTooManyRequests means the server experienced too many requests within a
// given window and that the client must wait to perform the action again.
// DEPRECATED: please use http.StatusTooManyRequests, this will be removed in
// the future version.
StatusTooManyRequests = http.StatusTooManyRequests
)
// StatusError is an error intended for consumption by a REST API server; it can also be
// reconstructed by clients from a REST response. Public to allow easy type switches.
type StatusError struct {
@@ -70,6 +63,28 @@ func (e *StatusError) DebugError() (string, []interface{}) {
return "server response object: %#v", []interface{}{e.ErrStatus}
}
// HasStatusCause returns true if the provided error has a details cause
// with the provided type name.
func HasStatusCause(err error, name metav1.CauseType) bool {
_, ok := StatusCause(err, name)
return ok
}
// StatusCause returns the named cause from the provided error if it exists and
// the error is of the type APIStatus. Otherwise it returns false.
func StatusCause(err error, name metav1.CauseType) (metav1.StatusCause, bool) {
apierr, ok := err.(APIStatus)
if !ok || apierr == nil || apierr.Status().Details == nil {
return metav1.StatusCause{}, false
}
for _, cause := range apierr.Status().Details.Causes {
if cause.Type == name {
return cause, true
}
}
return metav1.StatusCause{}, false
}
// UnexpectedObjectError can be returned by FromObject if it's passed a non-status object.
type UnexpectedObjectError struct {
Object runtime.Object
@@ -201,6 +216,7 @@ func NewApplyConflict(causes []metav1.StatusCause, message string) *StatusError
}
// NewGone returns an error indicating the item no longer available at the server and no forwarding address is known.
// DEPRECATED: Please use NewResourceExpired instead.
func NewGone(message string) *StatusError {
return &StatusError{metav1.Status{
Status: metav1.StatusFailure,
@@ -460,127 +476,141 @@ func NewGenericServerResponse(code int, verb string, qualifiedResource schema.Gr
}
// IsNotFound returns true if the specified error was created by NewNotFound.
// It supports wrapped errors.
func IsNotFound(err error) bool {
return ReasonForError(err) == metav1.StatusReasonNotFound
}
// IsAlreadyExists determines if the err is an error which indicates that a specified resource already exists.
// It supports wrapped errors.
func IsAlreadyExists(err error) bool {
return ReasonForError(err) == metav1.StatusReasonAlreadyExists
}
// IsConflict determines if the err is an error which indicates the provided update conflicts.
// It supports wrapped errors.
func IsConflict(err error) bool {
return ReasonForError(err) == metav1.StatusReasonConflict
}
// IsInvalid determines if the err is an error which indicates the provided resource is not valid.
// It supports wrapped errors.
func IsInvalid(err error) bool {
return ReasonForError(err) == metav1.StatusReasonInvalid
}
// IsGone is true if the error indicates the requested resource is no longer available.
// It supports wrapped errors.
func IsGone(err error) bool {
return ReasonForError(err) == metav1.StatusReasonGone
}
// IsResourceExpired is true if the error indicates the resource has expired and the current action is
// no longer possible.
// It supports wrapped errors.
func IsResourceExpired(err error) bool {
return ReasonForError(err) == metav1.StatusReasonExpired
}
// IsNotAcceptable determines if err is an error which indicates that the request failed due to an invalid Accept header
// It supports wrapped errors.
func IsNotAcceptable(err error) bool {
return ReasonForError(err) == metav1.StatusReasonNotAcceptable
}
// IsUnsupportedMediaType determines if err is an error which indicates that the request failed due to an invalid Content-Type header
// It supports wrapped errors.
func IsUnsupportedMediaType(err error) bool {
return ReasonForError(err) == metav1.StatusReasonUnsupportedMediaType
}
// IsMethodNotSupported determines if the err is an error which indicates the provided action could not
// be performed because it is not supported by the server.
// It supports wrapped errors.
func IsMethodNotSupported(err error) bool {
return ReasonForError(err) == metav1.StatusReasonMethodNotAllowed
}
// IsServiceUnavailable is true if the error indicates the underlying service is no longer available.
// It supports wrapped errors.
func IsServiceUnavailable(err error) bool {
return ReasonForError(err) == metav1.StatusReasonServiceUnavailable
}
// IsBadRequest determines if err is an error which indicates that the request is invalid.
// It supports wrapped errors.
func IsBadRequest(err error) bool {
return ReasonForError(err) == metav1.StatusReasonBadRequest
}
// IsUnauthorized determines if err is an error which indicates that the request is unauthorized and
// requires authentication by the user.
// It supports wrapped errors.
func IsUnauthorized(err error) bool {
return ReasonForError(err) == metav1.StatusReasonUnauthorized
}
// IsForbidden determines if err is an error which indicates that the request is forbidden and cannot
// be completed as requested.
// It supports wrapped errors.
func IsForbidden(err error) bool {
return ReasonForError(err) == metav1.StatusReasonForbidden
}
// IsTimeout determines if err is an error which indicates that request times out due to long
// processing.
// It supports wrapped errors.
func IsTimeout(err error) bool {
return ReasonForError(err) == metav1.StatusReasonTimeout
}
// IsServerTimeout determines if err is an error which indicates that the request needs to be retried
// by the client.
// It supports wrapped errors.
func IsServerTimeout(err error) bool {
return ReasonForError(err) == metav1.StatusReasonServerTimeout
}
// IsInternalError determines if err is an error which indicates an internal server error.
// It supports wrapped errors.
func IsInternalError(err error) bool {
return ReasonForError(err) == metav1.StatusReasonInternalError
}
// IsTooManyRequests determines if err is an error which indicates that there are too many requests
// that the server cannot handle.
// It supports wrapped errors.
func IsTooManyRequests(err error) bool {
if ReasonForError(err) == metav1.StatusReasonTooManyRequests {
return true
}
switch t := err.(type) {
case APIStatus:
return t.Status().Code == http.StatusTooManyRequests
if status := APIStatus(nil); errors.As(err, &status) {
return status.Status().Code == http.StatusTooManyRequests
}
return false
}
// IsRequestEntityTooLargeError determines if err is an error which indicates
// the request entity is too large.
// It supports wrapped errors.
func IsRequestEntityTooLargeError(err error) bool {
if ReasonForError(err) == metav1.StatusReasonRequestEntityTooLarge {
return true
}
switch t := err.(type) {
case APIStatus:
return t.Status().Code == http.StatusRequestEntityTooLarge
if status := APIStatus(nil); errors.As(err, &status) {
return status.Status().Code == http.StatusRequestEntityTooLarge
}
return false
}
// IsUnexpectedServerError returns true if the server response was not in the expected API format,
// and may be the result of another HTTP actor.
// It supports wrapped errors.
func IsUnexpectedServerError(err error) bool {
switch t := err.(type) {
case APIStatus:
if d := t.Status().Details; d != nil {
for _, cause := range d.Causes {
if cause.Type == metav1.CauseTypeUnexpectedServerResponse {
return true
}
if status := APIStatus(nil); errors.As(err, &status) && status.Status().Details != nil {
for _, cause := range status.Status().Details.Causes {
if cause.Type == metav1.CauseTypeUnexpectedServerResponse {
return true
}
}
}
@@ -588,38 +618,37 @@ func IsUnexpectedServerError(err error) bool {
}
// IsUnexpectedObjectError determines if err is due to an unexpected object from the master.
// It supports wrapped errors.
func IsUnexpectedObjectError(err error) bool {
_, ok := err.(*UnexpectedObjectError)
return err != nil && ok
uoe := &UnexpectedObjectError{}
return err != nil && errors.As(err, &uoe)
}
// SuggestsClientDelay returns true if this error suggests a client delay as well as the
// suggested seconds to wait, or false if the error does not imply a wait. It does not
// address whether the error *should* be retried, since some errors (like a 3xx) may
// request delay without retry.
// It supports wrapped errors.
func SuggestsClientDelay(err error) (int, bool) {
switch t := err.(type) {
case APIStatus:
if t.Status().Details != nil {
switch t.Status().Reason {
// this StatusReason explicitly requests the caller to delay the action
case metav1.StatusReasonServerTimeout:
return int(t.Status().Details.RetryAfterSeconds), true
}
// If the client requests that we retry after a certain number of seconds
if t.Status().Details.RetryAfterSeconds > 0 {
return int(t.Status().Details.RetryAfterSeconds), true
}
if t := APIStatus(nil); errors.As(err, &t) && t.Status().Details != nil {
switch t.Status().Reason {
// this StatusReason explicitly requests the caller to delay the action
case metav1.StatusReasonServerTimeout:
return int(t.Status().Details.RetryAfterSeconds), true
}
// If the client requests that we retry after a certain number of seconds
if t.Status().Details.RetryAfterSeconds > 0 {
return int(t.Status().Details.RetryAfterSeconds), true
}
}
return 0, false
}
// ReasonForError returns the HTTP status for a particular error.
// It supports wrapped errors.
func ReasonForError(err error) metav1.StatusReason {
switch t := err.(type) {
case APIStatus:
return t.Status().Reason
if status := APIStatus(nil); errors.As(err, &status) {
return status.Status().Reason
}
return metav1.StatusReasonUnknown
}

View File

@@ -14,14 +14,8 @@ reviewers:
- gmarek
- janetkuo
- ncdc
- eparis
- dims
- krousey
- markturansky
- fabioy
- resouer
- david-mcmahon
- mfojtik
- jianhuiz
- feihujiang
- ghodss

101
vendor/k8s.io/apimachinery/pkg/api/meta/conditions.go generated vendored Normal file
View File

@@ -0,0 +1,101 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package meta
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// SetStatusCondition sets the corresponding condition in conditions to newCondition.
// conditions must be non-nil.
// 1. if the condition of the specified type already exists (all fields of the existing condition are updated to
// newCondition, LastTransitionTime is set to now if the new status differs from the old status)
// 2. if a condition of the specified type does not exist (LastTransitionTime is set to now() if unset, and newCondition is appended)
func SetStatusCondition(conditions *[]metav1.Condition, newCondition metav1.Condition) {
if conditions == nil {
return
}
existingCondition := FindStatusCondition(*conditions, newCondition.Type)
if existingCondition == nil {
if newCondition.LastTransitionTime.IsZero() {
newCondition.LastTransitionTime = metav1.NewTime(time.Now())
}
*conditions = append(*conditions, newCondition)
return
}
if existingCondition.Status != newCondition.Status {
existingCondition.Status = newCondition.Status
if !newCondition.LastTransitionTime.IsZero() {
existingCondition.LastTransitionTime = newCondition.LastTransitionTime
} else {
existingCondition.LastTransitionTime = metav1.NewTime(time.Now())
}
}
existingCondition.Reason = newCondition.Reason
existingCondition.Message = newCondition.Message
}
// RemoveStatusCondition removes the corresponding conditionType from conditions.
// conditions must be non-nil.
func RemoveStatusCondition(conditions *[]metav1.Condition, conditionType string) {
if conditions == nil {
return
}
newConditions := make([]metav1.Condition, 0, len(*conditions)-1)
for _, condition := range *conditions {
if condition.Type != conditionType {
newConditions = append(newConditions, condition)
}
}
*conditions = newConditions
}
// FindStatusCondition finds the conditionType in conditions.
func FindStatusCondition(conditions []metav1.Condition, conditionType string) *metav1.Condition {
for i := range conditions {
if conditions[i].Type == conditionType {
return &conditions[i]
}
}
return nil
}
// IsStatusConditionTrue returns true when the conditionType is present and set to `metav1.ConditionTrue`
func IsStatusConditionTrue(conditions []metav1.Condition, conditionType string) bool {
return IsStatusConditionPresentAndEqual(conditions, conditionType, metav1.ConditionTrue)
}
// IsStatusConditionFalse returns true when the conditionType is present and set to `metav1.ConditionFalse`
func IsStatusConditionFalse(conditions []metav1.Condition, conditionType string) bool {
return IsStatusConditionPresentAndEqual(conditions, conditionType, metav1.ConditionFalse)
}
// IsStatusConditionPresentAndEqual returns true when conditionType is present and equal to status.
func IsStatusConditionPresentAndEqual(conditions []metav1.Condition, conditionType string, status metav1.ConditionStatus) bool {
for _, condition := range conditions {
if condition.Type == conditionType {
return condition.Status == status
}
}
return false
}

View File

@@ -25,7 +25,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog"
"k8s.io/klog/v2"
)
// errNotList is returned when an object implements the Object style interfaces but not the List style

View File

@@ -9,10 +9,5 @@ reviewers:
- mikedanese
- saad-ali
- janetkuo
- tallclair
- eparis
- jbeda
- xiang90
- mbohlool
- david-mcmahon
- goltermann

View File

@@ -36,7 +36,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func (m *Quantity) Reset() { *m = Quantity{} }
func (*Quantity) ProtoMessage() {}

View File

@@ -37,12 +37,8 @@ var (
big1024 = big.NewInt(1024)
// Commonly needed inf.Dec values-- treat as read only!
decZero = inf.NewDec(0, 0)
decOne = inf.NewDec(1, 0)
decMinusOne = inf.NewDec(-1, 0)
decThousand = inf.NewDec(1000, 0)
dec1024 = inf.NewDec(1024, 0)
decMinus1024 = inf.NewDec(-1024, 0)
decZero = inf.NewDec(0, 0)
decOne = inf.NewDec(1, 0)
// Largest (in magnitude) number allowed.
maxAllowed = infDecAmount{inf.NewDec((1<<63)-1, 0)} // == max int64

View File

@@ -634,6 +634,11 @@ func (q Quantity) MarshalJSON() ([]byte, error) {
return result, nil
}
// ToUnstructured implements the value.UnstructuredConverter interface.
func (q Quantity) ToUnstructured() interface{} {
return q.String()
}
// UnmarshalJSON implements the json.Unmarshaller interface.
// TODO: Remove support for leading/trailing whitespace
func (q *Quantity) UnmarshalJSON(value []byte) error {

View File

@@ -25,9 +25,7 @@ reviewers:
- krousey
- mml
- mbohlool
- david-mcmahon
- therc
- mqliang
- kevin-wangzefeng
- jianhuiz
- feihujiang

View File

@@ -22,7 +22,7 @@ import (
// IsControlledBy checks if the object has a controllerRef set to the given owner
func IsControlledBy(obj Object, owner Object) bool {
ref := GetControllerOf(obj)
ref := GetControllerOfNoCopy(obj)
if ref == nil {
return false
}
@@ -31,9 +31,20 @@ func IsControlledBy(obj Object, owner Object) bool {
// GetControllerOf returns a pointer to a copy of the controllerRef if controllee has a controller
func GetControllerOf(controllee Object) *OwnerReference {
for _, ref := range controllee.GetOwnerReferences() {
if ref.Controller != nil && *ref.Controller {
return &ref
ref := GetControllerOfNoCopy(controllee)
if ref == nil {
return nil
}
cp := *ref
return &cp
}
// GetControllerOf returns a pointer to the controllerRef if controllee has a controller
func GetControllerOfNoCopy(controllee Object) *OwnerReference {
refs := controllee.GetOwnerReferences()
for i := range refs {
if refs[i].Controller != nil && *refs[i].Controller {
return &refs[i]
}
}
return nil

View File

@@ -18,6 +18,7 @@ package v1
import (
"fmt"
"net/url"
"strconv"
"strings"
@@ -25,63 +26,10 @@ import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
)
func AddConversionFuncs(scheme *runtime.Scheme) error {
return scheme.AddConversionFuncs(
Convert_v1_TypeMeta_To_v1_TypeMeta,
Convert_v1_ListMeta_To_v1_ListMeta,
Convert_intstr_IntOrString_To_intstr_IntOrString,
Convert_Pointer_v1_Duration_To_v1_Duration,
Convert_v1_Duration_To_Pointer_v1_Duration,
Convert_Slice_string_To_v1_Time,
Convert_v1_Time_To_v1_Time,
Convert_v1_MicroTime_To_v1_MicroTime,
Convert_resource_Quantity_To_resource_Quantity,
Convert_string_To_labels_Selector,
Convert_labels_Selector_To_string,
Convert_string_To_fields_Selector,
Convert_fields_Selector_To_string,
Convert_Pointer_bool_To_bool,
Convert_bool_To_Pointer_bool,
Convert_Pointer_string_To_string,
Convert_string_To_Pointer_string,
Convert_Pointer_int64_To_int,
Convert_int_To_Pointer_int64,
Convert_Pointer_int32_To_int32,
Convert_int32_To_Pointer_int32,
Convert_Pointer_int64_To_int64,
Convert_int64_To_Pointer_int64,
Convert_Pointer_float64_To_float64,
Convert_float64_To_Pointer_float64,
Convert_Map_string_To_string_To_v1_LabelSelector,
Convert_v1_LabelSelector_To_Map_string_To_string,
Convert_Slice_string_To_Slice_int32,
Convert_Slice_string_To_v1_DeletionPropagation,
Convert_Slice_string_To_v1_IncludeObjectPolicy,
)
}
func Convert_Pointer_float64_To_float64(in **float64, out *float64, s conversion.Scope) error {
if *in == nil {
*out = 0
@@ -194,12 +142,33 @@ func Convert_v1_ListMeta_To_v1_ListMeta(in, out *ListMeta, s conversion.Scope) e
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_v1_DeleteOptions_To_v1_DeleteOptions(in, out *DeleteOptions, s conversion.Scope) error {
*out = *in
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_intstr_IntOrString_To_intstr_IntOrString(in, out *intstr.IntOrString, s conversion.Scope) error {
*out = *in
return nil
}
func Convert_Pointer_intstr_IntOrString_To_intstr_IntOrString(in **intstr.IntOrString, out *intstr.IntOrString, s conversion.Scope) error {
if *in == nil {
*out = intstr.IntOrString{} // zero value
return nil
}
*out = **in // copy
return nil
}
func Convert_intstr_IntOrString_To_Pointer_intstr_IntOrString(in *intstr.IntOrString, out **intstr.IntOrString, s conversion.Scope) error {
temp := *in // copy
*out = &temp
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_v1_Time_To_v1_Time(in *Time, out *Time, s conversion.Scope) error {
// Cannot deep copy these, because time.Time has unexported fields.
@@ -230,14 +199,30 @@ func Convert_v1_Duration_To_Pointer_v1_Duration(in *Duration, out **Duration, s
}
// Convert_Slice_string_To_v1_Time allows converting a URL query parameter value
func Convert_Slice_string_To_v1_Time(input *[]string, out *Time, s conversion.Scope) error {
func Convert_Slice_string_To_v1_Time(in *[]string, out *Time, s conversion.Scope) error {
str := ""
if len(*input) > 0 {
str = (*input)[0]
if len(*in) > 0 {
str = (*in)[0]
}
return out.UnmarshalQueryParameter(str)
}
func Convert_Slice_string_To_Pointer_v1_Time(in *[]string, out **Time, s conversion.Scope) error {
if in == nil {
return nil
}
str := ""
if len(*in) > 0 {
str = (*in)[0]
}
temp := Time{}
if err := temp.UnmarshalQueryParameter(str); err != nil {
return err
}
*out = &temp
return nil
}
func Convert_string_To_labels_Selector(in *string, out *labels.Selector, s conversion.Scope) error {
selector, err := labels.Parse(*in)
if err != nil {
@@ -310,20 +295,61 @@ func Convert_Slice_string_To_Slice_int32(in *[]string, out *[]int32, s conversio
return nil
}
// Convert_Slice_string_To_v1_DeletionPropagation allows converting a URL query parameter propagationPolicy
func Convert_Slice_string_To_v1_DeletionPropagation(input *[]string, out *DeletionPropagation, s conversion.Scope) error {
if len(*input) > 0 {
*out = DeletionPropagation((*input)[0])
// Convert_Slice_string_To_Pointer_v1_DeletionPropagation allows converting a URL query parameter propagationPolicy
func Convert_Slice_string_To_Pointer_v1_DeletionPropagation(in *[]string, out **DeletionPropagation, s conversion.Scope) error {
var str string
if len(*in) > 0 {
str = (*in)[0]
} else {
*out = ""
str = ""
}
temp := DeletionPropagation(str)
*out = &temp
return nil
}
// Convert_Slice_string_To_v1_IncludeObjectPolicy allows converting a URL query parameter value
func Convert_Slice_string_To_v1_IncludeObjectPolicy(input *[]string, out *IncludeObjectPolicy, s conversion.Scope) error {
if len(*input) > 0 {
*out = IncludeObjectPolicy((*input)[0])
func Convert_Slice_string_To_v1_IncludeObjectPolicy(in *[]string, out *IncludeObjectPolicy, s conversion.Scope) error {
if len(*in) > 0 {
*out = IncludeObjectPolicy((*in)[0])
}
return nil
}
// Convert_url_Values_To_v1_DeleteOptions allows converting a URL to DeleteOptions.
func Convert_url_Values_To_v1_DeleteOptions(in *url.Values, out *DeleteOptions, s conversion.Scope) error {
if err := autoConvert_url_Values_To_v1_DeleteOptions(in, out, s); err != nil {
return err
}
uid := types.UID("")
if values, ok := (*in)["uid"]; ok && len(values) > 0 {
uid = types.UID(values[0])
}
resourceVersion := ""
if values, ok := (*in)["resourceVersion"]; ok && len(values) > 0 {
resourceVersion = values[0]
}
if len(uid) > 0 || len(resourceVersion) > 0 {
if out.Preconditions == nil {
out.Preconditions = &Preconditions{}
}
if len(uid) > 0 {
out.Preconditions.UID = &uid
}
if len(resourceVersion) > 0 {
out.Preconditions.ResourceVersion = &resourceVersion
}
}
return nil
}
// Convert_Slice_string_To_v1_ResourceVersionMatch allows converting a URL query parameter to ResourceVersionMatch
func Convert_Slice_string_To_v1_ResourceVersionMatch(in *[]string, out *ResourceVersionMatch, s conversion.Scope) error {
if len(*in) > 0 {
*out = ResourceVersionMatch((*in)[0])
}
return nil
}

View File

@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// +k8s:conversion-gen=false
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta

View File

@@ -49,6 +49,11 @@ func (d Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(d.Duration.String())
}
// ToUnstructured implements the value.UnstructuredConverter interface.
func (d Duration) ToUnstructured() interface{} {
return d.Duration.String()
}
// OpenAPISchemaType is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
//

File diff suppressed because it is too large Load Diff

View File

@@ -134,6 +134,73 @@ message APIVersions {
repeated ServerAddressByClientCIDR serverAddressByClientCIDRs = 2;
}
// Condition contains details for one aspect of the current state of this API Resource.
// ---
// This struct is intended for direct use as an array at the field path .status.conditions. For example,
// type FooStatus struct{
// // Represents the observations of a foo's current state.
// // Known .status.conditions.type are: "Available", "Progressing", and "Degraded"
// // +patchMergeKey=type
// // +patchStrategy=merge
// // +listType=map
// // +listMapKey=type
// Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
//
// // other fields
// }
message Condition {
// type of condition in CamelCase or in foo.example.com/CamelCase.
// ---
// Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
// useful (see .node.status.conditions), the ability to deconflict is important.
// The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$`
// +kubebuilder:validation:MaxLength=316
optional string type = 1;
// status of the condition, one of True, False, Unknown.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=True;False;Unknown
optional string status = 2;
// observedGeneration represents the .metadata.generation that the condition was set based upon.
// For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
// with respect to the current state of the instance.
// +optional
// +kubebuilder:validation:Minimum=0
optional int64 observedGeneration = 3;
// lastTransitionTime is the last time the condition transitioned from one status to another.
// This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Format=date-time
optional Time lastTransitionTime = 4;
// reason contains a programmatic identifier indicating the reason for the condition's last transition.
// Producers of specific condition types may define expected values and meanings for this field,
// and whether the values are considered a guaranteed API.
// The value should be a CamelCase string.
// This field may not be empty.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=1024
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$`
optional string reason = 5;
// message is a human readable message indicating details about the transition.
// This may be an empty string.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=32768
optional string message = 6;
}
// CreateOptions may be provided when creating an API object.
message CreateOptions {
// When present, indicates that modifications should not be
@@ -163,6 +230,7 @@ message DeleteOptions {
// Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be
// returned.
// +k8s:conversion-gen=false
// +optional
optional Preconditions preconditions = 2;
@@ -223,6 +291,7 @@ message ExportOptions {
// If a key maps to an empty Fields value, the field that key represents is part of the set.
//
// The exact format is defined in sigs.k8s.io/structured-merge-diff
// +protobuf.options.(gogoproto.goproto_stringer)=false
message FieldsV1 {
// Raw is the underlying serialization of this object.
optional bytes Raw = 1;
@@ -230,10 +299,12 @@ message FieldsV1 {
// GetOptions is the standard query options to the standard REST get call.
message GetOptions {
// When specified:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
// resourceVersion sets a constraint on what resource versions a request may be served from.
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for
// details.
//
// Defaults to unset
// +optional
optional string resourceVersion = 1;
}
@@ -416,21 +487,27 @@ message ListOptions {
// If this is not a watch, this field is ignored.
// If the feature gate WatchBookmarks is not enabled in apiserver,
// this field is ignored.
//
// This field is beta.
//
// +optional
optional bool allowWatchBookmarks = 9;
// When specified with a watch call, shows changes that occur after that particular version of a resource.
// Defaults to changes from the beginning of history.
// When specified for list:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
// resourceVersion sets a constraint on what resource versions a request may be served from.
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for
// details.
//
// Defaults to unset
// +optional
optional string resourceVersion = 4;
// resourceVersionMatch determines how resourceVersion is applied to list calls.
// It is highly recommended that resourceVersionMatch be set for list calls where
// resourceVersion is set
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for
// details.
//
// Defaults to unset
// +optional
optional string resourceVersionMatch = 10;
// Timeout for the list/watch call.
// This limits the duration of the call, regardless of any activity or inactivity.
// +optional
@@ -548,7 +625,7 @@ message ObjectMeta {
// +optional
optional string generateName = 2;
// Namespace defines the space within each name must be unique. An empty namespace is
// Namespace defines the space within which each name must be unique. An empty namespace is
// equivalent to the "default" namespace, but "default" is the canonical representation.
// Not all objects are required to be scoped to a namespace - the value of this field for
// those objects will be empty.
@@ -663,6 +740,15 @@ message ObjectMeta {
// is an identifier for the responsible component that will remove the entry
// from the list. If the deletionTimestamp of the object is non-nil, entries
// in this list can only be removed.
// Finalizers may be processed and removed in any order. Order is NOT enforced
// because it introduces significant risk of stuck finalizers.
// finalizers is a shared field, any actor with permission can reorder it.
// If the finalizer list is processed in order, then this can lead to a situation
// in which the component responsible for the first finalizer in the list is
// waiting for a signal (field value, external system, or other) produced by a
// component responsible for a finalizer later in the list, resulting in a deadlock.
// Without enforced ordering finalizers are free to order amongst themselves and
// are not vulnerable to ordering changes in the list.
// +optional
// +patchStrategy=merge
repeated string finalizers = 14;

View File

@@ -252,7 +252,9 @@ func ResetObjectMetaForStatus(meta, existingMeta Object) {
meta.SetAnnotations(existingMeta.GetAnnotations())
meta.SetFinalizers(existingMeta.GetFinalizers())
meta.SetOwnerReferences(existingMeta.GetOwnerReferences())
meta.SetManagedFields(existingMeta.GetManagedFields())
// managedFields must be preserved since it's been modified to
// track changed fields in the status update.
//meta.SetManagedFields(existingMeta.GetManagedFields())
}
// MarshalJSON implements json.Marshaler

View File

@@ -156,7 +156,9 @@ func (meta *ObjectMeta) GetDeletionTimestamp() *Time { return meta.DeletionTimes
func (meta *ObjectMeta) SetDeletionTimestamp(deletionTimestamp *Time) {
meta.DeletionTimestamp = deletionTimestamp
}
func (meta *ObjectMeta) GetDeletionGracePeriodSeconds() *int64 { return meta.DeletionGracePeriodSeconds }
func (meta *ObjectMeta) GetDeletionGracePeriodSeconds() *int64 {
return meta.DeletionGracePeriodSeconds
}
func (meta *ObjectMeta) SetDeletionGracePeriodSeconds(deletionGracePeriodSeconds *int64) {
meta.DeletionGracePeriodSeconds = deletionGracePeriodSeconds
}

View File

@@ -25,6 +25,13 @@ import (
// GroupName is the group name for this API.
const GroupName = "meta.k8s.io"
var (
// localSchemeBuilder is used to make compiler happy for autogenerated
// conversions. However, it's not used.
schemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &schemeBuilder
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
@@ -40,6 +47,22 @@ func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// scheme is the registry for the common types that adhere to the meta v1 API spec.
var scheme = runtime.NewScheme()
// ParameterCodec knows about query parameters used with the meta v1 API spec.
var ParameterCodec = runtime.NewParameterCodec(scheme)
var optionsTypes = []runtime.Object{
&ListOptions{},
&ExportOptions{},
&GetOptions{},
&DeleteOptions{},
&CreateOptions{},
&UpdateOptions{},
&PatchOptions{},
}
// AddToGroupVersion registers common meta types into schemas.
func AddToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion) {
scheme.AddKnownTypeWithName(groupVersion.WithKind(WatchEventKind), &WatchEvent{})
@@ -48,21 +71,7 @@ func AddToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion)
&InternalEvent{},
)
// Supports legacy code paths, most callers should use metav1.ParameterCodec for now
scheme.AddKnownTypes(groupVersion,
&ListOptions{},
&ExportOptions{},
&GetOptions{},
&DeleteOptions{},
&CreateOptions{},
&UpdateOptions{},
&PatchOptions{},
)
utilruntime.Must(scheme.AddConversionFuncs(
Convert_v1_WatchEvent_To_watch_Event,
Convert_v1_InternalEvent_To_v1_WatchEvent,
Convert_watch_Event_To_v1_WatchEvent,
Convert_v1_WatchEvent_To_v1_InternalEvent,
))
scheme.AddKnownTypes(groupVersion, optionsTypes...)
// Register Unversioned types under their own special group
scheme.AddUnversionedTypes(Unversioned,
&Status{},
@@ -73,35 +82,11 @@ func AddToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion)
)
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
utilruntime.Must(AddConversionFuncs(scheme))
utilruntime.Must(RegisterDefaults(scheme))
}
// scheme is the registry for the common types that adhere to the meta v1 API spec.
var scheme = runtime.NewScheme()
// ParameterCodec knows about query parameters used with the meta v1 API spec.
var ParameterCodec = runtime.NewParameterCodec(scheme)
func init() {
scheme.AddUnversionedTypes(SchemeGroupVersion,
&ListOptions{},
&ExportOptions{},
&GetOptions{},
&DeleteOptions{},
&CreateOptions{},
&UpdateOptions{},
&PatchOptions{},
)
if err := AddMetaToScheme(scheme); err != nil {
panic(err)
}
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
utilruntime.Must(RegisterConversions(scheme))
utilruntime.Must(RegisterDefaults(scheme))
}
// AddMetaToScheme registers base meta types into schemas.
func AddMetaToScheme(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Table{},
@@ -110,7 +95,14 @@ func AddMetaToScheme(scheme *runtime.Scheme) error {
&PartialObjectMetadataList{},
)
return scheme.AddConversionFuncs(
Convert_Slice_string_To_v1_IncludeObjectPolicy,
)
return nil
}
func init() {
scheme.AddUnversionedTypes(SchemeGroupVersion, optionsTypes...)
utilruntime.Must(AddMetaToScheme(scheme))
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
utilruntime.Must(RegisterDefaults(scheme))
}

View File

@@ -153,6 +153,16 @@ func (t Time) MarshalJSON() ([]byte, error) {
return buf, nil
}
// ToUnstructured implements the value.UnstructuredConverter interface.
func (t Time) ToUnstructured() interface{} {
if t.IsZero() {
return nil
}
buf := make([]byte, 0, len(time.RFC3339))
buf = t.UTC().AppendFormat(buf, time.RFC3339)
return string(buf)
}
// OpenAPISchemaType is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
//

View File

@@ -135,7 +135,7 @@ type ObjectMeta struct {
// +optional
GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`
// Namespace defines the space within each name must be unique. An empty namespace is
// Namespace defines the space within which each name must be unique. An empty namespace is
// equivalent to the "default" namespace, but "default" is the canonical representation.
// Not all objects are required to be scoped to a namespace - the value of this field for
// those objects will be empty.
@@ -250,6 +250,15 @@ type ObjectMeta struct {
// is an identifier for the responsible component that will remove the entry
// from the list. If the deletionTimestamp of the object is non-nil, entries
// in this list can only be removed.
// Finalizers may be processed and removed in any order. Order is NOT enforced
// because it introduces significant risk of stuck finalizers.
// finalizers is a shared field, any actor with permission can reorder it.
// If the finalizer list is processed in order, then this can lead to a situation
// in which the component responsible for the first finalizer in the list is
// waiting for a signal (field value, external system, or other) produced by a
// component responsible for a finalizer later in the list, resulting in a deadlock.
// Without enforced ordering finalizers are free to order amongst themselves and
// are not vulnerable to ordering changes in the list.
// +optional
// +patchStrategy=merge
Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`
@@ -313,6 +322,7 @@ type OwnerReference struct {
BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"`
}
// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ListOptions is the query options to a standard REST list call.
@@ -342,20 +352,26 @@ type ListOptions struct {
// If this is not a watch, this field is ignored.
// If the feature gate WatchBookmarks is not enabled in apiserver,
// this field is ignored.
//
// This field is beta.
//
// +optional
AllowWatchBookmarks bool `json:"allowWatchBookmarks,omitempty" protobuf:"varint,9,opt,name=allowWatchBookmarks"`
// When specified with a watch call, shows changes that occur after that particular version of a resource.
// Defaults to changes from the beginning of history.
// When specified for list:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
// resourceVersion sets a constraint on what resource versions a request may be served from.
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for
// details.
//
// Defaults to unset
// +optional
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,4,opt,name=resourceVersion"`
// resourceVersionMatch determines how resourceVersion is applied to list calls.
// It is highly recommended that resourceVersionMatch be set for list calls where
// resourceVersion is set
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for
// details.
//
// Defaults to unset
// +optional
ResourceVersionMatch ResourceVersionMatch `json:"resourceVersionMatch,omitempty" protobuf:"bytes,10,opt,name=resourceVersionMatch,casttype=ResourceVersionMatch"`
// Timeout for the list/watch call.
// This limits the duration of the call, regardless of any activity or inactivity.
// +optional
@@ -395,6 +411,26 @@ type ListOptions struct {
Continue string `json:"continue,omitempty" protobuf:"bytes,8,opt,name=continue"`
}
// resourceVersionMatch specifies how the resourceVersion parameter is applied. resourceVersionMatch
// may only be set if resourceVersion is also set.
//
// "NotOlderThan" matches data at least as new as the provided resourceVersion.
// "Exact" matches data at the exact resourceVersion provided.
//
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for
// details.
type ResourceVersionMatch string
const (
// ResourceVersionMatchNotOlderThan matches data at least as new as the provided
// resourceVersion.
ResourceVersionMatchNotOlderThan ResourceVersionMatch = "NotOlderThan"
// ResourceVersionMatchExact matches data at the exact resourceVersion
// provided.
ResourceVersionMatchExact ResourceVersionMatch = "Exact"
)
// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ExportOptions is the query options to the standard REST get call.
@@ -409,15 +445,18 @@ type ExportOptions struct {
Exact bool `json:"exact" protobuf:"varint,2,opt,name=exact"`
}
// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// GetOptions is the standard query options to the standard REST get call.
type GetOptions struct {
TypeMeta `json:",inline"`
// When specified:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
// resourceVersion sets a constraint on what resource versions a request may be served from.
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for
// details.
//
// Defaults to unset
// +optional
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,1,opt,name=resourceVersion"`
// +k8s:deprecated=includeUninitialized,protobuf=2
}
@@ -446,6 +485,7 @@ const (
DryRunAll = "All"
)
// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DeleteOptions may be provided when deleting an API object.
@@ -461,6 +501,7 @@ type DeleteOptions struct {
// Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be
// returned.
// +k8s:conversion-gen=false
// +optional
Preconditions *Preconditions `json:"preconditions,omitempty" protobuf:"bytes,2,opt,name=preconditions"`
@@ -491,6 +532,7 @@ type DeleteOptions struct {
DryRun []string `json:"dryRun,omitempty" protobuf:"bytes,5,rep,name=dryRun"`
}
// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CreateOptions may be provided when creating an API object.
@@ -514,6 +556,7 @@ type CreateOptions struct {
FieldManager string `json:"fieldManager,omitempty" protobuf:"bytes,3,name=fieldManager"`
}
// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// PatchOptions may be provided when patching an API object.
@@ -546,6 +589,7 @@ type PatchOptions struct {
FieldManager string `json:"fieldManager,omitempty" protobuf:"bytes,3,name=fieldManager"`
}
// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// UpdateOptions may be provided when updating an API object.
@@ -859,6 +903,9 @@ const (
// FieldManagerConflict is used to report when another client claims to manage this field,
// It should only be returned for a request using server-side apply.
CauseTypeFieldManagerConflict CauseType = "FieldManagerConflict"
// CauseTypeResourceVersionTooLarge is used to report that the requested resource version
// is newer than the data observed by the API server, so the request cannot be served.
CauseTypeResourceVersionTooLarge CauseType = "ResourceVersionTooLarge"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -1131,11 +1178,16 @@ const (
// If a key maps to an empty Fields value, the field that key represents is part of the set.
//
// The exact format is defined in sigs.k8s.io/structured-merge-diff
// +protobuf.options.(gogoproto.goproto_stringer)=false
type FieldsV1 struct {
// Raw is the underlying serialization of this object.
Raw []byte `json:"-" protobuf:"bytes,1,opt,name=Raw"`
}
func (f FieldsV1) String() string {
return string(f.Raw)
}
// TODO: Table does not generate to protobuf because of the interface{} - fix protobuf
// generation to support a meta type that can accept any valid JSON. This can be introduced
// in a v1 because clients a) receive an error if they try to access proto today, and b)
@@ -1258,6 +1310,7 @@ const (
)
// TableOptions are used when a Table is requested by the caller.
// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type TableOptions struct {
TypeMeta `json:",inline"`
@@ -1296,3 +1349,65 @@ type PartialObjectMetadataList struct {
// items contains each of the included items.
Items []PartialObjectMetadata `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// Condition contains details for one aspect of the current state of this API Resource.
// ---
// This struct is intended for direct use as an array at the field path .status.conditions. For example,
// type FooStatus struct{
// // Represents the observations of a foo's current state.
// // Known .status.conditions.type are: "Available", "Progressing", and "Degraded"
// // +patchMergeKey=type
// // +patchStrategy=merge
// // +listType=map
// // +listMapKey=type
// Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
//
// // other fields
// }
type Condition struct {
// type of condition in CamelCase or in foo.example.com/CamelCase.
// ---
// Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
// useful (see .node.status.conditions), the ability to deconflict is important.
// The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$`
// +kubebuilder:validation:MaxLength=316
Type string `json:"type" protobuf:"bytes,1,opt,name=type"`
// status of the condition, one of True, False, Unknown.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=True;False;Unknown
Status ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status"`
// observedGeneration represents the .metadata.generation that the condition was set based upon.
// For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
// with respect to the current state of the instance.
// +optional
// +kubebuilder:validation:Minimum=0
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
// lastTransitionTime is the last time the condition transitioned from one status to another.
// This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Format=date-time
LastTransitionTime Time `json:"lastTransitionTime" protobuf:"bytes,4,opt,name=lastTransitionTime"`
// reason contains a programmatic identifier indicating the reason for the condition's last transition.
// Producers of specific condition types may define expected values and meanings for this field,
// and whether the values are considered a guaranteed API.
// The value should be a CamelCase string.
// This field may not be empty.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=1024
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$`
Reason string `json:"reason" protobuf:"bytes,5,opt,name=reason"`
// message is a human readable message indicating details about the transition.
// This may be an empty string.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=32768
Message string `json:"message" protobuf:"bytes,6,opt,name=message"`
}

View File

@@ -86,6 +86,20 @@ func (APIVersions) SwaggerDoc() map[string]string {
return map_APIVersions
}
var map_Condition = map[string]string{
"": "Condition contains details for one aspect of the current state of this API Resource.",
"type": "type of condition in CamelCase or in foo.example.com/CamelCase.",
"status": "status of the condition, one of True, False, Unknown.",
"observedGeneration": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.",
"lastTransitionTime": "lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.",
"reason": "reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.",
"message": "message is a human readable message indicating details about the transition. This may be an empty string.",
}
func (Condition) SwaggerDoc() map[string]string {
return map_Condition
}
var map_CreateOptions = map[string]string{
"": "CreateOptions may be provided when creating an API object.",
"dryRun": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
@@ -129,7 +143,7 @@ func (FieldsV1) SwaggerDoc() map[string]string {
var map_GetOptions = map[string]string{
"": "GetOptions is the standard query options to the standard REST get call.",
"resourceVersion": "When specified: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.",
"resourceVersion": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
}
func (GetOptions) SwaggerDoc() map[string]string {
@@ -190,15 +204,16 @@ func (ListMeta) SwaggerDoc() map[string]string {
}
var map_ListOptions = map[string]string{
"": "ListOptions is the query options to a standard REST list call.",
"labelSelector": "A selector to restrict the list of returned objects by their labels. Defaults to everything.",
"fieldSelector": "A selector to restrict the list of returned objects by their fields. Defaults to everything.",
"watch": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.",
"allowWatchBookmarks": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.",
"resourceVersion": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.",
"timeoutSeconds": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.",
"limit": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.",
"continue": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.",
"": "ListOptions is the query options to a standard REST list call.",
"labelSelector": "A selector to restrict the list of returned objects by their labels. Defaults to everything.",
"fieldSelector": "A selector to restrict the list of returned objects by their fields. Defaults to everything.",
"watch": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.",
"allowWatchBookmarks": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.",
"resourceVersion": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"resourceVersionMatch": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
"timeoutSeconds": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.",
"limit": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.",
"continue": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.",
}
func (ListOptions) SwaggerDoc() map[string]string {
@@ -223,7 +238,7 @@ var map_ObjectMeta = map[string]string{
"": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
"name": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
"generateName": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency",
"namespace": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces",
"namespace": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces",
"selfLink": "SelfLink is a URL representing this object. Populated by the system. Read-only.\n\nDEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.",
"uid": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids",
"resourceVersion": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency",
@@ -234,7 +249,7 @@ var map_ObjectMeta = map[string]string{
"labels": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels",
"annotations": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations",
"ownerReferences": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.",
"finalizers": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.",
"finalizers": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.",
"clusterName": "The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.",
"managedFields": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.",
}

View File

@@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/klog/v2"
)
// NestedFieldCopy returns a deep copy of the value of a nested field.
@@ -329,6 +330,8 @@ var UnstructuredJSONScheme runtime.Codec = unstructuredJSONScheme{}
type unstructuredJSONScheme struct{}
const unstructuredJSONSchemeIdentifier runtime.Identifier = "unstructuredJSON"
func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
var err error
if obj != nil {
@@ -349,7 +352,14 @@ func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind,
return obj, &gvk, nil
}
func (unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
func (s unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, w)
}
return s.doEncode(obj, w)
}
func (unstructuredJSONScheme) doEncode(obj runtime.Object, w io.Writer) error {
switch t := obj.(type) {
case *Unstructured:
return json.NewEncoder(w).Encode(t.Object)
@@ -373,6 +383,11 @@ func (unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
}
}
// Identifier implements runtime.Encoder interface.
func (unstructuredJSONScheme) Identifier() runtime.Identifier {
return unstructuredJSONSchemeIdentifier
}
func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) {
type detector struct {
Items gojson.RawMessage
@@ -400,12 +415,6 @@ func (s unstructuredJSONScheme) decodeInto(data []byte, obj runtime.Object) erro
return s.decodeToUnstructured(data, x)
case *UnstructuredList:
return s.decodeToList(data, x)
case *runtime.VersionedObjects:
o, err := s.decode(data)
if err == nil {
x.Objects = []runtime.Object{o}
}
return err
default:
return json.Unmarshal(data, x)
}
@@ -460,12 +469,30 @@ func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList
return nil
}
type JSONFallbackEncoder struct {
runtime.Encoder
type jsonFallbackEncoder struct {
encoder runtime.Encoder
identifier runtime.Identifier
}
func (c JSONFallbackEncoder) Encode(obj runtime.Object, w io.Writer) error {
err := c.Encoder.Encode(obj, w)
func NewJSONFallbackEncoder(encoder runtime.Encoder) runtime.Encoder {
result := map[string]string{
"name": "fallback",
"base": string(encoder.Identifier()),
}
identifier, err := gojson.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling identifier for jsonFallbackEncoder: %v", err)
}
return &jsonFallbackEncoder{
encoder: encoder,
identifier: runtime.Identifier(identifier),
}
}
func (c *jsonFallbackEncoder) Encode(obj runtime.Object, w io.Writer) error {
// There is no need to handle runtime.CacheableObject, as we only
// fallback to other encoders here.
err := c.encoder.Encode(obj, w)
if runtime.IsNotRegisteredError(err) {
switch obj.(type) {
case *Unstructured, *UnstructuredList:
@@ -474,3 +501,8 @@ func (c JSONFallbackEncoder) Encode(obj runtime.Object, w io.Writer) error {
}
return err
}
// Identifier implements runtime.Encoder interface.
func (c *jsonFallbackEncoder) Identifier() runtime.Identifier {
return c.identifier
}

View File

@@ -0,0 +1,535 @@
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by conversion-gen. DO NOT EDIT.
package v1
import (
url "net/url"
unsafe "unsafe"
resource "k8s.io/apimachinery/pkg/api/resource"
conversion "k8s.io/apimachinery/pkg/conversion"
fields "k8s.io/apimachinery/pkg/fields"
labels "k8s.io/apimachinery/pkg/labels"
runtime "k8s.io/apimachinery/pkg/runtime"
intstr "k8s.io/apimachinery/pkg/util/intstr"
watch "k8s.io/apimachinery/pkg/watch"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*CreateOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_url_Values_To_v1_CreateOptions(a.(*url.Values), b.(*CreateOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*DeleteOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_url_Values_To_v1_DeleteOptions(a.(*url.Values), b.(*DeleteOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*ExportOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_url_Values_To_v1_ExportOptions(a.(*url.Values), b.(*ExportOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*GetOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_url_Values_To_v1_GetOptions(a.(*url.Values), b.(*GetOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*ListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_url_Values_To_v1_ListOptions(a.(*url.Values), b.(*ListOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*PatchOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_url_Values_To_v1_PatchOptions(a.(*url.Values), b.(*PatchOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*TableOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_url_Values_To_v1_TableOptions(a.(*url.Values), b.(*TableOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*UpdateOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_url_Values_To_v1_UpdateOptions(a.(*url.Values), b.(*UpdateOptions), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*map[string]string)(nil), (*LabelSelector)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Map_string_To_string_To_v1_LabelSelector(a.(*map[string]string), b.(*LabelSelector), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((**bool)(nil), (*bool)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Pointer_bool_To_bool(a.(**bool), b.(*bool), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((**float64)(nil), (*float64)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Pointer_float64_To_float64(a.(**float64), b.(*float64), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((**int32)(nil), (*int32)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Pointer_int32_To_int32(a.(**int32), b.(*int32), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((**int64)(nil), (*int)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Pointer_int64_To_int(a.(**int64), b.(*int), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((**int64)(nil), (*int64)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Pointer_int64_To_int64(a.(**int64), b.(*int64), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((**intstr.IntOrString)(nil), (*intstr.IntOrString)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Pointer_intstr_IntOrString_To_intstr_IntOrString(a.(**intstr.IntOrString), b.(*intstr.IntOrString), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((**string)(nil), (*string)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Pointer_string_To_string(a.(**string), b.(*string), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((**Duration)(nil), (*Duration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Pointer_v1_Duration_To_v1_Duration(a.(**Duration), b.(*Duration), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*[]string)(nil), (**DeletionPropagation)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_Pointer_v1_DeletionPropagation(a.(*[]string), b.(**DeletionPropagation), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*[]string)(nil), (**Time)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_Pointer_v1_Time(a.(*[]string), b.(**Time), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*[]string)(nil), (*[]int32)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_Slice_int32(a.(*[]string), b.(*[]int32), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*[]string)(nil), (*IncludeObjectPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_v1_IncludeObjectPolicy(a.(*[]string), b.(*IncludeObjectPolicy), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*[]string)(nil), (*ResourceVersionMatch)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_v1_ResourceVersionMatch(a.(*[]string), b.(*ResourceVersionMatch), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*[]string)(nil), (*Time)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_v1_Time(a.(*[]string), b.(*Time), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*bool)(nil), (**bool)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_bool_To_Pointer_bool(a.(*bool), b.(**bool), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*fields.Selector)(nil), (*string)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_fields_Selector_To_string(a.(*fields.Selector), b.(*string), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*float64)(nil), (**float64)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_float64_To_Pointer_float64(a.(*float64), b.(**float64), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*int32)(nil), (**int32)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_int32_To_Pointer_int32(a.(*int32), b.(**int32), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*int64)(nil), (**int64)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_int64_To_Pointer_int64(a.(*int64), b.(**int64), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*int)(nil), (**int64)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_int_To_Pointer_int64(a.(*int), b.(**int64), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*intstr.IntOrString)(nil), (**intstr.IntOrString)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_intstr_IntOrString_To_Pointer_intstr_IntOrString(a.(*intstr.IntOrString), b.(**intstr.IntOrString), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*intstr.IntOrString)(nil), (*intstr.IntOrString)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_intstr_IntOrString_To_intstr_IntOrString(a.(*intstr.IntOrString), b.(*intstr.IntOrString), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*labels.Selector)(nil), (*string)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_labels_Selector_To_string(a.(*labels.Selector), b.(*string), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*resource.Quantity)(nil), (*resource.Quantity)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_Quantity_To_resource_Quantity(a.(*resource.Quantity), b.(*resource.Quantity), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*string)(nil), (**string)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_string_To_Pointer_string(a.(*string), b.(**string), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*string)(nil), (*fields.Selector)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_string_To_fields_Selector(a.(*string), b.(*fields.Selector), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*string)(nil), (*labels.Selector)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_string_To_labels_Selector(a.(*string), b.(*labels.Selector), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*url.Values)(nil), (*DeleteOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_url_Values_To_v1_DeleteOptions(a.(*url.Values), b.(*DeleteOptions), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*DeleteOptions)(nil), (*DeleteOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_DeleteOptions_To_v1_DeleteOptions(a.(*DeleteOptions), b.(*DeleteOptions), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*Duration)(nil), (**Duration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_Duration_To_Pointer_v1_Duration(a.(*Duration), b.(**Duration), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*InternalEvent)(nil), (*WatchEvent)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_InternalEvent_To_v1_WatchEvent(a.(*InternalEvent), b.(*WatchEvent), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*LabelSelector)(nil), (*map[string]string)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_LabelSelector_To_Map_string_To_string(a.(*LabelSelector), b.(*map[string]string), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*ListMeta)(nil), (*ListMeta)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_ListMeta_To_v1_ListMeta(a.(*ListMeta), b.(*ListMeta), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*MicroTime)(nil), (*MicroTime)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_MicroTime_To_v1_MicroTime(a.(*MicroTime), b.(*MicroTime), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*Time)(nil), (*Time)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_Time_To_v1_Time(a.(*Time), b.(*Time), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*TypeMeta)(nil), (*TypeMeta)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_TypeMeta_To_v1_TypeMeta(a.(*TypeMeta), b.(*TypeMeta), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*WatchEvent)(nil), (*InternalEvent)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_WatchEvent_To_v1_InternalEvent(a.(*WatchEvent), b.(*InternalEvent), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*WatchEvent)(nil), (*watch.Event)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_WatchEvent_To_watch_Event(a.(*WatchEvent), b.(*watch.Event), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*watch.Event)(nil), (*WatchEvent)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_watch_Event_To_v1_WatchEvent(a.(*watch.Event), b.(*WatchEvent), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_url_Values_To_v1_CreateOptions(in *url.Values, out *CreateOptions, s conversion.Scope) error {
// WARNING: Field TypeMeta does not have json tag, skipping.
if values, ok := map[string][]string(*in)["dryRun"]; ok && len(values) > 0 {
out.DryRun = *(*[]string)(unsafe.Pointer(&values))
} else {
out.DryRun = nil
}
if values, ok := map[string][]string(*in)["fieldManager"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_string(&values, &out.FieldManager, s); err != nil {
return err
}
} else {
out.FieldManager = ""
}
return nil
}
// Convert_url_Values_To_v1_CreateOptions is an autogenerated conversion function.
func Convert_url_Values_To_v1_CreateOptions(in *url.Values, out *CreateOptions, s conversion.Scope) error {
return autoConvert_url_Values_To_v1_CreateOptions(in, out, s)
}
func autoConvert_url_Values_To_v1_DeleteOptions(in *url.Values, out *DeleteOptions, s conversion.Scope) error {
// WARNING: Field TypeMeta does not have json tag, skipping.
if values, ok := map[string][]string(*in)["gracePeriodSeconds"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_Pointer_int64(&values, &out.GracePeriodSeconds, s); err != nil {
return err
}
} else {
out.GracePeriodSeconds = nil
}
// INFO: in.Preconditions opted out of conversion generation
if values, ok := map[string][]string(*in)["orphanDependents"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_Pointer_bool(&values, &out.OrphanDependents, s); err != nil {
return err
}
} else {
out.OrphanDependents = nil
}
if values, ok := map[string][]string(*in)["propagationPolicy"]; ok && len(values) > 0 {
if err := Convert_Slice_string_To_Pointer_v1_DeletionPropagation(&values, &out.PropagationPolicy, s); err != nil {
return err
}
} else {
out.PropagationPolicy = nil
}
if values, ok := map[string][]string(*in)["dryRun"]; ok && len(values) > 0 {
out.DryRun = *(*[]string)(unsafe.Pointer(&values))
} else {
out.DryRun = nil
}
return nil
}
func autoConvert_url_Values_To_v1_ExportOptions(in *url.Values, out *ExportOptions, s conversion.Scope) error {
// WARNING: Field TypeMeta does not have json tag, skipping.
if values, ok := map[string][]string(*in)["export"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_bool(&values, &out.Export, s); err != nil {
return err
}
} else {
out.Export = false
}
if values, ok := map[string][]string(*in)["exact"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_bool(&values, &out.Exact, s); err != nil {
return err
}
} else {
out.Exact = false
}
return nil
}
// Convert_url_Values_To_v1_ExportOptions is an autogenerated conversion function.
func Convert_url_Values_To_v1_ExportOptions(in *url.Values, out *ExportOptions, s conversion.Scope) error {
return autoConvert_url_Values_To_v1_ExportOptions(in, out, s)
}
func autoConvert_url_Values_To_v1_GetOptions(in *url.Values, out *GetOptions, s conversion.Scope) error {
// WARNING: Field TypeMeta does not have json tag, skipping.
if values, ok := map[string][]string(*in)["resourceVersion"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_string(&values, &out.ResourceVersion, s); err != nil {
return err
}
} else {
out.ResourceVersion = ""
}
return nil
}
// Convert_url_Values_To_v1_GetOptions is an autogenerated conversion function.
func Convert_url_Values_To_v1_GetOptions(in *url.Values, out *GetOptions, s conversion.Scope) error {
return autoConvert_url_Values_To_v1_GetOptions(in, out, s)
}
func autoConvert_url_Values_To_v1_ListOptions(in *url.Values, out *ListOptions, s conversion.Scope) error {
// WARNING: Field TypeMeta does not have json tag, skipping.
if values, ok := map[string][]string(*in)["labelSelector"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_string(&values, &out.LabelSelector, s); err != nil {
return err
}
} else {
out.LabelSelector = ""
}
if values, ok := map[string][]string(*in)["fieldSelector"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_string(&values, &out.FieldSelector, s); err != nil {
return err
}
} else {
out.FieldSelector = ""
}
if values, ok := map[string][]string(*in)["watch"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_bool(&values, &out.Watch, s); err != nil {
return err
}
} else {
out.Watch = false
}
if values, ok := map[string][]string(*in)["allowWatchBookmarks"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_bool(&values, &out.AllowWatchBookmarks, s); err != nil {
return err
}
} else {
out.AllowWatchBookmarks = false
}
if values, ok := map[string][]string(*in)["resourceVersion"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_string(&values, &out.ResourceVersion, s); err != nil {
return err
}
} else {
out.ResourceVersion = ""
}
if values, ok := map[string][]string(*in)["resourceVersionMatch"]; ok && len(values) > 0 {
if err := Convert_Slice_string_To_v1_ResourceVersionMatch(&values, &out.ResourceVersionMatch, s); err != nil {
return err
}
} else {
out.ResourceVersionMatch = ""
}
if values, ok := map[string][]string(*in)["timeoutSeconds"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_Pointer_int64(&values, &out.TimeoutSeconds, s); err != nil {
return err
}
} else {
out.TimeoutSeconds = nil
}
if values, ok := map[string][]string(*in)["limit"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_int64(&values, &out.Limit, s); err != nil {
return err
}
} else {
out.Limit = 0
}
if values, ok := map[string][]string(*in)["continue"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_string(&values, &out.Continue, s); err != nil {
return err
}
} else {
out.Continue = ""
}
return nil
}
// Convert_url_Values_To_v1_ListOptions is an autogenerated conversion function.
func Convert_url_Values_To_v1_ListOptions(in *url.Values, out *ListOptions, s conversion.Scope) error {
return autoConvert_url_Values_To_v1_ListOptions(in, out, s)
}
func autoConvert_url_Values_To_v1_PatchOptions(in *url.Values, out *PatchOptions, s conversion.Scope) error {
// WARNING: Field TypeMeta does not have json tag, skipping.
if values, ok := map[string][]string(*in)["dryRun"]; ok && len(values) > 0 {
out.DryRun = *(*[]string)(unsafe.Pointer(&values))
} else {
out.DryRun = nil
}
if values, ok := map[string][]string(*in)["force"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_Pointer_bool(&values, &out.Force, s); err != nil {
return err
}
} else {
out.Force = nil
}
if values, ok := map[string][]string(*in)["fieldManager"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_string(&values, &out.FieldManager, s); err != nil {
return err
}
} else {
out.FieldManager = ""
}
return nil
}
// Convert_url_Values_To_v1_PatchOptions is an autogenerated conversion function.
func Convert_url_Values_To_v1_PatchOptions(in *url.Values, out *PatchOptions, s conversion.Scope) error {
return autoConvert_url_Values_To_v1_PatchOptions(in, out, s)
}
func autoConvert_url_Values_To_v1_TableOptions(in *url.Values, out *TableOptions, s conversion.Scope) error {
// WARNING: Field TypeMeta does not have json tag, skipping.
if values, ok := map[string][]string(*in)["-"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_bool(&values, &out.NoHeaders, s); err != nil {
return err
}
} else {
out.NoHeaders = false
}
if values, ok := map[string][]string(*in)["includeObject"]; ok && len(values) > 0 {
if err := Convert_Slice_string_To_v1_IncludeObjectPolicy(&values, &out.IncludeObject, s); err != nil {
return err
}
} else {
out.IncludeObject = ""
}
return nil
}
// Convert_url_Values_To_v1_TableOptions is an autogenerated conversion function.
func Convert_url_Values_To_v1_TableOptions(in *url.Values, out *TableOptions, s conversion.Scope) error {
return autoConvert_url_Values_To_v1_TableOptions(in, out, s)
}
func autoConvert_url_Values_To_v1_UpdateOptions(in *url.Values, out *UpdateOptions, s conversion.Scope) error {
// WARNING: Field TypeMeta does not have json tag, skipping.
if values, ok := map[string][]string(*in)["dryRun"]; ok && len(values) > 0 {
out.DryRun = *(*[]string)(unsafe.Pointer(&values))
} else {
out.DryRun = nil
}
if values, ok := map[string][]string(*in)["fieldManager"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_string(&values, &out.FieldManager, s); err != nil {
return err
}
} else {
out.FieldManager = ""
}
return nil
}
// Convert_url_Values_To_v1_UpdateOptions is an autogenerated conversion function.
func Convert_url_Values_To_v1_UpdateOptions(in *url.Values, out *UpdateOptions, s conversion.Scope) error {
return autoConvert_url_Values_To_v1_UpdateOptions(in, out, s)
}

View File

@@ -191,6 +191,23 @@ func (in *APIVersions) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Condition) DeepCopyInto(out *Condition) {
*out = *in
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition.
func (in *Condition) DeepCopy() *Condition {
if in == nil {
return nil
}
out := new(Condition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CreateOptions) DeepCopyInto(out *CreateOptions) {
*out = *in

View File

@@ -54,7 +54,8 @@ type Converter struct {
generatedConversionFuncs ConversionFuncs
// Set of conversions that should be treated as a no-op
ignoredConversions map[typePair]struct{}
ignoredConversions map[typePair]struct{}
ignoredUntypedConversions map[typePair]struct{}
// This is a map from a source field type and name, to a list of destination
// field type and name.
@@ -83,17 +84,23 @@ type Converter struct {
// NewConverter creates a new Converter object.
func NewConverter(nameFn NameFunc) *Converter {
c := &Converter{
conversionFuncs: NewConversionFuncs(),
generatedConversionFuncs: NewConversionFuncs(),
ignoredConversions: make(map[typePair]struct{}),
nameFunc: nameFn,
structFieldDests: make(map[typeNamePair][]typeNamePair),
structFieldSources: make(map[typeNamePair][]typeNamePair),
conversionFuncs: NewConversionFuncs(),
generatedConversionFuncs: NewConversionFuncs(),
ignoredConversions: make(map[typePair]struct{}),
ignoredUntypedConversions: make(map[typePair]struct{}),
nameFunc: nameFn,
structFieldDests: make(map[typeNamePair][]typeNamePair),
structFieldSources: make(map[typeNamePair][]typeNamePair),
inputFieldMappingFuncs: make(map[reflect.Type]FieldMappingFunc),
inputDefaultFlags: make(map[reflect.Type]FieldMatchingFlags),
}
c.RegisterConversionFunc(Convert_Slice_byte_To_Slice_byte)
c.RegisterUntypedConversionFunc(
(*[]byte)(nil), (*[]byte)(nil),
func(a, b interface{}, s Scope) error {
return Convert_Slice_byte_To_Slice_byte(a.(*[]byte), b.(*[]byte), s)
},
)
return c
}
@@ -131,10 +138,6 @@ type Scope interface {
// parameters, you'll run out of stack space before anything useful happens.
Convert(src, dest interface{}, flags FieldMatchingFlags) error
// DefaultConvert performs the default conversion, without calling a conversion func
// on the current stack frame. This makes it safe to call from a conversion func.
DefaultConvert(src, dest interface{}, flags FieldMatchingFlags) error
// SrcTags and DestTags contain the struct tags that src and dest had, respectively.
// If the enclosing object was not a struct, then these will contain no tags, of course.
SrcTag() reflect.StructTag
@@ -153,31 +156,14 @@ type FieldMappingFunc func(key string, sourceTag, destTag reflect.StructTag) (so
func NewConversionFuncs() ConversionFuncs {
return ConversionFuncs{
fns: make(map[typePair]reflect.Value),
untyped: make(map[typePair]ConversionFunc),
}
}
type ConversionFuncs struct {
fns map[typePair]reflect.Value
untyped map[typePair]ConversionFunc
}
// Add adds the provided conversion functions to the lookup table - they must have the signature
// `func(type1, type2, Scope) error`. Functions are added in the order passed and will override
// previously registered pairs.
func (c ConversionFuncs) Add(fns ...interface{}) error {
for _, fn := range fns {
fv := reflect.ValueOf(fn)
ft := fv.Type()
if err := verifyConversionFunctionSignature(ft); err != nil {
return err
}
c.fns[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv
}
return nil
}
// AddUntyped adds the provided conversion function to the lookup table for the types that are
// supplied as a and b. a and b must be pointers or an error is returned. This method overwrites
// previously defined functions.
@@ -197,12 +183,6 @@ func (c ConversionFuncs) AddUntyped(a, b interface{}, fn ConversionFunc) error {
// both other and c, with other conversions taking precedence.
func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs {
merged := NewConversionFuncs()
for k, v := range c.fns {
merged.fns[k] = v
}
for k, v := range other.fns {
merged.fns[k] = v
}
for k, v := range c.untyped {
merged.untyped[k] = v
}
@@ -290,12 +270,6 @@ func (s *scope) Convert(src, dest interface{}, flags FieldMatchingFlags) error {
return s.converter.Convert(src, dest, flags, s.meta)
}
// DefaultConvert continues a conversion, performing a default conversion (no conversion func)
// for the current stack frame.
func (s *scope) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags) error {
return s.converter.DefaultConvert(src, dest, flags, s.meta)
}
// SrcTag returns the tag of the struct containing the current source item, if any.
func (s *scope) SrcTag() reflect.StructTag {
return s.srcStack.top().tag
@@ -360,29 +334,6 @@ func verifyConversionFunctionSignature(ft reflect.Type) error {
return nil
}
// RegisterConversionFunc registers a conversion func with the
// Converter. conversionFunc must take three parameters: a pointer to the input
// type, a pointer to the output type, and a conversion.Scope (which should be
// used if recursive conversion calls are desired). It must return an error.
//
// Example:
// c.RegisterConversionFunc(
// func(in *Pod, out *v1.Pod, s Scope) error {
// // conversion logic...
// return nil
// })
// DEPRECATED: Will be removed in favor of RegisterUntypedConversionFunc
func (c *Converter) RegisterConversionFunc(conversionFunc interface{}) error {
return c.conversionFuncs.Add(conversionFunc)
}
// Similar to RegisterConversionFunc, but registers conversion function that were
// automatically generated.
// DEPRECATED: Will be removed in favor of RegisterGeneratedUntypedConversionFunc
func (c *Converter) RegisterGeneratedConversionFunc(conversionFunc interface{}) error {
return c.generatedConversionFuncs.Add(conversionFunc)
}
// RegisterUntypedConversionFunc registers a function that converts between a and b by passing objects of those
// types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
// any other guarantee.
@@ -409,6 +360,7 @@ func (c *Converter) RegisterIgnoredConversion(from, to interface{}) error {
return fmt.Errorf("expected pointer arg for 'to' param 1, got: %v", typeTo)
}
c.ignoredConversions[typePair{typeFrom.Elem(), typeTo.Elem()}] = struct{}{}
c.ignoredUntypedConversions[typePair{typeFrom, typeTo}] = struct{}{}
return nil
}
@@ -470,18 +422,6 @@ func (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, met
return c.doConversion(src, dest, flags, meta, c.convert)
}
// DefaultConvert will translate src to dest if it knows how. Both must be pointers.
// No conversion func is used. If the default copying mechanism
// doesn't work on this type pair, an error will be returned.
// Read the comments on the various FieldMatchingFlags constants to understand
// what the 'flags' parameter does.
// 'meta' is given to allow you to pass information to conversion functions,
// it is not used by DefaultConvert() other than storing it in the scope.
// Not safe for objects with cyclic references!
func (c *Converter) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {
return c.doConversion(src, dest, flags, meta, c.defaultConvert)
}
type conversionFunc func(sv, dv reflect.Value, scope *scope) error
func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags, meta *Meta, f conversionFunc) error {
@@ -491,59 +431,51 @@ func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags
flags: flags,
meta: meta,
}
// ignore conversions of this type
if _, ok := c.ignoredUntypedConversions[pair]; ok {
return nil
}
if fn, ok := c.conversionFuncs.untyped[pair]; ok {
return fn(src, dest, scope)
}
if fn, ok := c.generatedConversionFuncs.untyped[pair]; ok {
return fn(src, dest, scope)
}
// TODO: consider everything past this point deprecated - we want to support only point to point top level
// conversions
dv, err := EnforcePtr(dest)
if err != nil {
return err
}
if !dv.CanAddr() && !dv.CanSet() {
return fmt.Errorf("can't write to dest")
}
sv, err := EnforcePtr(src)
if err != nil {
return err
}
return fmt.Errorf("converting (%s) to (%s): unknown conversion", sv.Type(), dv.Type())
// TODO: Everything past this point is deprecated.
// Remove in 1.20 once we're sure it didn't break anything.
// Leave something on the stack, so that calls to struct tag getters never fail.
scope.srcStack.push(scopeStackElem{})
scope.destStack.push(scopeStackElem{})
return f(sv, dv, scope)
}
// callCustom calls 'custom' with sv & dv. custom must be a conversion function.
func (c *Converter) callCustom(sv, dv, custom reflect.Value, scope *scope) error {
if !sv.CanAddr() {
sv2 := reflect.New(sv.Type())
sv2.Elem().Set(sv)
sv = sv2
} else {
sv = sv.Addr()
}
// callUntyped calls predefined conversion func.
func (c *Converter) callUntyped(sv, dv reflect.Value, f ConversionFunc, scope *scope) error {
if !dv.CanAddr() {
if !dv.CanSet() {
return scope.errorf("can't addr or set dest.")
}
dvOrig := dv
dv := reflect.New(dvOrig.Type())
defer func() { dvOrig.Set(dv) }()
return scope.errorf("cant addr dest")
}
var svPointer reflect.Value
if sv.CanAddr() {
svPointer = sv.Addr()
} else {
dv = dv.Addr()
svPointer = reflect.New(sv.Type())
svPointer.Elem().Set(sv)
}
args := []reflect.Value{sv, dv, reflect.ValueOf(scope)}
ret := custom.Call(args)[0].Interface()
// This convolution is necessary because nil interfaces won't convert
// to errors.
if ret == nil {
return nil
}
return ret.(error)
dvPointer := dv.Addr()
return f(svPointer.Interface(), dvPointer.Interface(), scope)
}
// convert recursively copies sv into dv, calling an appropriate conversion function if
@@ -561,27 +493,14 @@ func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error {
}
// Convert sv to dv.
if fv, ok := c.conversionFuncs.fns[pair]; ok {
if c.Debug != nil {
c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt)
}
return c.callCustom(sv, dv, fv, scope)
pair = typePair{reflect.PtrTo(sv.Type()), reflect.PtrTo(dv.Type())}
if f, ok := c.conversionFuncs.untyped[pair]; ok {
return c.callUntyped(sv, dv, f, scope)
}
if fv, ok := c.generatedConversionFuncs.fns[pair]; ok {
if c.Debug != nil {
c.Debug.Logf("Calling generated conversion of '%v' to '%v'", st, dt)
}
return c.callCustom(sv, dv, fv, scope)
if f, ok := c.generatedConversionFuncs.untyped[pair]; ok {
return c.callUntyped(sv, dv, f, scope)
}
return c.defaultConvert(sv, dv, scope)
}
// defaultConvert recursively copies sv into dv. no conversion function is called
// for the current stack frame (but conversion functions may be called for nested objects)
func (c *Converter) defaultConvert(sv, dv reflect.Value, scope *scope) error {
dt, st := dv.Type(), sv.Type()
if !dv.CanSet() {
return scope.errorf("Cannot set dest. (Tried to deep copy something with unexported fields?)")
}

View File

@@ -57,13 +57,15 @@ type Selector interface {
type nothingSelector struct{}
func (n nothingSelector) Matches(_ Fields) bool { return false }
func (n nothingSelector) Empty() bool { return false }
func (n nothingSelector) String() string { return "" }
func (n nothingSelector) Requirements() Requirements { return nil }
func (n nothingSelector) DeepCopySelector() Selector { return n }
func (n nothingSelector) RequiresExactMatch(field string) (value string, found bool) { return "", false }
func (n nothingSelector) Transform(fn TransformFunc) (Selector, error) { return n, nil }
func (n nothingSelector) Matches(_ Fields) bool { return false }
func (n nothingSelector) Empty() bool { return false }
func (n nothingSelector) String() string { return "" }
func (n nothingSelector) Requirements() Requirements { return nil }
func (n nothingSelector) DeepCopySelector() Selector { return n }
func (n nothingSelector) RequiresExactMatch(field string) (value string, found bool) {
return "", false
}
func (n nothingSelector) Transform(fn TransformFunc) (Selector, error) { return n, nil }
// Nothing returns a selector that matches no fields
func Nothing() Selector {

View File

@@ -57,14 +57,22 @@ func (ls Set) Get(label string) string {
return ls[label]
}
// AsSelector converts labels into a selectors.
// AsSelector converts labels into a selectors. It does not
// perform any validation, which means the server will reject
// the request if the Set contains invalid values.
func (ls Set) AsSelector() Selector {
return SelectorFromSet(ls)
}
// AsValidatedSelector converts labels into a selectors.
// The Set is validated client-side, which allows to catch errors early.
func (ls Set) AsValidatedSelector() (Selector, error) {
return ValidatedSelectorFromSet(ls)
}
// AsSelectorPreValidated converts labels into a selector, but
// assumes that labels are already validated and thus don't
// preform any validation.
// assumes that labels are already validated and thus doesn't
// perform any validation.
// According to our measurements this is significantly faster
// in codepaths that matter at high scale.
func (ls Set) AsSelectorPreValidated() Selector {

View File

@@ -26,7 +26,7 @@ import (
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/klog"
"k8s.io/klog/v2"
)
// Requirements is AND of all requirements.
@@ -54,6 +54,11 @@ type Selector interface {
// Make a deep copy of the selector.
DeepCopySelector() Selector
// RequiresExactMatch allows a caller to introspect whether a given selector
// requires a single specific label to be set, and if so returns the value it
// requires.
RequiresExactMatch(label string) (value string, found bool)
}
// Everything returns a selector that matches all labels.
@@ -69,6 +74,9 @@ func (n nothingSelector) String() string { return "" }
func (n nothingSelector) Add(_ ...Requirement) Selector { return n }
func (n nothingSelector) Requirements() (Requirements, bool) { return nil, false }
func (n nothingSelector) DeepCopySelector() Selector { return n }
func (n nothingSelector) RequiresExactMatch(label string) (value string, found bool) {
return "", false
}
// Nothing returns a selector that matches no labels
func Nothing() Selector {
@@ -215,7 +223,7 @@ func (r *Requirement) Matches(ls Labels) bool {
return false
}
// There should be only one strValue in r.strValues, and can be converted to a integer.
// There should be only one strValue in r.strValues, and can be converted to an integer.
if len(r.strValues) != 1 {
klog.V(10).Infof("Invalid values count %+v of requirement %#v, for 'Gt', 'Lt' operators, exactly one value is required", len(r.strValues), r)
return false
@@ -358,6 +366,23 @@ func (lsel internalSelector) String() string {
return strings.Join(reqs, ",")
}
// RequiresExactMatch introspect whether a given selector requires a single specific field
// to be set, and if so returns the value it requires.
func (lsel internalSelector) RequiresExactMatch(label string) (value string, found bool) {
for ix := range lsel {
if lsel[ix].key == label {
switch lsel[ix].operator {
case selection.Equals, selection.DoubleEquals, selection.In:
if len(lsel[ix].strValues) == 1 {
return lsel[ix].strValues[0], true
}
}
return "", false
}
}
return "", false
}
// Token represents constant definition for lexer token
type Token int
@@ -846,23 +871,30 @@ func validateLabelValue(k, v string) error {
// SelectorFromSet returns a Selector which will match exactly the given Set. A
// nil and empty Sets are considered equivalent to Everything().
// It does not perform any validation, which means the server will reject
// the request if the Set contains invalid values.
func SelectorFromSet(ls Set) Selector {
return SelectorFromValidatedSet(ls)
}
// ValidatedSelectorFromSet returns a Selector which will match exactly the given Set. A
// nil and empty Sets are considered equivalent to Everything().
// The Set is validated client-side, which allows to catch errors early.
func ValidatedSelectorFromSet(ls Set) (Selector, error) {
if ls == nil || len(ls) == 0 {
return internalSelector{}
return internalSelector{}, nil
}
var requirements internalSelector
requirements := make([]Requirement, 0, len(ls))
for label, value := range ls {
r, err := NewRequirement(label, selection.Equals, []string{value})
if err == nil {
requirements = append(requirements, *r)
} else {
//TODO: double check errors when input comes from serialization?
return internalSelector{}
if err != nil {
return nil, err
}
requirements = append(requirements, *r)
}
// sort to have deterministic string representation
sort.Sort(ByKey(requirements))
return requirements
return internalSelector(requirements), nil
}
// SelectorFromValidatedSet returns a Selector which will match exactly the given Set.
@@ -872,13 +904,13 @@ func SelectorFromValidatedSet(ls Set) Selector {
if ls == nil || len(ls) == 0 {
return internalSelector{}
}
var requirements internalSelector
requirements := make([]Requirement, 0, len(ls))
for label, value := range ls {
requirements = append(requirements, Requirement{key: label, operator: selection.Equals, strValues: []string{value}})
}
// sort to have deterministic string representation
sort.Sort(ByKey(requirements))
return requirements
return internalSelector(requirements)
}
// ParseToRequirements takes a string representing a selector and returns a list of

View File

@@ -19,13 +19,17 @@ package runtime
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/url"
"reflect"
"strconv"
"strings"
"k8s.io/apimachinery/pkg/conversion/queryparams"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
)
// codec binds an encoder and decoder.
@@ -100,10 +104,19 @@ type NoopEncoder struct {
var _ Serializer = NoopEncoder{}
const noopEncoderIdentifier Identifier = "noop"
func (n NoopEncoder) Encode(obj Object, w io.Writer) error {
// There is no need to handle runtime.CacheableObject, as we don't
// process the obj at all.
return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder))
}
// Identifier implements runtime.Encoder interface.
func (n NoopEncoder) Identifier() Identifier {
return noopEncoderIdentifier
}
// NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding.
type NoopDecoder struct {
Encoder
@@ -193,19 +206,51 @@ func (c *parameterCodec) EncodeParameters(obj Object, to schema.GroupVersion) (u
type base64Serializer struct {
Encoder
Decoder
identifier Identifier
}
func NewBase64Serializer(e Encoder, d Decoder) Serializer {
return &base64Serializer{e, d}
return &base64Serializer{
Encoder: e,
Decoder: d,
identifier: identifier(e),
}
}
func identifier(e Encoder) Identifier {
result := map[string]string{
"name": "base64",
}
if e != nil {
result["encoder"] = string(e.Identifier())
}
identifier, err := json.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling identifier for base64Serializer: %v", err)
}
return Identifier(identifier)
}
func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
if co, ok := obj.(CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, stream)
}
return s.doEncode(obj, stream)
}
func (s base64Serializer) doEncode(obj Object, stream io.Writer) error {
e := base64.NewEncoder(base64.StdEncoding, stream)
err := s.Encoder.Encode(obj, e)
e.Close()
return err
}
// Identifier implements runtime.Encoder interface.
func (s base64Serializer) Identifier() Identifier {
return s.identifier
}
func (s base64Serializer) Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) {
out := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
n, err := base64.StdEncoding.Decode(out, data)
@@ -238,6 +283,11 @@ var (
DisabledGroupVersioner GroupVersioner = disabledGroupVersioner{}
)
const (
internalGroupVersionerIdentifier = "internal"
disabledGroupVersionerIdentifier = "disabled"
)
type internalGroupVersioner struct{}
// KindForGroupVersionKinds returns an internal Kind if one is found, or converts the first provided kind to the internal version.
@@ -253,6 +303,11 @@ func (internalGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersi
return schema.GroupVersionKind{}, false
}
// Identifier implements GroupVersioner interface.
func (internalGroupVersioner) Identifier() string {
return internalGroupVersionerIdentifier
}
type disabledGroupVersioner struct{}
// KindForGroupVersionKinds returns false for any input.
@@ -260,19 +315,9 @@ func (disabledGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersi
return schema.GroupVersionKind{}, false
}
// GroupVersioners implements GroupVersioner and resolves to the first exact match for any kind.
type GroupVersioners []GroupVersioner
// KindForGroupVersionKinds returns the first match of any of the group versioners, or false if no match occurred.
func (gvs GroupVersioners) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
for _, gv := range gvs {
target, ok := gv.KindForGroupVersionKinds(kinds)
if !ok {
continue
}
return target, true
}
return schema.GroupVersionKind{}, false
// Identifier implements GroupVersioner interface.
func (disabledGroupVersioner) Identifier() string {
return disabledGroupVersionerIdentifier
}
// Assert that schema.GroupVersion and GroupVersions implement GroupVersioner
@@ -330,3 +375,22 @@ func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersio
}
return schema.GroupVersionKind{}, false
}
// Identifier implements GroupVersioner interface.
func (v multiGroupVersioner) Identifier() string {
groupKinds := make([]string, 0, len(v.acceptedGroupKinds))
for _, gk := range v.acceptedGroupKinds {
groupKinds = append(groupKinds, gk.String())
}
result := map[string]string{
"name": "multi",
"target": v.target.String(),
"accepted": strings.Join(groupKinds, ","),
"coerce": strconv.FormatBool(v.coerce),
}
identifier, err := json.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling Identifier for %#v: %v", v, err)
}
return string(identifier)
}

View File

@@ -21,6 +21,7 @@ import (
"reflect"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/json"
)
// CheckCodec makes sure that the codec can encode objects like internalType,
@@ -32,7 +33,14 @@ func CheckCodec(c Codec, internalType Object, externalTypes ...schema.GroupVersi
return fmt.Errorf("Internal type not encodable: %v", err)
}
for _, et := range externalTypes {
exBytes := []byte(fmt.Sprintf(`{"kind":"%v","apiVersion":"%v"}`, et.Kind, et.GroupVersion().String()))
typeMeta := TypeMeta{
Kind: et.Kind,
APIVersion: et.GroupVersion().String(),
}
exBytes, err := json.Marshal(&typeMeta)
if err != nil {
return err
}
obj, err := Decode(c, exBytes)
if err != nil {
return fmt.Errorf("external type %s not interpretable: %v", et, err)

View File

@@ -53,27 +53,21 @@ func JSONKeyMapper(key string, sourceTag, destTag reflect.StructTag) (string, st
return key, key
}
// DefaultStringConversions are helpers for converting []string and string to real values.
var DefaultStringConversions = []interface{}{
Convert_Slice_string_To_string,
Convert_Slice_string_To_int,
Convert_Slice_string_To_bool,
Convert_Slice_string_To_int64,
}
func Convert_Slice_string_To_string(input *[]string, out *string, s conversion.Scope) error {
if len(*input) == 0 {
func Convert_Slice_string_To_string(in *[]string, out *string, s conversion.Scope) error {
if len(*in) == 0 {
*out = ""
return nil
}
*out = (*input)[0]
*out = (*in)[0]
return nil
}
func Convert_Slice_string_To_int(input *[]string, out *int, s conversion.Scope) error {
if len(*input) == 0 {
func Convert_Slice_string_To_int(in *[]string, out *int, s conversion.Scope) error {
if len(*in) == 0 {
*out = 0
return nil
}
str := (*input)[0]
str := (*in)[0]
i, err := strconv.Atoi(str)
if err != nil {
return err
@@ -83,15 +77,16 @@ func Convert_Slice_string_To_int(input *[]string, out *int, s conversion.Scope)
}
// Convert_Slice_string_To_bool will convert a string parameter to boolean.
// Only the absence of a value, a value of "false", or a value of "0" resolve to false.
// Only the absence of a value (i.e. zero-length slice), a value of "false", or a
// value of "0" resolve to false.
// Any other value (including empty string) resolves to true.
func Convert_Slice_string_To_bool(input *[]string, out *bool, s conversion.Scope) error {
if len(*input) == 0 {
func Convert_Slice_string_To_bool(in *[]string, out *bool, s conversion.Scope) error {
if len(*in) == 0 {
*out = false
return nil
}
switch strings.ToLower((*input)[0]) {
case "false", "0":
switch {
case (*in)[0] == "0", strings.EqualFold((*in)[0], "false"):
*out = false
default:
*out = true
@@ -99,15 +94,103 @@ func Convert_Slice_string_To_bool(input *[]string, out *bool, s conversion.Scope
return nil
}
func Convert_Slice_string_To_int64(input *[]string, out *int64, s conversion.Scope) error {
if len(*input) == 0 {
*out = 0
// Convert_Slice_string_To_bool will convert a string parameter to boolean.
// Only the absence of a value (i.e. zero-length slice), a value of "false", or a
// value of "0" resolve to false.
// Any other value (including empty string) resolves to true.
func Convert_Slice_string_To_Pointer_bool(in *[]string, out **bool, s conversion.Scope) error {
if len(*in) == 0 {
boolVar := false
*out = &boolVar
return nil
}
str := (*input)[0]
i, err := strconv.ParseInt(str, 10, 64)
switch {
case (*in)[0] == "0", strings.EqualFold((*in)[0], "false"):
boolVar := false
*out = &boolVar
default:
boolVar := true
*out = &boolVar
}
return nil
}
func string_to_int64(in string) (int64, error) {
return strconv.ParseInt(in, 10, 64)
}
func Convert_string_To_int64(in *string, out *int64, s conversion.Scope) error {
if in == nil {
*out = 0
return nil
}
i, err := string_to_int64(*in)
if err != nil {
return err
}
*out = i
return nil
}
func Convert_Slice_string_To_int64(in *[]string, out *int64, s conversion.Scope) error {
if len(*in) == 0 {
*out = 0
return nil
}
i, err := string_to_int64((*in)[0])
if err != nil {
return err
}
*out = i
return nil
}
func Convert_string_To_Pointer_int64(in *string, out **int64, s conversion.Scope) error {
if in == nil {
*out = nil
return nil
}
i, err := string_to_int64(*in)
if err != nil {
return err
}
*out = &i
return nil
}
func Convert_Slice_string_To_Pointer_int64(in *[]string, out **int64, s conversion.Scope) error {
if len(*in) == 0 {
*out = nil
return nil
}
i, err := string_to_int64((*in)[0])
if err != nil {
return err
}
*out = &i
return nil
}
func RegisterStringConversions(s *Scheme) error {
if err := s.AddConversionFunc((*[]string)(nil), (*string)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_string(a.(*[]string), b.(*string), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*[]string)(nil), (*int)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_int(a.(*[]string), b.(*int), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*[]string)(nil), (*bool)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_bool(a.(*[]string), b.(*bool), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*[]string)(nil), (*int64)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_Slice_string_To_int64(a.(*[]string), b.(*int64), scope)
}); err != nil {
return err
}
return nil
}

View File

@@ -17,7 +17,6 @@ limitations under the License.
package runtime
import (
"bytes"
encodingjson "encoding/json"
"fmt"
"math"
@@ -32,8 +31,9 @@ import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/util/json"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"sigs.k8s.io/structured-merge-diff/v4/value"
"k8s.io/klog"
"k8s.io/klog/v2"
)
// UnstructuredConverter is an interface for converting between interface{}
@@ -68,13 +68,8 @@ func newFieldsCache() *fieldsCache {
}
var (
marshalerType = reflect.TypeOf(new(encodingjson.Marshaler)).Elem()
unmarshalerType = reflect.TypeOf(new(encodingjson.Unmarshaler)).Elem()
mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
stringType = reflect.TypeOf(string(""))
int64Type = reflect.TypeOf(int64(0))
float64Type = reflect.TypeOf(float64(0))
boolType = reflect.TypeOf(bool(false))
fieldCache = newFieldsCache()
// DefaultUnstructuredConverter performs unstructured to Go typed object conversions.
@@ -208,13 +203,9 @@ func fromUnstructured(sv, dv reflect.Value) error {
}
// Check if the object has a custom JSON marshaller/unmarshaller.
if reflect.PtrTo(dt).Implements(unmarshalerType) {
data, err := json.Marshal(sv.Interface())
if err != nil {
return fmt.Errorf("error encoding %s to json: %v", st.String(), err)
}
unmarshaler := dv.Addr().Interface().(encodingjson.Unmarshaler)
return unmarshaler.UnmarshalJSON(data)
entry := value.TypeReflectEntryOf(dv.Type())
if entry.CanConvertFromUnstructured() {
return entry.FromUnstructured(sv, dv)
}
switch dt.Kind() {
@@ -256,6 +247,7 @@ func fieldInfoFromField(structType reflect.Type, field int) *fieldInfo {
for i := range items {
if items[i] == "omitempty" {
info.omitempty = true
break
}
}
}
@@ -483,112 +475,28 @@ func toUnstructuredViaJSON(obj interface{}, u *map[string]interface{}) error {
return json.Unmarshal(data, u)
}
var (
nullBytes = []byte("null")
trueBytes = []byte("true")
falseBytes = []byte("false")
)
func getMarshaler(v reflect.Value) (encodingjson.Marshaler, bool) {
// Check value receivers if v is not a pointer and pointer receivers if v is a pointer
if v.Type().Implements(marshalerType) {
return v.Interface().(encodingjson.Marshaler), true
}
// Check pointer receivers if v is not a pointer
if v.Kind() != reflect.Ptr && v.CanAddr() {
v = v.Addr()
if v.Type().Implements(marshalerType) {
return v.Interface().(encodingjson.Marshaler), true
}
}
return nil, false
}
func toUnstructured(sv, dv reflect.Value) error {
// Check if the object has a custom JSON marshaller/unmarshaller.
if marshaler, ok := getMarshaler(sv); ok {
if sv.Kind() == reflect.Ptr && sv.IsNil() {
// We're done - we don't need to store anything.
return nil
}
data, err := marshaler.MarshalJSON()
// Check if the object has a custom string converter.
entry := value.TypeReflectEntryOf(sv.Type())
if entry.CanConvertToUnstructured() {
v, err := entry.ToUnstructured(sv)
if err != nil {
return err
}
switch {
case len(data) == 0:
return fmt.Errorf("error decoding from json: empty value")
case bytes.Equal(data, nullBytes):
// We're done - we don't need to store anything.
case bytes.Equal(data, trueBytes):
dv.Set(reflect.ValueOf(true))
case bytes.Equal(data, falseBytes):
dv.Set(reflect.ValueOf(false))
case data[0] == '"':
var result string
err := json.Unmarshal(data, &result)
if err != nil {
return fmt.Errorf("error decoding string from json: %v", err)
}
dv.Set(reflect.ValueOf(result))
case data[0] == '{':
result := make(map[string]interface{})
err := json.Unmarshal(data, &result)
if err != nil {
return fmt.Errorf("error decoding object from json: %v", err)
}
dv.Set(reflect.ValueOf(result))
case data[0] == '[':
result := make([]interface{}, 0)
err := json.Unmarshal(data, &result)
if err != nil {
return fmt.Errorf("error decoding array from json: %v", err)
}
dv.Set(reflect.ValueOf(result))
default:
var (
resultInt int64
resultFloat float64
err error
)
if err = json.Unmarshal(data, &resultInt); err == nil {
dv.Set(reflect.ValueOf(resultInt))
} else if err = json.Unmarshal(data, &resultFloat); err == nil {
dv.Set(reflect.ValueOf(resultFloat))
} else {
return fmt.Errorf("error decoding number from json: %v", err)
}
if v != nil {
dv.Set(reflect.ValueOf(v))
}
return nil
}
st, dt := sv.Type(), dv.Type()
st := sv.Type()
switch st.Kind() {
case reflect.String:
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(stringType))
}
dv.Set(reflect.ValueOf(sv.String()))
return nil
case reflect.Bool:
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(boolType))
}
dv.Set(reflect.ValueOf(sv.Bool()))
return nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(int64Type))
}
dv.Set(reflect.ValueOf(sv.Int()))
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
@@ -596,15 +504,9 @@ func toUnstructured(sv, dv reflect.Value) error {
if uVal > math.MaxInt64 {
return fmt.Errorf("unsigned value %d does not fit into int64 (overflow)", uVal)
}
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(int64Type))
}
dv.Set(reflect.ValueOf(int64(uVal)))
return nil
case reflect.Float32, reflect.Float64:
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(float64Type))
}
dv.Set(reflect.ValueOf(sv.Float()))
return nil
case reflect.Map:

View File

@@ -134,9 +134,16 @@ func Convert_runtime_RawExtension_To_runtime_Object(in *RawExtension, out *Objec
return nil
}
func DefaultEmbeddedConversions() []interface{} {
return []interface{}{
Convert_runtime_Object_To_runtime_RawExtension,
Convert_runtime_RawExtension_To_runtime_Object,
func RegisterEmbeddedConversions(s *Scheme) error {
if err := s.AddConversionFunc((*Object)(nil), (*RawExtension)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_runtime_Object_To_runtime_RawExtension(a.(*Object), b.(*RawExtension), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*RawExtension)(nil), (*Object)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_runtime_RawExtension_To_runtime_Object(a.(*RawExtension), b.(*Object), scope)
}); err != nil {
return err
}
return nil
}

View File

@@ -40,7 +40,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func (m *RawExtension) Reset() { *m = RawExtension{} }
func (*RawExtension) ProtoMessage() {}
@@ -772,6 +772,7 @@ func (m *Unknown) Unmarshal(dAtA []byte) error {
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
@@ -803,10 +804,8 @@ func skipGenerated(dAtA []byte) (n int, err error) {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
@@ -827,55 +826,30 @@ func skipGenerated(dAtA []byte) (n int, err error) {
return 0, ErrInvalidLengthGenerated
}
iNdEx += length
if iNdEx < 0 {
return 0, ErrInvalidLengthGenerated
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipGenerated(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
if iNdEx < 0 {
return 0, ErrInvalidLengthGenerated
}
}
return iNdEx, nil
depth++
case 4:
return iNdEx, nil
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupGenerated
}
depth--
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthGenerated
}
if depth == 0 {
return iNdEx, nil
}
}
panic("unreachable")
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupGenerated = fmt.Errorf("proto: unexpected end of group")
)

View File

@@ -37,13 +37,36 @@ type GroupVersioner interface {
// Scheme.New(target) and then perform a conversion between the current Go type and the destination Go type.
// Sophisticated implementations may use additional information about the input kinds to pick a destination kind.
KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (target schema.GroupVersionKind, ok bool)
// Identifier returns string representation of the object.
// Identifiers of two different encoders should be equal only if for every input
// kinds they return the same result.
Identifier() string
}
// Identifier represents an identifier.
// Identitier of two different objects should be equal if and only if for every
// input the output they produce is exactly the same.
type Identifier string
// Encoder writes objects to a serialized form
type Encoder interface {
// Encode writes an object to a stream. Implementations may return errors if the versions are
// incompatible, or if no conversion is defined.
Encode(obj Object, w io.Writer) error
// Identifier returns an identifier of the encoder.
// Identifiers of two different encoders should be equal if and only if for every input
// object it will be encoded to the same representation by both of them.
//
// Identifier is inteted for use with CacheableObject#CacheEncode method. In order to
// correctly handle CacheableObject, Encode() method should look similar to below, where
// doEncode() is the encoding logic of implemented encoder:
// func (e *MyEncoder) Encode(obj Object, w io.Writer) error {
// if co, ok := obj.(CacheableObject); ok {
// return co.CacheEncode(e.Identifier(), e.doEncode, w)
// }
// return e.doEncode(obj, w)
// }
Identifier() Identifier
}
// Decoder attempts to load an object from data.
@@ -132,6 +155,28 @@ type NegotiatedSerializer interface {
DecoderToVersion(serializer Decoder, gv GroupVersioner) Decoder
}
// ClientNegotiator handles turning an HTTP content type into the appropriate encoder.
// Use NewClientNegotiator or NewVersionedClientNegotiator to create this interface from
// a NegotiatedSerializer.
type ClientNegotiator interface {
// Encoder returns the appropriate encoder for the provided contentType (e.g. application/json)
// and any optional mediaType parameters (e.g. pretty=1), or an error. If no serializer is found
// a NegotiateError will be returned. The current client implementations consider params to be
// optional modifiers to the contentType and will ignore unrecognized parameters.
Encoder(contentType string, params map[string]string) (Encoder, error)
// Decoder returns the appropriate decoder for the provided contentType (e.g. application/json)
// and any optional mediaType parameters (e.g. pretty=1), or an error. If no serializer is found
// a NegotiateError will be returned. The current client implementations consider params to be
// optional modifiers to the contentType and will ignore unrecognized parameters.
Decoder(contentType string, params map[string]string) (Decoder, error)
// StreamDecoder returns the appropriate stream decoder for the provided contentType (e.g.
// application/json) and any optional mediaType parameters (e.g. pretty=1), or an error. If no
// serializer is found a NegotiateError will be returned. The Serializer and Framer will always
// be returned if a Decoder is returned. The current client implementations consider params to be
// optional modifiers to the contentType and will ignore unrecognized parameters.
StreamDecoder(contentType string, params map[string]string) (Decoder, Serializer, Framer, error)
}
// StorageSerializer is an interface used for obtaining encoders, decoders, and serializers
// that can read and write data at rest. This would commonly be used by client tools that must
// read files, or server side storage interfaces that persist restful objects.
@@ -256,6 +301,27 @@ type Object interface {
DeepCopyObject() Object
}
// CacheableObject allows an object to cache its different serializations
// to avoid performing the same serialization multiple times.
type CacheableObject interface {
// CacheEncode writes an object to a stream. The <encode> function will
// be used in case of cache miss. The <encode> function takes ownership
// of the object.
// If CacheableObject is a wrapper, then deep-copy of the wrapped object
// should be passed to <encode> function.
// CacheEncode assumes that for two different calls with the same <id>,
// <encode> function will also be the same.
CacheEncode(id Identifier, encode func(Object, io.Writer) error, w io.Writer) error
// GetObject returns a deep-copy of an object to be encoded - the caller of
// GetObject() is the owner of returned object. The reason for making a copy
// is to avoid bugs, where caller modifies the object and forgets to copy it,
// thus modifying the object for everyone.
// The object returned by GetObject should be the same as the one that is supposed
// to be passed to <encode> function in CacheEncode method.
// If CacheableObject is a wrapper, the copy of wrapped object should be returned.
GetObject() Object
}
// Unstructured objects store values as map[string]interface{}, with only values that can be serialized
// to JSON allowed.
type Unstructured interface {

146
vendor/k8s.io/apimachinery/pkg/runtime/negotiate.go generated vendored Normal file
View File

@@ -0,0 +1,146 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package runtime
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// NegotiateError is returned when a ClientNegotiator is unable to locate
// a serializer for the requested operation.
type NegotiateError struct {
ContentType string
Stream bool
}
func (e NegotiateError) Error() string {
if e.Stream {
return fmt.Sprintf("no stream serializers registered for %s", e.ContentType)
}
return fmt.Sprintf("no serializers registered for %s", e.ContentType)
}
type clientNegotiator struct {
serializer NegotiatedSerializer
encode, decode GroupVersioner
}
func (n *clientNegotiator) Encoder(contentType string, params map[string]string) (Encoder, error) {
// TODO: `pretty=1` is handled in NegotiateOutputMediaType, consider moving it to this method
// if client negotiators truly need to use it
mediaTypes := n.serializer.SupportedMediaTypes()
info, ok := SerializerInfoForMediaType(mediaTypes, contentType)
if !ok {
if len(contentType) != 0 || len(mediaTypes) == 0 {
return nil, NegotiateError{ContentType: contentType}
}
info = mediaTypes[0]
}
return n.serializer.EncoderForVersion(info.Serializer, n.encode), nil
}
func (n *clientNegotiator) Decoder(contentType string, params map[string]string) (Decoder, error) {
mediaTypes := n.serializer.SupportedMediaTypes()
info, ok := SerializerInfoForMediaType(mediaTypes, contentType)
if !ok {
if len(contentType) != 0 || len(mediaTypes) == 0 {
return nil, NegotiateError{ContentType: contentType}
}
info = mediaTypes[0]
}
return n.serializer.DecoderToVersion(info.Serializer, n.decode), nil
}
func (n *clientNegotiator) StreamDecoder(contentType string, params map[string]string) (Decoder, Serializer, Framer, error) {
mediaTypes := n.serializer.SupportedMediaTypes()
info, ok := SerializerInfoForMediaType(mediaTypes, contentType)
if !ok {
if len(contentType) != 0 || len(mediaTypes) == 0 {
return nil, nil, nil, NegotiateError{ContentType: contentType, Stream: true}
}
info = mediaTypes[0]
}
if info.StreamSerializer == nil {
return nil, nil, nil, NegotiateError{ContentType: info.MediaType, Stream: true}
}
return n.serializer.DecoderToVersion(info.Serializer, n.decode), info.StreamSerializer.Serializer, info.StreamSerializer.Framer, nil
}
// NewClientNegotiator will attempt to retrieve the appropriate encoder, decoder, or
// stream decoder for a given content type. Does not perform any conversion, but will
// encode the object to the desired group, version, and kind. Use when creating a client.
func NewClientNegotiator(serializer NegotiatedSerializer, gv schema.GroupVersion) ClientNegotiator {
return &clientNegotiator{
serializer: serializer,
encode: gv,
}
}
// NewInternalClientNegotiator applies the default client rules for connecting to a Kubernetes apiserver
// where objects are converted to gv prior to sending and decoded to their internal representation prior
// to retrieval.
//
// DEPRECATED: Internal clients are deprecated and will be removed in a future Kubernetes release.
func NewInternalClientNegotiator(serializer NegotiatedSerializer, gv schema.GroupVersion) ClientNegotiator {
decode := schema.GroupVersions{
{
Group: gv.Group,
Version: APIVersionInternal,
},
// always include the legacy group as a decoding target to handle non-error `Status` return types
{
Group: "",
Version: APIVersionInternal,
},
}
return &clientNegotiator{
encode: gv,
decode: decode,
serializer: serializer,
}
}
// NewSimpleClientNegotiator will negotiate for a single serializer. This should only be used
// for testing or when the caller is taking responsibility for setting the GVK on encoded objects.
func NewSimpleClientNegotiator(info SerializerInfo, gv schema.GroupVersion) ClientNegotiator {
return &clientNegotiator{
serializer: &simpleNegotiatedSerializer{info: info},
encode: gv,
}
}
type simpleNegotiatedSerializer struct {
info SerializerInfo
}
func NewSimpleNegotiatedSerializer(info SerializerInfo) NegotiatedSerializer {
return &simpleNegotiatedSerializer{info: info}
}
func (n *simpleNegotiatedSerializer) SupportedMediaTypes() []SerializerInfo {
return []SerializerInfo{n.info}
}
func (n *simpleNegotiatedSerializer) EncoderForVersion(e Encoder, _ GroupVersioner) Encoder {
return e
}
func (n *simpleNegotiatedSerializer) DecoderToVersion(d Decoder, _gv GroupVersioner) Decoder {
return d
}

View File

@@ -29,33 +29,3 @@ func (obj *TypeMeta) GroupVersionKind() schema.GroupVersionKind {
}
func (obj *TypeMeta) GetObjectKind() schema.ObjectKind { return obj }
// GetObjectKind implements Object for VersionedObjects, returning an empty ObjectKind
// interface if no objects are provided, or the ObjectKind interface of the object in the
// highest array position.
func (obj *VersionedObjects) GetObjectKind() schema.ObjectKind {
last := obj.Last()
if last == nil {
return schema.EmptyObjectKind
}
return last.GetObjectKind()
}
// First returns the leftmost object in the VersionedObjects array, which is usually the
// object as serialized on the wire.
func (obj *VersionedObjects) First() Object {
if len(obj.Objects) == 0 {
return nil
}
return obj.Objects[0]
}
// Last is the rightmost object in the VersionedObjects array, which is the object after
// all transformations have been applied. This is the same object that would be returned
// by Decode in a normal invocation (without VersionedObjects in the into argument).
func (obj *VersionedObjects) Last() Object {
if len(obj.Objects) == 0 {
return nil
}
return obj.Objects[len(obj.Objects)-1]
}

View File

@@ -36,7 +36,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func init() {
proto.RegisterFile("k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/runtime/schema/generated.proto", fileDescriptor_0462724132518e0d)

View File

@@ -176,21 +176,17 @@ func (gv GroupVersion) Empty() bool {
// String puts "group" and "version" into a single "group/version" string. For the legacy v1
// it returns "v1".
func (gv GroupVersion) String() string {
// special case the internal apiVersion for the legacy kube types
if gv.Empty() {
return ""
}
// special case of "v1" for backward compatibility
if len(gv.Group) == 0 && gv.Version == "v1" {
return gv.Version
}
if len(gv.Group) > 0 {
return gv.Group + "/" + gv.Version
}
return gv.Version
}
// Identifier implements runtime.GroupVersioner interface.
func (gv GroupVersion) Identifier() string {
return gv.String()
}
// KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
// if none of the options match the group. It prefers a match to group and version over just group.
// TODO: Move GroupVersion to a package under pkg/runtime, since it's used by scheme.
@@ -246,6 +242,15 @@ func (gv GroupVersion) WithResource(resource string) GroupVersionResource {
// in fewer places.
type GroupVersions []GroupVersion
// Identifier implements runtime.GroupVersioner interface.
func (gvs GroupVersions) Identifier() string {
groupVersions := make([]string, 0, len(gvs))
for i := range gvs {
groupVersions = append(groupVersions, gvs[i].String())
}
return fmt.Sprintf("[%s]", strings.Join(groupVersions, ","))
}
// KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
// if none of the options match the group.
func (gvs GroupVersions) KindForGroupVersionKinds(kinds []GroupVersionKind) (GroupVersionKind, bool) {

View File

@@ -102,10 +102,10 @@ func NewScheme() *Scheme {
}
s.converter = conversion.NewConverter(s.nameFunc)
utilruntime.Must(s.AddConversionFuncs(DefaultEmbeddedConversions()...))
// Enable couple default conversions by default.
utilruntime.Must(RegisterEmbeddedConversions(s))
utilruntime.Must(RegisterStringConversions(s))
// Enable map[string][]string conversions by default
utilruntime.Must(s.AddConversionFuncs(DefaultStringConversions...))
utilruntime.Must(s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields))
utilruntime.Must(s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields))
return s
@@ -211,6 +211,19 @@ func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
}
}
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
// if the type implements DeepCopyInto(<obj>), register a self-conversion
if m := reflect.ValueOf(obj).MethodByName("DeepCopyInto"); m.IsValid() && m.Type().NumIn() == 1 && m.Type().NumOut() == 0 && m.Type().In(0) == reflect.TypeOf(obj) {
if err := s.AddGeneratedConversionFunc(obj, obj, func(a, b interface{}, scope conversion.Scope) error {
// copy a to b
reflect.ValueOf(a).MethodByName("DeepCopyInto").Call([]reflect.Value{reflect.ValueOf(b)})
// clear TypeMeta to match legacy reflective conversion
b.(Object).GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
return nil
}); err != nil {
panic(err)
}
}
}
// KnownTypes returns the types known for the given version.
@@ -308,45 +321,6 @@ func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error {
return s.converter.RegisterIgnoredConversion(from, to)
}
// AddConversionFuncs adds functions to the list of conversion functions. The given
// functions should know how to convert between two of your API objects, or their
// sub-objects. We deduce how to call these functions from the types of their two
// parameters; see the comment for Converter.Register.
//
// Note that, if you need to copy sub-objects that didn't change, you can use the
// conversion.Scope object that will be passed to your conversion function.
// Additionally, all conversions started by Scheme will set the SrcVersion and
// DestVersion fields on the Meta object. Example:
//
// s.AddConversionFuncs(
// func(in *InternalObject, out *ExternalObject, scope conversion.Scope) error {
// // You can depend on Meta() being non-nil, and this being set to
// // the source version, e.g., ""
// s.Meta().SrcVersion
// // You can depend on this being set to the destination version,
// // e.g., "v1".
// s.Meta().DestVersion
// // Call scope.Convert to copy sub-fields.
// s.Convert(&in.SubFieldThatMoved, &out.NewLocation.NewName, 0)
// return nil
// },
// )
//
// (For more detail about conversion functions, see Converter.Register's comment.)
//
// Also note that the default behavior, if you don't add a conversion function, is to
// sanely copy fields that have the same names and same type names. It's OK if the
// destination type has extra fields, but it must not remove any. So you only need to
// add conversion functions for things with changed/removed fields.
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
for _, f := range conversionFuncs {
if err := s.converter.RegisterConversionFunc(f); err != nil {
return err
}
}
return nil
}
// AddConversionFunc registers a function that converts between a and b by passing objects of those
// types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
// any other guarantee.

View File

@@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
"k8s.io/apimachinery/pkg/util/framer"
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/klog/v2"
)
// NewSerializer creates a JSON serializer that handles encoding versioned objects into the proper JSON form. If typer
@@ -53,13 +54,28 @@ func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer ru
// and are immutable.
func NewSerializerWithOptions(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, options SerializerOptions) *Serializer {
return &Serializer{
meta: meta,
creater: creater,
typer: typer,
options: options,
meta: meta,
creater: creater,
typer: typer,
options: options,
identifier: identifier(options),
}
}
// identifier computes Identifier of Encoder based on the given options.
func identifier(options SerializerOptions) runtime.Identifier {
result := map[string]string{
"name": "json",
"yaml": strconv.FormatBool(options.Yaml),
"pretty": strconv.FormatBool(options.Pretty),
}
identifier, err := json.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling identifier for json Serializer: %v", err)
}
return runtime.Identifier(identifier)
}
// SerializerOptions holds the options which are used to configure a JSON/YAML serializer.
// example:
// (1) To configure a JSON serializer, set `Yaml` to `false`.
@@ -85,6 +101,8 @@ type Serializer struct {
options SerializerOptions
creater runtime.ObjectCreater
typer runtime.ObjectTyper
identifier runtime.Identifier
}
// Serializer implements Serializer
@@ -122,27 +140,7 @@ func (customNumberDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
}
iter.ReportError("DecodeNumber", err.Error())
default:
// init depth, if needed
if iter.Attachment == nil {
iter.Attachment = int(1)
}
// remember current depth
originalAttachment := iter.Attachment
// increment depth before descending
if i, ok := iter.Attachment.(int); ok {
iter.Attachment = i + 1
if i > 10000 {
iter.ReportError("parse", "exceeded max depth")
return
}
}
*(*interface{})(ptr) = iter.Read()
// restore current depth
iter.Attachment = originalAttachment
}
}
@@ -208,16 +206,6 @@ func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVer
// On success or most errors, the method will return the calculated schema kind.
// The gvk calculate priority will be originalData > default gvk > into
func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
if versioned, ok := into.(*runtime.VersionedObjects); ok {
into = versioned.Last()
obj, actual, err := s.Decode(originalData, gvk, into)
if err != nil {
return nil, actual, err
}
versioned.Objects = []runtime.Object{obj}
return versioned, actual, nil
}
data := originalData
if s.options.Yaml {
altered, err := yaml.YAMLToJSON(data)
@@ -306,6 +294,13 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
// Encode serializes the provided object to the given writer.
func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, w)
}
return s.doEncode(obj, w)
}
func (s *Serializer) doEncode(obj runtime.Object, w io.Writer) error {
if s.options.Yaml {
json, err := caseSensitiveJsonIterator.Marshal(obj)
if err != nil {
@@ -331,6 +326,11 @@ func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
return encoder.Encode(obj)
}
// Identifier implements runtime.Encoder interface.
func (s *Serializer) Identifier() runtime.Identifier {
return s.identifier
}
// RecognizesData implements the RecognizingDecoder interface.
func (s *Serializer) RecognizesData(peek io.Reader) (ok, unknown bool, err error) {
if s.options.Yaml {

View File

@@ -86,6 +86,8 @@ type Serializer struct {
var _ runtime.Serializer = &Serializer{}
var _ recognizer.RecognizingDecoder = &Serializer{}
const serializerIdentifier runtime.Identifier = "protobuf"
// Decode attempts to convert the provided data into a protobuf message, extract the stored schema kind, apply the provided default
// gvk, and then load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown,
// the raw data will be extracted and no decoding will be performed. If into is not registered with the typer, then the object will
@@ -93,23 +95,6 @@ var _ recognizer.RecognizingDecoder = &Serializer{}
// not fully qualified with kind/version/group, the type of the into will be used to alter the returned gvk. On success or most
// errors, the method will return the calculated schema kind.
func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
if versioned, ok := into.(*runtime.VersionedObjects); ok {
into = versioned.Last()
obj, actual, err := s.Decode(originalData, gvk, into)
if err != nil {
return nil, actual, err
}
// the last item in versioned becomes into, so if versioned was not originally empty we reset the object
// array so the first position is the decoded object and the second position is the outermost object.
// if there were no objects in the versioned list passed to us, only add ourselves.
if into != nil && into != obj {
versioned.Objects = []runtime.Object{obj, into}
} else {
versioned.Objects = []runtime.Object{obj}
}
return versioned, actual, err
}
prefixLen := len(s.prefix)
switch {
case len(originalData) == 0:
@@ -176,6 +161,13 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
// Encode serializes the provided object to the given writer.
func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, w)
}
return s.doEncode(obj, w)
}
func (s *Serializer) doEncode(obj runtime.Object, w io.Writer) error {
prefixSize := uint64(len(s.prefix))
var unk runtime.Unknown
@@ -245,6 +237,11 @@ func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
}
}
// Identifier implements runtime.Encoder interface.
func (s *Serializer) Identifier() runtime.Identifier {
return serializerIdentifier
}
// RecognizesData implements the RecognizingDecoder interface.
func (s *Serializer) RecognizesData(peek io.Reader) (bool, bool, error) {
prefix := make([]byte, 4)
@@ -321,6 +318,8 @@ type RawSerializer struct {
var _ runtime.Serializer = &RawSerializer{}
const rawSerializerIdentifier runtime.Identifier = "raw-protobuf"
// Decode attempts to convert the provided data into a protobuf message, extract the stored schema kind, apply the provided default
// gvk, and then load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown,
// the raw data will be extracted and no decoding will be performed. If into is not registered with the typer, then the object will
@@ -332,20 +331,6 @@ func (s *RawSerializer) Decode(originalData []byte, gvk *schema.GroupVersionKind
return nil, nil, fmt.Errorf("this serializer requires an object to decode into: %#v", s)
}
if versioned, ok := into.(*runtime.VersionedObjects); ok {
into = versioned.Last()
obj, actual, err := s.Decode(originalData, gvk, into)
if err != nil {
return nil, actual, err
}
if into != nil && into != obj {
versioned.Objects = []runtime.Object{obj, into}
} else {
versioned.Objects = []runtime.Object{obj}
}
return versioned, actual, err
}
if len(originalData) == 0 {
// TODO: treat like decoding {} from JSON with defaulting
return nil, nil, fmt.Errorf("empty data")
@@ -419,6 +404,13 @@ func unmarshalToObject(typer runtime.ObjectTyper, creater runtime.ObjectCreater,
// Encode serializes the provided object to the given writer. Overrides is ignored.
func (s *RawSerializer) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(s.Identifier(), s.doEncode, w)
}
return s.doEncode(obj, w)
}
func (s *RawSerializer) doEncode(obj runtime.Object, w io.Writer) error {
switch t := obj.(type) {
case bufferedReverseMarshaller:
// this path performs a single allocation during write but requires the caller to implement
@@ -460,6 +452,11 @@ func (s *RawSerializer) Encode(obj runtime.Object, w io.Writer) error {
}
}
// Identifier implements runtime.Encoder interface.
func (s *RawSerializer) Identifier() runtime.Identifier {
return rawSerializerIdentifier
}
var LengthDelimitedFramer = lengthDelimitedFramer{}
type lengthDelimitedFramer struct{}

View File

@@ -17,12 +17,15 @@ limitations under the License.
package versioning
import (
"encoding/json"
"io"
"reflect"
"sync"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
)
// NewDefaultingCodecForScheme is a convenience method for callers that are using a scheme.
@@ -62,6 +65,8 @@ func NewCodec(
encodeVersion: encodeVersion,
decodeVersion: decodeVersion,
identifier: identifier(encodeVersion, encoder),
originalSchemeName: originalSchemeName,
}
return internal
@@ -78,19 +83,47 @@ type codec struct {
encodeVersion runtime.GroupVersioner
decodeVersion runtime.GroupVersioner
identifier runtime.Identifier
// originalSchemeName is optional, but when filled in it holds the name of the scheme from which this codec originates
originalSchemeName string
}
var identifiersMap sync.Map
type codecIdentifier struct {
EncodeGV string `json:"encodeGV,omitempty"`
Encoder string `json:"encoder,omitempty"`
Name string `json:"name,omitempty"`
}
// identifier computes Identifier of Encoder based on codec parameters.
func identifier(encodeGV runtime.GroupVersioner, encoder runtime.Encoder) runtime.Identifier {
result := codecIdentifier{
Name: "versioning",
}
if encodeGV != nil {
result.EncodeGV = encodeGV.Identifier()
}
if encoder != nil {
result.Encoder = string(encoder.Identifier())
}
if id, ok := identifiersMap.Load(result); ok {
return id.(runtime.Identifier)
}
identifier, err := json.Marshal(result)
if err != nil {
klog.Fatalf("Failed marshaling identifier for codec: %v", err)
}
identifiersMap.Store(result, runtime.Identifier(identifier))
return runtime.Identifier(identifier)
}
// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is
// successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an
// into that matches the serialized version.
func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
versioned, isVersioned := into.(*runtime.VersionedObjects)
if isVersioned {
into = versioned.Last()
}
// 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
@@ -115,22 +148,11 @@ func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into ru
if into != nil {
// perform defaulting if requested
if c.defaulter != nil {
// create a copy to ensure defaulting is not applied to the original versioned objects
if isVersioned {
versioned.Objects = []runtime.Object{obj.DeepCopyObject()}
}
c.defaulter.Default(obj)
} else {
if isVersioned {
versioned.Objects = []runtime.Object{obj}
}
}
// Short-circuit conversion if the into object is same object
if into == obj {
if isVersioned {
return versioned, gvk, nil
}
return into, gvk, nil
}
@@ -138,19 +160,9 @@ func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into ru
return nil, gvk, err
}
if isVersioned {
versioned.Objects = append(versioned.Objects, into)
return versioned, gvk, nil
}
return into, gvk, nil
}
// Convert if needed.
if isVersioned {
// create a copy, because ConvertToVersion does not guarantee non-mutation of objects
versioned.Objects = []runtime.Object{obj.DeepCopyObject()}
}
// perform defaulting if requested
if c.defaulter != nil {
c.defaulter.Default(obj)
@@ -160,18 +172,19 @@ func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into ru
if err != nil {
return nil, gvk, err
}
if isVersioned {
if versioned.Last() != out {
versioned.Objects = append(versioned.Objects, out)
}
return versioned, gvk, nil
}
return out, gvk, nil
}
// Encode ensures the provided object is output in the appropriate group and version, invoking
// conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is.
func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
if co, ok := obj.(runtime.CacheableObject); ok {
return co.CacheEncode(c.Identifier(), c.doEncode, w)
}
return c.doEncode(obj, w)
}
func (c *codec) doEncode(obj runtime.Object, w io.Writer) error {
switch obj := obj.(type) {
case *runtime.Unknown:
return c.encoder.Encode(obj, w)
@@ -230,3 +243,8 @@ func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
// Conversion is responsible for setting the proper group, version, and kind onto the outgoing object
return c.encoder.Encode(out, w)
}
// Identifier implements runtime.Encoder interface.
func (c *codec) Identifier() runtime.Identifier {
return c.identifier
}

View File

@@ -124,16 +124,3 @@ type Unknown struct {
// Unspecified means ContentTypeJSON.
ContentType string `protobuf:"bytes,4,opt,name=contentType"`
}
// VersionedObjects is used by Decoders to give callers a way to access all versions
// of an object during the decoding process.
//
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:deepcopy-gen=true
type VersionedObjects struct {
// Objects is the set of objects retrieved during decoding, in order of conversion.
// The 0 index is the object as serialized on the wire. If conversion has occurred,
// other objects may be present. The right most object is the same as would be returned
// by a normal Decode call.
Objects []Object
}

View File

@@ -73,36 +73,3 @@ func (in *Unknown) DeepCopyObject() Object {
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VersionedObjects) DeepCopyInto(out *VersionedObjects) {
*out = *in
if in.Objects != nil {
in, out := &in.Objects, &out.Objects
*out = make([]Object, len(*in))
for i := range *in {
if (*in)[i] != nil {
(*out)[i] = (*in)[i].DeepCopyObject()
}
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VersionedObjects.
func (in *VersionedObjects) DeepCopy() *VersionedObjects {
if in == nil {
return nil
}
out := new(VersionedObjects)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new Object.
func (in *VersionedObjects) DeepCopyObject() Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

View File

@@ -52,23 +52,26 @@ func (RealClock) Since(ts time.Time) time.Duration {
return time.Since(ts)
}
// Same as time.After(d).
// After is the same as time.After(d).
func (RealClock) After(d time.Duration) <-chan time.Time {
return time.After(d)
}
// NewTimer returns a new Timer.
func (RealClock) NewTimer(d time.Duration) Timer {
return &realTimer{
timer: time.NewTimer(d),
}
}
// NewTicker returns a new Ticker.
func (RealClock) NewTicker(d time.Duration) Ticker {
return &realTicker{
ticker: time.NewTicker(d),
}
}
// Sleep pauses the RealClock for duration d.
func (RealClock) Sleep(d time.Duration) {
time.Sleep(d)
}
@@ -94,12 +97,14 @@ type fakeClockWaiter struct {
destChan chan time.Time
}
// NewFakePassiveClock returns a new FakePassiveClock.
func NewFakePassiveClock(t time.Time) *FakePassiveClock {
return &FakePassiveClock{
time: t,
}
}
// NewFakeClock returns a new FakeClock
func NewFakeClock(t time.Time) *FakeClock {
return &FakeClock{
FakePassiveClock: *NewFakePassiveClock(t),
@@ -120,14 +125,14 @@ func (f *FakePassiveClock) Since(ts time.Time) time.Duration {
return f.time.Sub(ts)
}
// Sets the time.
// SetTime sets the time on the FakePassiveClock.
func (f *FakePassiveClock) SetTime(t time.Time) {
f.lock.Lock()
defer f.lock.Unlock()
f.time = t
}
// Fake version of time.After(d).
// After is the Fake version of time.After(d).
func (f *FakeClock) After(d time.Duration) <-chan time.Time {
f.lock.Lock()
defer f.lock.Unlock()
@@ -140,7 +145,7 @@ func (f *FakeClock) After(d time.Duration) <-chan time.Time {
return ch
}
// Fake version of time.NewTimer(d).
// NewTimer is the Fake version of time.NewTimer(d).
func (f *FakeClock) NewTimer(d time.Duration) Timer {
f.lock.Lock()
defer f.lock.Unlock()
@@ -157,6 +162,7 @@ func (f *FakeClock) NewTimer(d time.Duration) Timer {
return timer
}
// NewTicker returns a new Ticker.
func (f *FakeClock) NewTicker(d time.Duration) Ticker {
f.lock.Lock()
defer f.lock.Unlock()
@@ -174,14 +180,14 @@ func (f *FakeClock) NewTicker(d time.Duration) Ticker {
}
}
// Move clock by Duration, notify anyone that's called After, Tick, or NewTimer
// Step moves clock by Duration, notifies anyone that's called After, Tick, or NewTimer
func (f *FakeClock) Step(d time.Duration) {
f.lock.Lock()
defer f.lock.Unlock()
f.setTimeLocked(f.time.Add(d))
}
// Sets the time.
// SetTime sets the time on a FakeClock.
func (f *FakeClock) SetTime(t time.Time) {
f.lock.Lock()
defer f.lock.Unlock()
@@ -219,7 +225,7 @@ func (f *FakeClock) setTimeLocked(t time.Time) {
f.waiters = newWaiters
}
// Returns true if After has been called on f but not yet satisfied (so you can
// HasWaiters returns true if After has been called on f but not yet satisfied (so you can
// write race-free tests).
func (f *FakeClock) HasWaiters() bool {
f.lock.RLock()
@@ -227,6 +233,7 @@ func (f *FakeClock) HasWaiters() bool {
return len(f.waiters) > 0
}
// Sleep pauses the FakeClock for duration d.
func (f *FakeClock) Sleep(d time.Duration) {
f.Step(d)
}
@@ -248,24 +255,25 @@ func (i *IntervalClock) Since(ts time.Time) time.Duration {
return i.Time.Sub(ts)
}
// Unimplemented, will panic.
// After is currently unimplemented, will panic.
// TODO: make interval clock use FakeClock so this can be implemented.
func (*IntervalClock) After(d time.Duration) <-chan time.Time {
panic("IntervalClock doesn't implement After")
}
// Unimplemented, will panic.
// NewTimer is currently unimplemented, will panic.
// TODO: make interval clock use FakeClock so this can be implemented.
func (*IntervalClock) NewTimer(d time.Duration) Timer {
panic("IntervalClock doesn't implement NewTimer")
}
// Unimplemented, will panic.
// NewTicker is currently unimplemented, will panic.
// TODO: make interval clock use FakeClock so this can be implemented.
func (*IntervalClock) NewTicker(d time.Duration) Ticker {
panic("IntervalClock doesn't implement NewTicker")
}
// Sleep is currently unimplemented; will panic.
func (*IntervalClock) Sleep(d time.Duration) {
panic("IntervalClock doesn't implement Sleep")
}
@@ -355,6 +363,7 @@ func (f *fakeTimer) Reset(d time.Duration) bool {
return false
}
// Ticker defines the Ticker interface
type Ticker interface {
C() <-chan time.Time
Stop()

View File

@@ -28,9 +28,14 @@ type MessageCountMap map[string]int
// Aggregate represents an object that contains multiple errors, but does not
// necessarily have singular semantic meaning.
// The aggregate can be used with `errors.Is()` to check for the occurrence of
// a specific error type.
// Errors.As() is not supported, because the caller presumably cares about a
// specific error of potentially multiple that match the given type.
type Aggregate interface {
error
Errors() []error
Is(error) bool
}
// NewAggregate converts a slice of errors into an Aggregate interface, which
@@ -71,16 +76,17 @@ func (agg aggregate) Error() string {
}
seenerrs := sets.NewString()
result := ""
agg.visit(func(err error) {
agg.visit(func(err error) bool {
msg := err.Error()
if seenerrs.Has(msg) {
return
return false
}
seenerrs.Insert(msg)
if len(seenerrs) > 1 {
result += ", "
}
result += msg
return false
})
if len(seenerrs) == 1 {
return result
@@ -88,19 +94,33 @@ func (agg aggregate) Error() string {
return "[" + result + "]"
}
func (agg aggregate) visit(f func(err error)) {
func (agg aggregate) Is(target error) bool {
return agg.visit(func(err error) bool {
return errors.Is(err, target)
})
}
func (agg aggregate) visit(f func(err error) bool) bool {
for _, err := range agg {
switch err := err.(type) {
case aggregate:
err.visit(f)
if match := err.visit(f); match {
return match
}
case Aggregate:
for _, nestedErr := range err.Errors() {
f(nestedErr)
if match := f(nestedErr); match {
return match
}
}
default:
f(err)
if match := f(err); match {
return match
}
}
}
return false
}
// Errors is part of the Aggregate interface.

View File

@@ -114,6 +114,18 @@ func negotiateProtocol(clientProtocols, serverProtocols []string) string {
return ""
}
func commaSeparatedHeaderValues(header []string) []string {
var parsedClientProtocols []string
for i := range header {
for _, clientProtocol := range strings.Split(header[i], ",") {
if proto := strings.Trim(clientProtocol, " "); len(proto) > 0 {
parsedClientProtocols = append(parsedClientProtocols, proto)
}
}
}
return parsedClientProtocols
}
// Handshake performs a subprotocol negotiation. If the client did request a
// subprotocol, Handshake will select the first common value found in
// serverProtocols. If a match is found, Handshake adds a response header
@@ -121,17 +133,13 @@ func negotiateProtocol(clientProtocols, serverProtocols []string) string {
// returned, along with a response header containing the list of protocols the
// server can accept.
func Handshake(req *http.Request, w http.ResponseWriter, serverProtocols []string) (string, error) {
clientProtocols := req.Header[http.CanonicalHeaderKey(HeaderProtocolVersion)]
clientProtocols := commaSeparatedHeaderValues(req.Header[http.CanonicalHeaderKey(HeaderProtocolVersion)])
if len(clientProtocols) == 0 {
// Kube 1.0 clients didn't support subprotocol negotiation.
// TODO require clientProtocols once Kube 1.0 is no longer supported
return "", nil
return "", fmt.Errorf("unable to upgrade: %s is required", HeaderProtocolVersion)
}
if len(serverProtocols) == 0 {
// Kube 1.0 servers didn't support subprotocol negotiation. This is mainly for testing.
// TODO require serverProtocols once Kube 1.0 is no longer supported
return "", nil
panic(fmt.Errorf("unable to upgrade: serverProtocols is required"))
}
negotiatedProtocol := negotiateProtocol(clientProtocols, serverProtocols)

View File

@@ -24,7 +24,7 @@ import (
"github.com/docker/spdystream"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/klog"
"k8s.io/klog/v2"
)
// connection maintains state about a spdystream.Connection and its associated

View File

@@ -76,19 +76,20 @@ var _ utilnet.TLSClientConfigHolder = &SpdyRoundTripper{}
var _ httpstream.UpgradeRoundTripper = &SpdyRoundTripper{}
var _ utilnet.Dialer = &SpdyRoundTripper{}
// NewRoundTripper creates a new SpdyRoundTripper that will use
// the specified tlsConfig.
func NewRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) httpstream.UpgradeRoundTripper {
return NewSpdyRoundTripper(tlsConfig, followRedirects, requireSameHostRedirects)
// NewRoundTripper creates a new SpdyRoundTripper that will use the specified
// tlsConfig.
func NewRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) *SpdyRoundTripper {
return NewRoundTripperWithProxy(tlsConfig, followRedirects, requireSameHostRedirects, utilnet.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment))
}
// NewSpdyRoundTripper creates a new SpdyRoundTripper that will use
// the specified tlsConfig. This function is mostly meant for unit tests.
func NewSpdyRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) *SpdyRoundTripper {
// NewRoundTripperWithProxy creates a new SpdyRoundTripper that will use the
// specified tlsConfig and proxy func.
func NewRoundTripperWithProxy(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool, proxier func(*http.Request) (*url.URL, error)) *SpdyRoundTripper {
return &SpdyRoundTripper{
tlsConfig: tlsConfig,
followRedirects: followRedirects,
requireSameHostRedirects: requireSameHostRedirects,
proxier: proxier,
}
}
@@ -116,11 +117,7 @@ func (s *SpdyRoundTripper) Dial(req *http.Request) (net.Conn, error) {
// dial dials the host specified by req, using TLS if appropriate, optionally
// using a proxy server if one is configured via environment variables.
func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
proxier := s.proxier
if proxier == nil {
proxier = utilnet.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment)
}
proxyURL, err := proxier(req)
proxyURL, err := s.proxier(req)
if err != nil {
return nil, err
}

View File

@@ -38,7 +38,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func (m *IntOrString) Reset() { *m = IntOrString{} }
func (*IntOrString) ProtoMessage() {}
@@ -289,6 +289,7 @@ func (m *IntOrString) Unmarshal(dAtA []byte) error {
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
@@ -320,10 +321,8 @@ func skipGenerated(dAtA []byte) (n int, err error) {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
@@ -344,55 +343,30 @@ func skipGenerated(dAtA []byte) (n int, err error) {
return 0, ErrInvalidLengthGenerated
}
iNdEx += length
if iNdEx < 0 {
return 0, ErrInvalidLengthGenerated
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipGenerated(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
if iNdEx < 0 {
return 0, ErrInvalidLengthGenerated
}
}
return iNdEx, nil
depth++
case 4:
return iNdEx, nil
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupGenerated
}
depth--
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthGenerated
}
if depth == 0 {
return iNdEx, nil
}
}
panic("unreachable")
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupGenerated = fmt.Errorf("proto: unexpected end of group")
)

View File

@@ -26,7 +26,7 @@ import (
"strings"
"github.com/google/gofuzz"
"k8s.io/klog"
"k8s.io/klog/v2"
)
// IntOrString is a type that can hold an int32 or a string. When used in
@@ -97,7 +97,8 @@ func (intstr *IntOrString) String() string {
}
// IntValue returns the IntVal if type Int, or if
// it is a String, will attempt a conversion to int.
// it is a String, will attempt a conversion to int,
// returning 0 if a parsing error occurs.
func (intstr *IntOrString) IntValue() int {
if intstr.Type == String {
i, _ := strconv.Atoi(intstr.StrVal)

View File

@@ -66,11 +66,36 @@ func Unmarshal(data []byte, v interface{}) error {
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
return convertSliceNumbers(*v, 0)
case *interface{}:
// Build a decoder from the given data
decoder := json.NewDecoder(bytes.NewBuffer(data))
// Preserve numbers, rather than casting to float64 automatically
decoder.UseNumber()
// Run the decode
if err := decoder.Decode(v); err != nil {
return err
}
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
return convertInterfaceNumbers(v, 0)
default:
return json.Unmarshal(data, v)
}
}
func convertInterfaceNumbers(v *interface{}, depth int) error {
var err error
switch v2 := (*v).(type) {
case json.Number:
*v, err = convertNumber(v2)
case map[string]interface{}:
err = convertMapNumbers(v2, depth+1)
case []interface{}:
err = convertSliceNumbers(v2, depth+1)
}
return err
}
// convertMapNumbers traverses the map, converting any json.Number values to int64 or float64.
// values which are map[string]interface{} or []interface{} are recursively visited
func convertMapNumbers(m map[string]interface{}, depth int) error {

View File

@@ -82,7 +82,7 @@ var stackCreator = regexp.MustCompile(`(?m)^created by (.*)\n\s+(.*):(\d+) \+0x[
func extractStackCreator() (string, int, bool) {
stack := debug.Stack()
matches := stackCreator.FindStringSubmatch(string(stack))
if matches == nil || len(matches) != 4 {
if len(matches) != 4 {
return "", 0, false
}
line, err := strconv.Atoi(matches[3])

View File

@@ -21,18 +21,23 @@ import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"mime"
"net"
"net/http"
"net/url"
"os"
"path"
"regexp"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/net/http2"
"k8s.io/klog"
"k8s.io/klog/v2"
)
// JoinPreservingTrailingSlash does a path.Join of the specified elements,
@@ -55,6 +60,15 @@ func JoinPreservingTrailingSlash(elem ...string) string {
return result
}
// IsTimeout returns true if the given error is a network timeout error
func IsTimeout(err error) bool {
var neterr net.Error
if errors.As(err, &neterr) {
return neterr != nil && neterr.Timeout()
}
return false
}
// IsProbableEOF returns true if the given error resembles a connection termination
// scenario that would justify assuming that the watch is empty.
// These errors are what the Go http stack returns back to us which are general
@@ -65,13 +79,16 @@ func IsProbableEOF(err error) bool {
if err == nil {
return false
}
if uerr, ok := err.(*url.Error); ok {
var uerr *url.Error
if errors.As(err, &uerr) {
err = uerr.Err
}
msg := err.Error()
switch {
case err == io.EOF:
return true
case err == io.ErrUnexpectedEOF:
return true
case msg == "http: can't write HTTP request on broken connection":
return true
case strings.Contains(msg, "http2: server sent GOAWAY and closed the connection"):
@@ -206,13 +223,17 @@ func GetHTTPClient(req *http.Request) string {
return "unknown"
}
// SourceIPs splits the comma separated X-Forwarded-For header or returns the X-Real-Ip header or req.RemoteAddr,
// in that order, ignoring invalid IPs. It returns nil if all of these are empty or invalid.
// SourceIPs splits the comma separated X-Forwarded-For header and joins it with
// the X-Real-Ip header and/or req.RemoteAddr, ignoring invalid IPs.
// The X-Real-Ip is omitted if it's already present in the X-Forwarded-For chain.
// The req.RemoteAddr is always the last IP in the returned list.
// It returns nil if all of these are empty or invalid.
func SourceIPs(req *http.Request) []net.IP {
var srcIPs []net.IP
hdr := req.Header
// First check the X-Forwarded-For header for requests via proxy.
hdrForwardedFor := hdr.Get("X-Forwarded-For")
forwardedForIPs := []net.IP{}
if hdrForwardedFor != "" {
// X-Forwarded-For can be a csv of IPs in case of multiple proxies.
// Use the first valid one.
@@ -220,38 +241,49 @@ func SourceIPs(req *http.Request) []net.IP {
for _, part := range parts {
ip := net.ParseIP(strings.TrimSpace(part))
if ip != nil {
forwardedForIPs = append(forwardedForIPs, ip)
srcIPs = append(srcIPs, ip)
}
}
}
if len(forwardedForIPs) > 0 {
return forwardedForIPs
}
// Try the X-Real-Ip header.
hdrRealIp := hdr.Get("X-Real-Ip")
if hdrRealIp != "" {
ip := net.ParseIP(hdrRealIp)
if ip != nil {
return []net.IP{ip}
// Only append the X-Real-Ip if it's not already contained in the X-Forwarded-For chain.
if ip != nil && !containsIP(srcIPs, ip) {
srcIPs = append(srcIPs, ip)
}
}
// Fallback to Remote Address in request, which will give the correct client IP when there is no proxy.
// Always include the request Remote Address as it cannot be easily spoofed.
var remoteIP net.IP
// Remote Address in Go's HTTP server is in the form host:port so we need to split that first.
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err == nil {
if remoteIP := net.ParseIP(host); remoteIP != nil {
return []net.IP{remoteIP}
remoteIP = net.ParseIP(host)
}
// Fallback if Remote Address was just IP.
if remoteIP == nil {
remoteIP = net.ParseIP(req.RemoteAddr)
}
// Don't duplicate remote IP if it's already the last address in the chain.
if remoteIP != nil && (len(srcIPs) == 0 || !remoteIP.Equal(srcIPs[len(srcIPs)-1])) {
srcIPs = append(srcIPs, remoteIP)
}
return srcIPs
}
// Checks whether the given IP address is contained in the list of IPs.
func containsIP(ips []net.IP, ip net.IP) bool {
for _, v := range ips {
if v.Equal(ip) {
return true
}
}
// Fallback if Remote Address was just IP.
if remoteIP := net.ParseIP(req.RemoteAddr); remoteIP != nil {
return []net.IP{remoteIP}
}
return nil
return false
}
// Extracts and returns the clients IP from the given request.
@@ -425,7 +457,7 @@ redirectLoop:
// Only follow redirects to the same host. Otherwise, propagate the redirect response back.
if requireSameHostRedirects && location.Hostname() != originalLocation.Hostname() {
break redirectLoop
return nil, nil, fmt.Errorf("hostname mismatch: expected %s, found %s", originalLocation.Hostname(), location.Hostname())
}
// Reset the connection.
@@ -461,3 +493,232 @@ func CloneHeader(in http.Header) http.Header {
}
return out
}
// WarningHeader contains a single RFC2616 14.46 warnings header
type WarningHeader struct {
// Codeindicates the type of warning. 299 is a miscellaneous persistent warning
Code int
// Agent contains the name or pseudonym of the server adding the Warning header.
// A single "-" is recommended when agent is unknown.
Agent string
// Warning text
Text string
}
// ParseWarningHeaders extract RFC2616 14.46 warnings headers from the specified set of header values.
// Multiple comma-separated warnings per header are supported.
// If errors are encountered on a header, the remainder of that header are skipped and subsequent headers are parsed.
// Returns successfully parsed warnings and any errors encountered.
func ParseWarningHeaders(headers []string) ([]WarningHeader, []error) {
var (
results []WarningHeader
errs []error
)
for _, header := range headers {
for len(header) > 0 {
result, remainder, err := ParseWarningHeader(header)
if err != nil {
errs = append(errs, err)
break
}
results = append(results, result)
header = remainder
}
}
return results, errs
}
var (
codeMatcher = regexp.MustCompile(`^[0-9]{3}$`)
wordDecoder = &mime.WordDecoder{}
)
// ParseWarningHeader extracts one RFC2616 14.46 warning from the specified header,
// returning an error if the header does not contain a correctly formatted warning.
// Any remaining content in the header is returned.
func ParseWarningHeader(header string) (result WarningHeader, remainder string, err error) {
// https://tools.ietf.org/html/rfc2616#section-14.46
// updated by
// https://tools.ietf.org/html/rfc7234#section-5.5
// https://tools.ietf.org/html/rfc7234#appendix-A
// Some requirements regarding production and processing of the Warning
// header fields have been relaxed, as it is not widely implemented.
// Furthermore, the Warning header field no longer uses RFC 2047
// encoding, nor does it allow multiple languages, as these aspects were
// not implemented.
//
// Format is one of:
// warn-code warn-agent "warn-text"
// warn-code warn-agent "warn-text" "warn-date"
//
// warn-code is a three digit number
// warn-agent is unquoted and contains no spaces
// warn-text is quoted with backslash escaping (RFC2047-encoded according to RFC2616, not encoded according to RFC7234)
// warn-date is optional, quoted, and in HTTP-date format (no embedded or escaped quotes)
//
// additional warnings can optionally be included in the same header by comma-separating them:
// warn-code warn-agent "warn-text" "warn-date"[, warn-code warn-agent "warn-text" "warn-date", ...]
// tolerate leading whitespace
header = strings.TrimSpace(header)
parts := strings.SplitN(header, " ", 3)
if len(parts) != 3 {
return WarningHeader{}, "", errors.New("invalid warning header: fewer than 3 segments")
}
code, agent, textDateRemainder := parts[0], parts[1], parts[2]
// verify code format
if !codeMatcher.Match([]byte(code)) {
return WarningHeader{}, "", errors.New("invalid warning header: code segment is not 3 digits between 100-299")
}
codeInt, _ := strconv.ParseInt(code, 10, 64)
// verify agent presence
if len(agent) == 0 {
return WarningHeader{}, "", errors.New("invalid warning header: empty agent segment")
}
if !utf8.ValidString(agent) || hasAnyRunes(agent, unicode.IsControl) {
return WarningHeader{}, "", errors.New("invalid warning header: invalid agent")
}
// verify textDateRemainder presence
if len(textDateRemainder) == 0 {
return WarningHeader{}, "", errors.New("invalid warning header: empty text segment")
}
// extract text
text, dateAndRemainder, err := parseQuotedString(textDateRemainder)
if err != nil {
return WarningHeader{}, "", fmt.Errorf("invalid warning header: %v", err)
}
// tolerate RFC2047-encoded text from warnings produced according to RFC2616
if decodedText, err := wordDecoder.DecodeHeader(text); err == nil {
text = decodedText
}
if !utf8.ValidString(text) || hasAnyRunes(text, unicode.IsControl) {
return WarningHeader{}, "", errors.New("invalid warning header: invalid text")
}
result = WarningHeader{Code: int(codeInt), Agent: agent, Text: text}
if len(dateAndRemainder) > 0 {
if dateAndRemainder[0] == '"' {
// consume date
foundEndQuote := false
for i := 1; i < len(dateAndRemainder); i++ {
if dateAndRemainder[i] == '"' {
foundEndQuote = true
remainder = strings.TrimSpace(dateAndRemainder[i+1:])
break
}
}
if !foundEndQuote {
return WarningHeader{}, "", errors.New("invalid warning header: unterminated date segment")
}
} else {
remainder = dateAndRemainder
}
}
if len(remainder) > 0 {
if remainder[0] == ',' {
// consume comma if present
remainder = strings.TrimSpace(remainder[1:])
} else {
return WarningHeader{}, "", errors.New("invalid warning header: unexpected token after warn-date")
}
}
return result, remainder, nil
}
func parseQuotedString(quotedString string) (string, string, error) {
if len(quotedString) == 0 {
return "", "", errors.New("invalid quoted string: 0-length")
}
if quotedString[0] != '"' {
return "", "", errors.New("invalid quoted string: missing initial quote")
}
quotedString = quotedString[1:]
var remainder string
escaping := false
closedQuote := false
result := &bytes.Buffer{}
loop:
for i := 0; i < len(quotedString); i++ {
b := quotedString[i]
switch b {
case '"':
if escaping {
result.WriteByte(b)
escaping = false
} else {
closedQuote = true
remainder = strings.TrimSpace(quotedString[i+1:])
break loop
}
case '\\':
if escaping {
result.WriteByte(b)
escaping = false
} else {
escaping = true
}
default:
result.WriteByte(b)
escaping = false
}
}
if !closedQuote {
return "", "", errors.New("invalid quoted string: missing closing quote")
}
return result.String(), remainder, nil
}
func NewWarningHeader(code int, agent, text string) (string, error) {
if code < 0 || code > 999 {
return "", errors.New("code must be between 0 and 999")
}
if len(agent) == 0 {
agent = "-"
} else if !utf8.ValidString(agent) || strings.ContainsAny(agent, `\"`) || hasAnyRunes(agent, unicode.IsSpace, unicode.IsControl) {
return "", errors.New("agent must be valid UTF-8 and must not contain spaces, quotes, backslashes, or control characters")
}
if !utf8.ValidString(text) || hasAnyRunes(text, unicode.IsControl) {
return "", errors.New("text must be valid UTF-8 and must not contain control characters")
}
return fmt.Sprintf("%03d %s %s", code, agent, makeQuotedString(text)), nil
}
func hasAnyRunes(s string, runeCheckers ...func(rune) bool) bool {
for _, r := range s {
for _, checker := range runeCheckers {
if checker(r) {
return true
}
}
}
return false
}
func makeQuotedString(s string) string {
result := &bytes.Buffer{}
// opening quote
result.WriteRune('"')
for _, c := range s {
switch c {
case '"', '\\':
// escape " and \
result.WriteRune('\\')
result.WriteRune(c)
default:
// write everything else as-is
result.WriteRune(c)
}
}
// closing quote
result.WriteRune('"')
return result.String()
}

View File

@@ -26,7 +26,7 @@ import (
"strings"
"k8s.io/klog"
"k8s.io/klog/v2"
)
type AddressFamily uint
@@ -36,6 +36,18 @@ const (
familyIPv6 AddressFamily = 6
)
type AddressFamilyPreference []AddressFamily
var (
preferIPv4 = AddressFamilyPreference{familyIPv4, familyIPv6}
preferIPv6 = AddressFamilyPreference{familyIPv6, familyIPv4}
)
const (
// LoopbackInterfaceName is the default name of the loopback interface
LoopbackInterfaceName = "lo"
)
const (
ipv4RouteFile = "/proc/net/route"
ipv6RouteFile = "/proc/net/ipv6_route"
@@ -53,7 +65,7 @@ type RouteFile struct {
parse func(input io.Reader) ([]Route, error)
}
// noRoutesError can be returned by ChooseBindAddress() in case of no routes
// noRoutesError can be returned in case of no routes
type noRoutesError struct {
message string
}
@@ -254,7 +266,7 @@ func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInte
return nil, nil
}
// memberOF tells if the IP is of the desired family. Used for checking interface addresses.
// memberOf tells if the IP is of the desired family. Used for checking interface addresses.
func memberOf(ip net.IP, family AddressFamily) bool {
if ip.To4() != nil {
return family == familyIPv4
@@ -265,8 +277,8 @@ func memberOf(ip net.IP, family AddressFamily) bool {
// chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
// has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
// Searches for IPv4 addresses, and then IPv6 addresses.
func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
// addressFamilies determines whether it prefers IPv4 or IPv6
func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
intfs, err := nw.Interfaces()
if err != nil {
return nil, err
@@ -274,7 +286,7 @@ func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
if len(intfs) == 0 {
return nil, fmt.Errorf("no interfaces found on host.")
}
for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
for _, family := range addressFamilies {
klog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
for _, intf := range intfs {
if !isInterfaceUp(&intf) {
@@ -321,15 +333,19 @@ func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
// IP of the interface with a gateway on it (with priority given to IPv4). For a node
// with no internet connection, it returns error.
func ChooseHostInterface() (net.IP, error) {
return chooseHostInterface(preferIPv4)
}
func chooseHostInterface(addressFamilies AddressFamilyPreference) (net.IP, error) {
var nw networkInterfacer = networkInterface{}
if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
return chooseIPFromHostInterfaces(nw)
return chooseIPFromHostInterfaces(nw, addressFamilies)
}
routes, err := getAllDefaultRoutes()
if err != nil {
return nil, err
}
return chooseHostInterfaceFromRoute(routes, nw)
return chooseHostInterfaceFromRoute(routes, nw, addressFamilies)
}
// networkInterfacer defines an interface for several net library functions. Production
@@ -377,10 +393,10 @@ func getAllDefaultRoutes() ([]Route, error) {
}
// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
// global IP address from the interface for the route. Will first look all each IPv4 route for
// an IPv4 IP, and then will look at each IPv6 route for an IPv6 IP.
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP, error) {
for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
// global IP address from the interface for the route. addressFamilies determines whether it
// prefers IPv4 or IPv6
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
for _, family := range addressFamilies {
klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
for _, route := range routes {
if route.Family != family {
@@ -401,12 +417,19 @@ func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP,
return nil, fmt.Errorf("unable to select an IP from default routes.")
}
// If bind-address is usable, return it directly
// If bind-address is not usable (unset, 0.0.0.0, or loopback), we will use the host's default
// interface.
func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
// ResolveBindAddress returns the IP address of a daemon, based on the given bindAddress:
// If bindAddress is unset, it returns the host's default IP, as with ChooseHostInterface().
// If bindAddress is unspecified or loopback, it returns the default IP of the same
// address family as bindAddress.
// Otherwise, it just returns bindAddress.
func ResolveBindAddress(bindAddress net.IP) (net.IP, error) {
addressFamilies := preferIPv4
if bindAddress != nil && memberOf(bindAddress, familyIPv6) {
addressFamilies = preferIPv6
}
if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
hostIP, err := ChooseHostInterface()
hostIP, err := chooseHostInterface(addressFamilies)
if err != nil {
return nil, err
}
@@ -414,3 +437,21 @@ func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
}
return bindAddress, nil
}
// ChooseBindAddressForInterface choose a global IP for a specific interface, with priority given to IPv4.
// This is required in case of network setups where default routes are present, but network
// interfaces use only link-local addresses (e.g. as described in RFC5549).
// e.g when using BGP to announce a host IP over link-local ip addresses and this ip address is attached to the lo interface.
func ChooseBindAddressForInterface(intfName string) (net.IP, error) {
var nw networkInterfacer = networkInterface{}
for _, family := range preferIPv4 {
ip, err := getIPFromInterface(intfName, family, nw)
if err != nil {
return nil, err
}
if ip != nil {
return ip, nil
}
}
return nil, fmt.Errorf("unable to select an IP from %s network interface", intfName)
}

View File

@@ -17,9 +17,8 @@ limitations under the License.
package net
import (
"errors"
"net"
"net/url"
"os"
"reflect"
"syscall"
)
@@ -40,34 +39,18 @@ func IPNetEqual(ipnet1, ipnet2 *net.IPNet) bool {
// Returns if the given err is "connection reset by peer" error.
func IsConnectionReset(err error) bool {
if urlErr, ok := err.(*url.Error); ok {
err = urlErr.Err
}
if opErr, ok := err.(*net.OpError); ok {
err = opErr.Err
}
if osErr, ok := err.(*os.SyscallError); ok {
err = osErr.Err
}
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ECONNRESET {
return true
var errno syscall.Errno
if errors.As(err, &errno) {
return errno == syscall.ECONNRESET
}
return false
}
// Returns if the given err is "connection refused" error
func IsConnectionRefused(err error) bool {
if urlErr, ok := err.(*url.Error); ok {
err = urlErr.Err
}
if opErr, ok := err.(*net.OpError); ok {
err = opErr.Err
}
if osErr, ok := err.(*os.SyscallError); ok {
err = osErr.Err
}
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ECONNREFUSED {
return true
var errno syscall.Errno
if errors.As(err, &errno) {
return errno == syscall.ECONNREFUSED
}
return false
}

View File

@@ -23,7 +23,7 @@ import (
"sync"
"time"
"k8s.io/klog"
"k8s.io/klog/v2"
)
var (

View File

@@ -204,7 +204,7 @@ func Forbidden(field *Path, detail string) *Error {
// Invalid, but the returned error will not include the too-long
// value.
func TooLong(field *Path, value interface{}, maxLength int) *Error {
return &Error{ErrorTypeTooLong, field.String(), value, fmt.Sprintf("must have at most %d characters", maxLength)}
return &Error{ErrorTypeTooLong, field.String(), value, fmt.Sprintf("must have at most %d bytes", maxLength)}
}
// TooMany returns a *Error indicating "too many". This is used to

View File

@@ -70,7 +70,11 @@ func IsQualifiedName(value string) []string {
return errs
}
// IsFullyQualifiedName checks if the name is fully qualified.
// IsFullyQualifiedName checks if the name is fully qualified. This is similar
// to IsFullyQualifiedDomainName but requires a minimum of 3 segments instead of
// 2 and does not accept a trailing . as valid.
// TODO: This function is deprecated and preserved until all callers migrate to
// IsFullyQualifiedDomainName; please don't add new callers.
func IsFullyQualifiedName(fldPath *field.Path, name string) field.ErrorList {
var allErrors field.ErrorList
if len(name) == 0 {
@@ -85,6 +89,69 @@ func IsFullyQualifiedName(fldPath *field.Path, name string) field.ErrorList {
return allErrors
}
// IsFullyQualifiedDomainName checks if the domain name is fully qualified. This
// is similar to IsFullyQualifiedName but only requires a minimum of 2 segments
// instead of 3 and accepts a trailing . as valid.
func IsFullyQualifiedDomainName(fldPath *field.Path, name string) field.ErrorList {
var allErrors field.ErrorList
if len(name) == 0 {
return append(allErrors, field.Required(fldPath, ""))
}
if strings.HasSuffix(name, ".") {
name = name[:len(name)-1]
}
if errs := IsDNS1123Subdomain(name); len(errs) > 0 {
return append(allErrors, field.Invalid(fldPath, name, strings.Join(errs, ",")))
}
if len(strings.Split(name, ".")) < 2 {
return append(allErrors, field.Invalid(fldPath, name, "should be a domain with at least two segments separated by dots"))
}
for _, label := range strings.Split(name, ".") {
if errs := IsDNS1123Label(label); len(errs) > 0 {
return append(allErrors, field.Invalid(fldPath, label, strings.Join(errs, ",")))
}
}
return allErrors
}
// Allowed characters in an HTTP Path as defined by RFC 3986. A HTTP path may
// contain:
// * unreserved characters (alphanumeric, '-', '.', '_', '~')
// * percent-encoded octets
// * sub-delims ("!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=")
// * a colon character (":")
const httpPathFmt string = `[A-Za-z0-9/\-._~%!$&'()*+,;=:]+`
var httpPathRegexp = regexp.MustCompile("^" + httpPathFmt + "$")
// IsDomainPrefixedPath checks if the given string is a domain-prefixed path
// (e.g. acme.io/foo). All characters before the first "/" must be a valid
// subdomain as defined by RFC 1123. All characters trailing the first "/" must
// be valid HTTP Path characters as defined by RFC 3986.
func IsDomainPrefixedPath(fldPath *field.Path, dpPath string) field.ErrorList {
var allErrs field.ErrorList
if len(dpPath) == 0 {
return append(allErrs, field.Required(fldPath, ""))
}
segments := strings.SplitN(dpPath, "/", 2)
if len(segments) != 2 || len(segments[0]) == 0 || len(segments[1]) == 0 {
return append(allErrs, field.Invalid(fldPath, dpPath, "must be a domain-prefixed path (such as \"acme.io/foo\")"))
}
host := segments[0]
for _, err := range IsDNS1123Subdomain(host) {
allErrs = append(allErrs, field.Invalid(fldPath, host, err))
}
path := segments[1]
if !httpPathRegexp.MatchString(path) {
return append(allErrs, field.Invalid(fldPath, path, RegexError("Invalid path", httpPathFmt)))
}
return allErrs
}
const labelValueFmt string = "(" + qualifiedNameFmt + ")?"
const labelValueErrMsg string = "a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
@@ -285,6 +352,26 @@ func IsValidIP(value string) []string {
return nil
}
// IsValidIPv4Address tests that the argument is a valid IPv4 address.
func IsValidIPv4Address(fldPath *field.Path, value string) field.ErrorList {
var allErrors field.ErrorList
ip := net.ParseIP(value)
if ip == nil || ip.To4() == nil {
allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv4 address"))
}
return allErrors
}
// IsValidIPv6Address tests that the argument is a valid IPv6 address.
func IsValidIPv6Address(fldPath *field.Path, value string) field.ErrorList {
var allErrors field.ErrorList
ip := net.ParseIP(value)
if ip == nil || ip.To4() != nil {
allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv6 address"))
}
return allErrors
}
const percentFmt string = "[0-9]+%"
const percentErrMsg string = "a valid percent string must be a numeric string followed by an ending '%'"

19
vendor/k8s.io/apimachinery/pkg/util/wait/doc.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package wait provides tools for polling or listening for changes
// to a condition.
package wait // import "k8s.io/apimachinery/pkg/util/wait"

606
vendor/k8s.io/apimachinery/pkg/util/wait/wait.go generated vendored Normal file
View File

@@ -0,0 +1,606 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package wait
import (
"context"
"errors"
"math"
"math/rand"
"sync"
"time"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/runtime"
)
// For any test of the style:
// ...
// <- time.After(timeout):
// t.Errorf("Timed out")
// The value for timeout should effectively be "forever." Obviously we don't want our tests to truly lock up forever, but 30s
// is long enough that it is effectively forever for the things that can slow down a run on a heavily contended machine
// (GC, seeks, etc), but not so long as to make a developer ctrl-c a test run if they do happen to break that test.
var ForeverTestTimeout = time.Second * 30
// NeverStop may be passed to Until to make it never stop.
var NeverStop <-chan struct{} = make(chan struct{})
// Group allows to start a group of goroutines and wait for their completion.
type Group struct {
wg sync.WaitGroup
}
func (g *Group) Wait() {
g.wg.Wait()
}
// StartWithChannel starts f in a new goroutine in the group.
// stopCh is passed to f as an argument. f should stop when stopCh is available.
func (g *Group) StartWithChannel(stopCh <-chan struct{}, f func(stopCh <-chan struct{})) {
g.Start(func() {
f(stopCh)
})
}
// StartWithContext starts f in a new goroutine in the group.
// ctx is passed to f as an argument. f should stop when ctx.Done() is available.
func (g *Group) StartWithContext(ctx context.Context, f func(context.Context)) {
g.Start(func() {
f(ctx)
})
}
// Start starts f in a new goroutine in the group.
func (g *Group) Start(f func()) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
f()
}()
}
// Forever calls f every period for ever.
//
// Forever is syntactic sugar on top of Until.
func Forever(f func(), period time.Duration) {
Until(f, period, NeverStop)
}
// Until loops until stop channel is closed, running f every period.
//
// Until is syntactic sugar on top of JitterUntil with zero jitter factor and
// with sliding = true (which means the timer for period starts after the f
// completes).
func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
JitterUntil(f, period, 0.0, true, stopCh)
}
// UntilWithContext loops until context is done, running f every period.
//
// UntilWithContext is syntactic sugar on top of JitterUntilWithContext
// with zero jitter factor and with sliding = true (which means the timer
// for period starts after the f completes).
func UntilWithContext(ctx context.Context, f func(context.Context), period time.Duration) {
JitterUntilWithContext(ctx, f, period, 0.0, true)
}
// NonSlidingUntil loops until stop channel is closed, running f every
// period.
//
// NonSlidingUntil is syntactic sugar on top of JitterUntil with zero jitter
// factor, with sliding = false (meaning the timer for period starts at the same
// time as the function starts).
func NonSlidingUntil(f func(), period time.Duration, stopCh <-chan struct{}) {
JitterUntil(f, period, 0.0, false, stopCh)
}
// NonSlidingUntilWithContext loops until context is done, running f every
// period.
//
// NonSlidingUntilWithContext is syntactic sugar on top of JitterUntilWithContext
// with zero jitter factor, with sliding = false (meaning the timer for period
// starts at the same time as the function starts).
func NonSlidingUntilWithContext(ctx context.Context, f func(context.Context), period time.Duration) {
JitterUntilWithContext(ctx, f, period, 0.0, false)
}
// JitterUntil loops until stop channel is closed, running f every period.
//
// If jitterFactor is positive, the period is jittered before every run of f.
// If jitterFactor is not positive, the period is unchanged and not jittered.
//
// If sliding is true, the period is computed after f runs. If it is false then
// period includes the runtime for f.
//
// Close stopCh to stop. f may not be invoked if stop channel is already
// closed. Pass NeverStop to if you don't want it stop.
func JitterUntil(f func(), period time.Duration, jitterFactor float64, sliding bool, stopCh <-chan struct{}) {
BackoffUntil(f, NewJitteredBackoffManager(period, jitterFactor, &clock.RealClock{}), sliding, stopCh)
}
// BackoffUntil loops until stop channel is closed, run f every duration given by BackoffManager.
//
// If sliding is true, the period is computed after f runs. If it is false then
// period includes the runtime for f.
func BackoffUntil(f func(), backoff BackoffManager, sliding bool, stopCh <-chan struct{}) {
var t clock.Timer
for {
select {
case <-stopCh:
return
default:
}
if !sliding {
t = backoff.Backoff()
}
func() {
defer runtime.HandleCrash()
f()
}()
if sliding {
t = backoff.Backoff()
}
// NOTE: b/c there is no priority selection in golang
// it is possible for this to race, meaning we could
// trigger t.C and stopCh, and t.C select falls through.
// In order to mitigate we re-check stopCh at the beginning
// of every loop to prevent extra executions of f().
select {
case <-stopCh:
return
case <-t.C():
}
}
}
// JitterUntilWithContext loops until context is done, running f every period.
//
// If jitterFactor is positive, the period is jittered before every run of f.
// If jitterFactor is not positive, the period is unchanged and not jittered.
//
// If sliding is true, the period is computed after f runs. If it is false then
// period includes the runtime for f.
//
// Cancel context to stop. f may not be invoked if context is already expired.
func JitterUntilWithContext(ctx context.Context, f func(context.Context), period time.Duration, jitterFactor float64, sliding bool) {
JitterUntil(func() { f(ctx) }, period, jitterFactor, sliding, ctx.Done())
}
// Jitter returns a time.Duration between duration and duration + maxFactor *
// duration.
//
// This allows clients to avoid converging on periodic behavior. If maxFactor
// is 0.0, a suggested default value will be chosen.
func Jitter(duration time.Duration, maxFactor float64) time.Duration {
if maxFactor <= 0.0 {
maxFactor = 1.0
}
wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration))
return wait
}
// ErrWaitTimeout is returned when the condition exited without success.
var ErrWaitTimeout = errors.New("timed out waiting for the condition")
// ConditionFunc returns true if the condition is satisfied, or an error
// if the loop should be aborted.
type ConditionFunc func() (done bool, err error)
// runConditionWithCrashProtection runs a ConditionFunc with crash protection
func runConditionWithCrashProtection(condition ConditionFunc) (bool, error) {
defer runtime.HandleCrash()
return condition()
}
// Backoff holds parameters applied to a Backoff function.
type Backoff struct {
// The initial duration.
Duration time.Duration
// Duration is multiplied by factor each iteration, if factor is not zero
// and the limits imposed by Steps and Cap have not been reached.
// Should not be negative.
// The jitter does not contribute to the updates to the duration parameter.
Factor float64
// The sleep at each iteration is the duration plus an additional
// amount chosen uniformly at random from the interval between
// zero and `jitter*duration`.
Jitter float64
// The remaining number of iterations in which the duration
// parameter may change (but progress can be stopped earlier by
// hitting the cap). If not positive, the duration is not
// changed. Used for exponential backoff in combination with
// Factor and Cap.
Steps int
// A limit on revised values of the duration parameter. If a
// multiplication by the factor parameter would make the duration
// exceed the cap then the duration is set to the cap and the
// steps parameter is set to zero.
Cap time.Duration
}
// Step (1) returns an amount of time to sleep determined by the
// original Duration and Jitter and (2) mutates the provided Backoff
// to update its Steps and Duration.
func (b *Backoff) Step() time.Duration {
if b.Steps < 1 {
if b.Jitter > 0 {
return Jitter(b.Duration, b.Jitter)
}
return b.Duration
}
b.Steps--
duration := b.Duration
// calculate the next step
if b.Factor != 0 {
b.Duration = time.Duration(float64(b.Duration) * b.Factor)
if b.Cap > 0 && b.Duration > b.Cap {
b.Duration = b.Cap
b.Steps = 0
}
}
if b.Jitter > 0 {
duration = Jitter(duration, b.Jitter)
}
return duration
}
// contextForChannel derives a child context from a parent channel.
//
// The derived context's Done channel is closed when the returned cancel function
// is called or when the parent channel is closed, whichever happens first.
//
// Note the caller must *always* call the CancelFunc, otherwise resources may be leaked.
func contextForChannel(parentCh <-chan struct{}) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(context.Background())
go func() {
select {
case <-parentCh:
cancel()
case <-ctx.Done():
}
}()
return ctx, cancel
}
// BackoffManager manages backoff with a particular scheme based on its underlying implementation. It provides
// an interface to return a timer for backoff, and caller shall backoff until Timer.C() drains. If the second Backoff()
// is called before the timer from the first Backoff() call finishes, the first timer will NOT be drained and result in
// undetermined behavior.
// The BackoffManager is supposed to be called in a single-threaded environment.
type BackoffManager interface {
Backoff() clock.Timer
}
type exponentialBackoffManagerImpl struct {
backoff *Backoff
backoffTimer clock.Timer
lastBackoffStart time.Time
initialBackoff time.Duration
backoffResetDuration time.Duration
clock clock.Clock
}
// NewExponentialBackoffManager returns a manager for managing exponential backoff. Each backoff is jittered and
// backoff will not exceed the given max. If the backoff is not called within resetDuration, the backoff is reset.
// This backoff manager is used to reduce load during upstream unhealthiness.
func NewExponentialBackoffManager(initBackoff, maxBackoff, resetDuration time.Duration, backoffFactor, jitter float64, c clock.Clock) BackoffManager {
return &exponentialBackoffManagerImpl{
backoff: &Backoff{
Duration: initBackoff,
Factor: backoffFactor,
Jitter: jitter,
// the current impl of wait.Backoff returns Backoff.Duration once steps are used up, which is not
// what we ideally need here, we set it to max int and assume we will never use up the steps
Steps: math.MaxInt32,
Cap: maxBackoff,
},
backoffTimer: nil,
initialBackoff: initBackoff,
lastBackoffStart: c.Now(),
backoffResetDuration: resetDuration,
clock: c,
}
}
func (b *exponentialBackoffManagerImpl) getNextBackoff() time.Duration {
if b.clock.Now().Sub(b.lastBackoffStart) > b.backoffResetDuration {
b.backoff.Steps = math.MaxInt32
b.backoff.Duration = b.initialBackoff
}
b.lastBackoffStart = b.clock.Now()
return b.backoff.Step()
}
// Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer for exponential backoff.
// The returned timer must be drained before calling Backoff() the second time
func (b *exponentialBackoffManagerImpl) Backoff() clock.Timer {
if b.backoffTimer == nil {
b.backoffTimer = b.clock.NewTimer(b.getNextBackoff())
} else {
b.backoffTimer.Reset(b.getNextBackoff())
}
return b.backoffTimer
}
type jitteredBackoffManagerImpl struct {
clock clock.Clock
duration time.Duration
jitter float64
backoffTimer clock.Timer
}
// NewJitteredBackoffManager returns a BackoffManager that backoffs with given duration plus given jitter. If the jitter
// is negative, backoff will not be jittered.
func NewJitteredBackoffManager(duration time.Duration, jitter float64, c clock.Clock) BackoffManager {
return &jitteredBackoffManagerImpl{
clock: c,
duration: duration,
jitter: jitter,
backoffTimer: nil,
}
}
func (j *jitteredBackoffManagerImpl) getNextBackoff() time.Duration {
jitteredPeriod := j.duration
if j.jitter > 0.0 {
jitteredPeriod = Jitter(j.duration, j.jitter)
}
return jitteredPeriod
}
// Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer for jittered backoff.
// The returned timer must be drained before calling Backoff() the second time
func (j *jitteredBackoffManagerImpl) Backoff() clock.Timer {
backoff := j.getNextBackoff()
if j.backoffTimer == nil {
j.backoffTimer = j.clock.NewTimer(backoff)
} else {
j.backoffTimer.Reset(backoff)
}
return j.backoffTimer
}
// ExponentialBackoff repeats a condition check with exponential backoff.
//
// It repeatedly checks the condition and then sleeps, using `backoff.Step()`
// to determine the length of the sleep and adjust Duration and Steps.
// Stops and returns as soon as:
// 1. the condition check returns true or an error,
// 2. `backoff.Steps` checks of the condition have been done, or
// 3. a sleep truncated by the cap on duration has been completed.
// In case (1) the returned error is what the condition function returned.
// In all other cases, ErrWaitTimeout is returned.
func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error {
for backoff.Steps > 0 {
if ok, err := runConditionWithCrashProtection(condition); err != nil || ok {
return err
}
if backoff.Steps == 1 {
break
}
time.Sleep(backoff.Step())
}
return ErrWaitTimeout
}
// Poll tries a condition func until it returns true, an error, or the timeout
// is reached.
//
// Poll always waits the interval before the run of 'condition'.
// 'condition' will always be invoked at least once.
//
// Some intervals may be missed if the condition takes too long or the time
// window is too short.
//
// If you want to Poll something forever, see PollInfinite.
func Poll(interval, timeout time.Duration, condition ConditionFunc) error {
return pollInternal(poller(interval, timeout), condition)
}
func pollInternal(wait WaitFunc, condition ConditionFunc) error {
done := make(chan struct{})
defer close(done)
return WaitFor(wait, condition, done)
}
// PollImmediate tries a condition func until it returns true, an error, or the timeout
// is reached.
//
// PollImmediate always checks 'condition' before waiting for the interval. 'condition'
// will always be invoked at least once.
//
// Some intervals may be missed if the condition takes too long or the time
// window is too short.
//
// If you want to immediately Poll something forever, see PollImmediateInfinite.
func PollImmediate(interval, timeout time.Duration, condition ConditionFunc) error {
return pollImmediateInternal(poller(interval, timeout), condition)
}
func pollImmediateInternal(wait WaitFunc, condition ConditionFunc) error {
done, err := runConditionWithCrashProtection(condition)
if err != nil {
return err
}
if done {
return nil
}
return pollInternal(wait, condition)
}
// PollInfinite tries a condition func until it returns true or an error
//
// PollInfinite always waits the interval before the run of 'condition'.
//
// Some intervals may be missed if the condition takes too long or the time
// window is too short.
func PollInfinite(interval time.Duration, condition ConditionFunc) error {
done := make(chan struct{})
defer close(done)
return PollUntil(interval, condition, done)
}
// PollImmediateInfinite tries a condition func until it returns true or an error
//
// PollImmediateInfinite runs the 'condition' before waiting for the interval.
//
// Some intervals may be missed if the condition takes too long or the time
// window is too short.
func PollImmediateInfinite(interval time.Duration, condition ConditionFunc) error {
done, err := runConditionWithCrashProtection(condition)
if err != nil {
return err
}
if done {
return nil
}
return PollInfinite(interval, condition)
}
// PollUntil tries a condition func until it returns true, an error or stopCh is
// closed.
//
// PollUntil always waits interval before the first run of 'condition'.
// 'condition' will always be invoked at least once.
func PollUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error {
ctx, cancel := contextForChannel(stopCh)
defer cancel()
return WaitFor(poller(interval, 0), condition, ctx.Done())
}
// PollImmediateUntil tries a condition func until it returns true, an error or stopCh is closed.
//
// PollImmediateUntil runs the 'condition' before waiting for the interval.
// 'condition' will always be invoked at least once.
func PollImmediateUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error {
done, err := condition()
if err != nil {
return err
}
if done {
return nil
}
select {
case <-stopCh:
return ErrWaitTimeout
default:
return PollUntil(interval, condition, stopCh)
}
}
// WaitFunc creates a channel that receives an item every time a test
// should be executed and is closed when the last test should be invoked.
type WaitFunc func(done <-chan struct{}) <-chan struct{}
// WaitFor continually checks 'fn' as driven by 'wait'.
//
// WaitFor gets a channel from 'wait()'', and then invokes 'fn' once for every value
// placed on the channel and once more when the channel is closed. If the channel is closed
// and 'fn' returns false without error, WaitFor returns ErrWaitTimeout.
//
// If 'fn' returns an error the loop ends and that error is returned. If
// 'fn' returns true the loop ends and nil is returned.
//
// ErrWaitTimeout will be returned if the 'done' channel is closed without fn ever
// returning true.
//
// When the done channel is closed, because the golang `select` statement is
// "uniform pseudo-random", the `fn` might still run one or multiple time,
// though eventually `WaitFor` will return.
func WaitFor(wait WaitFunc, fn ConditionFunc, done <-chan struct{}) error {
stopCh := make(chan struct{})
defer close(stopCh)
c := wait(stopCh)
for {
select {
case _, open := <-c:
ok, err := runConditionWithCrashProtection(fn)
if err != nil {
return err
}
if ok {
return nil
}
if !open {
return ErrWaitTimeout
}
case <-done:
return ErrWaitTimeout
}
}
}
// poller returns a WaitFunc that will send to the channel every interval until
// timeout has elapsed and then closes the channel.
//
// Over very short intervals you may receive no ticks before the channel is
// closed. A timeout of 0 is interpreted as an infinity, and in such a case
// it would be the caller's responsibility to close the done channel.
// Failure to do so would result in a leaked goroutine.
//
// Output ticks are not buffered. If the channel is not ready to receive an
// item, the tick is skipped.
func poller(interval, timeout time.Duration) WaitFunc {
return WaitFunc(func(done <-chan struct{}) <-chan struct{} {
ch := make(chan struct{})
go func() {
defer close(ch)
tick := time.NewTicker(interval)
defer tick.Stop()
var after <-chan time.Time
if timeout != 0 {
// time.After is more convenient, but it
// potentially leaves timers around much longer
// than necessary if we exit early.
timer := time.NewTimer(timeout)
after = timer.C
defer timer.Stop()
}
for {
select {
case <-tick.C:
// If the consumer isn't ready for this signal drop it and
// check the other channels.
select {
case ch <- struct{}{}:
default:
}
case <-after:
return
case <-done:
return
}
}
}()
return ch
})
}

View File

@@ -26,7 +26,7 @@ import (
"strings"
"unicode"
"k8s.io/klog"
"k8s.io/klog/v2"
"sigs.k8s.io/yaml"
)
@@ -92,6 +92,10 @@ type YAMLDecoder struct {
// the caller in framing the chunk.
func NewDocumentDecoder(r io.ReadCloser) io.ReadCloser {
scanner := bufio.NewScanner(r)
// the size of initial allocation for buffer 4k
buf := make([]byte, 4*1024)
// the maximum size used to buffer a token 5M
scanner.Buffer(buf, 5*1024*1024)
scanner.Split(splitYAMLDocument)
return &YAMLDecoder{
r: r,

View File

@@ -21,7 +21,7 @@ import (
"io"
"sync"
"k8s.io/klog"
"k8s.io/klog/v2"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/net"
@@ -113,7 +113,7 @@ func (sw *StreamWatcher) receive() {
case io.ErrUnexpectedEOF:
klog.V(1).Infof("Unexpected EOF during watch stream event decoding: %v", err)
default:
if net.IsProbableEOF(err) {
if net.IsProbableEOF(err) || net.IsTimeout(err) {
klog.V(5).Infof("Unable to decode an event from the watch stream: %v", err)
} else {
sw.result <- Event{

View File

@@ -20,7 +20,7 @@ import (
"fmt"
"sync"
"k8s.io/klog"
"k8s.io/klog/v2"
"k8s.io/apimachinery/pkg/runtime"
)
@@ -32,8 +32,8 @@ type Interface interface {
Stop()
// Returns a chan which will receive all the events. If an error occurs
// or Stop() is called, this channel will be closed, in which case the
// watch should be completely cleaned up.
// or Stop() is called, the implementation will close this channel and
// release any resources used by the watch.
ResultChan() <-chan Event
}
@@ -46,7 +46,9 @@ const (
Deleted EventType = "DELETED"
Bookmark EventType = "BOOKMARK"
Error EventType = "ERROR"
)
var (
DefaultChanSize int32 = 100
)
@@ -90,7 +92,7 @@ func (w emptyWatch) ResultChan() <-chan Event {
// FakeWatcher lets you test anything that consumes a watch.Interface; threadsafe.
type FakeWatcher struct {
result chan Event
Stopped bool
stopped bool
sync.Mutex
}
@@ -110,24 +112,24 @@ func NewFakeWithChanSize(size int, blocking bool) *FakeWatcher {
func (f *FakeWatcher) Stop() {
f.Lock()
defer f.Unlock()
if !f.Stopped {
if !f.stopped {
klog.V(4).Infof("Stopping fake watcher.")
close(f.result)
f.Stopped = true
f.stopped = true
}
}
func (f *FakeWatcher) IsStopped() bool {
f.Lock()
defer f.Unlock()
return f.Stopped
return f.stopped
}
// Reset prepares the watcher to be reused.
func (f *FakeWatcher) Reset() {
f.Lock()
defer f.Unlock()
f.Stopped = false
f.stopped = false
f.result = make(chan Event)
}