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-10-21 22:24:05 +00:00
unversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
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-10-07 19:06:57 +00:00
"k8s.io/kubernetes/pkg/controller/informers"
2016-08-17 01:47:15 +00:00
"k8s.io/kubernetes/pkg/labels"
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"
)
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
2016-10-07 19:06:57 +00:00
dLister * cache . StoreToDeploymentLister
2016-01-20 00:40:18 +00:00
// A store of ReplicaSets, populated by the rsController
2016-10-07 19:06:57 +00:00
rsLister * cache . StoreToReplicaSetLister
2015-09-21 07:06:45 +00:00
// A store of pods, populated by the podController
2016-10-07 19:06:57 +00:00
podLister * cache . StoreToPodLister
2016-07-08 12:48:38 +00:00
2016-10-04 17:11:07 +00:00
// dListerSynced returns true if the Deployment store has been synced at least once.
2016-07-08 12:48:38 +00:00
// Added as a member to the struct to allow injection for testing.
2016-10-07 19:06:57 +00:00
dListerSynced cache . InformerSynced
2016-10-04 17:11:07 +00:00
// rsListerSynced returns true if the ReplicaSet store has been synced at least once.
2016-07-08 12:48:38 +00:00
// Added as a member to the struct to allow injection for testing.
2016-10-07 19:06:57 +00:00
rsListerSynced cache . InformerSynced
2016-10-04 17:11:07 +00:00
// podListerSynced returns true if the pod store has been synced at least once.
2015-09-21 07:06:45 +00:00
// Added as a member to the struct to allow injection for testing.
2016-10-07 19:06:57 +00:00
podListerSynced cache . InformerSynced
2015-09-21 07:06:45 +00:00
// 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-10-07 19:06:57 +00:00
func NewDeploymentController ( dInformer informers . DeploymentInformer , rsInformer informers . ReplicaSetInformer , podInformer informers . PodInformer , client clientset . Interface ) * 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-10-13 12:56:07 +00:00
if client != nil && client . Core ( ) . RESTClient ( ) . GetRateLimiter ( ) != nil {
metrics . RegisterMetricAndTrackRateLimiterUsage ( "deployment_controller" , client . Core ( ) . RESTClient ( ) . GetRateLimiter ( ) )
2016-04-13 18:38:32 +00:00
}
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-10-07 19:06:57 +00:00
dInformer . Informer ( ) . AddEventHandler ( cache . ResourceEventHandlerFuncs {
AddFunc : dc . addDeploymentNotification ,
UpdateFunc : dc . updateDeploymentNotification ,
// This will enter the sync loop and no-op, because the deployment has been deleted from the store.
DeleteFunc : dc . deleteDeploymentNotification ,
} )
rsInformer . Informer ( ) . AddEventHandler ( cache . ResourceEventHandlerFuncs {
AddFunc : dc . addReplicaSet ,
UpdateFunc : dc . updateReplicaSet ,
DeleteFunc : dc . deleteReplicaSet ,
} )
2015-09-21 07:06:45 +00:00
dc . syncHandler = dc . syncDeployment
2016-10-07 19:06:57 +00:00
dc . dLister = dInformer . Lister ( )
dc . rsLister = rsInformer . Lister ( )
dc . podLister = podInformer . Lister ( )
dc . dListerSynced = dInformer . Informer ( ) . HasSynced
dc . rsListerSynced = dInformer . Informer ( ) . HasSynced
dc . podListerSynced = dInformer . Informer ( ) . 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-10-07 19:06:57 +00:00
defer dc . queue . ShutDown ( )
2016-07-08 12:48:38 +00:00
2016-10-07 19:06:57 +00:00
glog . Infof ( "Starting deployment controller" )
2016-07-08 12:48:38 +00:00
2016-10-07 19:06:57 +00:00
if ! cache . WaitForCacheSync ( stopCh , dc . dListerSynced , dc . rsListerSynced , dc . podListerSynced ) {
2016-07-08 12:48:38 +00:00
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" )
2016-07-08 12:48:38 +00:00
}
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 {
2016-10-04 17:11:07 +00:00
deployments , err := dc . dLister . 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 )
}
2016-10-04 17:11:07 +00:00
return deployments [ 0 ]
2015-09-21 07:06:45 +00:00
}
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-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
}
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-10-04 17:11:07 +00:00
obj , exists , err := dc . dLister . 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 )
// 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-10-14 12:40:37 +00:00
everything := unversioned . LabelSelector { }
if reflect . DeepEqual ( d . Spec . Selector , & everything ) {
dc . eventRecorder . Eventf ( d , api . EventTypeWarning , "SelectingAll" , "This deployment is selecting all pods. A non-empty selector is required." )
if d . Status . ObservedGeneration < d . Generation {
d . Status . ObservedGeneration = d . Generation
dc . client . Extensions ( ) . Deployments ( d . Namespace ) . UpdateStatus ( d )
}
return nil
}
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 {
2016-10-14 12:40:37 +00:00
dc . eventRecorder . Eventf ( d , api . EventTypeWarning , "SelectorOverlap" , err . Error ( ) )
2016-09-26 15:56:53 +00:00
return nil
2016-08-17 01:47:15 +00:00
}
2016-09-15 15:57:53 +00:00
// Update deployment conditions with an Unknown condition when pausing/resuming
// a deployment. In this way, we can be sure that we won't timeout when a user
// resumes a Deployment with a set progressDeadlineSeconds.
if err = dc . checkPausedConditions ( d ) ; err != nil {
return err
}
_ , err = dc . hasFailed ( d )
if err != nil {
return err
}
// TODO: Automatically rollback here if we failed above. Locate the last complete
// revision and populate the rollback spec with it.
// See https://github.com/kubernetes/kubernetes/issues/23211.
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 {
2016-10-04 17:11:07 +00:00
deployments , err := dc . dLister . Deployments ( d . Namespace ) . List ( labels . Everything ( ) )
2016-08-17 01:47:15 +00:00
if err != nil {
return fmt . Errorf ( "error listing deployments in namespace %s: %v" , d . Namespace , err )
}
overlapping := false
2016-10-04 17:11:07 +00:00
for _ , other := range deployments {
2016-11-02 21:43:23 +00:00
foundOverlaps , err := util . OverlapsWith ( d , other )
if err != nil {
return err
}
if foundOverlaps {
2016-10-04 17:11:07 +00:00
deploymentCopy , err := util . DeploymentDeepCopy ( other )
if err != nil {
return err
}
2016-08-17 01:47:15 +00:00
overlapping = true
2016-10-14 12:40:37 +00:00
// Skip syncing this one if older overlapping one is found.
2016-10-04 17:11:07 +00:00
if util . SelectorUpdatedBefore ( deploymentCopy , d ) {
2016-10-14 12:40:37 +00:00
// We don't care if the overlapping annotation update failed or not (we don't make decision on it)
dc . markDeploymentOverlap ( d , deploymentCopy . Name )
dc . clearDeploymentOverlap ( deploymentCopy )
return fmt . Errorf ( "found deployment %s/%s has overlapping selector with an older deployment %s/%s, skip syncing it" , d . Namespace , d . Name , deploymentCopy . Namespace , deploymentCopy . Name )
2016-08-17 01:47:15 +00:00
}
2016-10-14 12:40:37 +00:00
dc . markDeploymentOverlap ( deploymentCopy , d . Name )
d , _ = dc . clearDeploymentOverlap ( d )
2016-08-17 01:47:15 +00:00
}
}
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
}
2016-10-14 12:40:37 +00:00
func ( dc * DeploymentController ) markDeploymentOverlap ( deployment * extensions . Deployment , withDeployment string ) ( * extensions . Deployment , error ) {
if deployment . Annotations [ util . OverlapAnnotation ] == withDeployment && deployment . Status . ObservedGeneration >= deployment . Generation {
return deployment , nil
}
if deployment . Annotations == nil {
deployment . Annotations = make ( map [ string ] string )
}
// Update observedGeneration for overlapping deployments so that their deletion won't be blocked.
deployment . Status . ObservedGeneration = deployment . Generation
deployment . Annotations [ util . OverlapAnnotation ] = withDeployment
return dc . client . Extensions ( ) . Deployments ( deployment . Namespace ) . UpdateStatus ( 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 ) . UpdateStatus ( deployment )
}