func deletePods(kubeClient clientset.Interface, ns string, before unversioned.Time) (int64, error) { items, err := kubeClient.Legacy().Pods(ns).List(api.ListOptions{}) if err != nil { return 0, err } expired := unversioned.Now().After(before.Time) var deleteOptions *api.DeleteOptions if expired { deleteOptions = api.NewDeleteOptions(0) } estimate := int64(0) for i := range items.Items { if items.Items[i].Spec.TerminationGracePeriodSeconds != nil { grace := *items.Items[i].Spec.TerminationGracePeriodSeconds if grace > estimate { estimate = grace } } err := kubeClient.Legacy().Pods(ns).Delete(items.Items[i].Name, deleteOptions) if err != nil && !errors.IsNotFound(err) { return 0, err } } if expired { estimate = 0 } return estimate, nil }
func New(kubeClient clientset.Interface, resyncPeriod controller.ResyncPeriodFunc, threshold int) *GCController { gcc := &GCController{ kubeClient: kubeClient, threshold: threshold, deletePod: func(namespace, name string) error { return kubeClient.Legacy().Pods(namespace).Delete(name, api.NewDeleteOptions(0)) }, } terminatedSelector := fields.ParseSelectorOrDie("status.phase!=" + string(api.PodPending) + ",status.phase!=" + string(api.PodRunning) + ",status.phase!=" + string(api.PodUnknown)) gcc.podStore.Store, gcc.podStoreSyncer = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { options.FieldSelector = terminatedSelector return gcc.kubeClient.Legacy().Pods(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { options.FieldSelector = terminatedSelector return gcc.kubeClient.Legacy().Pods(api.NamespaceAll).Watch(options) }, }, &api.Pod{}, resyncPeriod(), framework.ResourceEventHandlerFuncs{}, ) return gcc }
// GetNewRC returns an RC that matches the intent of the given deployment; get RCList from client interface. // Returns nil if the new RC doesnt exist yet. func GetNewRC(deployment extensions.Deployment, c clientset.Interface) (*api.ReplicationController, error) { return GetNewRCFromList(deployment, c, func(namespace string, options api.ListOptions) ([]api.ReplicationController, error) { rcList, err := c.Legacy().ReplicationControllers(namespace).List(options) return rcList.Items, err }) }
func forcefullyDeletePod(c clientset.Interface, pod *api.Pod) { var zero int64 err := c.Legacy().Pods(pod.Namespace).Delete(pod.Name, &api.DeleteOptions{GracePeriodSeconds: &zero}) if err != nil { utilruntime.HandleError(err) } }
// updateNamespaceStatusFunc will verify that the status of the namespace is correct func updateNamespaceStatusFunc(kubeClient clientset.Interface, namespace *api.Namespace) (*api.Namespace, error) { if namespace.DeletionTimestamp.IsZero() || namespace.Status.Phase == api.NamespaceTerminating { return namespace, nil } newNamespace := api.Namespace{} newNamespace.ObjectMeta = namespace.ObjectMeta newNamespace.Status = namespace.Status newNamespace.Status.Phase = api.NamespaceTerminating return kubeClient.Legacy().Namespaces().UpdateStatus(&newNamespace) }
func deleteResourceQuotas(kubeClient clientset.Interface, ns string) error { resourceQuotas, err := kubeClient.Legacy().ResourceQuotas(ns).List(api.ListOptions{}) if err != nil { return err } for i := range resourceQuotas.Items { err := kubeClient.Legacy().ResourceQuotas(ns).Delete(resourceQuotas.Items[i].Name, nil) if err != nil && !errors.IsNotFound(err) { return err } } return nil }
func deletePersistentVolumeClaims(kubeClient clientset.Interface, ns string) error { items, err := kubeClient.Legacy().PersistentVolumeClaims(ns).List(api.ListOptions{}) if err != nil { return err } for i := range items.Items { err := kubeClient.Legacy().PersistentVolumeClaims(ns).Delete(items.Items[i].Name, nil) if err != nil && !errors.IsNotFound(err) { return err } } return nil }
func getPodsForRCs(c clientset.Interface, replicationControllers []*api.ReplicationController) ([]api.Pod, error) { allPods := []api.Pod{} for _, rc := range replicationControllers { selector := labels.SelectorFromSet(rc.Spec.Selector) options := api.ListOptions{LabelSelector: selector} podList, err := c.Legacy().Pods(rc.ObjectMeta.Namespace).List(options) if err != nil { return allPods, fmt.Errorf("error listing pods: %v", err) } allPods = append(allPods, podList.Items...) } return allPods, nil }
// NewResourceQuota creates a new resource quota admission control handler func NewResourceQuota(client clientset.Interface) admission.Interface { lw := &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return client.Legacy().ResourceQuotas(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return client.Legacy().ResourceQuotas(api.NamespaceAll).Watch(options) }, } indexer, reflector := cache.NewNamespaceKeyedIndexerAndReflector(lw, &api.ResourceQuota{}, 0) reflector.Run() return createResourceQuota(client, indexer) }
// New returns a new service controller to keep cloud provider service resources // (like load balancers) in sync with the registry. func New(cloud cloudprovider.Interface, kubeClient clientset.Interface, clusterName string) *ServiceController { broadcaster := record.NewBroadcaster() broadcaster.StartRecordingToSink(&unversioned_legacy.EventSinkImpl{kubeClient.Legacy().Events("")}) recorder := broadcaster.NewRecorder(api.EventSource{Component: "service-controller"}) return &ServiceController{ cloud: cloud, kubeClient: kubeClient, clusterName: clusterName, cache: &serviceCache{serviceMap: make(map[string]*cachedService)}, eventBroadcaster: broadcaster, eventRecorder: recorder, nodeLister: cache.StoreToNodeLister{ Store: cache.NewStore(cache.MetaNamespaceKeyFunc), }, } }
// retryOnConflictError retries the specified fn if there was a conflict error // TODO RetryOnConflict should be a generic concept in client code func retryOnConflictError(kubeClient clientset.Interface, namespace *api.Namespace, fn updateNamespaceFunc) (result *api.Namespace, err error) { latestNamespace := namespace for { result, err = fn(kubeClient, latestNamespace) if err == nil { return result, nil } if !errors.IsConflict(err) { return nil, err } latestNamespace, err = kubeClient.Legacy().Namespaces().Get(latestNamespace.Name) if err != nil { return nil, err } } return }
// PersistentVolumeRecycler creates a new PersistentVolumeRecycler func NewPersistentVolumeRecycler(kubeClient clientset.Interface, syncPeriod time.Duration, plugins []volume.VolumePlugin, cloud cloudprovider.Interface) (*PersistentVolumeRecycler, error) { recyclerClient := NewRecyclerClient(kubeClient) recycler := &PersistentVolumeRecycler{ client: recyclerClient, kubeClient: kubeClient, cloud: cloud, } if err := recycler.pluginMgr.InitPlugins(plugins, recycler); err != nil { return nil, fmt.Errorf("Could not initialize volume plugins for PVClaimBinder: %+v", err) } _, volumeController := framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return kubeClient.Legacy().PersistentVolumes().List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return kubeClient.Legacy().PersistentVolumes().Watch(options) }, }, &api.PersistentVolume{}, syncPeriod, framework.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { pv, ok := obj.(*api.PersistentVolume) if !ok { glog.Errorf("Error casting object to PersistentVolume: %v", obj) return } recycler.reclaimVolume(pv) }, UpdateFunc: func(oldObj, newObj interface{}) { pv, ok := newObj.(*api.PersistentVolume) if !ok { glog.Errorf("Error casting object to PersistentVolume: %v", newObj) return } recycler.reclaimVolume(pv) }, }, ) recycler.volumeController = volumeController return recycler, nil }
// NewLimitRanger returns an object that enforces limits based on the supplied limit function func NewLimitRanger(client clientset.Interface, limitFunc LimitFunc) admission.Interface { lw := &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return client.Legacy().LimitRanges(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return client.Legacy().LimitRanges(api.NamespaceAll).Watch(options) }, } indexer, reflector := cache.NewNamespaceKeyedIndexerAndReflector(lw, &api.LimitRange{}, 0) reflector.Run() return &limitRanger{ Handler: admission.NewHandler(admission.Create, admission.Update), client: client, limitFunc: limitFunc, indexer: indexer, } }
// NewProvision creates a new namespace provision admission control handler func NewProvision(c clientset.Interface) admission.Interface { store := cache.NewStore(cache.MetaNamespaceKeyFunc) reflector := cache.NewReflector( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return c.Legacy().Namespaces().List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return c.Legacy().Namespaces().Watch(options) }, }, &api.Namespace{}, store, 0, ) reflector.Run() return createProvision(c, store) }
// SyncAllPodsWithStore lists all pods and inserts them into the given store. // Though this function is written in a generic manner, it is only used by the // controllers for a specific purpose, to synchronously populate the store // with the first batch of pods that would otherwise be sent by the Informer. // Doing this avoids more complicated forms of synchronization with the // Informer, though it also means that the controller calling this function // will receive "OnUpdate" events for all the pods in the store, instead of // "OnAdd". This should be ok, since most controllers are level triggered // and make decisions based on the contents of the store. // // TODO: Extend this logic to load arbitrary local state for the controllers // instead of just pods. func SyncAllPodsWithStore(kubeClient clientset.Interface, store cache.Store) { var allPods *api.PodList var err error listOptions := api.ListOptions{LabelSelector: labels.Everything(), FieldSelector: fields.Everything()} for { if allPods, err = kubeClient.Legacy().Pods(api.NamespaceAll).List(listOptions); err != nil { glog.Warningf("Retrying pod list: %v", err) continue } break } pods := []interface{}{} for i := range allPods.Items { p := allPods.Items[i] glog.V(4).Infof("Initializing store with pod %v/%v", p.Namespace, p.Name) pods = append(pods, &p) } store.Replace(pods, allPods.ResourceVersion) return }
// NewPersistentVolumeClaimBinder creates a new PersistentVolumeClaimBinder func NewPersistentVolumeClaimBinder(kubeClient clientset.Interface, syncPeriod time.Duration) *PersistentVolumeClaimBinder { volumeIndex := NewPersistentVolumeOrderedIndex() binderClient := NewBinderClient(kubeClient) binder := &PersistentVolumeClaimBinder{ volumeIndex: volumeIndex, client: binderClient, } _, volumeController := framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return kubeClient.Legacy().PersistentVolumes().List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return kubeClient.Legacy().PersistentVolumes().Watch(options) }, }, &api.PersistentVolume{}, // TODO: Can we have much longer period here? syncPeriod, framework.ResourceEventHandlerFuncs{ AddFunc: binder.addVolume, UpdateFunc: binder.updateVolume, DeleteFunc: binder.deleteVolume, }, ) _, claimController := framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return kubeClient.Legacy().PersistentVolumeClaims(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return kubeClient.Legacy().PersistentVolumeClaims(api.NamespaceAll).Watch(options) }, }, &api.PersistentVolumeClaim{}, // TODO: Can we have much longer period here? syncPeriod, framework.ResourceEventHandlerFuncs{ AddFunc: binder.addClaim, UpdateFunc: binder.updateClaim, DeleteFunc: binder.deleteClaim, }, ) binder.claimController = claimController binder.volumeController = volumeController return binder }
// NewExists creates a new namespace exists admission control handler func NewExists(c clientset.Interface) admission.Interface { store := cache.NewStore(cache.MetaNamespaceKeyFunc) reflector := cache.NewReflector( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return c.Legacy().Namespaces().List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return c.Legacy().Namespaces().Watch(options) }, }, &api.Namespace{}, store, 5*time.Minute, ) reflector.Run() return &exists{ client: c, store: store, Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete), } }
// finalize will finalize the namespace for kubernetes func finalizeNamespaceFunc(kubeClient clientset.Interface, namespace *api.Namespace) (*api.Namespace, error) { namespaceFinalize := api.Namespace{} namespaceFinalize.ObjectMeta = namespace.ObjectMeta namespaceFinalize.Spec = namespace.Spec finalizerSet := sets.NewString() for i := range namespace.Spec.Finalizers { if namespace.Spec.Finalizers[i] != api.FinalizerKubernetes { finalizerSet.Insert(string(namespace.Spec.Finalizers[i])) } } namespaceFinalize.Spec.Finalizers = make([]api.FinalizerName, 0, len(finalizerSet)) for _, value := range finalizerSet.List() { namespaceFinalize.Spec.Finalizers = append(namespaceFinalize.Spec.Finalizers, api.FinalizerName(value)) } namespace, err := kubeClient.Legacy().Namespaces().Finalize(&namespaceFinalize) if err != nil { // it was removed already, so life is good if errors.IsNotFound(err) { return namespace, nil } } return namespace, err }
// NewServiceAccount returns an admission.Interface implementation which limits admission of Pod CREATE requests based on the pod's ServiceAccount: // 1. If the pod does not specify a ServiceAccount, it sets the pod's ServiceAccount to "default" // 2. It ensures the ServiceAccount referenced by the pod exists // 3. If LimitSecretReferences is true, it rejects the pod if the pod references Secret objects which the pod's ServiceAccount does not reference // 4. If the pod does not contain any ImagePullSecrets, the ImagePullSecrets of the service account are added. // 5. If MountServiceAccountToken is true, it adds a VolumeMount with the pod's ServiceAccount's api token secret to containers func NewServiceAccount(cl clientset.Interface) *serviceAccount { serviceAccountsIndexer, serviceAccountsReflector := cache.NewNamespaceKeyedIndexerAndReflector( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return cl.Legacy().ServiceAccounts(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return cl.Legacy().ServiceAccounts(api.NamespaceAll).Watch(options) }, }, &api.ServiceAccount{}, 0, ) tokenSelector := fields.SelectorFromSet(map[string]string{client.SecretType: string(api.SecretTypeServiceAccountToken)}) secretsIndexer, secretsReflector := cache.NewNamespaceKeyedIndexerAndReflector( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { options.FieldSelector = tokenSelector return cl.Legacy().Secrets(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { options.FieldSelector = tokenSelector return cl.Legacy().Secrets(api.NamespaceAll).Watch(options) }, }, &api.Secret{}, 0, ) return &serviceAccount{ Handler: admission.NewHandler(admission.Create), // TODO: enable this once we've swept secret usage to account for adding secret references to service accounts LimitSecretReferences: false, // Auto mount service account API token secrets MountServiceAccountToken: true, // Reject pod creation until a service account token is available RequireAPIToken: true, client: cl, serviceAccounts: serviceAccountsIndexer, serviceAccountsReflector: serviceAccountsReflector, secrets: secretsIndexer, secretsReflector: secretsReflector, } }
func deleteEvents(kubeClient clientset.Interface, ns string) error { return kubeClient.Legacy().Events(ns).DeleteCollection(nil, api.ListOptions{}) }
func NewDaemonSetsController(kubeClient clientset.Interface, resyncPeriod controller.ResyncPeriodFunc) *DaemonSetsController { eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) // TODO: remove the wrapper when every clients have moved to use the clientset. eventBroadcaster.StartRecordingToSink(&unversioned_legacy.EventSinkImpl{kubeClient.Legacy().Events("")}) dsc := &DaemonSetsController{ kubeClient: kubeClient, podControl: controller.RealPodControl{ KubeClient: kubeClient, Recorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "daemon-set"}), }, burstReplicas: BurstReplicas, expectations: controller.NewControllerExpectations(), queue: workqueue.New(), } // Manage addition/update of daemon sets. dsc.dsStore.Store, dsc.dsController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return dsc.kubeClient.Extensions().DaemonSets(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return dsc.kubeClient.Extensions().DaemonSets(api.NamespaceAll).Watch(options) }, }, &extensions.DaemonSet{}, // TODO: Can we have much longer period here? FullDaemonSetResyncPeriod, framework.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { ds := obj.(*extensions.DaemonSet) glog.V(4).Infof("Adding daemon set %s", ds.Name) dsc.enqueueDaemonSet(obj) }, UpdateFunc: func(old, cur interface{}) { oldDS := old.(*extensions.DaemonSet) glog.V(4).Infof("Updating daemon set %s", oldDS.Name) dsc.enqueueDaemonSet(cur) }, DeleteFunc: func(obj interface{}) { ds := obj.(*extensions.DaemonSet) glog.V(4).Infof("Deleting daemon set %s", ds.Name) dsc.enqueueDaemonSet(obj) }, }, ) // Watch for creation/deletion of pods. The reason we watch is that we don't want a daemon set to create/delete // more pods until all the effects (expectations) of a daemon set's create/delete have been observed. dsc.podStore.Store, dsc.podController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return dsc.kubeClient.Legacy().Pods(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return dsc.kubeClient.Legacy().Pods(api.NamespaceAll).Watch(options) }, }, &api.Pod{}, resyncPeriod(), framework.ResourceEventHandlerFuncs{ AddFunc: dsc.addPod, UpdateFunc: dsc.updatePod, DeleteFunc: dsc.deletePod, }, ) // Watch for new nodes or updates to nodes - daemon pods are launched on new nodes, and possibly when labels on nodes change, dsc.nodeStore.Store, dsc.nodeController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return dsc.kubeClient.Legacy().Nodes().List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return dsc.kubeClient.Legacy().Nodes().Watch(options) }, }, &api.Node{}, resyncPeriod(), framework.ResourceEventHandlerFuncs{ AddFunc: dsc.addNode, UpdateFunc: dsc.updateNode, }, ) dsc.syncHandler = dsc.syncDaemonSet dsc.podStoreSynced = dsc.podController.HasSynced return dsc }
// deleteAllContent will delete all content known to the system in a namespace. It returns an estimate // of the time remaining before the remaining resources are deleted. If estimate > 0 not all resources // are guaranteed to be gone. func deleteAllContent(kubeClient clientset.Interface, versions *unversioned.APIVersions, namespace string, before unversioned.Time) (estimate int64, err error) { err = deleteServiceAccounts(kubeClient, namespace) if err != nil { return estimate, err } err = deleteServices(kubeClient, namespace) if err != nil { return estimate, err } err = deleteReplicationControllers(kubeClient, namespace) if err != nil { return estimate, err } estimate, err = deletePods(kubeClient, namespace, before) if err != nil { return estimate, err } err = deleteSecrets(kubeClient, namespace) if err != nil { return estimate, err } err = deletePersistentVolumeClaims(kubeClient, namespace) if err != nil { return estimate, err } err = deleteLimitRanges(kubeClient, namespace) if err != nil { return estimate, err } err = deleteResourceQuotas(kubeClient, namespace) if err != nil { return estimate, err } err = deleteEvents(kubeClient, namespace) if err != nil { return estimate, err } // If experimental mode, delete all experimental resources for the namespace. if containsVersion(versions, "extensions/v1beta1") { resources, err := kubeClient.Discovery().ServerResourcesForGroupVersion("extensions/v1beta1") if err != nil { return estimate, err } if containsResource(resources, "horizontalpodautoscalers") { err = deleteHorizontalPodAutoscalers(kubeClient.Extensions(), namespace) if err != nil { return estimate, err } } if containsResource(resources, "ingresses") { err = deleteIngress(kubeClient.Extensions(), namespace) if err != nil { return estimate, err } } if containsResource(resources, "daemonsets") { err = deleteDaemonSets(kubeClient.Extensions(), namespace) if err != nil { return estimate, err } } if containsResource(resources, "jobs") { err = deleteJobs(kubeClient.Extensions(), namespace) if err != nil { return estimate, err } } if containsResource(resources, "deployments") { err = deleteDeployments(kubeClient.Extensions(), namespace) if err != nil { return estimate, err } } } return estimate, nil }
// NewDeploymentController creates a new DeploymentController. func NewDeploymentController(client clientset.Interface, resyncPeriod controller.ResyncPeriodFunc) *DeploymentController { eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) // TODO: remove the wrapper when every clients have moved to use the clientset. eventBroadcaster.StartRecordingToSink(&unversioned_legacy.EventSinkImpl{client.Legacy().Events("")}) dc := &DeploymentController{ client: client, eventRecorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "deployment-controller"}), queue: workqueue.New(), podExpectations: controller.NewControllerExpectations(), rcExpectations: controller.NewControllerExpectations(), } dc.dStore.Store, dc.dController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return dc.client.Extensions().Deployments(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return dc.client.Extensions().Deployments(api.NamespaceAll).Watch(options) }, }, &extensions.Deployment{}, FullDeploymentResyncPeriod, framework.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { d := obj.(*extensions.Deployment) glog.V(4).Infof("Adding deployment %s", d.Name) dc.enqueueDeployment(obj) }, UpdateFunc: func(old, cur interface{}) { oldD := old.(*extensions.Deployment) glog.V(4).Infof("Updating deployment %s", oldD.Name) // Resync on deployment object relist. dc.enqueueDeployment(cur) }, // This will enter the sync loop and no-op, because the deployment has been deleted from the store. DeleteFunc: func(obj interface{}) { d := obj.(*extensions.Deployment) glog.V(4).Infof("Deleting deployment %s", d.Name) dc.enqueueDeployment(obj) }, }, ) dc.rcStore.Store, dc.rcController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return dc.client.Legacy().ReplicationControllers(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return dc.client.Legacy().ReplicationControllers(api.NamespaceAll).Watch(options) }, }, &api.ReplicationController{}, resyncPeriod(), framework.ResourceEventHandlerFuncs{ AddFunc: dc.addRC, UpdateFunc: dc.updateRC, DeleteFunc: dc.deleteRC, }, ) dc.podStore.Store, dc.podController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return dc.client.Legacy().Pods(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return dc.client.Legacy().Pods(api.NamespaceAll).Watch(options) }, }, &api.Pod{}, resyncPeriod(), framework.ResourceEventHandlerFuncs{ // When pod updates (becomes ready), we need to enqueue deployment UpdateFunc: dc.updatePod, // When pod is deleted, we need to update deployment's expectations DeleteFunc: dc.deletePod, }, ) dc.syncHandler = dc.syncDeployment dc.rcStoreSynced = dc.rcController.HasSynced dc.podStoreSynced = dc.podController.HasSynced return dc }
// IncrementUsage updates the supplied ResourceQuotaStatus object based on the incoming operation // Return true if the usage must be recorded prior to admitting the new resource // Return an error if the operation should not pass admission control func IncrementUsage(a admission.Attributes, status *api.ResourceQuotaStatus, client clientset.Interface) (bool, error) { // on update, the only resource that can modify the value of a quota is pods // so if your not a pod, we exit quickly if a.GetOperation() == admission.Update && a.GetResource() != api.Resource("pods") { return false, nil } var errs []error dirty := true set := map[api.ResourceName]bool{} for k := range status.Hard { set[k] = true } obj := a.GetObject() // handle max counts for each kind of resource (pods, services, replicationControllers, etc.) if a.GetOperation() == admission.Create { resourceName := resourceToResourceName[a.GetResource()] hard, hardFound := status.Hard[resourceName] if hardFound { used, usedFound := status.Used[resourceName] if !usedFound { return false, fmt.Errorf("quota usage stats are not yet known, unable to admit resource until an accurate count is completed.") } if used.Value() >= hard.Value() { errs = append(errs, fmt.Errorf("limited to %s %s", hard.String(), resourceName)) dirty = false } else { status.Used[resourceName] = *resource.NewQuantity(used.Value()+int64(1), resource.DecimalSI) } } } if a.GetResource() == api.Resource("pods") { for _, resourceName := range []api.ResourceName{api.ResourceMemory, api.ResourceCPU} { // ignore tracking the resource if it's not in the quota document if !set[resourceName] { continue } hard, hardFound := status.Hard[resourceName] if !hardFound { continue } // if we do not yet know how much of the current resource is used, we cannot accept any request used, usedFound := status.Used[resourceName] if !usedFound { return false, fmt.Errorf("unable to admit pod until quota usage stats are calculated.") } // the amount of resource being requested, or an error if it does not make a request that is tracked pod := obj.(*api.Pod) delta, err := resourcequotacontroller.PodRequests(pod, resourceName) if err != nil { return false, fmt.Errorf("%s is limited by quota, must make explicit request.", resourceName) } // if this operation is an update, we need to find the delta usage from the previous state if a.GetOperation() == admission.Update { oldPod, err := client.Legacy().Pods(a.GetNamespace()).Get(pod.Name) if err != nil { return false, err } // if the previous version of the resource made a resource request, we need to subtract the old request // from the current to get the actual resource request delta. if the previous version of the pod // made no request on the resource, then we get an err value. we ignore the err value, and delta // will just be equal to the total resource request on the pod since there is nothing to subtract. oldRequest, err := resourcequotacontroller.PodRequests(oldPod, resourceName) if err == nil { err = delta.Sub(*oldRequest) if err != nil { return false, err } } } newUsage := used.Copy() newUsage.Add(*delta) // make the most precise comparison possible newUsageValue := newUsage.Value() hardUsageValue := hard.Value() if newUsageValue <= resource.MaxMilliValue && hardUsageValue <= resource.MaxMilliValue { newUsageValue = newUsage.MilliValue() hardUsageValue = hard.MilliValue() } if newUsageValue > hardUsageValue { errs = append(errs, fmt.Errorf("%s quota is %s, current usage is %s, requesting %s.", resourceName, hard.String(), used.String(), delta.String())) dirty = false } else { status.Used[resourceName] = *newUsage } } } return dirty, utilerrors.NewAggregate(errs) }
// syncNamespace orchestrates deletion of a Namespace and its associated content. func syncNamespace(kubeClient clientset.Interface, versions *unversioned.APIVersions, namespace *api.Namespace) error { if namespace.DeletionTimestamp == nil { return nil } // multiple controllers may edit a namespace during termination // first get the latest state of the namespace before proceeding // if the namespace was deleted already, don't do anything namespace, err := kubeClient.Legacy().Namespaces().Get(namespace.Name) if err != nil { if errors.IsNotFound(err) { return nil } return err } glog.V(4).Infof("Syncing namespace %s", namespace.Name) // ensure that the status is up to date on the namespace // if we get a not found error, we assume the namespace is truly gone namespace, err = retryOnConflictError(kubeClient, namespace, updateNamespaceStatusFunc) if err != nil { if errors.IsNotFound(err) { return nil } return err } // if the namespace is already finalized, delete it if finalized(namespace) { err = kubeClient.Legacy().Namespaces().Delete(namespace.Name, nil) if err != nil && !errors.IsNotFound(err) { return err } return nil } // there may still be content for us to remove estimate, err := deleteAllContent(kubeClient, versions, namespace.Name, *namespace.DeletionTimestamp) if err != nil { return err } if estimate > 0 { return &contentRemainingError{estimate} } // we have removed content, so mark it finalized by us result, err := retryOnConflictError(kubeClient, namespace, finalizeNamespaceFunc) if err != nil { return err } // now check if all finalizers have reported that we delete now if finalized(result) { err = kubeClient.Legacy().Namespaces().Delete(namespace.Name, nil) if err != nil && !errors.IsNotFound(err) { return err } } return nil }
// NewNodeController returns a new node controller to sync instances from cloudprovider. func NewNodeController( cloud cloudprovider.Interface, kubeClient clientset.Interface, podEvictionTimeout time.Duration, deletionEvictionLimiter util.RateLimiter, terminationEvictionLimiter util.RateLimiter, nodeMonitorGracePeriod time.Duration, nodeStartupGracePeriod time.Duration, nodeMonitorPeriod time.Duration, clusterCIDR *net.IPNet, allocateNodeCIDRs bool) *NodeController { eventBroadcaster := record.NewBroadcaster() recorder := eventBroadcaster.NewRecorder(api.EventSource{Component: "controllermanager"}) eventBroadcaster.StartLogging(glog.Infof) if kubeClient != nil { glog.Infof("Sending events to api server.") eventBroadcaster.StartRecordingToSink(&unversioned_legacy.EventSinkImpl{kubeClient.Legacy().Events("")}) } else { glog.Infof("No api server defined - no events will be sent to API server.") } if allocateNodeCIDRs && clusterCIDR == nil { glog.Fatal("NodeController: Must specify clusterCIDR if allocateNodeCIDRs == true.") } evictorLock := sync.Mutex{} nc := &NodeController{ cloud: cloud, knownNodeSet: make(sets.String), kubeClient: kubeClient, recorder: recorder, podEvictionTimeout: podEvictionTimeout, maximumGracePeriod: 5 * time.Minute, evictorLock: &evictorLock, podEvictor: NewRateLimitedTimedQueue(deletionEvictionLimiter), terminationEvictor: NewRateLimitedTimedQueue(terminationEvictionLimiter), nodeStatusMap: make(map[string]nodeStatusData), nodeMonitorGracePeriod: nodeMonitorGracePeriod, nodeMonitorPeriod: nodeMonitorPeriod, nodeStartupGracePeriod: nodeStartupGracePeriod, lookupIP: net.LookupIP, now: unversioned.Now, clusterCIDR: clusterCIDR, allocateNodeCIDRs: allocateNodeCIDRs, forcefullyDeletePod: func(p *api.Pod) { forcefullyDeletePod(kubeClient, p) }, } nc.podStore.Store, nc.podController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return nc.kubeClient.Legacy().Pods(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return nc.kubeClient.Legacy().Pods(api.NamespaceAll).Watch(options) }, }, &api.Pod{}, controller.NoResyncPeriodFunc(), framework.ResourceEventHandlerFuncs{ AddFunc: nc.maybeDeleteTerminatingPod, UpdateFunc: func(_, obj interface{}) { nc.maybeDeleteTerminatingPod(obj) }, }, ) nc.nodeStore.Store, nc.nodeController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return nc.kubeClient.Legacy().Nodes().List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return nc.kubeClient.Legacy().Nodes().Watch(options) }, }, &api.Node{}, controller.NoResyncPeriodFunc(), framework.ResourceEventHandlerFuncs{}, ) nc.daemonSetStore.Store, nc.daemonSetController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return nc.kubeClient.Extensions().DaemonSets(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return nc.kubeClient.Extensions().DaemonSets(api.NamespaceAll).Watch(options) }, }, &api.Node{}, controller.NoResyncPeriodFunc(), framework.ResourceEventHandlerFuncs{}, ) return nc }
func NewJobController(kubeClient clientset.Interface, resyncPeriod controller.ResyncPeriodFunc) *JobController { eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) // TODO: remove the wrapper when every clients have moved to use the clientset. eventBroadcaster.StartRecordingToSink(&unversioned_legacy.EventSinkImpl{kubeClient.Legacy().Events("")}) jm := &JobController{ kubeClient: kubeClient, podControl: controller.RealPodControl{ KubeClient: kubeClient, Recorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "job-controller"}), }, expectations: controller.NewControllerExpectations(), queue: workqueue.New(), recorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "job-controller"}), } jm.jobStore.Store, jm.jobController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return jm.kubeClient.Extensions().Jobs(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return jm.kubeClient.Extensions().Jobs(api.NamespaceAll).Watch(options) }, }, &extensions.Job{}, // TODO: Can we have much longer period here? replicationcontroller.FullControllerResyncPeriod, framework.ResourceEventHandlerFuncs{ AddFunc: jm.enqueueController, UpdateFunc: func(old, cur interface{}) { if job := cur.(*extensions.Job); !isJobFinished(job) { jm.enqueueController(job) } }, DeleteFunc: jm.enqueueController, }, ) jm.podStore.Store, jm.podController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return jm.kubeClient.Legacy().Pods(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return jm.kubeClient.Legacy().Pods(api.NamespaceAll).Watch(options) }, }, &api.Pod{}, resyncPeriod(), framework.ResourceEventHandlerFuncs{ AddFunc: jm.addPod, UpdateFunc: jm.updatePod, DeleteFunc: jm.deletePod, }, ) jm.updateHandler = jm.updateJobStatus jm.syncHandler = jm.syncJob jm.podStoreSynced = jm.podController.HasSynced return jm }
// NewReplicaSetController creates a new ReplicaSetController. func NewReplicaSetController(kubeClient clientset.Interface, resyncPeriod controller.ResyncPeriodFunc, burstReplicas int) *ReplicaSetController { eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) eventBroadcaster.StartRecordingToSink(&unversioned_legacy.EventSinkImpl{kubeClient.Legacy().Events("")}) rsc := &ReplicaSetController{ kubeClient: kubeClient, podControl: controller.RealPodControl{ KubeClient: kubeClient, Recorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "replicaset-controller"}), }, burstReplicas: burstReplicas, expectations: controller.NewControllerExpectations(), queue: workqueue.New(), } rsc.rsStore.Store, rsc.rsController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return rsc.kubeClient.Extensions().ReplicaSets(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return rsc.kubeClient.Extensions().ReplicaSets(api.NamespaceAll).Watch(options) }, }, &extensions.ReplicaSet{}, // TODO: Can we have much longer period here? FullControllerResyncPeriod, framework.ResourceEventHandlerFuncs{ AddFunc: rsc.enqueueReplicaSet, UpdateFunc: func(old, cur interface{}) { // You might imagine that we only really need to enqueue the // replica set when Spec changes, but it is safer to sync any // time this function is triggered. That way a full informer // resync can requeue any replica set that don't yet have pods // but whose last attempts at creating a pod have failed (since // we don't block on creation of pods) instead of those // replica sets stalling indefinitely. Enqueueing every time // does result in some spurious syncs (like when Status.Replica // is updated and the watch notification from it retriggers // this function), but in general extra resyncs shouldn't be // that bad as ReplicaSets that haven't met expectations yet won't // sync, and all the listing is done using local stores. oldRS := old.(*extensions.ReplicaSet) curRS := cur.(*extensions.ReplicaSet) if oldRS.Status.Replicas != curRS.Status.Replicas { glog.V(4).Infof("Observed updated replica count for ReplicaSet: %v, %d->%d", curRS.Name, oldRS.Status.Replicas, curRS.Status.Replicas) } rsc.enqueueReplicaSet(cur) }, // This will enter the sync loop and no-op, because the replica set has been deleted from the store. // Note that deleting a replica set immediately after scaling it to 0 will not work. The recommended // way of achieving this is by performing a `stop` operation on the replica set. DeleteFunc: rsc.enqueueReplicaSet, }, ) rsc.podStore.Store, rsc.podController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return rsc.kubeClient.Legacy().Pods(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return rsc.kubeClient.Legacy().Pods(api.NamespaceAll).Watch(options) }, }, &api.Pod{}, resyncPeriod(), framework.ResourceEventHandlerFuncs{ AddFunc: rsc.addPod, // This invokes the ReplicaSet for every pod change, eg: host assignment. Though this might seem like // overkill the most frequent pod update is status, and the associated ReplicaSet will only list from // local storage, so it should be ok. UpdateFunc: rsc.updatePod, DeleteFunc: rsc.deletePod, }, ) rsc.syncHandler = rsc.syncReplicaSet rsc.podStoreSynced = rsc.podController.HasSynced return rsc }
// NewNamespaceController creates a new NamespaceController func NewNamespaceController(kubeClient clientset.Interface, versions *unversioned.APIVersions, resyncPeriod time.Duration) *NamespaceController { var controller *framework.Controller _, controller = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { return kubeClient.Legacy().Namespaces().List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return kubeClient.Legacy().Namespaces().Watch(options) }, }, &api.Namespace{}, // TODO: Can we have much longer period here? resyncPeriod, framework.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { namespace := obj.(*api.Namespace) if err := syncNamespace(kubeClient, versions, namespace); err != nil { if estimate, ok := err.(*contentRemainingError); ok { go func() { // Estimate is the aggregate total of TerminationGracePeriodSeconds, which defaults to 30s // for pods. However, most processes will terminate faster - within a few seconds, probably // with a peak within 5-10s. So this division is a heuristic that avoids waiting the full // duration when in many cases things complete more quickly. The extra second added is to // ensure we never wait 0 seconds. t := estimate.Estimate/2 + 1 glog.V(4).Infof("Content remaining in namespace %s, waiting %d seconds", namespace.Name, t) time.Sleep(time.Duration(t) * time.Second) if err := controller.Requeue(namespace); err != nil { utilruntime.HandleError(err) } }() return } utilruntime.HandleError(err) } }, UpdateFunc: func(oldObj, newObj interface{}) { namespace := newObj.(*api.Namespace) if err := syncNamespace(kubeClient, versions, namespace); err != nil { if estimate, ok := err.(*contentRemainingError); ok { go func() { t := estimate.Estimate/2 + 1 glog.V(4).Infof("Content remaining in namespace %s, waiting %d seconds", namespace.Name, t) time.Sleep(time.Duration(t) * time.Second) if err := controller.Requeue(namespace); err != nil { utilruntime.HandleError(err) } }() return } utilruntime.HandleError(err) } }, }, ) return &NamespaceController{ controller: controller, } }