2015-09-03 00:02:22 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2015 The Kubernetes Authors .
2015-09-03 00:02:22 +00:00
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 .
* /
2016-06-25 09:31:32 +00:00
// Package deployment contains all the logic for handling Kubernetes Deployments.
// It implements a set of strategies (rolling, recreate) for deploying an application,
// the means to rollback to previous versions, proportional scaling for mitigating
// risk, cleanup policy, and other useful features of Deployments.
2015-09-03 00:02:22 +00:00
package deployment
import (
"fmt"
2016-01-15 02:04:05 +00:00
"reflect"
2016-08-17 01:47:15 +00:00
"sort"
2015-09-03 00:02:22 +00:00
"time"
2015-09-29 15:09:33 +00:00
"github.com/golang/glog"
2015-09-03 00:02:22 +00:00
"k8s.io/kubernetes/pkg/api"
2016-03-14 19:07:56 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-10-09 22:04:41 +00:00
"k8s.io/kubernetes/pkg/apis/extensions"
2015-09-21 07:06:45 +00:00
"k8s.io/kubernetes/pkg/client/cache"
2016-02-05 21:58:03 +00:00
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
2016-03-29 21:52:43 +00:00
unversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
2015-09-29 23:55:06 +00:00
"k8s.io/kubernetes/pkg/client/record"
2015-09-21 07:06:45 +00:00
"k8s.io/kubernetes/pkg/controller"
2016-07-08 12:48:38 +00:00
"k8s.io/kubernetes/pkg/controller/deployment/util"
2016-08-17 01:47:15 +00:00
"k8s.io/kubernetes/pkg/labels"
2015-09-21 07:06:45 +00:00
"k8s.io/kubernetes/pkg/runtime"
2016-04-13 18:38:32 +00:00
"k8s.io/kubernetes/pkg/util/metrics"
2016-01-15 07:32:10 +00:00
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
2016-02-02 10:57:06 +00:00
"k8s.io/kubernetes/pkg/util/wait"
2015-09-21 07:06:45 +00:00
"k8s.io/kubernetes/pkg/util/workqueue"
"k8s.io/kubernetes/pkg/watch"
)
const (
2015-11-18 23:12:11 +00:00
// FullDeploymentResyncPeriod means we'll attempt to recompute the required replicas
2016-02-28 02:13:32 +00:00
// of all deployments.
2015-11-18 23:12:11 +00:00
// This recomputation happens based on contents in the local caches.
2015-09-21 07:06:45 +00:00
FullDeploymentResyncPeriod = 30 * time . Second
2016-02-11 19:31:47 +00:00
// We must avoid creating new replica set / counting pods until the replica set / pods store has synced.
// If it hasn't synced, to avoid a hot loop, we'll wait this long between checks.
StoreSyncedPollPeriod = 100 * time . Millisecond
2016-07-08 12:48:38 +00:00
// MaxRetries is the number of times a deployment will be retried before it is dropped out of the queue.
MaxRetries = 5
2015-09-03 00:02:22 +00:00
)
2015-11-18 23:12:11 +00:00
// DeploymentController is responsible for synchronizing Deployment objects stored
2016-01-20 00:40:18 +00:00
// in the system with actual running replica sets and pods.
2015-09-03 00:02:22 +00:00
type DeploymentController struct {
2016-01-15 05:00:58 +00:00
client clientset . Interface
2015-09-29 23:55:06 +00:00
eventRecorder record . EventRecorder
2015-09-21 07:06:45 +00:00
// To allow injection of syncDeployment for testing.
syncHandler func ( dKey string ) error
// A store of deployments, populated by the dController
dStore cache . StoreToDeploymentLister
// Watches changes to all deployments
2016-09-14 18:35:38 +00:00
dController * cache . Controller
2016-01-20 00:40:18 +00:00
// A store of ReplicaSets, populated by the rsController
rsStore cache . StoreToReplicaSetLister
// Watches changes to all ReplicaSets
2016-09-14 18:35:38 +00:00
rsController * cache . Controller
2015-09-21 07:06:45 +00:00
// A store of pods, populated by the podController
podStore cache . StoreToPodLister
// Watches changes to all pods
2016-09-14 18:35:38 +00:00
podController * cache . Controller
2016-07-08 12:48:38 +00:00
// dStoreSynced returns true if the Deployment store has been synced at least once.
// Added as a member to the struct to allow injection for testing.
dStoreSynced func ( ) bool
// rsStoreSynced returns true if the ReplicaSet store has been synced at least once.
// Added as a member to the struct to allow injection for testing.
rsStoreSynced func ( ) bool
2015-09-21 07:06:45 +00:00
// podStoreSynced returns true if the pod store has been synced at least once.
// Added as a member to the struct to allow injection for testing.
podStoreSynced func ( ) bool
// Deployments that need to be synced
2016-07-08 12:48:38 +00:00
queue workqueue . RateLimitingInterface
2015-09-03 00:02:22 +00:00
}
2015-11-18 23:12:11 +00:00
// NewDeploymentController creates a new DeploymentController.
2016-01-15 05:00:58 +00:00
func NewDeploymentController ( client clientset . Interface , resyncPeriod controller . ResyncPeriodFunc ) * DeploymentController {
2015-09-29 23:55:06 +00:00
eventBroadcaster := record . NewBroadcaster ( )
eventBroadcaster . StartLogging ( glog . Infof )
2016-01-15 05:00:58 +00:00
// TODO: remove the wrapper when every clients have moved to use the clientset.
2016-03-23 23:45:24 +00:00
eventBroadcaster . StartRecordingToSink ( & unversionedcore . EventSinkImpl { Interface : client . Core ( ) . Events ( "" ) } )
2015-09-29 23:55:06 +00:00
2016-04-13 18:38:32 +00:00
if client != nil && client . Core ( ) . GetRESTClient ( ) . GetRateLimiter ( ) != nil {
metrics . RegisterMetricAndTrackRateLimiterUsage ( "deployment_controller" , client . Core ( ) . GetRESTClient ( ) . GetRateLimiter ( ) )
}
2015-09-21 07:06:45 +00:00
dc := & DeploymentController {
2016-02-28 02:13:32 +00:00
client : client ,
eventRecorder : eventBroadcaster . NewRecorder ( api . EventSource { Component : "deployment-controller" } ) ,
2016-08-22 18:15:45 +00:00
queue : workqueue . NewNamedRateLimitingQueue ( workqueue . DefaultControllerRateLimiter ( ) , "deployment" ) ,
2015-09-03 00:02:22 +00:00
}
2015-09-21 07:06:45 +00:00
2016-09-14 18:35:38 +00:00
dc . dStore . Indexer , dc . dController = cache . NewIndexerInformer (
2015-09-21 07:06:45 +00:00
& cache . ListWatch {
2015-12-04 00:00:13 +00:00
ListFunc : func ( options api . ListOptions ) ( runtime . Object , error ) {
2016-01-15 05:00:58 +00:00
return dc . client . Extensions ( ) . Deployments ( api . NamespaceAll ) . List ( options )
2015-09-21 07:06:45 +00:00
} ,
2015-12-04 00:00:13 +00:00
WatchFunc : func ( options api . ListOptions ) ( watch . Interface , error ) {
2016-01-15 05:00:58 +00:00
return dc . client . Extensions ( ) . Deployments ( api . NamespaceAll ) . Watch ( options )
2015-09-21 07:06:45 +00:00
} ,
} ,
& extensions . Deployment { } ,
FullDeploymentResyncPeriod ,
2016-09-14 18:35:38 +00:00
cache . ResourceEventHandlerFuncs {
2016-03-01 02:13:01 +00:00
AddFunc : dc . addDeploymentNotification ,
UpdateFunc : dc . updateDeploymentNotification ,
2015-09-21 07:06:45 +00:00
// This will enter the sync loop and no-op, because the deployment has been deleted from the store.
2016-03-01 02:13:01 +00:00
DeleteFunc : dc . deleteDeploymentNotification ,
2015-09-21 07:06:45 +00:00
} ,
2016-08-17 01:47:15 +00:00
cache . Indexers { cache . NamespaceIndex : cache . MetaNamespaceIndexFunc } ,
2015-09-21 07:06:45 +00:00
)
2016-09-14 18:35:38 +00:00
dc . rsStore . Store , dc . rsController = cache . NewInformer (
2015-09-21 07:06:45 +00:00
& cache . ListWatch {
2015-12-04 00:00:13 +00:00
ListFunc : func ( options api . ListOptions ) ( runtime . Object , error ) {
2016-01-20 00:40:18 +00:00
return dc . client . Extensions ( ) . ReplicaSets ( api . NamespaceAll ) . List ( options )
2015-09-21 07:06:45 +00:00
} ,
2015-12-04 00:00:13 +00:00
WatchFunc : func ( options api . ListOptions ) ( watch . Interface , error ) {
2016-01-20 00:40:18 +00:00
return dc . client . Extensions ( ) . ReplicaSets ( api . NamespaceAll ) . Watch ( options )
2015-09-21 07:06:45 +00:00
} ,
} ,
2016-01-20 00:40:18 +00:00
& extensions . ReplicaSet { } ,
2015-11-18 23:12:11 +00:00
resyncPeriod ( ) ,
2016-09-14 18:35:38 +00:00
cache . ResourceEventHandlerFuncs {
2016-01-20 00:40:18 +00:00
AddFunc : dc . addReplicaSet ,
UpdateFunc : dc . updateReplicaSet ,
DeleteFunc : dc . deleteReplicaSet ,
2015-09-21 07:06:45 +00:00
} ,
)
2016-09-14 18:35:38 +00:00
dc . podStore . Indexer , dc . podController = cache . NewIndexerInformer (
2015-09-21 07:06:45 +00:00
& cache . ListWatch {
2015-12-04 00:00:13 +00:00
ListFunc : func ( options api . ListOptions ) ( runtime . Object , error ) {
2016-02-03 21:21:05 +00:00
return dc . client . Core ( ) . Pods ( api . NamespaceAll ) . List ( options )
2015-09-21 07:06:45 +00:00
} ,
2015-12-04 00:00:13 +00:00
WatchFunc : func ( options api . ListOptions ) ( watch . Interface , error ) {
2016-02-03 21:21:05 +00:00
return dc . client . Core ( ) . Pods ( api . NamespaceAll ) . Watch ( options )
2015-09-21 07:06:45 +00:00
} ,
} ,
& api . Pod { } ,
2015-11-18 23:12:11 +00:00
resyncPeriod ( ) ,
2016-09-14 18:35:38 +00:00
cache . ResourceEventHandlerFuncs {
2016-02-28 02:13:32 +00:00
AddFunc : dc . addPod ,
2015-11-18 23:12:11 +00:00
UpdateFunc : dc . updatePod ,
2015-12-04 00:00:13 +00:00
DeleteFunc : dc . deletePod ,
2015-11-18 23:12:11 +00:00
} ,
2016-04-07 12:15:21 +00:00
cache . Indexers { cache . NamespaceIndex : cache . MetaNamespaceIndexFunc } ,
2015-09-21 07:06:45 +00:00
)
dc . syncHandler = dc . syncDeployment
2016-07-08 12:48:38 +00:00
dc . dStoreSynced = dc . dController . HasSynced
2016-01-20 00:40:18 +00:00
dc . rsStoreSynced = dc . rsController . HasSynced
2015-11-18 23:12:11 +00:00
dc . podStoreSynced = dc . podController . HasSynced
2015-09-21 07:06:45 +00:00
return dc
2015-09-03 00:02:22 +00:00
}
2015-11-18 23:12:11 +00:00
// Run begins watching and syncing.
func ( dc * DeploymentController ) Run ( workers int , stopCh <- chan struct { } ) {
2016-01-15 07:32:10 +00:00
defer utilruntime . HandleCrash ( )
2016-07-08 12:48:38 +00:00
2015-11-18 23:12:11 +00:00
go dc . dController . Run ( stopCh )
2016-01-20 00:40:18 +00:00
go dc . rsController . Run ( stopCh )
2015-11-18 23:12:11 +00:00
go dc . podController . Run ( stopCh )
2016-07-08 12:48:38 +00:00
// Wait for the rc and dc stores to sync before starting any work in this controller.
ready := make ( chan struct { } )
go dc . waitForSyncedStores ( ready , stopCh )
select {
case <- ready :
case <- stopCh :
return
}
2015-11-18 23:12:11 +00:00
for i := 0 ; i < workers ; i ++ {
2016-02-02 10:57:06 +00:00
go wait . Until ( dc . worker , time . Second , stopCh )
2015-11-18 23:12:11 +00:00
}
2016-07-08 12:48:38 +00:00
2015-11-18 23:12:11 +00:00
<- stopCh
glog . Infof ( "Shutting down deployment controller" )
dc . queue . ShutDown ( )
}
2016-07-08 12:48:38 +00:00
func ( dc * DeploymentController ) waitForSyncedStores ( ready chan <- struct { } , stopCh <- chan struct { } ) {
defer utilruntime . HandleCrash ( )
for ! dc . dStoreSynced ( ) || ! dc . rsStoreSynced ( ) || ! dc . podStoreSynced ( ) {
select {
case <- time . After ( StoreSyncedPollPeriod ) :
case <- stopCh :
return
}
}
close ( ready )
}
2016-03-01 02:13:01 +00:00
func ( dc * DeploymentController ) addDeploymentNotification ( obj interface { } ) {
d := obj . ( * extensions . Deployment )
glog . V ( 4 ) . Infof ( "Adding deployment %s" , d . Name )
dc . enqueueDeployment ( d )
}
func ( dc * DeploymentController ) updateDeploymentNotification ( old , cur interface { } ) {
oldD := old . ( * extensions . Deployment )
glog . V ( 4 ) . Infof ( "Updating deployment %s" , oldD . Name )
// Resync on deployment object relist.
dc . enqueueDeployment ( cur . ( * extensions . Deployment ) )
}
func ( dc * DeploymentController ) deleteDeploymentNotification ( obj interface { } ) {
d , ok := obj . ( * extensions . Deployment )
if ! ok {
tombstone , ok := obj . ( cache . DeletedFinalStateUnknown )
if ! ok {
2016-06-14 12:04:38 +00:00
glog . Errorf ( "Couldn't get object from tombstone %#v" , obj )
2016-03-01 02:13:01 +00:00
return
}
d , ok = tombstone . Obj . ( * extensions . Deployment )
if ! ok {
2016-06-14 12:04:38 +00:00
glog . Errorf ( "Tombstone contained object that is not a Deployment %#v" , obj )
2016-03-01 02:13:01 +00:00
return
}
}
glog . V ( 4 ) . Infof ( "Deleting deployment %s" , d . Name )
dc . enqueueDeployment ( d )
}
2016-01-20 00:40:18 +00:00
// addReplicaSet enqueues the deployment that manages a ReplicaSet when the ReplicaSet is created.
func ( dc * DeploymentController ) addReplicaSet ( obj interface { } ) {
rs := obj . ( * extensions . ReplicaSet )
glog . V ( 4 ) . Infof ( "ReplicaSet %s added." , rs . Name )
if d := dc . getDeploymentForReplicaSet ( rs ) ; d != nil {
2015-09-21 07:06:45 +00:00
dc . enqueueDeployment ( d )
}
2015-09-03 00:02:22 +00:00
}
2016-01-20 00:40:18 +00:00
// getDeploymentForReplicaSet returns the deployment managing the given ReplicaSet.
func ( dc * DeploymentController ) getDeploymentForReplicaSet ( rs * extensions . ReplicaSet ) * extensions . Deployment {
deployments , err := dc . dStore . GetDeploymentsForReplicaSet ( rs )
2015-11-18 23:12:11 +00:00
if err != nil || len ( deployments ) == 0 {
2016-01-20 00:40:18 +00:00
glog . V ( 4 ) . Infof ( "Error: %v. No deployment found for ReplicaSet %v, deployment controller will avoid syncing." , err , rs . Name )
2015-09-21 07:06:45 +00:00
return nil
}
2016-01-20 00:40:18 +00:00
// Because all ReplicaSet's belonging to a deployment should have a unique label key,
2015-09-21 07:06:45 +00:00
// there should never be more than one deployment returned by the above method.
// If that happens we should probably dynamically repair the situation by ultimately
2016-08-17 01:47:15 +00:00
// trying to clean up one of the controllers, for now we just return the older one
if len ( deployments ) > 1 {
sort . Sort ( util . BySelectorLastUpdateTime ( deployments ) )
glog . Errorf ( "user error! more than one deployment is selecting replica set %s/%s with labels: %#v, returning %s/%s" , rs . Namespace , rs . Name , rs . Labels , deployments [ 0 ] . Namespace , deployments [ 0 ] . Name )
}
2015-09-21 07:06:45 +00:00
return & deployments [ 0 ]
}
2016-01-20 00:40:18 +00:00
// updateReplicaSet figures out what deployment(s) manage a ReplicaSet when the ReplicaSet
// is updated and wake them up. If the anything of the ReplicaSets have changed, we need to
// awaken both the old and new deployments. old and cur must be *extensions.ReplicaSet
// types.
func ( dc * DeploymentController ) updateReplicaSet ( old , cur interface { } ) {
2016-08-09 13:57:21 +00:00
curRS := cur . ( * extensions . ReplicaSet )
oldRS := old . ( * extensions . ReplicaSet )
if curRS . ResourceVersion == oldRS . ResourceVersion {
// Periodic resync will send update events for all known replica sets.
// Two different versions of the same replica set will always have different RVs.
2015-09-21 07:06:45 +00:00
return
2015-09-03 00:02:22 +00:00
}
2015-09-21 07:06:45 +00:00
// TODO: Write a unittest for this case
2016-01-20 00:40:18 +00:00
glog . V ( 4 ) . Infof ( "ReplicaSet %s updated." , curRS . Name )
if d := dc . getDeploymentForReplicaSet ( curRS ) ; d != nil {
2015-09-21 07:06:45 +00:00
dc . enqueueDeployment ( d )
}
// A number of things could affect the old deployment: labels changing,
// pod template changing, etc.
2016-01-20 00:40:18 +00:00
if ! api . Semantic . DeepEqual ( oldRS , curRS ) {
if oldD := dc . getDeploymentForReplicaSet ( oldRS ) ; oldD != nil {
2015-09-21 07:06:45 +00:00
dc . enqueueDeployment ( oldD )
}
}
}
2016-01-20 00:40:18 +00:00
// deleteReplicaSet enqueues the deployment that manages a ReplicaSet when
// the ReplicaSet is deleted. obj could be an *extensions.ReplicaSet, or
// a DeletionFinalStateUnknown marker item.
func ( dc * DeploymentController ) deleteReplicaSet ( obj interface { } ) {
rs , ok := obj . ( * extensions . ReplicaSet )
2015-09-21 07:06:45 +00:00
// When a delete is dropped, the relist will notice a pod in the store not
// in the list, leading to the insertion of a tombstone object which contains
2016-01-20 00:40:18 +00:00
// the deleted key/value. Note that this value might be stale. If the ReplicaSet
2015-09-21 07:06:45 +00:00
// changed labels the new deployment will not be woken up till the periodic resync.
if ! ok {
tombstone , ok := obj . ( cache . DeletedFinalStateUnknown )
if ! ok {
2016-06-14 12:04:38 +00:00
glog . Errorf ( "Couldn't get object from tombstone %#v, could take up to %v before a deployment recreates/updates replicasets" , obj , FullDeploymentResyncPeriod )
2015-09-21 07:06:45 +00:00
return
}
2016-01-20 00:40:18 +00:00
rs , ok = tombstone . Obj . ( * extensions . ReplicaSet )
2015-09-21 07:06:45 +00:00
if ! ok {
2016-06-14 12:04:38 +00:00
glog . Errorf ( "Tombstone contained object that is not a ReplicaSet %#v, could take up to %v before a deployment recreates/updates replicasets" , obj , FullDeploymentResyncPeriod )
2015-09-21 07:06:45 +00:00
return
2015-09-03 00:02:22 +00:00
}
}
2016-01-20 00:40:18 +00:00
glog . V ( 4 ) . Infof ( "ReplicaSet %s deleted." , rs . Name )
if d := dc . getDeploymentForReplicaSet ( rs ) ; d != nil {
2015-09-21 07:06:45 +00:00
dc . enqueueDeployment ( d )
}
}
2016-08-17 01:47:15 +00:00
// getDeploymentForPod returns the deployment that manages the given Pod.
// If there are multiple deployments for a given Pod, only return the oldest one.
2015-11-18 23:12:11 +00:00
func ( dc * DeploymentController ) getDeploymentForPod ( pod * api . Pod ) * extensions . Deployment {
2016-08-17 01:47:15 +00:00
deployments , err := dc . dStore . GetDeploymentsForPod ( pod )
if err != nil || len ( deployments ) == 0 {
glog . V ( 4 ) . Infof ( "Error: %v. No deployment found for Pod %v, deployment controller will avoid syncing." , err , pod . Name )
2015-11-18 23:12:11 +00:00
return nil
}
2016-08-17 01:47:15 +00:00
if len ( deployments ) > 1 {
sort . Sort ( util . BySelectorLastUpdateTime ( deployments ) )
glog . Errorf ( "user error! more than one deployment is selecting pod %s/%s with labels: %#v, returning %s/%s" , pod . Namespace , pod . Name , pod . Labels , deployments [ 0 ] . Namespace , deployments [ 0 ] . Name )
2015-11-18 23:12:11 +00:00
}
2016-08-17 01:47:15 +00:00
return & deployments [ 0 ]
2015-11-18 23:12:11 +00:00
}
2016-02-28 02:13:32 +00:00
// When a pod is created, ensure its controller syncs
2016-02-25 06:40:14 +00:00
func ( dc * DeploymentController ) addPod ( obj interface { } ) {
pod , ok := obj . ( * api . Pod )
if ! ok {
return
}
2016-06-14 12:04:38 +00:00
glog . V ( 4 ) . Infof ( "Pod %s created: %#v." , pod . Name , pod )
2016-02-25 06:40:14 +00:00
if d := dc . getDeploymentForPod ( pod ) ; d != nil {
dc . enqueueDeployment ( d )
}
}
2016-01-20 00:40:18 +00:00
// updatePod figures out what deployment(s) manage the ReplicaSet that manages the Pod when the Pod
2015-11-18 23:12:11 +00:00
// is updated and wake them up. If anything of the Pods have changed, we need to awaken both
// the old and new deployments. old and cur must be *api.Pod types.
func ( dc * DeploymentController ) updatePod ( old , cur interface { } ) {
curPod := cur . ( * api . Pod )
2016-03-04 21:57:34 +00:00
oldPod := old . ( * api . Pod )
2016-08-09 13:57:21 +00:00
if curPod . ResourceVersion == oldPod . ResourceVersion {
// Periodic resync will send update events for all known pods.
// Two different versions of the same pod will always have different RVs.
return
}
2016-05-31 22:59:46 +00:00
glog . V ( 4 ) . Infof ( "Pod %s updated %#v -> %#v." , curPod . Name , oldPod , curPod )
2015-11-18 23:12:11 +00:00
if d := dc . getDeploymentForPod ( curPod ) ; d != nil {
dc . enqueueDeployment ( d )
}
if ! api . Semantic . DeepEqual ( oldPod , curPod ) {
if oldD := dc . getDeploymentForPod ( oldPod ) ; oldD != nil {
dc . enqueueDeployment ( oldD )
}
}
}
2016-02-28 02:13:32 +00:00
// When a pod is deleted, ensure its controller syncs.
2015-12-04 00:00:13 +00:00
// obj could be an *api.Pod, or a DeletionFinalStateUnknown marker item.
func ( dc * DeploymentController ) deletePod ( obj interface { } ) {
pod , ok := obj . ( * api . Pod )
// When a delete is dropped, the relist will notice a pod in the store not
// in the list, leading to the insertion of a tombstone object which contains
// the deleted key/value. Note that this value might be stale. If the pod
2016-01-20 00:40:18 +00:00
// changed labels the new ReplicaSet will not be woken up till the periodic
// resync.
2015-12-04 00:00:13 +00:00
if ! ok {
tombstone , ok := obj . ( cache . DeletedFinalStateUnknown )
if ! ok {
2016-06-14 12:04:38 +00:00
glog . Errorf ( "Couldn't get object from tombstone %#v" , obj )
2015-12-04 00:00:13 +00:00
return
}
pod , ok = tombstone . Obj . ( * api . Pod )
if ! ok {
2016-06-14 12:04:38 +00:00
glog . Errorf ( "Tombstone contained object that is not a pod %#v" , obj )
2015-12-04 00:00:13 +00:00
return
}
}
2016-06-14 12:04:38 +00:00
glog . V ( 4 ) . Infof ( "Pod %s deleted: %#v." , pod . Name , pod )
2015-12-04 00:00:13 +00:00
if d := dc . getDeploymentForPod ( pod ) ; d != nil {
2016-02-25 06:40:14 +00:00
dc . enqueueDeployment ( d )
2015-12-04 00:00:13 +00:00
}
}
2016-02-28 02:13:32 +00:00
func ( dc * DeploymentController ) enqueueDeployment ( deployment * extensions . Deployment ) {
key , err := controller . KeyFunc ( deployment )
2015-09-21 07:06:45 +00:00
if err != nil {
2016-06-14 12:04:38 +00:00
glog . Errorf ( "Couldn't get key for object %#v: %v" , deployment , err )
2015-09-21 07:06:45 +00:00
return
}
dc . queue . Add ( key )
2015-09-03 00:02:22 +00:00
}
2016-08-17 01:47:15 +00:00
func ( dc * DeploymentController ) markDeploymentOverlap ( deployment * extensions . Deployment , withDeployment string ) ( * extensions . Deployment , error ) {
if deployment . Annotations [ util . OverlapAnnotation ] == withDeployment {
return deployment , nil
}
if deployment . Annotations == nil {
deployment . Annotations = make ( map [ string ] string )
}
deployment . Annotations [ util . OverlapAnnotation ] = withDeployment
return dc . client . Extensions ( ) . Deployments ( deployment . Namespace ) . Update ( deployment )
}
func ( dc * DeploymentController ) clearDeploymentOverlap ( deployment * extensions . Deployment ) ( * extensions . Deployment , error ) {
if len ( deployment . Annotations [ util . OverlapAnnotation ] ) == 0 {
return deployment , nil
}
delete ( deployment . Annotations , util . OverlapAnnotation )
return dc . client . Extensions ( ) . Deployments ( deployment . Namespace ) . Update ( deployment )
}
2015-09-21 07:06:45 +00:00
// 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 ( dc * DeploymentController ) worker ( ) {
2016-07-08 12:48:38 +00:00
work := func ( ) bool {
key , quit := dc . queue . Get ( )
if quit {
return true
}
defer dc . queue . Done ( key )
err := dc . syncHandler ( key . ( string ) )
dc . handleErr ( err , key )
return false
}
2015-09-21 07:06:45 +00:00
for {
2016-07-08 12:48:38 +00:00
if quit := work ( ) ; quit {
return
}
}
}
func ( dc * DeploymentController ) handleErr ( err error , key interface { } ) {
if err == nil {
dc . queue . Forget ( key )
return
}
if dc . queue . NumRequeues ( key ) < MaxRetries {
glog . V ( 2 ) . Infof ( "Error syncing deployment %v: %v" , key , err )
dc . queue . AddRateLimited ( key )
return
2015-09-21 07:06:45 +00:00
}
2016-07-08 12:48:38 +00:00
utilruntime . HandleError ( err )
dc . queue . Forget ( key )
2015-09-21 07:06:45 +00:00
}
2015-11-18 23:12:11 +00:00
// syncDeployment will sync the deployment with the given key.
// This function is not meant to be invoked concurrently with the same key.
2015-09-21 07:06:45 +00:00
func ( dc * DeploymentController ) syncDeployment ( key string ) error {
startTime := time . Now ( )
defer func ( ) {
glog . V ( 4 ) . Infof ( "Finished syncing deployment %q (%v)" , key , time . Now ( ) . Sub ( startTime ) )
} ( )
2016-08-17 01:47:15 +00:00
obj , exists , err := dc . dStore . Indexer . GetByKey ( key )
2015-09-21 07:06:45 +00:00
if err != nil {
2016-09-12 06:44:08 +00:00
glog . Errorf ( "Unable to retrieve deployment %v from store: %v" , key , err )
2015-09-21 07:06:45 +00:00
return err
}
2015-11-18 23:12:11 +00:00
if ! exists {
glog . Infof ( "Deployment has been deleted %v" , key )
return nil
}
2016-02-25 06:40:14 +00:00
2016-07-08 12:48:38 +00:00
deployment := obj . ( * extensions . Deployment )
2016-03-25 07:40:12 +00:00
everything := unversioned . LabelSelector { }
2016-07-08 12:48:38 +00:00
if reflect . DeepEqual ( deployment . Spec . Selector , & everything ) {
dc . eventRecorder . Eventf ( deployment , api . EventTypeWarning , "SelectingAll" , "This deployment is selecting all pods. A non-empty selector is required." )
2016-03-25 07:40:12 +00:00
return nil
}
2016-02-25 06:40:14 +00:00
2016-07-08 12:48:38 +00:00
// Deep-copy otherwise we are mutating our cache.
// TODO: Deep-copy only when needed.
d , err := util . DeploymentDeepCopy ( deployment )
if err != nil {
return err
}
2016-06-17 11:46:43 +00:00
if d . DeletionTimestamp != nil {
return dc . syncStatusOnly ( d )
}
2016-08-17 01:47:15 +00:00
// Handle overlapping deployments by deterministically avoid syncing deployments that fight over ReplicaSets.
if err = dc . handleOverlap ( d ) ; err != nil {
return err
}
2016-01-21 10:12:58 +00:00
if d . Spec . Paused {
2016-01-28 16:35:14 +00:00
return dc . sync ( d )
2016-01-21 10:12:58 +00:00
}
2016-01-28 16:35:14 +00:00
2016-01-15 02:04:05 +00:00
if d . Spec . RollbackTo != nil {
revision := d . Spec . RollbackTo . Revision
2016-08-25 18:38:07 +00:00
if d , err = dc . rollback ( d , & revision ) ; err != nil {
2016-01-15 02:04:05 +00:00
return err
}
}
2016-06-28 11:31:03 +00:00
scalingEvent , err := dc . isScalingEvent ( d )
if err != nil {
return err
}
if scalingEvent {
2016-01-28 16:35:14 +00:00
return dc . sync ( d )
}
2015-09-21 07:06:45 +00:00
switch d . Spec . Strategy . Type {
2015-10-09 22:49:10 +00:00
case extensions . RecreateDeploymentStrategyType :
2016-01-28 16:35:14 +00:00
return dc . rolloutRecreate ( d )
2015-10-09 22:49:10 +00:00
case extensions . RollingUpdateDeploymentStrategyType :
2016-01-28 16:35:14 +00:00
return dc . rolloutRolling ( d )
2015-09-17 19:41:06 +00:00
}
2015-11-18 23:12:11 +00:00
return fmt . Errorf ( "unexpected deployment strategy type: %s" , d . Spec . Strategy . Type )
2015-09-17 19:41:06 +00:00
}
2016-08-17 01:47:15 +00:00
// handleOverlap relists all deployment in the same namespace for overlaps, and avoid syncing
// the newer overlapping ones (only sync the oldest one). New/old is determined by when the
// deployment's selector is last updated.
func ( dc * DeploymentController ) handleOverlap ( d * extensions . Deployment ) error {
selector , err := unversioned . LabelSelectorAsSelector ( d . Spec . Selector )
if err != nil {
return fmt . Errorf ( "deployment %s/%s has invalid label selector: %v" , d . Namespace , d . Name , err )
}
deployments , err := dc . dStore . Deployments ( d . Namespace ) . List ( labels . Everything ( ) )
if err != nil {
return fmt . Errorf ( "error listing deployments in namespace %s: %v" , d . Namespace , err )
}
overlapping := false
for i := range deployments {
other := & deployments [ i ]
if ! selector . Empty ( ) && selector . Matches ( labels . Set ( other . Spec . Template . Labels ) ) && d . UID != other . UID {
overlapping = true
// We don't care if the overlapping annotation update failed or not (we don't make decision on it)
d , _ = dc . markDeploymentOverlap ( d , other . Name )
other , _ = dc . markDeploymentOverlap ( other , d . Name )
// Skip syncing this one if older overlapping one is found
// TODO: figure out a better way to determine which deployment to skip,
// either with controller reference, or with validation.
// Using oldest active replica set to determine which deployment to skip wouldn't make much difference,
// since new replica set hasn't been created after selector update
if util . SelectorUpdatedBefore ( other , d ) {
return fmt . Errorf ( "found deployment %s/%s has overlapping selector with an older deployment %s/%s, skip syncing it" , d . Namespace , d . Name , other . Namespace , other . Name )
}
}
}
if ! overlapping {
// We don't care if the overlapping annotation update failed or not (we don't make decision on it)
d , _ = dc . clearDeploymentOverlap ( d )
}
return nil
}