Upgrade to k8s v1.5
parent
093ab51fbf
commit
9c3da934a7
File diff suppressed because it is too large
Load Diff
|
|
@ -27,7 +27,7 @@ minikube start
|
|||
--insecure-registry stringSlice Insecure Docker registries to pass to the Docker daemon
|
||||
--iso-url string Location of the minikube iso (default "https://storage.googleapis.com/minikube/minikube-0.7.iso")
|
||||
--kubernetes-version string The kubernetes version that the minikube VM will use (ex: v1.2.3)
|
||||
OR a URI which contains a localkube binary (ex: https://storage.googleapis.com/minikube/k8sReleases/v1.3.0/localkube-linux-amd64) (default "v1.5.0-beta.1")
|
||||
OR a URI which contains a localkube binary (ex: https://storage.googleapis.com/minikube/k8sReleases/v1.3.0/localkube-linux-amd64) (default "v1.5.0")
|
||||
--kvm-network string The KVM network name. (only supported with KVM driver) (default "default")
|
||||
--memory int Amount of RAM allocated to the minikube VM (default 2048)
|
||||
--network-plugin string The name of the network plugin
|
||||
|
|
|
|||
|
|
@ -369,11 +369,30 @@ func StartControllers(s *options.CMServer, kubeconfig *restclient.Config, rootCl
|
|||
// Find the list of namespaced resources via discovery that the namespace controller must manage
|
||||
namespaceKubeClient := client("namespace-controller")
|
||||
namespaceClientPool := dynamic.NewClientPool(restclient.AddUserAgent(kubeconfig, "namespace-controller"), restMapper, dynamic.LegacyAPIPathResolverFunc)
|
||||
groupVersionResources, err := namespaceKubeClient.Discovery().ServerPreferredNamespacedResources()
|
||||
// TODO: consider using a list-watch + cache here rather than polling
|
||||
var gvrFn func() ([]unversioned.GroupVersionResource, error)
|
||||
rsrcs, err := namespaceKubeClient.Discovery().ServerResources()
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to get supported resources from server: %v", err)
|
||||
glog.Fatalf("Failed to get group version resources: %v", err)
|
||||
}
|
||||
namespaceController := namespacecontroller.NewNamespaceController(namespaceKubeClient, namespaceClientPool, groupVersionResources, s.NamespaceSyncPeriod.Duration, api.FinalizerKubernetes)
|
||||
for _, rsrcList := range rsrcs {
|
||||
for ix := range rsrcList.APIResources {
|
||||
rsrc := &rsrcList.APIResources[ix]
|
||||
if rsrc.Kind == "ThirdPartyResource" {
|
||||
gvrFn = namespaceKubeClient.Discovery().ServerPreferredNamespacedResources
|
||||
}
|
||||
}
|
||||
}
|
||||
if gvrFn == nil {
|
||||
gvr, err := namespaceKubeClient.Discovery().ServerPreferredNamespacedResources()
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to get resources: %v", err)
|
||||
}
|
||||
gvrFn = func() ([]unversioned.GroupVersionResource, error) {
|
||||
return gvr, nil
|
||||
}
|
||||
}
|
||||
namespaceController := namespacecontroller.NewNamespaceController(namespaceKubeClient, namespaceClientPool, gvrFn, s.NamespaceSyncPeriod.Duration, api.FinalizerKubernetes)
|
||||
go namespaceController.Run(int(s.ConcurrentNamespaceSyncs), wait.NeverStop)
|
||||
time.Sleep(wait.Jitter(s.ControllerStartInterval.Duration, ControllerStartJitter))
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ func isSysFSWritable() (bool, error) {
|
|||
|
||||
for _, mountPoint := range mountPoints {
|
||||
const sysfsDevice = "sysfs"
|
||||
if mountPoint.Device != sysfsDevice {
|
||||
if mountPoint.Type != sysfsDevice {
|
||||
continue
|
||||
}
|
||||
// Check whether sysfs is 'rw'
|
||||
|
|
|
|||
|
|
@ -246,6 +246,7 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) {
|
|||
fs.DurationVar(&s.EvictionPressureTransitionPeriod.Duration, "eviction-pressure-transition-period", s.EvictionPressureTransitionPeriod.Duration, "Duration for which the kubelet has to wait before transitioning out of an eviction pressure condition.")
|
||||
fs.Int32Var(&s.EvictionMaxPodGracePeriod, "eviction-max-pod-grace-period", s.EvictionMaxPodGracePeriod, "Maximum allowed grace period (in seconds) to use when terminating pods in response to a soft eviction threshold being met. If negative, defer to pod specified value.")
|
||||
fs.StringVar(&s.EvictionMinimumReclaim, "eviction-minimum-reclaim", s.EvictionMinimumReclaim, "A set of minimum reclaims (e.g. imagefs.available=2Gi) that describes the minimum amount of resource the kubelet will reclaim when performing a pod eviction if that resource is under pressure.")
|
||||
fs.BoolVar(&s.ExperimentalKernelMemcgNotification, "experimental-kernel-memcg-notification", s.ExperimentalKernelMemcgNotification, "If enabled, the kubelet will integrate with the kernel memcg notification to determine if memory eviction thresholds are crossed rather than polling.")
|
||||
fs.Int32Var(&s.PodsPerCore, "pods-per-core", s.PodsPerCore, "Number of Pods per core that can run on this Kubelet. The total number of Pods on this Kubelet cannot exceed max-pods, so max-pods will be used if this calculation results in a larger number of Pods allowed on the Kubelet. A value of 0 disables this limit.")
|
||||
fs.BoolVar(&s.ProtectKernelDefaults, "protect-kernel-defaults", s.ProtectKernelDefaults, "Default kubelet behaviour for kernel tuning. If set, kubelet errors if any of kernel tunables is different than kubelet defaults.")
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,15 @@ func BeforeDelete(strategy RESTDeleteStrategy, ctx api.Context, obj runtime.Obje
|
|||
// if we are already being deleted, we may only shorten the deletion grace period
|
||||
// this means the object was gracefully deleted previously but deletionGracePeriodSeconds was not set,
|
||||
// so we force deletion immediately
|
||||
if objectMeta.DeletionGracePeriodSeconds == nil {
|
||||
// IMPORTANT:
|
||||
// The deletion operation happens in two phases.
|
||||
// 1. Update to set DeletionGracePeriodSeconds and DeletionTimestamp
|
||||
// 2. Delete the object from storage.
|
||||
// If the update succeeds, but the delete fails (network error, internal storage error, etc.),
|
||||
// a resource was previously left in a state that was non-recoverable. We
|
||||
// check if the existing stored resource has a grace period as 0 and if so
|
||||
// attempt to delete immediately in order to recover from this scenario.
|
||||
if objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds == 0 {
|
||||
return false, false, nil
|
||||
}
|
||||
// only a shorter grace period may be provided by a user
|
||||
|
|
|
|||
|
|
@ -289,6 +289,10 @@ type StorageMetadata interface {
|
|||
// ProducesMIMETypes returns a list of the MIME types the specified HTTP verb (GET, POST, DELETE,
|
||||
// PATCH) can respond with.
|
||||
ProducesMIMETypes(verb string) []string
|
||||
|
||||
// ProducesObject returns an object the specified HTTP verb respond with. It will overwrite storage object if
|
||||
// it is not nil. Only the type of the return object matters, the value will be ignored.
|
||||
ProducesObject(verb string) interface{}
|
||||
}
|
||||
|
||||
// ConnectRequest is an object passed to admission control for Connect operations
|
||||
|
|
|
|||
|
|
@ -1514,7 +1514,7 @@ message NodeSpec {
|
|||
optional string providerID = 3;
|
||||
|
||||
// Unschedulable controls node schedulability of new pods. By default, node is schedulable.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration"`
|
||||
// More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration"
|
||||
// +optional
|
||||
optional bool unschedulable = 4;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2818,7 +2818,7 @@ type NodeSpec struct {
|
|||
// +optional
|
||||
ProviderID string `json:"providerID,omitempty" protobuf:"bytes,3,opt,name=providerID"`
|
||||
// Unschedulable controls node schedulability of new pods. By default, node is schedulable.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration"`
|
||||
// More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration"
|
||||
// +optional
|
||||
Unschedulable bool `json:"unschedulable,omitempty" protobuf:"varint,4,opt,name=unschedulable"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -901,7 +901,7 @@ var map_NodeSpec = map[string]string{
|
|||
"podCIDR": "PodCIDR represents the pod IP range assigned to the node.",
|
||||
"externalID": "External ID of the node assigned by some machine database (e.g. a cloud provider). Deprecated.",
|
||||
"providerID": "ID of the node assigned by the cloud provider in the format: <ProviderName>://<ProviderSpecificNodeID>",
|
||||
"unschedulable": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration\"`",
|
||||
"unschedulable": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration\"",
|
||||
}
|
||||
|
||||
func (NodeSpec) SwaggerDoc() map[string]string {
|
||||
|
|
|
|||
|
|
@ -2665,9 +2665,6 @@ func ValidateServiceUpdate(service, oldService *api.Service) field.ErrorList {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(freehan): allow user to update loadbalancerSourceRanges
|
||||
allErrs = append(allErrs, ValidateImmutableField(service.Spec.LoadBalancerSourceRanges, oldService.Spec.LoadBalancerSourceRanges, field.NewPath("spec", "loadBalancerSourceRanges"))...)
|
||||
|
||||
allErrs = append(allErrs, validateServiceFields(service)...)
|
||||
allErrs = append(allErrs, validateServiceAnnotations(service, oldService)...)
|
||||
return allErrs
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
|||
&HorizontalPodAutoscaler{},
|
||||
&HorizontalPodAutoscalerList{},
|
||||
&api.ListOptions{},
|
||||
&api.DeleteOptions{},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,3 +37,16 @@ go_library(
|
|||
"//vendor:github.com/ugorji/go/codec",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = ["defaults_test.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/install:go_default_library",
|
||||
"//pkg/apis/autoscaling/install:go_default_library",
|
||||
"//pkg/apis/autoscaling/v1:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
|||
&CronJob{},
|
||||
&CronJobList{},
|
||||
&api.ListOptions{},
|
||||
&api.DeleteOptions{},
|
||||
)
|
||||
scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("ScheduledJob"), &CronJob{})
|
||||
scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("ScheduledJobList"), &CronJobList{})
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -424,6 +424,9 @@ type KubeletConfiguration struct {
|
|||
// Comma-delimited list of minimum reclaims (e.g. imagefs.available=2Gi) that describes the minimum amount of resource the kubelet will reclaim when performing a pod eviction if that resource is under pressure.
|
||||
// +optional
|
||||
EvictionMinimumReclaim string `json:"evictionMinimumReclaim,omitempty"`
|
||||
// If enabled, the kubelet will integrate with the kernel memcg notification to determine if memory eviction thresholds are crossed rather than polling.
|
||||
// +optional
|
||||
ExperimentalKernelMemcgNotification bool `json:"experimentalKernelMemcgNotification"`
|
||||
// Maximum number of pods per core. Cannot exceed MaxPods
|
||||
PodsPerCore int32 `json:"podsPerCore"`
|
||||
// enableControllerAttachDetach enables the Attach/Detach controller to
|
||||
|
|
|
|||
|
|
@ -374,6 +374,9 @@ func SetDefaults_KubeletConfiguration(obj *KubeletConfiguration) {
|
|||
if obj.EvictionPressureTransitionPeriod == zeroDuration {
|
||||
obj.EvictionPressureTransitionPeriod = unversioned.Duration{Duration: 5 * time.Minute}
|
||||
}
|
||||
if obj.ExperimentalKernelMemcgNotification == nil {
|
||||
obj.ExperimentalKernelMemcgNotification = boolVar(false)
|
||||
}
|
||||
if obj.SystemReserved == nil {
|
||||
obj.SystemReserved = make(map[string]string)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -462,6 +462,8 @@ type KubeletConfiguration struct {
|
|||
EvictionMaxPodGracePeriod int32 `json:"evictionMaxPodGracePeriod"`
|
||||
// Comma-delimited list of minimum reclaims (e.g. imagefs.available=2Gi) that describes the minimum amount of resource the kubelet will reclaim when performing a pod eviction if that resource is under pressure.
|
||||
EvictionMinimumReclaim string `json:"evictionMinimumReclaim"`
|
||||
// If enabled, the kubelet will integrate with the kernel memcg notification to determine if memory eviction thresholds are crossed rather than polling.
|
||||
ExperimentalKernelMemcgNotification *bool `json:"experimentalKernelMemcgNotification"`
|
||||
// Maximum number of pods per core. Cannot exceed MaxPods
|
||||
PodsPerCore int32 `json:"podsPerCore"`
|
||||
// enableControllerAttachDetach enables the Attach/Detach controller to
|
||||
|
|
|
|||
|
|
@ -387,6 +387,9 @@ func autoConvert_v1alpha1_KubeletConfiguration_To_componentconfig_KubeletConfigu
|
|||
out.EvictionPressureTransitionPeriod = in.EvictionPressureTransitionPeriod
|
||||
out.EvictionMaxPodGracePeriod = in.EvictionMaxPodGracePeriod
|
||||
out.EvictionMinimumReclaim = in.EvictionMinimumReclaim
|
||||
if err := api.Convert_Pointer_bool_To_bool(&in.ExperimentalKernelMemcgNotification, &out.ExperimentalKernelMemcgNotification, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.PodsPerCore = in.PodsPerCore
|
||||
if err := api.Convert_Pointer_bool_To_bool(&in.EnableControllerAttachDetach, &out.EnableControllerAttachDetach, s); err != nil {
|
||||
return err
|
||||
|
|
@ -556,6 +559,9 @@ func autoConvert_componentconfig_KubeletConfiguration_To_v1alpha1_KubeletConfigu
|
|||
out.EvictionPressureTransitionPeriod = in.EvictionPressureTransitionPeriod
|
||||
out.EvictionMaxPodGracePeriod = in.EvictionMaxPodGracePeriod
|
||||
out.EvictionMinimumReclaim = in.EvictionMinimumReclaim
|
||||
if err := api.Convert_bool_To_Pointer_bool(&in.ExperimentalKernelMemcgNotification, &out.ExperimentalKernelMemcgNotification, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.PodsPerCore = in.PodsPerCore
|
||||
if err := api.Convert_bool_To_Pointer_bool(&in.EnableControllerAttachDetach, &out.EnableControllerAttachDetach, s); err != nil {
|
||||
return err
|
||||
|
|
|
|||
7
vendor/k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1/zz_generated.deepcopy.go
generated
vendored
7
vendor/k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1/zz_generated.deepcopy.go
generated
vendored
|
|
@ -403,6 +403,13 @@ func DeepCopy_v1alpha1_KubeletConfiguration(in interface{}, out interface{}, c *
|
|||
out.EvictionPressureTransitionPeriod = in.EvictionPressureTransitionPeriod
|
||||
out.EvictionMaxPodGracePeriod = in.EvictionMaxPodGracePeriod
|
||||
out.EvictionMinimumReclaim = in.EvictionMinimumReclaim
|
||||
if in.ExperimentalKernelMemcgNotification != nil {
|
||||
in, out := &in.ExperimentalKernelMemcgNotification, &out.ExperimentalKernelMemcgNotification
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
} else {
|
||||
out.ExperimentalKernelMemcgNotification = nil
|
||||
}
|
||||
out.PodsPerCore = in.PodsPerCore
|
||||
if in.EnableControllerAttachDetach != nil {
|
||||
in, out := &in.EnableControllerAttachDetach, &out.EnableControllerAttachDetach
|
||||
|
|
|
|||
|
|
@ -358,6 +358,7 @@ func DeepCopy_componentconfig_KubeletConfiguration(in interface{}, out interface
|
|||
out.EvictionPressureTransitionPeriod = in.EvictionPressureTransitionPeriod
|
||||
out.EvictionMaxPodGracePeriod = in.EvictionMaxPodGracePeriod
|
||||
out.EvictionMinimumReclaim = in.EvictionMinimumReclaim
|
||||
out.ExperimentalKernelMemcgNotification = in.ExperimentalKernelMemcgNotification
|
||||
out.PodsPerCore = in.PodsPerCore
|
||||
out.EnableControllerAttachDetach = in.EnableControllerAttachDetach
|
||||
if in.SystemReserved != nil {
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@ type ThirdPartyResource struct {
|
|||
Description string `json:"description,omitempty"`
|
||||
|
||||
// Versions are versions for this third party object
|
||||
// +optional
|
||||
Versions []APIVersion `json:"versions,omitempty"`
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +142,6 @@ type ThirdPartyResourceList struct {
|
|||
// TODO: we should consider merge this struct with GroupVersion in unversioned.go
|
||||
type APIVersion struct {
|
||||
// Name of this version (e.g. 'v1').
|
||||
// +optional
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ func ValidateThirdPartyResource(obj *extensions.ThirdPartyResource) field.ErrorL
|
|||
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&obj.ObjectMeta, false, ValidateThirdPartyResourceName, field.NewPath("metadata"))...)
|
||||
|
||||
versions := sets.String{}
|
||||
if len(obj.Versions) == 0 {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("versions"), "must specify at least one version"))
|
||||
}
|
||||
for ix := range obj.Versions {
|
||||
version := &obj.Versions[ix]
|
||||
if len(version.Name) == 0 {
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versionedObject := indirectArbitraryPointer(versionedPtr)
|
||||
defaultVersionedObject := indirectArbitraryPointer(versionedPtr)
|
||||
kind := fqKindToRegister.Kind
|
||||
hasSubresource := len(subresource) > 0
|
||||
|
||||
|
|
@ -503,6 +503,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||
Kind: fqKindToRegister,
|
||||
}
|
||||
for _, action := range actions {
|
||||
versionedObject := storageMeta.ProducesObject(action.Verb)
|
||||
if versionedObject == nil {
|
||||
versionedObject = defaultVersionedObject
|
||||
}
|
||||
reqScope.Namer = action.Namer
|
||||
namespaced := ""
|
||||
if apiResource.Namespaced {
|
||||
|
|
@ -1022,6 +1026,10 @@ func (defaultStorageMetadata) ProducesMIMETypes(verb string) []string {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (defaultStorageMetadata) ProducesObject(verb string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// splitSubresource checks if the given storage path is the path of a subresource and returns
|
||||
// the resource and subresource components.
|
||||
func splitSubresource(path string) (string, string, error) {
|
||||
|
|
|
|||
|
|
@ -591,11 +591,11 @@ func patchResource(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, currentObjectJS, versionedObj, strategicpatch.SMPatchVersionLatest)
|
||||
currentPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, currentObjectJS, versionedObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
originalPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, originalPatchedObjJS, versionedObj, strategicpatch.SMPatchVersionLatest)
|
||||
originalPatch, err := strategicpatch.CreateStrategicMergePatch(originalObjJS, originalPatchedObjJS, versionedObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,12 +259,16 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
|
|||
r.setLastSyncResourceVersion(resourceVersion)
|
||||
|
||||
resyncerrc := make(chan error, 1)
|
||||
cancelCh := make(chan struct{})
|
||||
defer close(cancelCh)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-resyncCh:
|
||||
case <-stopCh:
|
||||
return
|
||||
case <-cancelCh:
|
||||
return
|
||||
}
|
||||
glog.V(4).Infof("%s: forcing resync", r.name)
|
||||
if err := r.store.Resync(); err != nil {
|
||||
|
|
|
|||
|
|
@ -244,9 +244,7 @@ func (e *eventLogger) eventObserve(newEvent *api.Event) (*api.Event, []byte, err
|
|||
|
||||
newData, _ := json.Marshal(event)
|
||||
oldData, _ := json.Marshal(eventCopy2)
|
||||
// TODO: need to figure out if we need to let eventObserve() use the new behavior of StrategicMergePatch.
|
||||
// Currently default to old behavior now. Ref: issue #35936
|
||||
patch, err = strategicpatch.CreateStrategicMergePatch(oldData, newData, event, strategicpatch.SMPatchVersion_1_0)
|
||||
patch, err = strategicpatch.CreateStrategicMergePatch(oldData, newData, event)
|
||||
}
|
||||
|
||||
// record our new observation
|
||||
|
|
|
|||
|
|
@ -213,9 +213,11 @@ func (d *DiscoveryClient) serverPreferredResources(namespaced bool) ([]unversion
|
|||
const maxRetries = 2
|
||||
var failedGroups map[unversioned.GroupVersion]error
|
||||
var results []unversioned.GroupVersionResource
|
||||
var resources map[unversioned.GroupResource]string
|
||||
RetrieveGroups:
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
results = []unversioned.GroupVersionResource{}
|
||||
resources = map[unversioned.GroupResource]string{}
|
||||
failedGroups = make(map[unversioned.GroupVersion]error)
|
||||
serverGroupList, err := d.ServerGroups()
|
||||
if err != nil {
|
||||
|
|
@ -223,9 +225,10 @@ RetrieveGroups:
|
|||
}
|
||||
|
||||
for _, apiGroup := range serverGroupList.Groups {
|
||||
preferredVersion := apiGroup.PreferredVersion
|
||||
groupVersion := unversioned.GroupVersion{Group: apiGroup.Name, Version: preferredVersion.Version}
|
||||
apiResourceList, err := d.ServerResourcesForGroupVersion(preferredVersion.GroupVersion)
|
||||
versions := apiGroup.Versions
|
||||
for _, version := range versions {
|
||||
groupVersion := unversioned.GroupVersion{Group: apiGroup.Name, Version: version.Version}
|
||||
apiResourceList, err := d.ServerResourcesForGroupVersion(version.GroupVersion)
|
||||
if err != nil {
|
||||
if i < maxRetries-1 {
|
||||
continue RetrieveGroups
|
||||
|
|
@ -241,7 +244,21 @@ RetrieveGroups:
|
|||
if strings.Contains(apiResource.Name, "/") {
|
||||
continue
|
||||
}
|
||||
results = append(results, groupVersion.WithResource(apiResource.Name))
|
||||
gvr := groupVersion.WithResource(apiResource.Name)
|
||||
if _, ok := resources[gvr.GroupResource()]; ok {
|
||||
if gvr.Version != apiGroup.PreferredVersion.Version {
|
||||
continue
|
||||
}
|
||||
// remove previous entry, because it will be replaced with a preferred one
|
||||
for i := range results {
|
||||
if results[i].GroupResource() == gvr.GroupResource() {
|
||||
results = append(results[:i], results[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
resources[gvr.GroupResource()] = gvr.Version
|
||||
results = append(results, gvr)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(failedGroups) == 0 {
|
||||
|
|
|
|||
|
|
@ -151,9 +151,12 @@ func getPrimaryIPConfig(nic network.Interface) (*network.InterfaceIPConfiguratio
|
|||
return &((*nic.Properties.IPConfigurations)[0]), nil
|
||||
}
|
||||
|
||||
// we're here because we either have multiple ipconfigs and can't determine the primary:
|
||||
// https://github.com/Azure/azure-rest-api-specs/issues/305
|
||||
// or somehow we had zero ipconfigs
|
||||
for _, ref := range *nic.Properties.IPConfigurations {
|
||||
if *ref.Properties.Primary {
|
||||
return &ref, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to determine the determine primary ipconfig. nicname=%q", *nic.Name)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -520,11 +520,11 @@ func (r RealPodControl) DeletePod(namespace string, podID string, object runtime
|
|||
if err != nil {
|
||||
return fmt.Errorf("object does not have ObjectMeta, %v", err)
|
||||
}
|
||||
glog.V(2).Infof("Controller %v deleting pod %v/%v", accessor.GetName(), namespace, podID)
|
||||
if err := r.KubeClient.Core().Pods(namespace).Delete(podID, nil); err != nil {
|
||||
r.Recorder.Eventf(object, api.EventTypeWarning, FailedDeletePodReason, "Error deleting: %v", err)
|
||||
return fmt.Errorf("unable to delete pods: %v", err)
|
||||
} else {
|
||||
glog.V(4).Infof("Controller %v deleted pod %v", accessor.GetName(), podID)
|
||||
r.Recorder.Eventf(object, api.EventTypeNormal, SuccessfulDeletePodReason, "Deleted pod: %v", podID)
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ func SyncOne(sj batch.CronJob, js []batch.Job, now time.Time, jc jobControlInter
|
|||
}
|
||||
errList := []error{}
|
||||
for _, pod := range podList.Items {
|
||||
glog.V(2).Infof("CronJob controller is deleting Pod %v/%v", pod.Namespace, pod.Name)
|
||||
if err := pc.DeletePod(pod.Namespace, pod.Name); err != nil {
|
||||
// ignores the error when the pod isn't found
|
||||
if !errors.IsNotFound(err) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,16 @@ import (
|
|||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
// namespaceDeletionGracePeriod is the time period to wait before processing a received namespace event.
|
||||
// This allows time for the following to occur:
|
||||
// * lifecycle admission plugins on HA apiservers to also observe a namespace
|
||||
// deletion and prevent new objects from being created in the terminating namespace
|
||||
// * non-leader etcd servers to observe last-minute object creations in a namespace
|
||||
// so this controller's cleanup can actually clean up all objects
|
||||
namespaceDeletionGracePeriod = 5 * time.Second
|
||||
)
|
||||
|
||||
// NamespaceController is responsible for performing actions dependent upon a namespace phase
|
||||
type NamespaceController struct {
|
||||
// client that purges namespace content, must have list/delete privileges on all content
|
||||
|
|
@ -47,8 +57,8 @@ type NamespaceController struct {
|
|||
controller *cache.Controller
|
||||
// namespaces that have been queued up for processing by workers
|
||||
queue workqueue.RateLimitingInterface
|
||||
// list of preferred group versions and their corresponding resource set for namespace deletion
|
||||
groupVersionResources []unversioned.GroupVersionResource
|
||||
// function to list of preferred group versions and their corresponding resource set for namespace deletion
|
||||
groupVersionResourcesFn func() ([]unversioned.GroupVersionResource, error)
|
||||
// opCache is a cache to remember if a particular operation is not supported to aid dynamic client.
|
||||
opCache *operationNotSupportedCache
|
||||
// finalizerToken is the finalizer token managed by this controller
|
||||
|
|
@ -59,7 +69,7 @@ type NamespaceController struct {
|
|||
func NewNamespaceController(
|
||||
kubeClient clientset.Interface,
|
||||
clientPool dynamic.ClientPool,
|
||||
groupVersionResources []unversioned.GroupVersionResource,
|
||||
groupVersionResourcesFn func() ([]unversioned.GroupVersionResource, error),
|
||||
resyncPeriod time.Duration,
|
||||
finalizerToken api.FinalizerName) *NamespaceController {
|
||||
|
||||
|
|
@ -86,7 +96,7 @@ func NewNamespaceController(
|
|||
kubeClient: kubeClient,
|
||||
clientPool: clientPool,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"),
|
||||
groupVersionResources: groupVersionResources,
|
||||
groupVersionResourcesFn: groupVersionResourcesFn,
|
||||
opCache: opCache,
|
||||
finalizerToken: finalizerToken,
|
||||
}
|
||||
|
|
@ -132,7 +142,9 @@ func (nm *NamespaceController) enqueueNamespace(obj interface{}) {
|
|||
glog.Errorf("Couldn't get key for object %+v: %v", obj, err)
|
||||
return
|
||||
}
|
||||
nm.queue.Add(key)
|
||||
// delay processing namespace events to allow HA api servers to observe namespace deletion,
|
||||
// and HA etcd servers to observe last minute object creations inside the namespace
|
||||
nm.queue.AddAfter(key, namespaceDeletionGracePeriod)
|
||||
}
|
||||
|
||||
// worker processes the queue of namespace objects.
|
||||
|
|
@ -191,7 +203,7 @@ func (nm *NamespaceController) syncNamespaceFromKey(key string) (err error) {
|
|||
return err
|
||||
}
|
||||
namespace := obj.(*api.Namespace)
|
||||
return syncNamespace(nm.kubeClient, nm.clientPool, nm.opCache, nm.groupVersionResources, namespace, nm.finalizerToken)
|
||||
return syncNamespace(nm.kubeClient, nm.clientPool, nm.opCache, nm.groupVersionResourcesFn, namespace, nm.finalizerToken)
|
||||
}
|
||||
|
||||
// Run starts observing the system with the specified number of workers.
|
||||
|
|
|
|||
6
vendor/k8s.io/kubernetes/pkg/controller/namespace/namespace_controller_utils.go
generated
vendored
6
vendor/k8s.io/kubernetes/pkg/controller/namespace/namespace_controller_utils.go
generated
vendored
|
|
@ -371,7 +371,7 @@ func syncNamespace(
|
|||
kubeClient clientset.Interface,
|
||||
clientPool dynamic.ClientPool,
|
||||
opCache *operationNotSupportedCache,
|
||||
groupVersionResources []unversioned.GroupVersionResource,
|
||||
groupVersionResourcesFn func() ([]unversioned.GroupVersionResource, error),
|
||||
namespace *api.Namespace,
|
||||
finalizerToken api.FinalizerName,
|
||||
) error {
|
||||
|
|
@ -422,6 +422,10 @@ func syncNamespace(
|
|||
}
|
||||
|
||||
// there may still be content for us to remove
|
||||
groupVersionResources, err := groupVersionResourcesFn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
estimate, err := deleteAllContent(kubeClient, clientPool, opCache, groupVersionResources, namespace.Name, *namespace.DeletionTimestamp)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ func setPodTerminationReason(kubeClient clientset.Interface, pod *api.Pod, nodeN
|
|||
|
||||
func forcefullyDeletePod(c clientset.Interface, pod *api.Pod) error {
|
||||
var zero int64
|
||||
glog.Infof("NodeController is force deleting Pod: %v:%v", pod.Namespace, pod.Name)
|
||||
err := c.Core().Pods(pod.Namespace).Delete(pod.Name, &api.DeleteOptions{GracePeriodSeconds: &zero})
|
||||
if err == nil {
|
||||
glog.V(4).Infof("forceful deletion of %s succeeded", pod.Name)
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ func (p *petSyncer) Sync(pet *pcb) error {
|
|||
}
|
||||
// if pet failed - we need to remove old one because of consistent naming
|
||||
if exists && realPet.pod.Status.Phase == api.PodFailed {
|
||||
glog.V(4).Infof("Delete evicted pod %v", realPet.pod.Name)
|
||||
glog.V(2).Infof("Deleting evicted pod %v/%v", realPet.pod.Namespace, realPet.pod.Name)
|
||||
if err := p.petClient.Delete(realPet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -156,7 +156,7 @@ func (p *petSyncer) Delete(pet *pcb) error {
|
|||
// The returned error will force a requeue.
|
||||
p.blockingPet = realPet
|
||||
if !p.isDying(realPet.pod) {
|
||||
glog.V(4).Infof("StatefulSet %v deleting pet %v", pet.parent.Name, pet.pod.Name)
|
||||
glog.V(2).Infof("StatefulSet %v deleting pet %v/%v", pet.parent.Name, pet.pod.Namespace, pet.pod.Name)
|
||||
return p.petClient.Delete(pet)
|
||||
}
|
||||
glog.V(4).Infof("StatefulSet %v waiting on pet %v to die in %v", pet.parent.Name, realPet.pod.Name, realPet.pod.DeletionTimestamp)
|
||||
|
|
|
|||
|
|
@ -292,9 +292,12 @@ func (a *HorizontalController) reconcileAutoscaler(hpa *autoscaling.HorizontalPo
|
|||
rescaleReason := ""
|
||||
timestamp := time.Now()
|
||||
|
||||
rescale := true
|
||||
|
||||
if scale.Spec.Replicas == 0 {
|
||||
// Autoscaling is disabled for this resource
|
||||
desiredReplicas = 0
|
||||
rescale = false
|
||||
} else if currentReplicas > hpa.Spec.MaxReplicas {
|
||||
rescaleReason = "Current number of replicas above Spec.MaxReplicas"
|
||||
desiredReplicas = hpa.Spec.MaxReplicas
|
||||
|
|
@ -360,9 +363,10 @@ func (a *HorizontalController) reconcileAutoscaler(hpa *autoscaling.HorizontalPo
|
|||
if desiredReplicas > calculateScaleUpLimit(currentReplicas) {
|
||||
desiredReplicas = calculateScaleUpLimit(currentReplicas)
|
||||
}
|
||||
|
||||
rescale = shouldScale(hpa, currentReplicas, desiredReplicas, timestamp)
|
||||
}
|
||||
|
||||
rescale := shouldScale(hpa, currentReplicas, desiredReplicas, timestamp)
|
||||
if rescale {
|
||||
scale.Spec.Replicas = desiredReplicas
|
||||
_, err = a.scaleNamespacer.Scales(hpa.Namespace).Update(hpa.Spec.ScaleTargetRef.Kind, scale)
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ func NewPodGC(kubeClient clientset.Interface, podInformer cache.SharedIndexInfor
|
|||
kubeClient: kubeClient,
|
||||
terminatedPodThreshold: terminatedPodThreshold,
|
||||
deletePod: func(namespace, name string) error {
|
||||
glog.Infof("PodGC is force deleting Pod: %v:%v", namespace, name)
|
||||
return kubeClient.Core().Pods(namespace).Delete(name, api.NewDeleteOptions(0))
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -342,8 +342,19 @@ func (rsc *ReplicaSetController) updatePod(old, cur interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
changedToReady := !api.IsPodReady(oldPod) && api.IsPodReady(curPod)
|
||||
if curRS := rsc.getPodReplicaSet(curPod); curRS != nil {
|
||||
rsc.enqueueReplicaSet(curRS)
|
||||
// TODO: MinReadySeconds in the Pod will generate an Available condition to be added in
|
||||
// the Pod status which in turn will trigger a requeue of the owning replica set thus
|
||||
// having its status updated with the newly available replica. For now, we can fake the
|
||||
// update by resyncing the controller MinReadySeconds after the it is requeued because
|
||||
// a Pod transitioned to Ready.
|
||||
// Note that this still suffers from #29229, we are just moving the problem one level
|
||||
// "closer" to kubelet (from the deployment to the replica set controller).
|
||||
if changedToReady && curRS.Spec.MinReadySeconds > 0 {
|
||||
rsc.enqueueReplicaSetAfter(curRS, time.Duration(curRS.Spec.MinReadySeconds)*time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -397,6 +408,23 @@ func (rsc *ReplicaSetController) enqueueReplicaSet(obj interface{}) {
|
|||
rsc.queue.Add(key)
|
||||
}
|
||||
|
||||
// obj could be an *extensions.ReplicaSet, or a DeletionFinalStateUnknown marker item.
|
||||
func (rsc *ReplicaSetController) enqueueReplicaSetAfter(obj interface{}, after time.Duration) {
|
||||
key, err := controller.KeyFunc(obj)
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("Couldn't get key for object %+v: %v", obj, err))
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Handle overlapping replica sets better. Either disallow them at admission time or
|
||||
// deterministically avoid syncing replica sets that fight over pods. Currently, we only
|
||||
// ensure that the same replica set is synced for a given pod. When we periodically relist
|
||||
// all replica sets there will still be some replica instability. One way to handle this is
|
||||
// by querying the store for all replica sets that this replica set overlaps, as well as all
|
||||
// replica sets that overlap this ReplicaSet, and sorting them.
|
||||
rsc.queue.AddAfter(key, after)
|
||||
}
|
||||
|
||||
// worker runs a worker thread that just dequeues items, processes them, and marks them done.
|
||||
// It enforces that the syncHandler is never invoked concurrently with the same key.
|
||||
func (rsc *ReplicaSetController) worker() {
|
||||
|
|
|
|||
|
|
@ -401,8 +401,19 @@ func (rm *ReplicationManager) updatePod(old, cur interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
changedToReady := !api.IsPodReady(oldPod) && api.IsPodReady(curPod)
|
||||
if curRC := rm.getPodController(curPod); curRC != nil {
|
||||
rm.enqueueController(curRC)
|
||||
// TODO: MinReadySeconds in the Pod will generate an Available condition to be added in
|
||||
// the Pod status which in turn will trigger a requeue of the owning replication controller
|
||||
// thus having its status updated with the newly available replica. For now, we can fake the
|
||||
// update by resyncing the controller MinReadySeconds after the it is requeued because a Pod
|
||||
// transitioned to Ready.
|
||||
// Note that this still suffers from #29229, we are just moving the problem one level
|
||||
// "closer" to kubelet (from the deployment to the replication controller manager).
|
||||
if changedToReady && curRC.Spec.MinReadySeconds > 0 {
|
||||
rm.enqueueControllerAfter(curRC, time.Duration(curRC.Spec.MinReadySeconds)*time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -456,6 +467,23 @@ func (rm *ReplicationManager) enqueueController(obj interface{}) {
|
|||
rm.queue.Add(key)
|
||||
}
|
||||
|
||||
// obj could be an *v1.ReplicationController, or a DeletionFinalStateUnknown marker item.
|
||||
func (rm *ReplicationManager) enqueueControllerAfter(obj interface{}, after time.Duration) {
|
||||
key, err := controller.KeyFunc(obj)
|
||||
if err != nil {
|
||||
glog.Errorf("Couldn't get key for object %+v: %v", obj, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Handle overlapping controllers better. Either disallow them at admission time or
|
||||
// deterministically avoid syncing controllers that fight over pods. Currently, we only
|
||||
// ensure that the same controller is synced for a given pod. When we periodically relist
|
||||
// all controllers there will still be some replica instability. One way to handle this is
|
||||
// by querying the store for all controllers that this rc overlaps, as well as all
|
||||
// controllers that overlap this rc, and sorting them.
|
||||
rm.queue.AddAfter(key, after)
|
||||
}
|
||||
|
||||
// worker runs a worker thread that just dequeues items, processes them, and marks them done.
|
||||
// It enforces that the syncHandler is never invoked concurrently with the same key.
|
||||
func (rm *ReplicationManager) worker() {
|
||||
|
|
|
|||
|
|
@ -430,6 +430,13 @@ func (s *ServiceController) needsUpdate(oldService *api.Service, newService *api
|
|||
oldService.Spec.Type, newService.Spec.Type)
|
||||
return true
|
||||
}
|
||||
|
||||
if wantsLoadBalancer(newService) && !reflect.DeepEqual(oldService.Spec.LoadBalancerSourceRanges, newService.Spec.LoadBalancerSourceRanges) {
|
||||
s.eventRecorder.Eventf(newService, api.EventTypeNormal, "LoadBalancerSourceRanges", "%v -> %v",
|
||||
oldService.Spec.LoadBalancerSourceRanges, newService.Spec.LoadBalancerSourceRanges)
|
||||
return true
|
||||
}
|
||||
|
||||
if !portsEqualForLB(oldService, newService) || oldService.Spec.SessionAffinity != newService.Spec.SessionAffinity {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ go_test(
|
|||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/util/rand:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//vendor:github.com/davecgh/go-spew/spew",
|
||||
"//vendor:github.com/golang/glog",
|
||||
],
|
||||
)
|
||||
|
|
|
|||
23
vendor/k8s.io/kubernetes/pkg/controller/volume/attachdetach/attach_detach_controller.go
generated
vendored
23
vendor/k8s.io/kubernetes/pkg/controller/volume/attachdetach/attach_detach_controller.go
generated
vendored
|
|
@ -239,6 +239,23 @@ func (adc *attachDetachController) podDelete(obj interface{}) {
|
|||
|
||||
func (adc *attachDetachController) nodeAdd(obj interface{}) {
|
||||
node, ok := obj.(*api.Node)
|
||||
// TODO: investigate if nodeName is empty then if we can return
|
||||
// kubernetes/kubernetes/issues/37777
|
||||
if node == nil || !ok {
|
||||
return
|
||||
}
|
||||
nodeName := types.NodeName(node.Name)
|
||||
adc.nodeUpdate(nil, obj)
|
||||
// kubernetes/kubernetes/issues/37586
|
||||
// This is to workaround the case when a node add causes to wipe out
|
||||
// the attached volumes field. This function ensures that we sync with
|
||||
// the actual status.
|
||||
adc.actualStateOfWorld.SetNodeStatusUpdateNeeded(nodeName)
|
||||
}
|
||||
|
||||
func (adc *attachDetachController) nodeUpdate(oldObj, newObj interface{}) {
|
||||
node, ok := newObj.(*api.Node)
|
||||
// TODO: investigate if nodeName is empty then if we can return
|
||||
if node == nil || !ok {
|
||||
return
|
||||
}
|
||||
|
|
@ -249,15 +266,9 @@ func (adc *attachDetachController) nodeAdd(obj interface{}) {
|
|||
// detach controller. Add it to desired state of world.
|
||||
adc.desiredStateOfWorld.AddNode(nodeName)
|
||||
}
|
||||
|
||||
adc.processVolumesInUse(nodeName, node.Status.VolumesInUse)
|
||||
}
|
||||
|
||||
func (adc *attachDetachController) nodeUpdate(oldObj, newObj interface{}) {
|
||||
// The flow for update is the same as add.
|
||||
adc.nodeAdd(newObj)
|
||||
}
|
||||
|
||||
func (adc *attachDetachController) nodeDelete(obj interface{}) {
|
||||
node, ok := obj.(*api.Node)
|
||||
if node == nil || !ok {
|
||||
|
|
|
|||
|
|
@ -116,6 +116,9 @@ type ActualStateOfWorld interface {
|
|||
// since volumes should be removed from this list as soon a detach operation
|
||||
// is considered, before the detach operation is triggered).
|
||||
GetVolumesToReportAttached() map[types.NodeName][]api.AttachedVolume
|
||||
|
||||
// GetNodesToUpdateStatusFor returns the map of nodeNames to nodeToUpdateStatusFor
|
||||
GetNodesToUpdateStatusFor() map[types.NodeName]nodeToUpdateStatusFor
|
||||
}
|
||||
|
||||
// AttachedVolume represents a volume that is attached to a node.
|
||||
|
|
@ -457,6 +460,7 @@ func (asw *actualStateOfWorld) updateNodeStatusUpdateNeeded(nodeName types.NodeN
|
|||
"Failed to set statusUpdateNeeded to needed %t because nodeName=%q does not exist",
|
||||
needed,
|
||||
nodeName)
|
||||
return
|
||||
}
|
||||
|
||||
nodeToUpdate.statusUpdateNeeded = needed
|
||||
|
|
@ -591,6 +595,10 @@ func (asw *actualStateOfWorld) GetVolumesToReportAttached() map[types.NodeName][
|
|||
return volumesToReportAttached
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) GetNodesToUpdateStatusFor() map[types.NodeName]nodeToUpdateStatusFor {
|
||||
return asw.nodesToUpdateStatusFor
|
||||
}
|
||||
|
||||
func getAttachedVolume(
|
||||
attachedVolume *attachedVolume,
|
||||
nodeAttachedTo *nodeAttachedTo) AttachedVolume {
|
||||
|
|
|
|||
4
vendor/k8s.io/kubernetes/pkg/controller/volume/attachdetach/reconciler/reconciler.go
generated
vendored
4
vendor/k8s.io/kubernetes/pkg/controller/volume/attachdetach/reconciler/reconciler.go
generated
vendored
|
|
@ -192,11 +192,11 @@ func (rc *reconciler) reconcile() {
|
|||
if rc.actualStateOfWorld.VolumeNodeExists(
|
||||
volumeToAttach.VolumeName, volumeToAttach.NodeName) {
|
||||
// Volume/Node exists, touch it to reset detachRequestedTime
|
||||
glog.V(1).Infof("Volume %q/Node %q is attached--touching.", volumeToAttach.VolumeName, volumeToAttach.NodeName)
|
||||
glog.V(5).Infof("Volume %q/Node %q is attached--touching.", volumeToAttach.VolumeName, volumeToAttach.NodeName)
|
||||
rc.actualStateOfWorld.ResetDetachRequestTime(volumeToAttach.VolumeName, volumeToAttach.NodeName)
|
||||
} else {
|
||||
// Volume/Node doesn't exist, spawn a goroutine to attach it
|
||||
glog.V(1).Infof("Attempting to start AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName)
|
||||
glog.V(5).Infof("Attempting to start AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName)
|
||||
err := rc.attacherDetacher.AttachVolume(volumeToAttach.VolumeToAttach, rc.actualStateOfWorld)
|
||||
if err == nil {
|
||||
glog.Infof("Started AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName)
|
||||
|
|
|
|||
|
|
@ -59,17 +59,15 @@ type nodeStatusUpdater struct {
|
|||
}
|
||||
|
||||
func (nsu *nodeStatusUpdater) UpdateNodeStatuses() error {
|
||||
smPatchVersion, err := strategicpatch.GetServerSupportedSMPatchVersion(nsu.kubeClient.Discovery())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: investigate right behavior if nodeName is empty
|
||||
// kubernetes/kubernetes/issues/37777
|
||||
nodesToUpdate := nsu.actualStateOfWorld.GetVolumesToReportAttached()
|
||||
for nodeName, attachedVolumes := range nodesToUpdate {
|
||||
nodeObj, exists, err := nsu.nodeInformer.GetStore().GetByKey(string(nodeName))
|
||||
if nodeObj == nil || !exists || err != nil {
|
||||
// If node does not exist, its status cannot be updated, log error and
|
||||
// reset flag statusUpdateNeeded back to true to indicate this node status
|
||||
// needs to be udpated again
|
||||
// needs to be updated again
|
||||
glog.V(2).Infof(
|
||||
"Could not update node status. Failed to find node %q in NodeInformer cache. %v",
|
||||
nodeName,
|
||||
|
|
@ -112,7 +110,7 @@ func (nsu *nodeStatusUpdater) UpdateNodeStatuses() error {
|
|||
}
|
||||
|
||||
patchBytes, err :=
|
||||
strategicpatch.CreateStrategicMergePatch(oldData, newData, node, smPatchVersion)
|
||||
strategicpatch.CreateStrategicMergePatch(oldData, newData, node)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to CreateStrategicMergePatch for node %q. %v",
|
||||
|
|
@ -123,7 +121,7 @@ func (nsu *nodeStatusUpdater) UpdateNodeStatuses() error {
|
|||
_, err = nsu.kubeClient.Core().Nodes().PatchStatus(string(nodeName), patchBytes)
|
||||
if err != nil {
|
||||
// If update node status fails, reset flag statusUpdateNeeded back to true
|
||||
// to indicate this node status needs to be udpated again
|
||||
// to indicate this node status needs to be updated again
|
||||
nsu.actualStateOfWorld.SetNodeStatusUpdateNeeded(nodeName)
|
||||
return fmt.Errorf(
|
||||
"failed to kubeClient.Core().Nodes().Patch for node %q. %v",
|
||||
|
|
|
|||
|
|
@ -2898,6 +2898,13 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
Format: "",
|
||||
},
|
||||
},
|
||||
"experimentalKernelMemcgNotification": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "If enabled, the kubelet will integrate with the kernel memcg notification to determine if memory eviction thresholds are crossed rather than polling.",
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"podsPerCore": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Maximum number of pods per core. Cannot exceed MaxPods",
|
||||
|
|
@ -3011,7 +3018,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"TypeMeta", "podManifestPath", "syncFrequency", "fileCheckFrequency", "httpCheckFrequency", "manifestURL", "manifestURLHeader", "enableServer", "address", "port", "readOnlyPort", "tlsCertFile", "tlsPrivateKeyFile", "certDirectory", "authentication", "authorization", "hostnameOverride", "podInfraContainerImage", "dockerEndpoint", "rootDirectory", "seccompProfileRoot", "allowPrivileged", "hostNetworkSources", "hostPIDSources", "hostIPCSources", "registryPullQPS", "registryBurst", "eventRecordQPS", "eventBurst", "enableDebuggingHandlers", "minimumGCAge", "maxPerPodContainerCount", "maxContainerCount", "cAdvisorPort", "healthzPort", "healthzBindAddress", "oomScoreAdj", "registerNode", "clusterDomain", "masterServiceNamespace", "clusterDNS", "streamingConnectionIdleTimeout", "nodeStatusUpdateFrequency", "imageMinimumGCAge", "imageGCHighThresholdPercent", "imageGCLowThresholdPercent", "lowDiskSpaceThresholdMB", "volumeStatsAggPeriod", "networkPluginName", "networkPluginMTU", "networkPluginDir", "cniConfDir", "cniBinDir", "volumePluginDir", "containerRuntime", "remoteRuntimeEndpoint", "remoteImageEndpoint", "experimentalMounterPath", "lockFilePath", "exitOnLockContention", "hairpinMode", "babysitDaemons", "maxPods", "nvidiaGPUs", "dockerExecHandlerName", "podCIDR", "resolvConf", "cpuCFSQuota", "containerized", "maxOpenFiles", "reconcileCIDR", "registerSchedulable", "contentType", "kubeAPIQPS", "kubeAPIBurst", "serializeImagePulls", "nodeLabels", "nonMasqueradeCIDR", "enableCustomMetrics", "podsPerCore", "enableControllerAttachDetach", "systemReserved", "kubeReserved", "protectKernelDefaults", "makeIPTablesUtilChains", "iptablesMasqueradeBit", "iptablesDropBit", "featureGates", "experimentalFailSwapOn", "ExperimentalCheckNodeCapabilitiesBeforeMount"},
|
||||
Required: []string{"TypeMeta", "podManifestPath", "syncFrequency", "fileCheckFrequency", "httpCheckFrequency", "manifestURL", "manifestURLHeader", "enableServer", "address", "port", "readOnlyPort", "tlsCertFile", "tlsPrivateKeyFile", "certDirectory", "authentication", "authorization", "hostnameOverride", "podInfraContainerImage", "dockerEndpoint", "rootDirectory", "seccompProfileRoot", "allowPrivileged", "hostNetworkSources", "hostPIDSources", "hostIPCSources", "registryPullQPS", "registryBurst", "eventRecordQPS", "eventBurst", "enableDebuggingHandlers", "minimumGCAge", "maxPerPodContainerCount", "maxContainerCount", "cAdvisorPort", "healthzPort", "healthzBindAddress", "oomScoreAdj", "registerNode", "clusterDomain", "masterServiceNamespace", "clusterDNS", "streamingConnectionIdleTimeout", "nodeStatusUpdateFrequency", "imageMinimumGCAge", "imageGCHighThresholdPercent", "imageGCLowThresholdPercent", "lowDiskSpaceThresholdMB", "volumeStatsAggPeriod", "networkPluginName", "networkPluginMTU", "networkPluginDir", "cniConfDir", "cniBinDir", "volumePluginDir", "containerRuntime", "remoteRuntimeEndpoint", "remoteImageEndpoint", "lockFilePath", "exitOnLockContention", "hairpinMode", "babysitDaemons", "maxPods", "nvidiaGPUs", "dockerExecHandlerName", "podCIDR", "resolvConf", "cpuCFSQuota", "containerized", "maxOpenFiles", "reconcileCIDR", "registerSchedulable", "contentType", "kubeAPIQPS", "kubeAPIBurst", "serializeImagePulls", "nodeLabels", "nonMasqueradeCIDR", "enableCustomMetrics", "podsPerCore", "enableControllerAttachDetach", "systemReserved", "kubeReserved", "protectKernelDefaults", "makeIPTablesUtilChains", "iptablesMasqueradeBit", "iptablesDropBit", "featureGates"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -3540,7 +3547,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"type", "status", "lastUpdateTime", "lastTransitionTime", "reason", "message"},
|
||||
Required: []string{"type", "status"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -3678,7 +3685,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"template", "progressDeadlineSeconds"},
|
||||
Required: []string{"template"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -3737,7 +3744,6 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"conditions"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -9751,7 +9757,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
"unschedulable": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration\"`",
|
||||
Description: "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration\"",
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
|
|
@ -10577,7 +10583,6 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"photonPersistentDisk"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -10685,7 +10690,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"pdID", "fsType"},
|
||||
Required: []string{"pdID"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
|
|
@ -12324,20 +12329,13 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
AdditionalProperties: &spec.SchemaOrBool{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"integer"},
|
||||
Type: []string{"string"},
|
||||
Format: "byte",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"stringData": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "stringData allows specifying non-binary secret data in string form. It is provided as a write-only convenience method. All keys and values are merged into the data field on write, overwriting any existing values. It is never output when reading from the API.",
|
||||
|
|
@ -13198,7 +13196,6 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"photonPersistentDisk"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -14705,6 +14702,13 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
Format: "",
|
||||
},
|
||||
},
|
||||
"experimentalKernelMemcgNotification": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "If enabled, the kubelet will integrate with the kernel memcg notification to determine if memory eviction thresholds are crossed rather than polling.",
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"podsPerCore": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Maximum number of pods per core. Cannot exceed MaxPods",
|
||||
|
|
@ -14818,7 +14822,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"TypeMeta", "podManifestPath", "syncFrequency", "fileCheckFrequency", "httpCheckFrequency", "manifestURL", "manifestURLHeader", "enableServer", "address", "port", "readOnlyPort", "tlsCertFile", "tlsPrivateKeyFile", "certDirectory", "authentication", "authorization", "hostnameOverride", "podInfraContainerImage", "dockerEndpoint", "rootDirectory", "seccompProfileRoot", "allowPrivileged", "hostNetworkSources", "hostPIDSources", "hostIPCSources", "registryPullQPS", "registryBurst", "eventRecordQPS", "eventBurst", "enableDebuggingHandlers", "minimumGCAge", "maxPerPodContainerCount", "maxContainerCount", "cAdvisorPort", "healthzPort", "healthzBindAddress", "oomScoreAdj", "registerNode", "clusterDomain", "masterServiceNamespace", "clusterDNS", "streamingConnectionIdleTimeout", "nodeStatusUpdateFrequency", "imageMinimumGCAge", "imageGCHighThresholdPercent", "imageGCLowThresholdPercent", "lowDiskSpaceThresholdMB", "volumeStatsAggPeriod", "networkPluginName", "networkPluginDir", "cniConfDir", "cniBinDir", "networkPluginMTU", "volumePluginDir", "cloudProvider", "cloudConfigFile", "kubeletCgroups", "runtimeCgroups", "systemCgroups", "cgroupRoot", "containerRuntime", "remoteRuntimeEndpoint", "remoteImageEndpoint", "runtimeRequestTimeout", "rktPath", "experimentalMounterPath", "rktAPIEndpoint", "rktStage1Image", "lockFilePath", "exitOnLockContention", "hairpinMode", "babysitDaemons", "maxPods", "nvidiaGPUs", "dockerExecHandlerName", "podCIDR", "resolvConf", "cpuCFSQuota", "containerized", "maxOpenFiles", "reconcileCIDR", "registerSchedulable", "contentType", "kubeAPIQPS", "kubeAPIBurst", "serializeImagePulls", "outOfDiskTransitionFrequency", "nodeIP", "nodeLabels", "nonMasqueradeCIDR", "enableCustomMetrics", "evictionHard", "evictionSoft", "evictionSoftGracePeriod", "evictionPressureTransitionPeriod", "evictionMaxPodGracePeriod", "evictionMinimumReclaim", "podsPerCore", "enableControllerAttachDetach", "systemReserved", "kubeReserved", "protectKernelDefaults", "makeIPTablesUtilChains", "iptablesMasqueradeBit", "iptablesDropBit", "featureGates", "experimentalFailSwapOn", "ExperimentalCheckNodeCapabilitiesBeforeMount"},
|
||||
Required: []string{"TypeMeta", "podManifestPath", "syncFrequency", "fileCheckFrequency", "httpCheckFrequency", "manifestURL", "manifestURLHeader", "enableServer", "address", "port", "readOnlyPort", "tlsCertFile", "tlsPrivateKeyFile", "certDirectory", "authentication", "authorization", "hostnameOverride", "podInfraContainerImage", "dockerEndpoint", "rootDirectory", "seccompProfileRoot", "allowPrivileged", "hostNetworkSources", "hostPIDSources", "hostIPCSources", "registryPullQPS", "registryBurst", "eventRecordQPS", "eventBurst", "enableDebuggingHandlers", "minimumGCAge", "maxPerPodContainerCount", "maxContainerCount", "cAdvisorPort", "healthzPort", "healthzBindAddress", "oomScoreAdj", "registerNode", "clusterDomain", "masterServiceNamespace", "clusterDNS", "streamingConnectionIdleTimeout", "nodeStatusUpdateFrequency", "imageMinimumGCAge", "imageGCHighThresholdPercent", "imageGCLowThresholdPercent", "lowDiskSpaceThresholdMB", "volumeStatsAggPeriod", "networkPluginName", "networkPluginDir", "cniConfDir", "cniBinDir", "networkPluginMTU", "volumePluginDir", "cloudProvider", "cloudConfigFile", "kubeletCgroups", "runtimeCgroups", "systemCgroups", "cgroupRoot", "containerRuntime", "remoteRuntimeEndpoint", "remoteImageEndpoint", "runtimeRequestTimeout", "rktPath", "rktAPIEndpoint", "rktStage1Image", "lockFilePath", "exitOnLockContention", "hairpinMode", "babysitDaemons", "maxPods", "nvidiaGPUs", "dockerExecHandlerName", "podCIDR", "resolvConf", "cpuCFSQuota", "containerized", "maxOpenFiles", "reconcileCIDR", "registerSchedulable", "contentType", "kubeAPIQPS", "kubeAPIBurst", "serializeImagePulls", "outOfDiskTransitionFrequency", "nodeIP", "nodeLabels", "nonMasqueradeCIDR", "enableCustomMetrics", "evictionHard", "evictionSoft", "evictionSoftGracePeriod", "evictionPressureTransitionPeriod", "evictionMaxPodGracePeriod", "evictionMinimumReclaim", "experimentalKernelMemcgNotification", "podsPerCore", "enableControllerAttachDetach", "systemReserved", "kubeReserved", "protectKernelDefaults", "makeIPTablesUtilChains", "iptablesMasqueradeBit", "iptablesDropBit"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -15729,7 +15733,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"type", "status", "lastUpdateTime", "lastTransitionTime", "reason", "message"},
|
||||
Required: []string{"type", "status"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -15870,7 +15874,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"template", "progressDeadlineSeconds"},
|
||||
Required: []string{"template"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -15930,7 +15934,6 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"conditions"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -15978,7 +15981,6 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"metadata", "deleteOptions"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -16955,7 +16957,6 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"metadata", "spec", "status"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -16984,7 +16985,7 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"metadata", "items"},
|
||||
Required: []string{"items"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
@ -17008,7 +17009,6 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"minAvailable", "selector"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
|
|
|||
|
|
@ -665,7 +665,8 @@ type PodSandboxConfig struct {
|
|||
// * runtime/default: the default profile for the container runtime
|
||||
// * unconfined: unconfined profile, ie, no seccomp sandboxing
|
||||
// * localhost/<profile-name>: the profile installed to the node's
|
||||
// local seccomp profile root
|
||||
// local seccomp profile root. Note that profile root is set in
|
||||
// kubelet, and it is not passed in CRI yet, see https://issues.k8s.io/36997.
|
||||
//
|
||||
// 3. Sysctls
|
||||
//
|
||||
|
|
|
|||
|
|
@ -255,7 +255,8 @@ message PodSandboxConfig {
|
|||
// * runtime/default: the default profile for the container runtime
|
||||
// * unconfined: unconfined profile, ie, no seccomp sandboxing
|
||||
// * localhost/<profile-name>: the profile installed to the node's
|
||||
// local seccomp profile root
|
||||
// local seccomp profile root. Note that profile root is set in
|
||||
// kubelet, and it is not passed in CRI yet, see https://issues.k8s.io/36997.
|
||||
//
|
||||
// 3. Sysctls
|
||||
//
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ go_library(
|
|||
"//pkg/api/unversioned:go_default_library",
|
||||
"//pkg/client/record:go_default_library",
|
||||
"//pkg/kubelet/api/v1alpha1/stats:go_default_library",
|
||||
"//pkg/kubelet/cm:go_default_library",
|
||||
"//pkg/kubelet/lifecycle:go_default_library",
|
||||
"//pkg/kubelet/qos:go_default_library",
|
||||
"//pkg/kubelet/server/stats:go_default_library",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/client/record"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||
"k8s.io/kubernetes/pkg/kubelet/server/stats"
|
||||
|
|
@ -33,7 +35,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
)
|
||||
|
||||
// managerImpl implements NodeStabilityManager
|
||||
// managerImpl implements Manager
|
||||
type managerImpl struct {
|
||||
// used to track time
|
||||
clock clock.Clock
|
||||
|
|
@ -65,6 +67,8 @@ type managerImpl struct {
|
|||
resourceToNodeReclaimFuncs map[api.ResourceName]nodeReclaimFuncs
|
||||
// last observations from synchronize
|
||||
lastObservations signalObservations
|
||||
// notifiersInitialized indicates if the threshold notifiers have been initialized (i.e. synchronize() has been called once)
|
||||
notifiersInitialized bool
|
||||
}
|
||||
|
||||
// ensure it implements the required interface
|
||||
|
|
@ -139,6 +143,39 @@ func (m *managerImpl) IsUnderDiskPressure() bool {
|
|||
return hasNodeCondition(m.nodeConditions, api.NodeDiskPressure)
|
||||
}
|
||||
|
||||
func startMemoryThresholdNotifier(thresholds []Threshold, observations signalObservations, hard bool, handler thresholdNotifierHandlerFunc) error {
|
||||
for _, threshold := range thresholds {
|
||||
if threshold.Signal != SignalMemoryAvailable || hard != isHardEvictionThreshold(threshold) {
|
||||
continue
|
||||
}
|
||||
observed, found := observations[SignalMemoryAvailable]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
cgroups, err := cm.GetCgroupSubsystems()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO add support for eviction from --cgroup-root
|
||||
cgpath, found := cgroups.MountPoints["memory"]
|
||||
if !found || len(cgpath) == 0 {
|
||||
return fmt.Errorf("memory cgroup mount point not found")
|
||||
}
|
||||
attribute := "memory.usage_in_bytes"
|
||||
quantity := getThresholdQuantity(threshold.Value, observed.capacity)
|
||||
usageThreshold := resource.NewQuantity(observed.capacity.Value(), resource.DecimalSI)
|
||||
usageThreshold.Sub(*quantity)
|
||||
description := fmt.Sprintf("<%s available", formatThresholdValue(threshold.Value))
|
||||
memcgThresholdNotifier, err := NewMemCGThresholdNotifier(cgpath, attribute, usageThreshold.String(), description, handler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go memcgThresholdNotifier.Start(wait.NeverStop)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// synchronize is the main control loop that enforces eviction thresholds.
|
||||
func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc ActivePodsFunc) {
|
||||
// if we have nothing to do, just return
|
||||
|
|
@ -166,8 +203,28 @@ func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc Act
|
|||
return
|
||||
}
|
||||
|
||||
// find the list of thresholds that are met independent of grace period
|
||||
now := m.clock.Now()
|
||||
// attempt to create a threshold notifier to improve eviction response time
|
||||
if m.config.KernelMemcgNotification && !m.notifiersInitialized {
|
||||
glog.Infof("eviction manager attempting to integrate with kernel memcg notification api")
|
||||
m.notifiersInitialized = true
|
||||
// start soft memory notification
|
||||
err = startMemoryThresholdNotifier(m.config.Thresholds, observations, false, func(desc string) {
|
||||
glog.Infof("soft memory eviction threshold crossed at %s", desc)
|
||||
// TODO wait grace period for soft memory limit
|
||||
m.synchronize(diskInfoProvider, podFunc)
|
||||
})
|
||||
if err != nil {
|
||||
glog.Warningf("eviction manager: failed to create hard memory threshold notifier: %v", err)
|
||||
}
|
||||
// start hard memory notification
|
||||
err = startMemoryThresholdNotifier(m.config.Thresholds, observations, true, func(desc string) {
|
||||
glog.Infof("hard memory eviction threshold crossed at %s", desc)
|
||||
m.synchronize(diskInfoProvider, podFunc)
|
||||
})
|
||||
if err != nil {
|
||||
glog.Warningf("eviction manager: failed to create soft memory threshold notifier: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// determine the set of thresholds met independent of grace period
|
||||
thresholds = thresholdsMet(thresholds, observations, false)
|
||||
|
|
@ -182,6 +239,7 @@ func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc Act
|
|||
thresholds = thresholdsUpdatedStats(thresholds, observations, m.lastObservations)
|
||||
|
||||
// track when a threshold was first observed
|
||||
now := m.clock.Now()
|
||||
thresholdsFirstObservedAt := thresholdsFirstObservedAt(thresholds, m.thresholdsFirstObservedAt, now)
|
||||
|
||||
// the set of node conditions that are triggered by currently observed thresholds
|
||||
|
|
@ -218,7 +276,7 @@ func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc Act
|
|||
glog.Warningf("eviction manager: attempting to reclaim %v", resourceToReclaim)
|
||||
|
||||
// determine if this is a soft or hard eviction associated with the resource
|
||||
softEviction := isSoftEviction(thresholds, resourceToReclaim)
|
||||
softEviction := isSoftEvictionThresholds(thresholds, resourceToReclaim)
|
||||
|
||||
// record an event about the resources we are now attempting to reclaim via eviction
|
||||
m.recorder.Eventf(m.nodeRef, api.EventTypeWarning, "EvictionThresholdMet", "Attempting to reclaim %s", resourceToReclaim)
|
||||
|
|
|
|||
|
|
@ -848,18 +848,23 @@ func getStarvedResources(thresholds []Threshold) []api.ResourceName {
|
|||
}
|
||||
|
||||
// isSoftEviction returns true if the thresholds met for the starved resource are only soft thresholds
|
||||
func isSoftEviction(thresholds []Threshold, starvedResource api.ResourceName) bool {
|
||||
func isSoftEvictionThresholds(thresholds []Threshold, starvedResource api.ResourceName) bool {
|
||||
for _, threshold := range thresholds {
|
||||
if resourceToCheck := signalToResource[threshold.Signal]; resourceToCheck != starvedResource {
|
||||
continue
|
||||
}
|
||||
if threshold.GracePeriod == time.Duration(0) {
|
||||
if isHardEvictionThreshold(threshold) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isSoftEviction returns true if the thresholds met for the starved resource are only soft thresholds
|
||||
func isHardEvictionThreshold(threshold Threshold) bool {
|
||||
return threshold.GracePeriod == time.Duration(0)
|
||||
}
|
||||
|
||||
// buildResourceToRankFunc returns ranking functions associated with resources
|
||||
func buildResourceToRankFunc(withImageFs bool) map[api.ResourceName]rankFunc {
|
||||
resourceToRankFunc := map[api.ResourceName]rankFunc{
|
||||
|
|
|
|||
119
vendor/k8s.io/kubernetes/pkg/kubelet/eviction/threshold_notifier_linux.go
generated
vendored
Normal file
119
vendor/k8s.io/kubernetes/pkg/kubelet/eviction/threshold_notifier_linux.go
generated
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
Copyright 2016 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 eviction
|
||||
|
||||
/*
|
||||
#include <sys/eventfd.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type memcgThresholdNotifier struct {
|
||||
watchfd int
|
||||
controlfd int
|
||||
eventfd int
|
||||
handler thresholdNotifierHandlerFunc
|
||||
description string
|
||||
}
|
||||
|
||||
var _ ThresholdNotifier = &memcgThresholdNotifier{}
|
||||
|
||||
// NewMemCGThresholdNotifier sends notifications when a cgroup threshold
|
||||
// is crossed (in either direction) for a given cgroup attribute
|
||||
func NewMemCGThresholdNotifier(path, attribute, threshold, description string, handler thresholdNotifierHandlerFunc) (ThresholdNotifier, error) {
|
||||
watchfd, err := syscall.Open(fmt.Sprintf("%s/%s", path, attribute), syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
syscall.Close(watchfd)
|
||||
}
|
||||
}()
|
||||
controlfd, err := syscall.Open(fmt.Sprintf("%s/cgroup.event_control", path), syscall.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
syscall.Close(controlfd)
|
||||
}
|
||||
}()
|
||||
efd, err := C.eventfd(0, C.EFD_CLOEXEC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eventfd := int(efd)
|
||||
if eventfd < 0 {
|
||||
err = fmt.Errorf("eventfd call failed")
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
syscall.Close(eventfd)
|
||||
}
|
||||
}()
|
||||
glog.V(2).Infof("eviction: setting notification threshold to %s", threshold)
|
||||
config := fmt.Sprintf("%d %d %s", eventfd, watchfd, threshold)
|
||||
_, err = syscall.Write(controlfd, []byte(config))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &memcgThresholdNotifier{
|
||||
watchfd: watchfd,
|
||||
controlfd: controlfd,
|
||||
eventfd: eventfd,
|
||||
handler: handler,
|
||||
description: description,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getThresholdEvents(eventfd int, eventCh chan<- int) {
|
||||
for {
|
||||
buf := make([]byte, 8)
|
||||
_, err := syscall.Read(eventfd, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
eventCh <- 0
|
||||
}
|
||||
}
|
||||
|
||||
func (n *memcgThresholdNotifier) Start(stopCh <-chan struct{}) {
|
||||
eventCh := make(chan int, 1)
|
||||
go getThresholdEvents(n.eventfd, eventCh)
|
||||
for {
|
||||
select {
|
||||
case <-stopCh:
|
||||
glog.V(2).Infof("eviction: stopping threshold notifier")
|
||||
syscall.Close(n.watchfd)
|
||||
syscall.Close(n.controlfd)
|
||||
syscall.Close(n.eventfd)
|
||||
close(eventCh)
|
||||
return
|
||||
case <-eventCh:
|
||||
glog.V(2).Infof("eviction: threshold crossed")
|
||||
n.handler(n.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/pkg/kubelet/eviction/threshold_notifier_unsupported.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/pkg/kubelet/eviction/threshold_notifier_unsupported.go
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 2016 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 eviction
|
||||
|
||||
import "fmt"
|
||||
|
||||
// NewMemCGThresholdNotifier sends notifications when a cgroup threshold
|
||||
// is crossed (in either direction) for a given cgroup attribute
|
||||
func NewMemCGThresholdNotifier(path, attribute, threshold, description string, handler thresholdNotifierHandlerFunc) (ThresholdNotifier, error) {
|
||||
return nil, fmt.Errorf("threshold notification not supported")
|
||||
}
|
||||
|
|
@ -69,6 +69,8 @@ type Config struct {
|
|||
MaxPodGracePeriodSeconds int64
|
||||
// Thresholds define the set of conditions monitored to trigger eviction.
|
||||
Thresholds []Threshold
|
||||
// KernelMemcgNotification if true will integrate with the kernel memcg notification to determine if memory thresholds are crossed.
|
||||
KernelMemcgNotification bool
|
||||
}
|
||||
|
||||
// ThresholdValue is a value holder that abstracts literal versus percentage based quantity
|
||||
|
|
@ -161,3 +163,11 @@ type nodeReclaimFunc func() (*resource.Quantity, error)
|
|||
|
||||
// nodeReclaimFuncs is an ordered list of nodeReclaimFunc
|
||||
type nodeReclaimFuncs []nodeReclaimFunc
|
||||
|
||||
// thresholdNotifierHandlerFunc is a function that takes action in response to a crossed threshold
|
||||
type thresholdNotifierHandlerFunc func(thresholdDescription string)
|
||||
|
||||
// ThresholdNotifier notifies the user when an attribute crosses a threshold value
|
||||
type ThresholdNotifier interface {
|
||||
Start(stopCh <-chan struct{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,6 +353,7 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub
|
|||
PressureTransitionPeriod: kubeCfg.EvictionPressureTransitionPeriod.Duration,
|
||||
MaxPodGracePeriodSeconds: int64(kubeCfg.EvictionMaxPodGracePeriod),
|
||||
Thresholds: thresholds,
|
||||
KernelMemcgNotification: kubeCfg.ExperimentalKernelMemcgNotification,
|
||||
}
|
||||
|
||||
reservation, err := ParseReservation(kubeCfg.KubeReserved, kubeCfg.SystemReserved)
|
||||
|
|
@ -1850,8 +1851,8 @@ func (kl *Kubelet) syncLoopIteration(configCh <-chan kubetypes.PodUpdate, handle
|
|||
}
|
||||
case <-housekeepingCh:
|
||||
if !kl.sourcesReady.AllReady() {
|
||||
// If the sources aren't ready, skip housekeeping, as we may
|
||||
// accidentally delete pods from unready sources.
|
||||
// If the sources aren't ready or volume manager has not yet synced the states,
|
||||
// skip housekeeping, as we may accidentally delete pods from unready sources.
|
||||
glog.V(4).Infof("SyncLoop (housekeeping, skipped): sources aren't ready yet.")
|
||||
} else {
|
||||
glog.V(4).Infof("SyncLoop (housekeeping)")
|
||||
|
|
@ -1910,22 +1911,32 @@ func (kl *Kubelet) HandlePodAdditions(pods []*api.Pod) {
|
|||
start := kl.clock.Now()
|
||||
sort.Sort(sliceutils.PodsByCreationTime(pods))
|
||||
for _, pod := range pods {
|
||||
if kubepod.IsMirrorPod(pod) {
|
||||
existingPods := kl.podManager.GetPods()
|
||||
// Always add the pod to the pod manager. Kubelet relies on the pod
|
||||
// manager as the source of truth for the desired state. If a pod does
|
||||
// not exist in the pod manager, it means that it has been deleted in
|
||||
// the apiserver and no action (other than cleanup) is required.
|
||||
kl.podManager.AddPod(pod)
|
||||
|
||||
if kubepod.IsMirrorPod(pod) {
|
||||
kl.handleMirrorPod(pod, start)
|
||||
continue
|
||||
}
|
||||
// Note that allPods excludes the new pod.
|
||||
allPods := kl.podManager.GetPods()
|
||||
|
||||
if !kl.podIsTerminated(pod) {
|
||||
// Only go through the admission process if the pod is not
|
||||
// terminated.
|
||||
|
||||
// We failed pods that we rejected, so activePods include all admitted
|
||||
// pods that are alive.
|
||||
activePods := kl.filterOutTerminatedPods(allPods)
|
||||
activePods := kl.filterOutTerminatedPods(existingPods)
|
||||
|
||||
// Check if we can admit the pod; if not, reject it.
|
||||
if ok, reason, message := kl.canAdmitPod(activePods, pod); !ok {
|
||||
kl.rejectPod(pod, reason, message)
|
||||
continue
|
||||
}
|
||||
kl.podManager.AddPod(pod)
|
||||
}
|
||||
mirrorPod, _ := kl.podManager.GetMirrorPodByPod(pod)
|
||||
kl.dispatchWork(pod, kubetypes.SyncPodCreate, mirrorPod, start)
|
||||
kl.probeManager.AddPod(pod)
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ func (kl *Kubelet) syncNodeStatus() {
|
|||
// updateNodeStatus updates node status to master with retries.
|
||||
func (kl *Kubelet) updateNodeStatus() error {
|
||||
for i := 0; i < nodeStatusUpdateRetry; i++ {
|
||||
if err := kl.tryUpdateNodeStatus(); err != nil {
|
||||
if err := kl.tryUpdateNodeStatus(i); err != nil {
|
||||
glog.Errorf("Error updating node status, will retry: %v", err)
|
||||
} else {
|
||||
return nil
|
||||
|
|
@ -309,12 +309,13 @@ func (kl *Kubelet) updateNodeStatus() error {
|
|||
|
||||
// tryUpdateNodeStatus tries to update node status to master. If ReconcileCBR0
|
||||
// is set, this function will also confirm that cbr0 is configured correctly.
|
||||
func (kl *Kubelet) tryUpdateNodeStatus() error {
|
||||
func (kl *Kubelet) tryUpdateNodeStatus(tryNumber int) error {
|
||||
// In large clusters, GET and PUT operations on Node objects coming
|
||||
// from here are the majority of load on apiserver and etcd.
|
||||
// To reduce the load on etcd, we are serving GET operations from
|
||||
// apiserver cache (the data might be slightly delayed but it doesn't
|
||||
// seem to cause more confilict - the delays are pretty small).
|
||||
// If it result in a conflict, all retries are served directly from etcd.
|
||||
// TODO: Currently apiserver doesn't support serving GET operations
|
||||
// from its cache. Thus we are hacking it by issuing LIST with
|
||||
// field selector for the name of the node (field selectors with
|
||||
|
|
@ -322,7 +323,9 @@ func (kl *Kubelet) tryUpdateNodeStatus() error {
|
|||
// apiserver supports GET from cache, change it here.
|
||||
opts := api.ListOptions{
|
||||
FieldSelector: fields.Set{"metadata.name": string(kl.nodeName)}.AsSelector(),
|
||||
ResourceVersion: "0",
|
||||
}
|
||||
if tryNumber == 0 {
|
||||
opts.ResourceVersion = "0"
|
||||
}
|
||||
nodes, err := kl.kubeClient.Core().Nodes().List(opts)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import (
|
|||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||
|
|
@ -105,24 +104,12 @@ func (kl *Kubelet) cleanupOrphanedPodDirs(
|
|||
glog.V(3).Infof("Orphaned pod %q found, but volumes are not cleaned up", uid)
|
||||
continue
|
||||
}
|
||||
// Check whether volume is still mounted on disk. If so, do not delete directory
|
||||
// If there are still volume directories, do not delete directory
|
||||
volumePaths, err := kl.getPodVolumePathListFromDisk(uid)
|
||||
if err != nil {
|
||||
if err != nil || len(volumePaths) > 0 {
|
||||
glog.Errorf("Orphaned pod %q found, but error %v occured during reading volume dir from disk", uid, err)
|
||||
continue
|
||||
} else if len(volumePaths) > 0 {
|
||||
for _, path := range volumePaths {
|
||||
notMount, err := mount.IsNotMountPoint(path)
|
||||
if err == nil && notMount {
|
||||
glog.V(2).Infof("Volume path %q is no longer mounted, remove it", path)
|
||||
os.Remove(path)
|
||||
} else {
|
||||
glog.Errorf("Orphaned pod %q found, but it might still mounted with error %v", uid, err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Orphaned pod %q found, removing", uid)
|
||||
if err := os.RemoveAll(kl.getPodDir(uid)); err != nil {
|
||||
glog.Errorf("Failed to remove orphaned pod %q dir; err: %v", uid, err)
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ func (h *handler) cleanupHostportMap(containerPortMap map[api.ContainerPort]targ
|
|||
for containerPort := range containerPortMap {
|
||||
hp := hostport{
|
||||
port: containerPort.HostPort,
|
||||
protocol: string(containerPort.Protocol),
|
||||
protocol: strings.ToLower(string(containerPort.Protocol)),
|
||||
}
|
||||
currentHostports[hp] = true
|
||||
}
|
||||
|
|
@ -379,6 +379,7 @@ func (h *handler) cleanupHostportMap(containerPortMap map[api.ContainerPort]targ
|
|||
for hp, socket := range h.hostPortMap {
|
||||
if _, ok := currentHostports[hp]; !ok {
|
||||
socket.Close()
|
||||
glog.V(3).Infof("Closed local port %s", hp.String())
|
||||
delete(h.hostPortMap, hp)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ func (mc *basicMirrorClient) DeleteMirrorPod(podFullName string) error {
|
|||
glog.Errorf("Failed to parse a pod full name %q", podFullName)
|
||||
return err
|
||||
}
|
||||
glog.V(4).Infof("Deleting a mirror pod %q", podFullName)
|
||||
glog.V(2).Infof("Deleting a mirror pod %q", podFullName)
|
||||
// TODO(random-liu): Delete the mirror pod with uid precondition in mirror pod manager
|
||||
if err := mc.apiserverClient.Core().Pods(namespace).Delete(name, api.NewDeleteOptions(0)); err != nil && !errors.IsNotFound(err) {
|
||||
glog.Errorf("Failed deleting a mirror pod %q: %v", podFullName, err)
|
||||
|
|
|
|||
|
|
@ -438,6 +438,7 @@ func (m *manager) syncPod(uid types.UID, status versionedPodStatus) {
|
|||
deleteOptions := api.NewDeleteOptions(0)
|
||||
// Use the pod UID as the precondition for deletion to prevent deleting a newly created pod with the same name and namespace.
|
||||
deleteOptions.Preconditions = api.NewUIDPreconditions(string(pod.UID))
|
||||
glog.V(2).Infof("Removing Pod %q from etcd", format.Pod(pod))
|
||||
if err = m.kubeClient.Core().Pods(pod.Namespace).Delete(pod.Name, deleteOptions); err == nil {
|
||||
glog.V(3).Infof("Pod %q fully terminated and removed from etcd", format.Pod(pod))
|
||||
m.deletePodStatus(uid)
|
||||
|
|
|
|||
|
|
@ -129,10 +129,18 @@ func (dswp *desiredStateOfWorldPopulator) populatorLoopFunc() func() {
|
|||
}
|
||||
}
|
||||
|
||||
func isPodTerminated(pod *api.Pod) bool {
|
||||
return pod.Status.Phase == api.PodFailed || pod.Status.Phase == api.PodSucceeded
|
||||
}
|
||||
|
||||
// Iterate through all pods and add to desired state of world if they don't
|
||||
// exist but should
|
||||
func (dswp *desiredStateOfWorldPopulator) findAndAddNewPods() {
|
||||
for _, pod := range dswp.podManager.GetPods() {
|
||||
if isPodTerminated(pod) {
|
||||
// Do not (re)add volumes for terminated pods
|
||||
continue
|
||||
}
|
||||
dswp.processPodVolumes(pod)
|
||||
}
|
||||
}
|
||||
|
|
@ -144,10 +152,19 @@ func (dswp *desiredStateOfWorldPopulator) findAndRemoveDeletedPods() {
|
|||
|
||||
runningPodsFetched := false
|
||||
for _, volumeToMount := range dswp.desiredStateOfWorld.GetVolumesToMount() {
|
||||
if _, podExists :=
|
||||
dswp.podManager.GetPodByUID(volumeToMount.Pod.UID); podExists {
|
||||
pod, podExists := dswp.podManager.GetPodByUID(volumeToMount.Pod.UID)
|
||||
if podExists {
|
||||
// Skip running pods
|
||||
if !isPodTerminated(pod) {
|
||||
continue
|
||||
}
|
||||
// Skip non-memory backed volumes belonging to terminated pods
|
||||
volume := volumeToMount.VolumeSpec.Volume
|
||||
if (volume.EmptyDir == nil || volume.EmptyDir.Medium != api.StorageMediumMemory) &&
|
||||
volume.ConfigMap == nil && volume.Secret == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Once a pod has been deleted from kubelet pod manager, do not delete
|
||||
// it immediately from volume manager. Instead, check the kubelet
|
||||
|
|
|
|||
|
|
@ -246,6 +246,9 @@ func (m *ThirdPartyResourceServer) InstallThirdPartyResource(rsrc *extensions.Th
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rsrc.Versions) == 0 {
|
||||
return fmt.Errorf("ThirdPartyResource %s has no defined versions", rsrc.Name)
|
||||
}
|
||||
plural, _ := meta.KindToResource(unversioned.GroupVersionKind{
|
||||
Group: group,
|
||||
Version: rsrc.Versions[0].Name,
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ type serviceInfo struct {
|
|||
nodePort int
|
||||
loadBalancerStatus api.LoadBalancerStatus
|
||||
sessionAffinityType api.ServiceAffinity
|
||||
stickyMaxAgeSeconds int
|
||||
stickyMaxAgeMinutes int
|
||||
externalIPs []string
|
||||
loadBalancerSourceRanges []string
|
||||
onlyNodeLocalEndpoints bool
|
||||
|
|
@ -155,7 +155,7 @@ type endpointsInfo struct {
|
|||
func newServiceInfo(service proxy.ServicePortName) *serviceInfo {
|
||||
return &serviceInfo{
|
||||
sessionAffinityType: api.ServiceAffinityNone, // default
|
||||
stickyMaxAgeSeconds: 180, // TODO: paramaterize this in the API.
|
||||
stickyMaxAgeMinutes: 180, // TODO: paramaterize this in the API.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -388,6 +388,9 @@ func (proxier *Proxier) sameConfig(info *serviceInfo, service *api.Service, port
|
|||
if info.onlyNodeLocalEndpoints != onlyNodeLocalEndpoints {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(info.loadBalancerSourceRanges, service.Spec.LoadBalancerSourceRanges) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -1165,7 +1168,7 @@ func (proxier *Proxier) syncProxyRules() {
|
|||
"-A", string(svcChain),
|
||||
"-m", "comment", "--comment", svcName.String(),
|
||||
"-m", "recent", "--name", string(endpointChain),
|
||||
"--rcheck", "--seconds", fmt.Sprintf("%d", svcInfo.stickyMaxAgeSeconds), "--reap",
|
||||
"--rcheck", "--seconds", fmt.Sprintf("%d", svcInfo.stickyMaxAgeMinutes*60), "--reap",
|
||||
"-j", string(endpointChain))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ go_library(
|
|||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apis/policy:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/policy/internalversion:go_default_library",
|
||||
"//pkg/client/retry:go_default_library",
|
||||
"//pkg/kubelet/client:go_default_library",
|
||||
"//pkg/labels:go_default_library",
|
||||
"//pkg/registry/cachesize:go_default_library",
|
||||
|
|
@ -35,6 +36,7 @@ go_library(
|
|||
"//pkg/registry/generic/registry:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/storage:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,13 +21,16 @@ import (
|
|||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
policyclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/policy/internalversion"
|
||||
"k8s.io/kubernetes/pkg/client/retry"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -40,6 +43,15 @@ const (
|
|||
MaxDisruptedPodSize = 2000
|
||||
)
|
||||
|
||||
// EvictionsRetry is the retry for a conflict where multiple clients
|
||||
// are making changes to the same resource.
|
||||
var EvictionsRetry = wait.Backoff{
|
||||
Steps: 20,
|
||||
Duration: 500 * time.Millisecond,
|
||||
Factor: 1.0,
|
||||
Jitter: 0.1,
|
||||
}
|
||||
|
||||
func newEvictionStorage(store *registry.Store, podDisruptionBudgetClient policyclient.PodDisruptionBudgetsGetter) *EvictionREST {
|
||||
return &EvictionREST{store: store, podDisruptionBudgetClient: podDisruptionBudgetClient}
|
||||
}
|
||||
|
|
@ -66,30 +78,35 @@ func (r *EvictionREST) Create(ctx api.Context, obj runtime.Object) (runtime.Obje
|
|||
return nil, err
|
||||
}
|
||||
pod := obj.(*api.Pod)
|
||||
var rtStatus *unversioned.Status
|
||||
var pdbName string
|
||||
err = retry.RetryOnConflict(EvictionsRetry, func() error {
|
||||
pdbs, err := r.getPodDisruptionBudgets(ctx, pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pdbs) > 1 {
|
||||
return &unversioned.Status{
|
||||
rtStatus = &unversioned.Status{
|
||||
Status: unversioned.StatusFailure,
|
||||
Message: "This pod has more than one PodDisruptionBudget, which the eviction subresource does not support.",
|
||||
Code: 500,
|
||||
}, nil
|
||||
}
|
||||
return nil
|
||||
} else if len(pdbs) == 1 {
|
||||
pdb := pdbs[0]
|
||||
pdbName = pdb.Name
|
||||
// Try to verify-and-decrement
|
||||
|
||||
// If it was false already, or if it becomes false during the course of our retries,
|
||||
// raise an error marked as a 429.
|
||||
ok, err := r.checkAndDecrement(pod.Namespace, pod.Name, pdb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return &unversioned.Status{
|
||||
rtStatus = &unversioned.Status{
|
||||
Status: unversioned.StatusFailure,
|
||||
// TODO(mml): Include some more details about why the eviction is disallowed.
|
||||
// Ideally any such text is generated by the DisruptionController (offline).
|
||||
|
|
@ -99,9 +116,21 @@ func (r *EvictionREST) Create(ctx api.Context, obj runtime.Object) (runtime.Obje
|
|||
// budgets, we can sometimes compute a sensible suggested value. But
|
||||
// even without that, we can give a suggestion (10 minutes?) that
|
||||
// prevents well-behaved clients from hammering us.
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err == wait.ErrWaitTimeout {
|
||||
err = errors.NewTimeoutError(fmt.Sprintf("couldn't update PodDisruptionBudget %q due to conflicts", pdbName), 10)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rtStatus != nil {
|
||||
return rtStatus, nil
|
||||
}
|
||||
|
||||
// At this point there was either no PDB or we succeded in decrementing
|
||||
|
||||
|
|
@ -115,15 +144,16 @@ func (r *EvictionREST) Create(ctx api.Context, obj runtime.Object) (runtime.Obje
|
|||
return &unversioned.Status{Status: unversioned.StatusSuccess}, nil
|
||||
}
|
||||
|
||||
// checkAndDecrement checks if the provided PodDisruptionBudget allows any disruption.
|
||||
func (r *EvictionREST) checkAndDecrement(namespace string, podName string, pdb policy.PodDisruptionBudget) (ok bool, err error) {
|
||||
if pdb.Status.ObservedGeneration != pdb.Generation {
|
||||
if pdb.Status.ObservedGeneration < pdb.Generation {
|
||||
return false, nil
|
||||
}
|
||||
if pdb.Status.PodDisruptionsAllowed < 0 {
|
||||
return false, fmt.Errorf("pdb disruptions allowed is negative")
|
||||
return false, errors.NewForbidden(policy.Resource("poddisruptionbudget"), pdb.Name, fmt.Errorf("pdb disruptions allowed is negative"))
|
||||
}
|
||||
if len(pdb.Status.DisruptedPods) > MaxDisruptedPodSize {
|
||||
return false, fmt.Errorf("DisrputedPods map too big - too many evictions not confirmed by PDB controller")
|
||||
return false, errors.NewForbidden(policy.Resource("poddisruptionbudget"), pdb.Name, fmt.Errorf("DisrputedPods map too big - too many evictions not confirmed by PDB controller"))
|
||||
}
|
||||
if pdb.Status.PodDisruptionsAllowed == 0 {
|
||||
return false, nil
|
||||
|
|
@ -144,18 +174,18 @@ func (r *EvictionREST) checkAndDecrement(namespace string, podName string, pdb p
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Returns any PDBs that match the pod.
|
||||
// err is set if there's an error.
|
||||
func (r *EvictionREST) getPodDisruptionBudgets(ctx api.Context, pod *api.Pod) (pdbs []policy.PodDisruptionBudget, err error) {
|
||||
// getPodDisruptionBudgets returns any PDBs that match the pod or err if there's an error.
|
||||
func (r *EvictionREST) getPodDisruptionBudgets(ctx api.Context, pod *api.Pod) ([]policy.PodDisruptionBudget, error) {
|
||||
if len(pod.Labels) == 0 {
|
||||
return
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
pdbList, err := r.podDisruptionBudgetClient.PodDisruptionBudgets(pod.Namespace).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pdbs []policy.PodDisruptionBudget
|
||||
for _, pdb := range pdbList.Items {
|
||||
if pdb.Namespace != pod.Namespace {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ func (r *LogREST) ProducesMIMETypes(verb string) []string {
|
|||
}
|
||||
}
|
||||
|
||||
// LogREST implements StorageMetadata, return string as the generating object
|
||||
func (r *LogREST) ProducesObject(verb string) interface{} {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Get retrieves a runtime.Object that will stream the contents of the pod log
|
||||
func (r *LogREST) Get(ctx api.Context, name string, opts runtime.Object) (runtime.Object, error) {
|
||||
logOpts, ok := opts.(*api.PodLogOptions)
|
||||
|
|
|
|||
2
vendor/k8s.io/kubernetes/pkg/registry/core/service/ipallocator/controller/repair.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/registry/core/service/ipallocator/controller/repair.go
generated
vendored
|
|
@ -105,7 +105,7 @@ func (c *Repair) runOnce() error {
|
|||
// the service collection. The caching layer keeps per-collection RVs,
|
||||
// and this is proper, since in theory the collections could be hosted
|
||||
// in separate etcd (or even non-etcd) instances.
|
||||
list, err := c.registry.ListServices(ctx, nil)
|
||||
list, err := c.registry.ListServices(ctx, &api.ListOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to refresh the service IP block: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ func (c *Repair) runOnce() error {
|
|||
// the service collection. The caching layer keeps per-collection RVs,
|
||||
// and this is proper, since in theory the collections could be hosted
|
||||
// in separate etcd (or even non-etcd) instances.
|
||||
list, err := c.registry.ListServices(ctx, nil)
|
||||
list, err := c.registry.ListServices(ctx, &api.ListOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to refresh the port block: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,7 +202,8 @@ func (e *Store) List(ctx api.Context, options *api.ListOptions) (runtime.Object,
|
|||
// ListPredicate returns a list of all the items matching m.
|
||||
func (e *Store) ListPredicate(ctx api.Context, p storage.SelectionPredicate, options *api.ListOptions) (runtime.Object, error) {
|
||||
if options == nil {
|
||||
options = &api.ListOptions{ResourceVersion: "0"}
|
||||
// By default we should serve the request from etcd.
|
||||
options = &api.ListOptions{ResourceVersion: ""}
|
||||
}
|
||||
list := e.NewListFunc()
|
||||
if name, ok := p.MatchesSingle(); ok {
|
||||
|
|
|
|||
|
|
@ -531,17 +531,23 @@ func (c *Cacher) dispatchEvents() {
|
|||
func (c *Cacher) dispatchEvent(event *watchCacheEvent) {
|
||||
triggerValues, supported := c.triggerValues(event)
|
||||
|
||||
// TODO: For now we assume we have a given <timeout> budget for dispatching
|
||||
// a single event. We should consider changing to the approach with:
|
||||
// - budget has upper bound at <max_timeout>
|
||||
// - we add <portion> to current timeout every second
|
||||
timeout := time.Duration(250) * time.Millisecond
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
// Iterate over "allWatchers" no matter what the trigger function is.
|
||||
for _, watcher := range c.watchers.allWatchers {
|
||||
watcher.add(event)
|
||||
watcher.add(event, &timeout)
|
||||
}
|
||||
if supported {
|
||||
// Iterate over watchers interested in the given values of the trigger.
|
||||
for _, triggerValue := range triggerValues {
|
||||
for _, watcher := range c.watchers.valueWatchers[triggerValue] {
|
||||
watcher.add(event)
|
||||
watcher.add(event, &timeout)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -554,7 +560,7 @@ func (c *Cacher) dispatchEvent(event *watchCacheEvent) {
|
|||
// Iterate over watchers interested in exact values for all values.
|
||||
for _, watchers := range c.watchers.valueWatchers {
|
||||
for _, watcher := range watchers {
|
||||
watcher.add(event)
|
||||
watcher.add(event, &timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -728,7 +734,7 @@ func (c *cacheWatcher) stop() {
|
|||
|
||||
var timerPool sync.Pool
|
||||
|
||||
func (c *cacheWatcher) add(event *watchCacheEvent) {
|
||||
func (c *cacheWatcher) add(event *watchCacheEvent, timeout *time.Duration) {
|
||||
// Try to send the event immediately, without blocking.
|
||||
select {
|
||||
case c.input <- *event:
|
||||
|
|
@ -736,20 +742,16 @@ func (c *cacheWatcher) add(event *watchCacheEvent) {
|
|||
default:
|
||||
}
|
||||
|
||||
// OK, block sending, but only for up to 5 seconds.
|
||||
// OK, block sending, but only for up to <timeout>.
|
||||
// cacheWatcher.add is called very often, so arrange
|
||||
// to reuse timers instead of constantly allocating.
|
||||
trace := util.NewTrace(
|
||||
fmt.Sprintf("cacheWatcher %v: waiting for add (initial result size %v)",
|
||||
reflect.TypeOf(event.Object).String(), len(c.result)))
|
||||
defer trace.LogIfLong(50 * time.Millisecond)
|
||||
startTime := time.Now()
|
||||
|
||||
const timeout = 5 * time.Second
|
||||
t, ok := timerPool.Get().(*time.Timer)
|
||||
if ok {
|
||||
t.Reset(timeout)
|
||||
t.Reset(*timeout)
|
||||
} else {
|
||||
t = time.NewTimer(timeout)
|
||||
t = time.NewTimer(*timeout)
|
||||
}
|
||||
defer timerPool.Put(t)
|
||||
|
||||
|
|
@ -768,6 +770,10 @@ func (c *cacheWatcher) add(event *watchCacheEvent) {
|
|||
c.forget(false)
|
||||
c.stop()
|
||||
}
|
||||
|
||||
if *timeout = *timeout - time.Since(startTime); *timeout < 0 {
|
||||
*timeout = 0
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: sendWatchCacheEvent is assumed to not modify <event> !!!
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ go_library(
|
|||
deps = [
|
||||
"//pkg/util/clock:go_default_library",
|
||||
"//pkg/util/integer:go_default_library",
|
||||
"//pkg/util/ratelimit:go_default_library",
|
||||
"//vendor:github.com/juju/ratelimit",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ package flowcontrol
|
|||
import (
|
||||
"sync"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/ratelimit"
|
||||
"github.com/juju/ratelimit"
|
||||
)
|
||||
|
||||
type RateLimiter interface {
|
||||
|
|
|
|||
|
|
@ -374,6 +374,12 @@ func (runner *runner) checkRule(table Table, chain Chain, args ...string) (bool,
|
|||
}
|
||||
}
|
||||
|
||||
var hexnumRE = regexp.MustCompile("0x0+([0-9])")
|
||||
|
||||
func trimhex(s string) string {
|
||||
return hexnumRE.ReplaceAllString(s, "0x$1")
|
||||
}
|
||||
|
||||
// Executes the rule check without using the "-C" flag, instead parsing iptables-save.
|
||||
// Present for compatibility with <1.4.11 versions of iptables. This is full
|
||||
// of hack and half-measures. We should nix this ASAP.
|
||||
|
|
@ -392,6 +398,7 @@ func (runner *runner) checkRuleWithoutCheck(table Table, chain Chain, args ...st
|
|||
var argsCopy []string
|
||||
for i := range args {
|
||||
tmpField := strings.Trim(args[i], "\"")
|
||||
tmpField = trimhex(tmpField)
|
||||
argsCopy = append(argsCopy, strings.Fields(tmpField)...)
|
||||
}
|
||||
argset := sets.NewString(argsCopy...)
|
||||
|
|
@ -409,6 +416,7 @@ func (runner *runner) checkRuleWithoutCheck(table Table, chain Chain, args ...st
|
|||
// Just remove all quotes.
|
||||
for i := range fields {
|
||||
fields[i] = strings.Trim(fields[i], "\"")
|
||||
fields[i] = trimhex(fields[i])
|
||||
}
|
||||
|
||||
// TODO: This misses reorderings e.g. "-x foo ! -y bar" will match "! -x foo -y bar"
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import (
|
|||
const (
|
||||
// Default mount command if mounter path is not specified
|
||||
defaultMountCommand = "mount"
|
||||
MountsInGlobalPDPath = "mounts"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
|
|
@ -189,9 +190,15 @@ func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (str
|
|||
glog.V(4).Infof("Directory %s is not mounted", mountPath)
|
||||
return "", fmt.Errorf("directory %s is not mounted", mountPath)
|
||||
}
|
||||
basemountPath := path.Join(pluginDir, MountsInGlobalPDPath)
|
||||
for _, ref := range refs {
|
||||
if strings.HasPrefix(ref, pluginDir) {
|
||||
return path.Base(ref), nil
|
||||
if strings.HasPrefix(ref, basemountPath) {
|
||||
volumeID, err := filepath.Rel(basemountPath, ref)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
|
||||
return "", err
|
||||
}
|
||||
return volumeID, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,9 +74,9 @@ func (mounter *Mounter) Mount(source string, target string, fstype string, optio
|
|||
}
|
||||
return doMount(mounterPath, source, target, fstype, bindRemountOpts)
|
||||
}
|
||||
// These filesystem types are expected to be supported by the mount utility on the host across all Linux distros.
|
||||
var defaultMounterFsTypes = sets.NewString("tmpfs", "ext4", "ext3", "ext2")
|
||||
if !defaultMounterFsTypes.Has(fstype) {
|
||||
// The list of filesystems that require containerized mounter on GCI image cluster
|
||||
fsTypesNeedMounter := sets.NewString("nfs", "glusterfs")
|
||||
if fsTypesNeedMounter.Has(fstype) {
|
||||
mounterPath = mounter.mounterPath
|
||||
}
|
||||
return doMount(mounterPath, source, target, fstype, options)
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyz0123456789")
|
||||
var numLetters = len(letters)
|
||||
var rng = struct {
|
||||
sync.Mutex
|
||||
rand *rand.Rand
|
||||
|
|
@ -72,12 +70,16 @@ func Perm(n int) []int {
|
|||
return rng.rand.Perm(n)
|
||||
}
|
||||
|
||||
// String generates a random alphanumeric string n characters long. This will
|
||||
// panic if n is less than zero.
|
||||
// We omit vowels from the set of available characters to reduce the chances
|
||||
// of "bad words" being formed.
|
||||
var alphanums = []rune("bcdfghjklmnpqrstvwxz0123456789")
|
||||
|
||||
// String generates a random alphanumeric string, without vowels, which is n
|
||||
// characters long. This will panic if n is less than zero.
|
||||
func String(length int) string {
|
||||
b := make([]rune, length)
|
||||
for i := range b {
|
||||
b[i] = letters[Intn(numLetters)]
|
||||
b[i] = alphanums[Intn(len(alphanums))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_binary",
|
||||
"go_library",
|
||||
"go_test",
|
||||
"cgo_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["bucket.go"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["bucket_test.go"],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [],
|
||||
)
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 ratelimit
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Bucket models a token bucket
|
||||
type Bucket struct {
|
||||
unitsPerNano float64
|
||||
nanosPerUnit float64
|
||||
capacity int64
|
||||
|
||||
mutex sync.Mutex
|
||||
available int64
|
||||
lastRefill int64
|
||||
// fractionalAvailable "buffers" any amounts that flowed into the bucket smaller than one unit
|
||||
// This lets us retain precision even with pathological refill rates like (1E9 + 1) per second
|
||||
fractionalAvailable float64
|
||||
}
|
||||
|
||||
// NewBucketWithRate creates a new token bucket, with maximum capacity = initial capacity, and a refill rate of qps
|
||||
// We use floats for refill calculations, which introduces the possibility of truncation and rounding errors.
|
||||
// For "sensible" qps values though, is is acceptable: jbeda did some tests here https://play.golang.org/p/LSKUOGz2LG
|
||||
func NewBucketWithRate(qps float64, capacity int64) *Bucket {
|
||||
unitsPerNano := qps / 1E9
|
||||
nanosPerUnit := 1E9 / qps
|
||||
b := &Bucket{
|
||||
unitsPerNano: unitsPerNano,
|
||||
nanosPerUnit: nanosPerUnit,
|
||||
capacity: capacity,
|
||||
available: capacity,
|
||||
lastRefill: time.Now().UnixNano(),
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Take takes n units from the bucket, reducing the available quantity even below zero,
|
||||
// but then returns the amount of time we should wait
|
||||
func (b *Bucket) Take(n int64) time.Duration {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
var d time.Duration
|
||||
if b.available >= n {
|
||||
// Fast path when bucket has sufficient availability before refilling
|
||||
} else {
|
||||
b.refill()
|
||||
|
||||
if b.available < n {
|
||||
deficit := n - b.available
|
||||
d = time.Duration(int64(float64(deficit) * b.nanosPerUnit))
|
||||
}
|
||||
}
|
||||
|
||||
b.available -= n
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// TakeAvailable immediately takes whatever quantity is available, up to max
|
||||
func (b *Bucket) TakeAvailable(max int64) int64 {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
var took int64
|
||||
if b.available >= max {
|
||||
// Fast path when bucket has sufficient availability before refilling
|
||||
took = max
|
||||
} else {
|
||||
b.refill()
|
||||
|
||||
took = b.available
|
||||
|
||||
if took < 0 {
|
||||
took = 0
|
||||
} else if took > max {
|
||||
took = max
|
||||
}
|
||||
}
|
||||
|
||||
if took > 0 {
|
||||
b.available -= took
|
||||
}
|
||||
|
||||
return took
|
||||
}
|
||||
|
||||
// Wait combines a call to Take with a sleep call
|
||||
func (b *Bucket) Wait(n int64) {
|
||||
d := b.Take(n)
|
||||
if d != 0 {
|
||||
time.Sleep(d)
|
||||
}
|
||||
}
|
||||
|
||||
// Capacity returns the maximum capacity of the bucket
|
||||
func (b *Bucket) Capacity() int64 {
|
||||
return b.capacity
|
||||
}
|
||||
|
||||
// Available returns the quantity available in the bucket (which may be negative), but does not take it.
|
||||
// This function is for diagnostic / informational purposes only - the returned capacity may immediately
|
||||
// be inaccurate if another thread is operating on the bucket concurrently.
|
||||
func (b *Bucket) Available() int64 {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
b.refill()
|
||||
|
||||
return b.available
|
||||
}
|
||||
|
||||
// refill replenishes the bucket based on elapsed time; mutex must be held
|
||||
func (b *Bucket) refill() {
|
||||
// Note that we really want a monotonic clock here, but go says no:
|
||||
// https://github.com/golang/go/issues/12914
|
||||
now := time.Now().UnixNano()
|
||||
|
||||
b.refillAtTimestamp(now)
|
||||
}
|
||||
|
||||
// refillAtTimestamp is the logic of the refill function, for testing
|
||||
func (b *Bucket) refillAtTimestamp(now int64) {
|
||||
nanosSinceLastRefill := now - b.lastRefill
|
||||
if nanosSinceLastRefill <= 0 {
|
||||
// we really want monotonic
|
||||
return
|
||||
}
|
||||
|
||||
// Compute units that have flowed into bucket
|
||||
refillFloat := (float64(nanosSinceLastRefill) * b.unitsPerNano) + b.fractionalAvailable
|
||||
if refillFloat > float64(b.capacity) {
|
||||
// float64 > MaxInt64 can be converted to negative int64; side step this
|
||||
b.available = b.capacity
|
||||
|
||||
// Don't worry about the fractional units with huge refill rates
|
||||
} else {
|
||||
whole, fraction := math.Modf(refillFloat)
|
||||
refill := int64(whole)
|
||||
b.fractionalAvailable = fraction
|
||||
if refill != 0 {
|
||||
// Refill with overflow
|
||||
b.available += refill
|
||||
if b.available >= b.capacity {
|
||||
b.available = b.capacity
|
||||
b.fractionalAvailable = 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
b.lastRefill = now
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ go_library(
|
|||
srcs = ["patch.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/client/typed/discovery:go_default_library",
|
||||
"//pkg/util/json:go_default_library",
|
||||
"//third_party/forked/golang/json:go_default_library",
|
||||
"//vendor:github.com/davecgh/go-spew/spew",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
"reflect"
|
||||
"sort"
|
||||
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/util/json"
|
||||
forkedjson "k8s.io/kubernetes/third_party/forked/golang/json"
|
||||
|
||||
|
|
@ -39,20 +38,11 @@ import (
|
|||
// Some of the content of this package was borrowed with minor adaptations from
|
||||
// evanphx/json-patch and openshift/origin.
|
||||
|
||||
type StrategicMergePatchVersion string
|
||||
|
||||
const (
|
||||
directiveMarker = "$patch"
|
||||
deleteDirective = "delete"
|
||||
replaceDirective = "replace"
|
||||
mergeDirective = "merge"
|
||||
mergePrimitivesListDirective = "mergeprimitiveslist"
|
||||
|
||||
// different versions of StrategicMergePatch
|
||||
SMPatchVersion_1_0 StrategicMergePatchVersion = "v1.0.0"
|
||||
SMPatchVersion_1_5 StrategicMergePatchVersion = "v1.5.0"
|
||||
Unknown StrategicMergePatchVersion = "Unknown"
|
||||
SMPatchVersionLatest = SMPatchVersion_1_5
|
||||
)
|
||||
|
||||
// IsPreconditionFailed returns true if the provided error indicates
|
||||
|
|
@ -97,7 +87,6 @@ func IsConflict(err error) bool {
|
|||
|
||||
var errBadJSONDoc = fmt.Errorf("Invalid JSON document")
|
||||
var errNoListOfLists = fmt.Errorf("Lists of lists are not supported")
|
||||
var errNoElementsInSlice = fmt.Errorf("no elements in any of the given slices")
|
||||
|
||||
// The following code is adapted from github.com/openshift/origin/pkg/util/jsonmerge.
|
||||
// Instead of defining a Delta that holds an original, a patch and a set of preconditions,
|
||||
|
|
@ -144,15 +133,15 @@ func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
|
|||
}
|
||||
|
||||
// Deprecated: Use the synonym CreateTwoWayMergePatch, instead.
|
||||
func CreateStrategicMergePatch(original, modified []byte, dataStruct interface{}, smPatchVersion StrategicMergePatchVersion) ([]byte, error) {
|
||||
return CreateTwoWayMergePatch(original, modified, dataStruct, smPatchVersion)
|
||||
func CreateStrategicMergePatch(original, modified []byte, dataStruct interface{}) ([]byte, error) {
|
||||
return CreateTwoWayMergePatch(original, modified, dataStruct)
|
||||
}
|
||||
|
||||
// CreateTwoWayMergePatch creates a patch that can be passed to StrategicMergePatch from an original
|
||||
// document and a modified document, which are passed to the method as json encoded content. It will
|
||||
// return a patch that yields the modified document when applied to the original document, or an error
|
||||
// if either of the two documents is invalid.
|
||||
func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, smPatchVersion StrategicMergePatchVersion, fns ...PreconditionFunc) ([]byte, error) {
|
||||
func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, fns ...PreconditionFunc) ([]byte, error) {
|
||||
originalMap := map[string]interface{}{}
|
||||
if len(original) > 0 {
|
||||
if err := json.Unmarshal(original, &originalMap); err != nil {
|
||||
|
|
@ -172,7 +161,7 @@ func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, s
|
|||
return nil, err
|
||||
}
|
||||
|
||||
patchMap, err := diffMaps(originalMap, modifiedMap, t, false, false, smPatchVersion)
|
||||
patchMap, err := diffMaps(originalMap, modifiedMap, t, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -188,7 +177,7 @@ func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, s
|
|||
}
|
||||
|
||||
// Returns a (recursive) strategic merge patch that yields modified when applied to original.
|
||||
func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreChangesAndAdditions, ignoreDeletions bool, smPatchVersion StrategicMergePatchVersion) (map[string]interface{}, error) {
|
||||
func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreChangesAndAdditions, ignoreDeletions bool) (map[string]interface{}, error) {
|
||||
patch := map[string]interface{}{}
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
|
|
@ -241,7 +230,7 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreC
|
|||
return nil, err
|
||||
}
|
||||
|
||||
patchValue, err := diffMaps(originalValueTyped, modifiedValueTyped, fieldType, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion)
|
||||
patchValue, err := diffMaps(originalValueTyped, modifiedValueTyped, fieldType, ignoreChangesAndAdditions, ignoreDeletions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -259,25 +248,13 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreC
|
|||
}
|
||||
|
||||
if fieldPatchStrategy == mergeDirective {
|
||||
patchValue, err := diffLists(originalValueTyped, modifiedValueTyped, fieldType.Elem(), fieldPatchMergeKey, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion)
|
||||
patchValue, err := diffLists(originalValueTyped, modifiedValueTyped, fieldType.Elem(), fieldPatchMergeKey, ignoreChangesAndAdditions, ignoreDeletions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if patchValue == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch typedPatchValue := patchValue.(type) {
|
||||
case []interface{}:
|
||||
if len(typedPatchValue) > 0 {
|
||||
patch[key] = typedPatchValue
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if len(typedPatchValue) > 0 {
|
||||
patch[key] = typedPatchValue
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid type of patch: %v", reflect.TypeOf(patchValue))
|
||||
if len(patchValue) > 0 {
|
||||
patch[key] = patchValue
|
||||
}
|
||||
|
||||
continue
|
||||
|
|
@ -307,7 +284,7 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreC
|
|||
|
||||
// Returns a (recursive) strategic merge patch that yields modified when applied to original,
|
||||
// for a pair of lists with merge semantics.
|
||||
func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string, ignoreChangesAndAdditions, ignoreDeletions bool, smPatchVersion StrategicMergePatchVersion) (interface{}, error) {
|
||||
func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string, ignoreChangesAndAdditions, ignoreDeletions bool) ([]interface{}, error) {
|
||||
if len(original) == 0 {
|
||||
if len(modified) == 0 || ignoreChangesAndAdditions {
|
||||
return nil, nil
|
||||
|
|
@ -321,14 +298,12 @@ func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var patch interface{}
|
||||
var patch []interface{}
|
||||
|
||||
if elementType.Kind() == reflect.Map {
|
||||
patch, err = diffListsOfMaps(original, modified, t, mergeKey, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion)
|
||||
} else if elementType.Kind() == reflect.Slice {
|
||||
err = errNoListOfLists
|
||||
} else {
|
||||
patch, err = diffListsOfScalars(original, modified, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion)
|
||||
patch, err = diffListsOfMaps(original, modified, t, mergeKey, ignoreChangesAndAdditions, ignoreDeletions)
|
||||
} else if !ignoreChangesAndAdditions {
|
||||
patch, err = diffListsOfScalars(original, modified)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -340,23 +315,8 @@ func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string
|
|||
|
||||
// Returns a (recursive) strategic merge patch that yields modified when applied to original,
|
||||
// for a pair of lists of scalars with merge semantics.
|
||||
func diffListsOfScalars(original, modified []interface{}, ignoreChangesAndAdditions, ignoreDeletions bool, smPatchVersion StrategicMergePatchVersion) (interface{}, error) {
|
||||
originalScalars := uniqifyAndSortScalars(original)
|
||||
modifiedScalars := uniqifyAndSortScalars(modified)
|
||||
|
||||
switch smPatchVersion {
|
||||
case SMPatchVersion_1_5:
|
||||
return diffListsOfScalarsIntoMap(originalScalars, modifiedScalars, ignoreChangesAndAdditions, ignoreDeletions)
|
||||
case SMPatchVersion_1_0:
|
||||
return diffListsOfScalarsIntoSlice(originalScalars, modifiedScalars, ignoreChangesAndAdditions, ignoreDeletions)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown StrategicMergePatchVersion: %v", smPatchVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func diffListsOfScalarsIntoSlice(originalScalars, modifiedScalars []interface{}, ignoreChangesAndAdditions, ignoreDeletions bool) ([]interface{}, error) {
|
||||
originalIndex, modifiedIndex := 0, 0
|
||||
if len(modifiedScalars) == 0 {
|
||||
func diffListsOfScalars(original, modified []interface{}) ([]interface{}, error) {
|
||||
if len(modified) == 0 {
|
||||
// There is no need to check the length of original because there is no way to create
|
||||
// a patch that deletes a scalar from a list of scalars with merge semantics.
|
||||
return nil, nil
|
||||
|
|
@ -364,14 +324,18 @@ func diffListsOfScalarsIntoSlice(originalScalars, modifiedScalars []interface{},
|
|||
|
||||
patch := []interface{}{}
|
||||
|
||||
originalScalars := uniqifyAndSortScalars(original)
|
||||
modifiedScalars := uniqifyAndSortScalars(modified)
|
||||
originalIndex, modifiedIndex := 0, 0
|
||||
|
||||
loopB:
|
||||
for ; modifiedIndex < len(modifiedScalars); modifiedIndex++ {
|
||||
for ; originalIndex < len(originalScalars); originalIndex++ {
|
||||
originalString := fmt.Sprintf("%v", originalScalars[originalIndex])
|
||||
modifiedString := fmt.Sprintf("%v", modifiedScalars[modifiedIndex])
|
||||
originalString := fmt.Sprintf("%v", original[originalIndex])
|
||||
modifiedString := fmt.Sprintf("%v", modified[modifiedIndex])
|
||||
if originalString >= modifiedString {
|
||||
if originalString != modifiedString {
|
||||
patch = append(patch, modifiedScalars[modifiedIndex])
|
||||
patch = append(patch, modified[modifiedIndex])
|
||||
}
|
||||
|
||||
continue loopB
|
||||
|
|
@ -385,57 +349,7 @@ loopB:
|
|||
|
||||
// Add any remaining items found only in modified
|
||||
for ; modifiedIndex < len(modifiedScalars); modifiedIndex++ {
|
||||
patch = append(patch, modifiedScalars[modifiedIndex])
|
||||
}
|
||||
|
||||
return patch, nil
|
||||
}
|
||||
|
||||
func diffListsOfScalarsIntoMap(originalScalars, modifiedScalars []interface{}, ignoreChangesAndAdditions, ignoreDeletions bool) (map[string]interface{}, error) {
|
||||
originalIndex, modifiedIndex := 0, 0
|
||||
patch := map[string]interface{}{}
|
||||
patch[directiveMarker] = mergePrimitivesListDirective
|
||||
|
||||
for originalIndex < len(originalScalars) && modifiedIndex < len(modifiedScalars) {
|
||||
originalString := fmt.Sprintf("%v", originalScalars[originalIndex])
|
||||
modifiedString := fmt.Sprintf("%v", modifiedScalars[modifiedIndex])
|
||||
|
||||
// objects are identical
|
||||
if originalString == modifiedString {
|
||||
originalIndex++
|
||||
modifiedIndex++
|
||||
continue
|
||||
}
|
||||
|
||||
if originalString > modifiedString {
|
||||
if !ignoreChangesAndAdditions {
|
||||
modifiedValue := modifiedScalars[modifiedIndex]
|
||||
patch[modifiedString] = modifiedValue
|
||||
}
|
||||
modifiedIndex++
|
||||
} else {
|
||||
if !ignoreDeletions {
|
||||
patch[originalString] = nil
|
||||
}
|
||||
originalIndex++
|
||||
}
|
||||
}
|
||||
|
||||
// Delete any remaining items found only in original
|
||||
if !ignoreDeletions {
|
||||
for ; originalIndex < len(originalScalars); originalIndex++ {
|
||||
originalString := fmt.Sprintf("%v", originalScalars[originalIndex])
|
||||
patch[originalString] = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Add any remaining items found only in modified
|
||||
if !ignoreChangesAndAdditions {
|
||||
for ; modifiedIndex < len(modifiedScalars); modifiedIndex++ {
|
||||
modifiedString := fmt.Sprintf("%v", modifiedScalars[modifiedIndex])
|
||||
modifiedValue := modifiedScalars[modifiedIndex]
|
||||
patch[modifiedString] = modifiedValue
|
||||
}
|
||||
patch = append(patch, modified[modifiedIndex])
|
||||
}
|
||||
|
||||
return patch, nil
|
||||
|
|
@ -446,7 +360,7 @@ var errBadArgTypeFmt = "expected a %s, but received a %s"
|
|||
|
||||
// Returns a (recursive) strategic merge patch that yields modified when applied to original,
|
||||
// for a pair of lists of maps with merge semantics.
|
||||
func diffListsOfMaps(original, modified []interface{}, t reflect.Type, mergeKey string, ignoreChangesAndAdditions, ignoreDeletions bool, smPatchVersion StrategicMergePatchVersion) ([]interface{}, error) {
|
||||
func diffListsOfMaps(original, modified []interface{}, t reflect.Type, mergeKey string, ignoreChangesAndAdditions, ignoreDeletions bool) ([]interface{}, error) {
|
||||
patch := make([]interface{}, 0)
|
||||
|
||||
originalSorted, err := sortMergeListsByNameArray(original, t, mergeKey, false)
|
||||
|
|
@ -492,7 +406,7 @@ loopB:
|
|||
if originalString >= modifiedString {
|
||||
if originalString == modifiedString {
|
||||
// Merge key values are equal, so recurse
|
||||
patchValue, err := diffMaps(originalMap, modifiedMap, t, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion)
|
||||
patchValue, err := diffMaps(originalMap, modifiedMap, t, ignoreChangesAndAdditions, ignoreDeletions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -628,16 +542,8 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin
|
|||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
if v == mergePrimitivesListDirective {
|
||||
// delete the directiveMarker's key-value pair to avoid delta map and delete map
|
||||
// overlaping with each other when calculating a ThreeWayDiff for list of Primitives.
|
||||
// Otherwise, the overlaping will cause it calling LookupPatchMetadata() which will
|
||||
// return an error since the metadata shows it's a slice but it is actually a map.
|
||||
delete(original, directiveMarker)
|
||||
} else {
|
||||
return nil, fmt.Errorf(errBadPatchTypeFmt, v, patch)
|
||||
}
|
||||
}
|
||||
|
||||
// nil is an accepted value for original to simplify logic in other places.
|
||||
// If original is nil, replace it with an empty map and then apply the patch.
|
||||
|
|
@ -672,9 +578,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin
|
|||
// If they're both maps or lists, recurse into the value.
|
||||
originalType := reflect.TypeOf(original[k])
|
||||
patchType := reflect.TypeOf(patchV)
|
||||
// check if we are trying to merge a slice with a map for list of primitives
|
||||
isMergeSliceOfPrimitivesWithAPatchMap := originalType != nil && patchType != nil && originalType.Kind() == reflect.Slice && patchType.Kind() == reflect.Map
|
||||
if originalType == patchType || isMergeSliceOfPrimitivesWithAPatchMap {
|
||||
if originalType == patchType {
|
||||
// First find the fieldPatchStrategy and fieldPatchMergeKey.
|
||||
fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k)
|
||||
if err != nil {
|
||||
|
|
@ -696,8 +600,9 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin
|
|||
if originalType.Kind() == reflect.Slice && fieldPatchStrategy == mergeDirective {
|
||||
elemType := fieldType.Elem()
|
||||
typedOriginal := original[k].([]interface{})
|
||||
typedPatch := patchV.([]interface{})
|
||||
var err error
|
||||
original[k], err = mergeSlice(typedOriginal, patchV, elemType, fieldPatchMergeKey)
|
||||
original[k], err = mergeSlice(typedOriginal, typedPatch, elemType, fieldPatchMergeKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -718,34 +623,13 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[strin
|
|||
// Merge two slices together. Note: This may modify both the original slice and
|
||||
// the patch because getting a deep copy of a slice in golang is highly
|
||||
// non-trivial.
|
||||
// The patch could be a map[string]interface{} representing a slice of primitives.
|
||||
// If the patch map doesn't has the specific directiveMarker (mergePrimitivesListDirective),
|
||||
// it returns an error. Please check patch_test.go and find the test case named
|
||||
// "merge lists of scalars for list of primitives" to see what the patch looks like.
|
||||
// Patch is still []interface{} for all the other types.
|
||||
func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type, mergeKey string) ([]interface{}, error) {
|
||||
t, err := sliceElementType(original)
|
||||
if err != nil && err != errNoElementsInSlice {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if patchMap, ok := patch.(map[string]interface{}); ok {
|
||||
// We try to merge the original slice with a patch map only when the map has
|
||||
// a specific directiveMarker. Otherwise, this patch will be treated as invalid.
|
||||
if directiveValue, ok := patchMap[directiveMarker]; ok && directiveValue == mergePrimitivesListDirective {
|
||||
return mergeSliceOfScalarsWithPatchMap(original, patchMap)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Unable to merge a slice with an invalid map")
|
||||
}
|
||||
}
|
||||
|
||||
typedPatch := patch.([]interface{})
|
||||
if len(original) == 0 && len(typedPatch) == 0 {
|
||||
func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey string) ([]interface{}, error) {
|
||||
if len(original) == 0 && len(patch) == 0 {
|
||||
return original, nil
|
||||
}
|
||||
|
||||
// All the values must be of the same type, but not a list.
|
||||
t, err = sliceElementType(original, typedPatch)
|
||||
t, err := sliceElementType(original, patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -754,7 +638,7 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type
|
|||
if t.Kind() != reflect.Map {
|
||||
// Maybe in the future add a "concat" mode that doesn't
|
||||
// uniqify.
|
||||
both := append(original, typedPatch...)
|
||||
both := append(original, patch...)
|
||||
return uniqifyScalars(both), nil
|
||||
}
|
||||
|
||||
|
|
@ -765,7 +649,7 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type
|
|||
// First look for any special $patch elements.
|
||||
patchWithoutSpecialElements := []interface{}{}
|
||||
replace := false
|
||||
for _, v := range typedPatch {
|
||||
for _, v := range patch {
|
||||
typedV := v.(map[string]interface{})
|
||||
patchType, ok := typedV[directiveMarker]
|
||||
if ok {
|
||||
|
|
@ -801,10 +685,10 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type
|
|||
return patchWithoutSpecialElements, nil
|
||||
}
|
||||
|
||||
typedPatch = patchWithoutSpecialElements
|
||||
patch = patchWithoutSpecialElements
|
||||
|
||||
// Merge patch into original.
|
||||
for _, v := range typedPatch {
|
||||
for _, v := range patch {
|
||||
// Because earlier we confirmed that all the elements are maps.
|
||||
typedV := v.(map[string]interface{})
|
||||
mergeValue, ok := typedV[mergeKey]
|
||||
|
|
@ -837,36 +721,6 @@ func mergeSlice(original []interface{}, patch interface{}, elemType reflect.Type
|
|||
return original, nil
|
||||
}
|
||||
|
||||
// mergeSliceOfScalarsWithPatchMap merges the original slice with a patch map and
|
||||
// returns an uniqified and sorted slice of primitives.
|
||||
// The patch map must have the specific directiveMarker (mergePrimitivesListDirective).
|
||||
func mergeSliceOfScalarsWithPatchMap(original []interface{}, patch map[string]interface{}) ([]interface{}, error) {
|
||||
// make sure the patch has the specific directiveMarker ()
|
||||
if directiveValue, ok := patch[directiveMarker]; ok && directiveValue != mergePrimitivesListDirective {
|
||||
return nil, fmt.Errorf("Unable to merge a slice with an invalid map")
|
||||
}
|
||||
delete(patch, directiveMarker)
|
||||
output := make([]interface{}, 0, len(original)+len(patch))
|
||||
for _, value := range original {
|
||||
valueString := fmt.Sprintf("%v", value)
|
||||
if v, ok := patch[valueString]; ok {
|
||||
if v != nil {
|
||||
output = append(output, v)
|
||||
}
|
||||
delete(patch, valueString)
|
||||
} else {
|
||||
output = append(output, value)
|
||||
}
|
||||
}
|
||||
for _, value := range patch {
|
||||
if value != nil {
|
||||
output = append(output, value)
|
||||
}
|
||||
// No action required to delete items that missing from the original slice.
|
||||
}
|
||||
return uniqifyAndSortScalars(output), nil
|
||||
}
|
||||
|
||||
// This method no longer panics if any element of the slice is not a map.
|
||||
func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{}) (map[string]interface{}, int, bool, error) {
|
||||
for k, v := range m {
|
||||
|
|
@ -1092,7 +946,7 @@ func sliceElementType(slices ...[]interface{}) (reflect.Type, error) {
|
|||
}
|
||||
|
||||
if prevType == nil {
|
||||
return nil, errNoElementsInSlice
|
||||
return nil, fmt.Errorf("no elements in any of the given slices")
|
||||
}
|
||||
|
||||
return prevType, nil
|
||||
|
|
@ -1181,10 +1035,6 @@ func mergingMapFieldsHaveConflicts(
|
|||
if leftMarker != rightMarker {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if leftMarker == mergePrimitivesListDirective && rightMarker == mergePrimitivesListDirective {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Check the individual keys.
|
||||
|
|
@ -1207,30 +1057,13 @@ func mergingMapFieldsHaveConflicts(
|
|||
}
|
||||
|
||||
func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType reflect.Type) (bool, error) {
|
||||
isForListOfPrimitives := false
|
||||
if leftDirective, ok := typedLeft[directiveMarker]; ok {
|
||||
if rightDirective, ok := typedRight[directiveMarker]; ok {
|
||||
if leftDirective == mergePrimitivesListDirective && rightDirective == rightDirective {
|
||||
isForListOfPrimitives = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for key, leftValue := range typedLeft {
|
||||
if key != directiveMarker {
|
||||
if rightValue, ok := typedRight[key]; ok {
|
||||
var fieldType reflect.Type
|
||||
var fieldPatchStrategy, fieldPatchMergeKey string
|
||||
var err error
|
||||
if isForListOfPrimitives {
|
||||
fieldType = reflect.TypeOf(leftValue)
|
||||
fieldPatchStrategy = ""
|
||||
fieldPatchMergeKey = ""
|
||||
} else {
|
||||
fieldType, fieldPatchStrategy, fieldPatchMergeKey, err = forkedjson.LookupPatchMetadata(structType, key)
|
||||
fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(structType, key)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
||||
if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue,
|
||||
fieldType, fieldPatchStrategy, fieldPatchMergeKey); hasConflicts {
|
||||
|
|
@ -1339,7 +1172,7 @@ func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, struc
|
|||
// than from original to current. In other words, a conflict occurs if modified changes any key
|
||||
// in a way that is different from how it is changed in current (e.g., deleting it, changing its
|
||||
// value).
|
||||
func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, smPatchVersion StrategicMergePatchVersion, fns ...PreconditionFunc) ([]byte, error) {
|
||||
func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, fns ...PreconditionFunc) ([]byte, error) {
|
||||
originalMap := map[string]interface{}{}
|
||||
if len(original) > 0 {
|
||||
if err := json.Unmarshal(original, &originalMap); err != nil {
|
||||
|
|
@ -1370,12 +1203,12 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
|
|||
// from original to modified. To find it, we compute deletions, which are the deletions from
|
||||
// original to modified, and delta, which is the difference from current to modified without
|
||||
// deletions, and then apply delta to deletions as a patch, which should be strictly additive.
|
||||
deltaMap, err := diffMaps(currentMap, modifiedMap, t, false, true, smPatchVersion)
|
||||
deltaMap, err := diffMaps(currentMap, modifiedMap, t, false, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deletionsMap, err := diffMaps(originalMap, modifiedMap, t, true, false, smPatchVersion)
|
||||
deletionsMap, err := diffMaps(originalMap, modifiedMap, t, true, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1395,7 +1228,7 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
|
|||
// If overwrite is false, and the patch contains any keys that were changed differently,
|
||||
// then return a conflict error.
|
||||
if !overwrite {
|
||||
changedMap, err := diffMaps(originalMap, currentMap, t, false, false, smPatchVersion)
|
||||
changedMap, err := diffMaps(originalMap, currentMap, t, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1430,20 +1263,3 @@ func toYAML(v interface{}) (string, error) {
|
|||
|
||||
return string(y), nil
|
||||
}
|
||||
|
||||
// GetServerSupportedSMPatchVersion takes a discoveryClient,
|
||||
// returns the max StrategicMergePatch version supported
|
||||
func GetServerSupportedSMPatchVersion(discoveryClient discovery.DiscoveryInterface) (StrategicMergePatchVersion, error) {
|
||||
serverVersion, err := discoveryClient.ServerVersion()
|
||||
if err != nil {
|
||||
return Unknown, err
|
||||
}
|
||||
serverGitVersion := serverVersion.GitVersion
|
||||
if serverGitVersion >= string(SMPatchVersion_1_5) {
|
||||
return SMPatchVersion_1_5, nil
|
||||
}
|
||||
if serverGitVersion >= string(SMPatchVersion_1_0) {
|
||||
return SMPatchVersion_1_0, nil
|
||||
}
|
||||
return Unknown, fmt.Errorf("The version is too old: %v\n", serverVersion)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ go_library(
|
|||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/util/clock:go_default_library",
|
||||
"//pkg/util/ratelimit:go_default_library",
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//vendor:github.com/juju/ratelimit",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/ratelimit"
|
||||
"github.com/juju/ratelimit"
|
||||
)
|
||||
|
||||
type RateLimiter interface {
|
||||
|
|
@ -35,7 +35,7 @@ type RateLimiter interface {
|
|||
}
|
||||
|
||||
// DefaultControllerRateLimiter is a no-arg constructor for a default rate limiter for a workqueue. It has
|
||||
// both overall and per-item rate limiting. The overall is a token bucket and the per-item is exponential
|
||||
// both overall and per-item rate limitting. The overall is a token bucket and the per-item is exponential
|
||||
func DefaultControllerRateLimiter() RateLimiter {
|
||||
return NewMaxOfRateLimiter(
|
||||
NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ var (
|
|||
// scripts consuming the kubectl version output - but most of
|
||||
// these should be looking at gitVersion already anyways.)
|
||||
gitMajor string = "1" // major version, always numeric
|
||||
gitMinor string = "5+" // minor version, numeric possibly followed by "+"
|
||||
gitMinor string = "5" // minor version, numeric possibly followed by "+"
|
||||
|
||||
// semantic version, derived by build scripts (see
|
||||
// https://github.com/kubernetes/kubernetes/blob/master/docs/design/versioning.md
|
||||
|
|
@ -51,7 +51,7 @@ var (
|
|||
// semantic version is a git hash, but the version itself is no
|
||||
// longer the direct output of "git describe", but a slight
|
||||
// translation to be semver compliant.
|
||||
gitVersion string = "v1.5.0-beta.1+$Format:%h$"
|
||||
gitVersion string = "v1.5.0+$Format:%h$"
|
||||
gitCommit string = "$Format:%H$" // sha1 from git, output of $(git rev-parse HEAD)
|
||||
gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ var _ volume.ProvisionableVolumePlugin = &awsElasticBlockStorePlugin{}
|
|||
|
||||
const (
|
||||
awsElasticBlockStorePluginName = "kubernetes.io/aws-ebs"
|
||||
awsURLNamePrefix = "aws://"
|
||||
)
|
||||
|
||||
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
|
||||
|
|
@ -189,10 +190,36 @@ func getVolumeSource(
|
|||
func (plugin *awsElasticBlockStorePlugin) ConstructVolumeSpec(volName, mountPath string) (*volume.Spec, error) {
|
||||
mounter := plugin.host.GetMounter()
|
||||
pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
|
||||
sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
|
||||
volumeID, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// This is a workaround to fix the issue in converting aws volume id from globalPDPath
|
||||
// There are three aws volume id formats and their volumeID from GetDeviceNameFromMount() are:
|
||||
// aws:///vol-1234 (aws/vol-1234)
|
||||
// aws://us-east-1/vol-1234 (aws/us-east-1/vol-1234)
|
||||
// vol-1234 (vol-1234)
|
||||
// This code is for converting volume id to aws style volume id for the first two cases.
|
||||
sourceName := volumeID
|
||||
if strings.HasPrefix(volumeID, "aws/") {
|
||||
names := strings.Split(volumeID, "/")
|
||||
length := len(names)
|
||||
if length < 2 || length > 3 {
|
||||
return nil, fmt.Errorf("Failed to get AWS volume id from mount path %q: invalid volume name format %q", mountPath, volumeID)
|
||||
}
|
||||
volName := names[length-1]
|
||||
if !strings.HasPrefix(volName, "vol-") {
|
||||
return nil, fmt.Errorf("Invalid volume name format for AWS volume (%q) retrieved from mount path %q", volName, mountPath)
|
||||
}
|
||||
if length == 2 {
|
||||
sourceName = awsURLNamePrefix + "" + "/" + volName // empty zone label
|
||||
}
|
||||
if length == 3 {
|
||||
sourceName = awsURLNamePrefix + names[1] + "/" + volName // names[1] is the zone label
|
||||
}
|
||||
glog.V(4).Infof("Convert aws volume name from %q to %q ", volumeID, sourceName)
|
||||
}
|
||||
|
||||
awsVolume := &api.Volume{
|
||||
Name: volName,
|
||||
VolumeSource: api.VolumeSource{
|
||||
|
|
@ -324,12 +351,12 @@ func makeGlobalPDPath(host volume.VolumeHost, volumeID aws.KubernetesVolumeID) s
|
|||
// Clean up the URI to be more fs-friendly
|
||||
name := string(volumeID)
|
||||
name = strings.Replace(name, "://", "/", -1)
|
||||
return path.Join(host.GetPluginDir(awsElasticBlockStorePluginName), "mounts", name)
|
||||
return path.Join(host.GetPluginDir(awsElasticBlockStorePluginName), mount.MountsInGlobalPDPath, name)
|
||||
}
|
||||
|
||||
// Reverses the mapping done in makeGlobalPDPath
|
||||
func getVolumeIDFromGlobalMount(host volume.VolumeHost, globalPath string) (string, error) {
|
||||
basePath := path.Join(host.GetPluginDir(awsElasticBlockStorePluginName), "mounts")
|
||||
basePath := path.Join(host.GetPluginDir(awsElasticBlockStorePluginName), mount.MountsInGlobalPDPath)
|
||||
rel, err := filepath.Rel(basePath, globalPath)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to get volume id from global mount %s - %v", globalPath, err)
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ func (b *azureDiskMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
}
|
||||
|
||||
func makeGlobalPDPath(host volume.VolumeHost, volume string) string {
|
||||
return path.Join(host.GetPluginDir(azureDataDiskPluginName), "mounts", volume)
|
||||
return path.Join(host.GetPluginDir(azureDataDiskPluginName), mount.MountsInGlobalPDPath, volume)
|
||||
}
|
||||
|
||||
func (azure *azureDisk) GetPath() string {
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ func (b *cinderVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
}
|
||||
|
||||
func makeGlobalPDName(host volume.VolumeHost, devName string) string {
|
||||
return path.Join(host.GetPluginDir(cinderVolumePluginName), "mounts", devName)
|
||||
return path.Join(host.GetPluginDir(cinderVolumePluginName), mount.MountsInGlobalPDPath, devName)
|
||||
}
|
||||
|
||||
func (cd *cinderVolume) GetPath() string {
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ func (b *gcePersistentDiskMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
}
|
||||
|
||||
func makeGlobalPDName(host volume.VolumeHost, devName string) string {
|
||||
return path.Join(host.GetPluginDir(gcePersistentDiskPluginName), "mounts", devName)
|
||||
return path.Join(host.GetPluginDir(gcePersistentDiskPluginName), mount.MountsInGlobalPDPath, devName)
|
||||
}
|
||||
|
||||
func (b *gcePersistentDiskMounter) GetPath() string {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ go_library(
|
|||
"//pkg/util/strings:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/util:go_default_library",
|
||||
"//pkg/volume/util/volumehelper:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/heketi/heketi/client/api/go-client",
|
||||
"//vendor:github.com/heketi/heketi/pkg/glusterfs/api",
|
||||
|
|
|
|||
|
|
@ -18,12 +18,9 @@ package glusterfs
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
dstrings "strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
gcli "github.com/heketi/heketi/client/api/go-client"
|
||||
|
|
@ -38,7 +35,6 @@ import (
|
|||
"k8s.io/kubernetes/pkg/util/strings"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volutil "k8s.io/kubernetes/pkg/volume/util"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
|
|
@ -64,8 +60,6 @@ const (
|
|||
volPrefix = "vol_"
|
||||
dynamicEpSvcPrefix = "glusterfs-dynamic-"
|
||||
replicaCount = 3
|
||||
gidMax = 600000
|
||||
gidMin = 2000
|
||||
durabilityType = "replicate"
|
||||
secretKeyName = "key" // key name used in secret
|
||||
annGlusterURL = "glusterfs.kubernetes.io/url"
|
||||
|
|
@ -487,8 +481,6 @@ func (d *glusterfsVolumeDeleter) Delete() error {
|
|||
|
||||
func (r *glusterfsVolumeProvisioner) Provision() (*api.PersistentVolume, error) {
|
||||
var err error
|
||||
var reqGid int64
|
||||
gidRandomizer := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
if r.options.PVC.Spec.Selector != nil {
|
||||
glog.V(4).Infof("glusterfs: not able to parse your claim Selector")
|
||||
return nil, fmt.Errorf("glusterfs: not able to parse your claim Selector")
|
||||
|
|
@ -500,9 +492,9 @@ func (r *glusterfsVolumeProvisioner) Provision() (*api.PersistentVolume, error)
|
|||
return nil, err
|
||||
}
|
||||
r.provisioningConfig = *cfg
|
||||
|
||||
glog.V(4).Infof("glusterfs: creating volume with configuration %+v", r.provisioningConfig)
|
||||
reqGid = gidMin + gidRandomizer.Int63n(gidMax)
|
||||
glusterfs, sizeGB, err := r.CreateVolume(reqGid)
|
||||
glusterfs, sizeGB, err := r.CreateVolume()
|
||||
if err != nil {
|
||||
glog.Errorf("glusterfs: create volume err: %v.", err)
|
||||
return nil, fmt.Errorf("glusterfs: create volume err: %v.", err)
|
||||
|
|
@ -514,15 +506,13 @@ func (r *glusterfsVolumeProvisioner) Provision() (*api.PersistentVolume, error)
|
|||
if len(pv.Spec.AccessModes) == 0 {
|
||||
pv.Spec.AccessModes = r.plugin.GetAccessModes()
|
||||
}
|
||||
sGid := strconv.FormatInt(reqGid, 10)
|
||||
pv.Annotations = map[string]string{volumehelper.VolumeGidAnnotationKey: sGid}
|
||||
pv.Spec.Capacity = api.ResourceList{
|
||||
api.ResourceName(api.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
|
||||
}
|
||||
return pv, nil
|
||||
}
|
||||
|
||||
func (p *glusterfsVolumeProvisioner) CreateVolume(reqGid int64) (r *api.GlusterfsVolumeSource, size int, err error) {
|
||||
func (p *glusterfsVolumeProvisioner) CreateVolume() (r *api.GlusterfsVolumeSource, size int, err error) {
|
||||
capacity := p.options.PVC.Spec.Resources.Requests[api.ResourceName(api.ResourceStorage)]
|
||||
volSizeBytes := capacity.Value()
|
||||
sz := int(volume.RoundUpSize(volSizeBytes, 1024*1024*1024))
|
||||
|
|
@ -536,7 +526,7 @@ func (p *glusterfsVolumeProvisioner) CreateVolume(reqGid int64) (r *api.Glusterf
|
|||
glog.Errorf("glusterfs: failed to create glusterfs rest client")
|
||||
return nil, 0, fmt.Errorf("failed to create glusterfs REST client, REST server authentication failed")
|
||||
}
|
||||
volumeReq := &gapi.VolumeCreateRequest{Size: sz, Gid: reqGid, Durability: gapi.VolumeDurabilityInfo{Type: durabilityType, Replicate: gapi.ReplicaDurability{Replica: replicaCount}}}
|
||||
volumeReq := &gapi.VolumeCreateRequest{Size: sz, Durability: gapi.VolumeDurabilityInfo{Type: durabilityType, Replicate: gapi.ReplicaDurability{Replica: replicaCount}}}
|
||||
volume, err := cli.VolumeCreate(volumeReq)
|
||||
if err != nil {
|
||||
glog.Errorf("glusterfs: error creating volume %v ", err)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ go_library(
|
|||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/conversion:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/uuid:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"regexp"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/uuid"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
|
|
@ -244,7 +245,11 @@ func (r *hostPathRecycler) GetPath() string {
|
|||
// Recycle blocks until the pod has completed or any error occurs.
|
||||
// HostPath recycling only works in single node clusters and is meant for testing purposes only.
|
||||
func (r *hostPathRecycler) Recycle() error {
|
||||
pod := r.config.RecyclerPodTemplate
|
||||
templateClone, err := conversion.NewCloner().DeepCopy(r.config.RecyclerPodTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pod := templateClone.(*api.Pod)
|
||||
// overrides
|
||||
pod.Spec.ActiveDeadlineSeconds = &r.timeout
|
||||
pod.Spec.Volumes[0].VolumeSource = api.VolumeSource{
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ go_library(
|
|||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/conversion:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
|
@ -332,7 +333,11 @@ func (r *nfsRecycler) GetPath() string {
|
|||
// Recycle recycles/scrubs clean an NFS volume.
|
||||
// Recycle blocks until the pod has completed or any error occurs.
|
||||
func (r *nfsRecycler) Recycle() error {
|
||||
pod := r.config.RecyclerPodTemplate
|
||||
templateClone, err := conversion.NewCloner().DeepCopy(r.config.RecyclerPodTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pod := templateClone.(*api.Pod)
|
||||
// overrides
|
||||
pod.Spec.ActiveDeadlineSeconds = &r.timeout
|
||||
pod.GenerateName = "pv-recycler-nfs-"
|
||||
|
|
|
|||
|
|
@ -119,12 +119,19 @@ func (plugin *photonPersistentDiskPlugin) newUnmounterInternal(volName string, p
|
|||
}}, nil
|
||||
}
|
||||
|
||||
func (plugin *photonPersistentDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
|
||||
func (plugin *photonPersistentDiskPlugin) ConstructVolumeSpec(volumeSpecName, mountPath string) (*volume.Spec, error) {
|
||||
mounter := plugin.host.GetMounter()
|
||||
pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
|
||||
pdID, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
photonPersistentDisk := &api.Volume{
|
||||
Name: volumeName,
|
||||
Name: volumeSpecName,
|
||||
VolumeSource: api.VolumeSource{
|
||||
PhotonPersistentDisk: &api.PhotonPersistentDiskVolumeSource{
|
||||
PdID: volumeName,
|
||||
PdID: pdID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -283,7 +290,7 @@ func (c *photonPersistentDiskUnmounter) TearDownAt(dir string) error {
|
|||
}
|
||||
|
||||
func makeGlobalPDPath(host volume.VolumeHost, devName string) string {
|
||||
return path.Join(host.GetPluginDir(photonPersistentDiskPluginName), "mounts", devName)
|
||||
return path.Join(host.GetPluginDir(photonPersistentDiskPluginName), mount.MountsInGlobalPDPath, devName)
|
||||
}
|
||||
|
||||
func (ppd *photonPersistentDisk) GetPath() string {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,12 @@ func internalRecycleVolumeByWatchingPodUntilCompletion(pvName string, pod *api.P
|
|||
return fmt.Errorf("unexpected error creating recycler pod: %+v\n", err)
|
||||
}
|
||||
}
|
||||
defer recyclerClient.DeletePod(pod.Name, pod.Namespace)
|
||||
defer func(pod *api.Pod) {
|
||||
glog.V(2).Infof("deleting recycler pod %s/%s", pod.Namespace, pod.Name)
|
||||
if err := recyclerClient.DeletePod(pod.Name, pod.Namespace); err != nil {
|
||||
glog.Errorf("failed to delete recycler pod %s/%s: %v", pod.Namespace, pod.Name, err)
|
||||
}
|
||||
}(pod)
|
||||
|
||||
// Now only the old pod or the new pod run. Watch it until it finishes
|
||||
// and send all events on the pod to the PV
|
||||
|
|
|
|||
22
vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_executor.go
generated
vendored
22
vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_executor.go
generated
vendored
|
|
@ -22,6 +22,7 @@ package operationexecutor
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
|
@ -1053,7 +1054,8 @@ func (oe *operationExecutor) generateUnmountDeviceFunc(
|
|||
err)
|
||||
}
|
||||
refs, err := attachableVolumePlugin.GetDeviceMountRefs(deviceMountPath)
|
||||
if err != nil || len(refs) > 0 {
|
||||
|
||||
if err != nil || hasMountRefs(deviceMountPath, refs) {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("The device mount path %q is still mounted by other references %v", deviceMountPath, refs)
|
||||
}
|
||||
|
|
@ -1124,6 +1126,24 @@ func (oe *operationExecutor) generateUnmountDeviceFunc(
|
|||
}, nil
|
||||
}
|
||||
|
||||
// TODO: this is a workaround for the unmount device issue caused by gci mounter.
|
||||
// In GCI cluster, if gci mounter is used for mounting, the container started by mounter
|
||||
// script will cause additional mounts created in the container. Since these mounts are
|
||||
// irrelavant to the original mounts, they should be not considered when checking the
|
||||
// mount references. Current solution is to filter out those mount paths that contain
|
||||
// the string of original mount path.
|
||||
// Plan to work on better approach to solve this issue.
|
||||
|
||||
func hasMountRefs(mountPath string, mountRefs []string) bool {
|
||||
count := 0
|
||||
for _, ref := range mountRefs {
|
||||
if !strings.Contains(ref, mountPath) {
|
||||
count = count + 1
|
||||
}
|
||||
}
|
||||
return count > 0
|
||||
}
|
||||
|
||||
func (oe *operationExecutor) generateVerifyControllerAttachedVolumeFunc(
|
||||
volumeToMount VolumeToMount,
|
||||
nodeName types.NodeName,
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ func UnmountPath(mountPath string, mounter mount.Interface) error {
|
|||
return err
|
||||
}
|
||||
if notMnt {
|
||||
glog.V(4).Info("%q is unmounted, deleting the directory", mountPath)
|
||||
glog.V(4).Infof("%q is unmounted, deleting the directory", mountPath)
|
||||
return os.Remove(mountPath)
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -51,6 +51,17 @@ func SetVolumeOwnership(mounter Mounter, fsGroup *int64) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// chown and chmod pass through to the underlying file for symlinks.
|
||||
// Symlinks have a mode of 777 but this really doesn't mean anything.
|
||||
// The permissions of the underlying file are what matter.
|
||||
// However, if one reads the mode of a symlink then chmods the symlink
|
||||
// with that mode, it changes the mode of the underlying file, overridden
|
||||
// the defaultMode and permissions initialized by the volume plugin, which
|
||||
// is not what we want; thus, we skip chown/chmod for symlinks.
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ func (v *vsphereVolumeUnmounter) TearDownAt(dir string) error {
|
|||
}
|
||||
|
||||
func makeGlobalPDPath(host volume.VolumeHost, devName string) string {
|
||||
return path.Join(host.GetPluginDir(vsphereVolumePluginName), "mounts", devName)
|
||||
return path.Join(host.GetPluginDir(vsphereVolumePluginName), mount.MountsInGlobalPDPath, devName)
|
||||
}
|
||||
|
||||
func (vv *vsphereVolume) GetPath() string {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,48 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// gcpAuthProvider is an auth provider plugin that uses GCP credentials to provide
|
||||
// tokens for kubectl to authenticate itself to the apiserver. A sample json config
|
||||
// is provided below with all recognized options described.
|
||||
//
|
||||
// {
|
||||
// 'auth-provider': {
|
||||
// # Required
|
||||
// "name": "gcp",
|
||||
//
|
||||
// 'config': {
|
||||
// # Caching options
|
||||
//
|
||||
// # Raw string data representing cached access token.
|
||||
// "access-token": "ya29.CjWdA4GiBPTt",
|
||||
// # RFC3339Nano expiration timestamp for cached access token.
|
||||
// "expiry": "2016-10-31 22:31:9.123",
|
||||
//
|
||||
// # Command execution options
|
||||
// # These options direct the plugin to execute a specified command and parse
|
||||
// # token and expiry time from the output of the command.
|
||||
//
|
||||
// # Command to execute for access token. String is split on whitespace
|
||||
// # with first field treated as the executable, remaining fields as args.
|
||||
// # Command output will be parsed as JSON.
|
||||
// "cmd-path": "/usr/bin/gcloud config config-helper --output=json",
|
||||
//
|
||||
// # JSONPath to the string field that represents the access token in
|
||||
// # command output. If omitted, defaults to "{.access_token}".
|
||||
// "token-key": "{.credential.access_token}",
|
||||
//
|
||||
// # JSONPath to the string field that represents expiration timestamp
|
||||
// # of the access token in the command output. If omitted, defaults to
|
||||
// # "{.token_expiry}"
|
||||
// "expiry-key": ""{.credential.token_expiry}",
|
||||
//
|
||||
// # golang reference time in the format that the expiration timestamp uses.
|
||||
// # If omitted, defaults to time.RFC3339Nano
|
||||
// "time-fmt": "2006-01-02 15:04:05.999999999"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
type gcpAuthProvider struct {
|
||||
tokenSource oauth2.TokenSource
|
||||
persister restclient.AuthProviderConfigPersister
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue