2015-09-18 20:35:56 +00:00
/ *
Copyright 2015 The Kubernetes Authors All rights reserved .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package deployment
import (
"fmt"
2016-01-20 23:48:52 +00:00
"strconv"
2015-11-11 23:22:57 +00:00
"time"
2015-09-18 20:35:56 +00:00
2016-02-25 00:41:26 +00:00
"github.com/golang/glog"
2015-09-18 20:35:56 +00:00
"k8s.io/kubernetes/pkg/api"
2016-02-06 02:43:02 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-10-09 22:04:41 +00:00
"k8s.io/kubernetes/pkg/apis/extensions"
2016-02-05 21:58:03 +00:00
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
2015-09-18 20:35:56 +00:00
"k8s.io/kubernetes/pkg/labels"
2016-02-05 02:05:38 +00:00
"k8s.io/kubernetes/pkg/util/integer"
intstrutil "k8s.io/kubernetes/pkg/util/intstr"
2016-01-12 23:37:51 +00:00
labelsutil "k8s.io/kubernetes/pkg/util/labels"
podutil "k8s.io/kubernetes/pkg/util/pod"
2016-03-02 20:52:12 +00:00
rsutil "k8s.io/kubernetes/pkg/util/replicaset"
2016-02-11 01:49:11 +00:00
"k8s.io/kubernetes/pkg/util/wait"
2015-09-18 20:35:56 +00:00
)
2016-01-13 01:52:18 +00:00
const (
2016-01-20 00:40:18 +00:00
// The revision annotation of a deployment's replica sets which records its rollout sequence
2016-01-13 01:52:18 +00:00
RevisionAnnotation = "deployment.kubernetes.io/revision"
2016-01-19 22:50:03 +00:00
// Here are the possible rollback event reasons
RollbackRevisionNotFound = "DeploymentRollbackRevisionNotFound"
RollbackTemplateUnchanged = "DeploymentRollbackTemplateUnchanged"
RollbackDone = "DeploymentRollback"
2016-01-13 01:52:18 +00:00
)
2016-01-20 00:40:18 +00:00
// GetOldReplicaSets returns the old replica sets targeted by the given Deployment; get PodList and ReplicaSetList from client interface.
// Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets.
2016-02-28 02:13:32 +00:00
func GetOldReplicaSets ( deployment * extensions . Deployment , c clientset . Interface ) ( [ ] * extensions . ReplicaSet , [ ] * extensions . ReplicaSet , error ) {
2016-01-20 00:40:18 +00:00
return GetOldReplicaSetsFromLists ( deployment , c ,
2015-12-04 00:00:13 +00:00
func ( namespace string , options api . ListOptions ) ( * api . PodList , error ) {
2016-02-03 21:21:05 +00:00
return c . Core ( ) . Pods ( namespace ) . List ( options )
2015-11-18 23:12:11 +00:00
} ,
2016-01-20 00:40:18 +00:00
func ( namespace string , options api . ListOptions ) ( [ ] extensions . ReplicaSet , error ) {
rsList , err := c . Extensions ( ) . ReplicaSets ( namespace ) . List ( options )
return rsList . Items , err
2015-11-18 23:12:11 +00:00
} )
}
2016-02-19 18:25:34 +00:00
// TODO: switch this to full namespacers
2016-02-11 01:49:11 +00:00
type rsListFunc func ( string , api . ListOptions ) ( [ ] extensions . ReplicaSet , error )
type podListFunc func ( string , api . ListOptions ) ( * api . PodList , error )
2016-01-20 00:40:18 +00:00
// GetOldReplicaSetsFromLists returns two sets of old replica sets targeted by the given Deployment; get PodList and ReplicaSetList with input functions.
// Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets.
2016-02-28 02:13:32 +00:00
func GetOldReplicaSetsFromLists ( deployment * extensions . Deployment , c clientset . Interface , getPodList podListFunc , getRSList rsListFunc ) ( [ ] * extensions . ReplicaSet , [ ] * extensions . ReplicaSet , error ) {
2016-02-11 18:57:42 +00:00
// Find all pods whose labels match deployment.Spec.Selector, and corresponding replica sets for pods in podList.
// All pods and replica sets are labeled with pod-template-hash to prevent overlapping
2016-01-20 00:40:18 +00:00
// TODO: Right now we list all replica sets and then filter. We should add an API for this.
oldRSs := map [ string ] extensions . ReplicaSet { }
allOldRSs := map [ string ] extensions . ReplicaSet { }
2016-02-11 18:57:42 +00:00
rsList , podList , err := rsAndPodsWithHashKeySynced ( deployment , c , getRSList , getPodList )
2015-09-18 20:35:56 +00:00
if err != nil {
2016-02-11 18:57:42 +00:00
return nil , nil , fmt . Errorf ( "error labeling replica sets and pods with pod-template-hash: %v" , err )
2015-09-18 20:35:56 +00:00
}
2016-01-20 00:40:18 +00:00
newRSTemplate := GetNewReplicaSetTemplate ( deployment )
2015-09-18 20:35:56 +00:00
for _ , pod := range podList . Items {
podLabelsSelector := labels . Set ( pod . ObjectMeta . Labels )
2016-01-20 00:40:18 +00:00
for _ , rs := range rsList {
2016-02-06 02:43:02 +00:00
rsLabelsSelector , err := unversioned . LabelSelectorAsSelector ( rs . Spec . Selector )
if err != nil {
2016-02-10 17:43:30 +00:00
return nil , nil , fmt . Errorf ( "invalid label selector: %v" , err )
2016-02-06 02:43:02 +00:00
}
2016-01-20 00:40:18 +00:00
// Filter out replica set that has the same pod template spec as the deployment - that is the new replica set.
if api . Semantic . DeepEqual ( rs . Spec . Template , & newRSTemplate ) {
2016-01-13 01:52:18 +00:00
continue
}
2016-01-20 00:40:18 +00:00
allOldRSs [ rs . ObjectMeta . Name ] = rs
if rsLabelsSelector . Matches ( podLabelsSelector ) {
oldRSs [ rs . ObjectMeta . Name ] = rs
2015-09-18 20:35:56 +00:00
}
}
}
2016-01-20 00:40:18 +00:00
requiredRSs := [ ] * extensions . ReplicaSet { }
for key := range oldRSs {
value := oldRSs [ key ]
requiredRSs = append ( requiredRSs , & value )
2015-09-18 20:35:56 +00:00
}
2016-01-20 00:40:18 +00:00
allRSs := [ ] * extensions . ReplicaSet { }
for key := range allOldRSs {
value := allOldRSs [ key ]
allRSs = append ( allRSs , & value )
2016-01-13 01:52:18 +00:00
}
2016-01-20 00:40:18 +00:00
return requiredRSs , allRSs , nil
2015-09-18 20:35:56 +00:00
}
2016-01-20 00:40:18 +00:00
// GetNewReplicaSet returns a replica set that matches the intent of the given deployment; get ReplicaSetList from client interface.
2016-02-12 19:33:32 +00:00
// Returns nil if the new replica set doesn't exist yet.
2016-02-28 02:13:32 +00:00
func GetNewReplicaSet ( deployment * extensions . Deployment , c clientset . Interface ) ( * extensions . ReplicaSet , error ) {
2016-01-20 00:40:18 +00:00
return GetNewReplicaSetFromList ( deployment , c ,
2016-02-11 01:49:11 +00:00
func ( namespace string , options api . ListOptions ) ( * api . PodList , error ) {
return c . Core ( ) . Pods ( namespace ) . List ( options )
} ,
2016-01-20 00:40:18 +00:00
func ( namespace string , options api . ListOptions ) ( [ ] extensions . ReplicaSet , error ) {
rsList , err := c . Extensions ( ) . ReplicaSets ( namespace ) . List ( options )
return rsList . Items , err
2015-11-18 23:12:11 +00:00
} )
}
2016-01-20 00:40:18 +00:00
// GetNewReplicaSetFromList returns a replica set that matches the intent of the given deployment; get ReplicaSetList with the input function.
2016-02-12 19:33:32 +00:00
// Returns nil if the new replica set doesn't exist yet.
2016-02-28 02:13:32 +00:00
func GetNewReplicaSetFromList ( deployment * extensions . Deployment , c clientset . Interface , getPodList podListFunc , getRSList rsListFunc ) ( * extensions . ReplicaSet , error ) {
2016-02-11 18:57:42 +00:00
rsList , _ , err := rsAndPodsWithHashKeySynced ( deployment , c , getRSList , getPodList )
2015-09-18 20:35:56 +00:00
if err != nil {
2016-01-20 00:40:18 +00:00
return nil , fmt . Errorf ( "error listing ReplicaSets: %v" , err )
2015-09-18 20:35:56 +00:00
}
2016-01-20 00:40:18 +00:00
newRSTemplate := GetNewReplicaSetTemplate ( deployment )
2015-09-18 20:35:56 +00:00
2016-01-20 00:40:18 +00:00
for i := range rsList {
if api . Semantic . DeepEqual ( rsList [ i ] . Spec . Template , & newRSTemplate ) {
// This is the new ReplicaSet.
return & rsList [ i ] , nil
2015-09-18 20:35:56 +00:00
}
}
2016-01-20 00:40:18 +00:00
// new ReplicaSet does not exist.
2015-09-18 20:35:56 +00:00
return nil , nil
}
2016-02-19 18:25:34 +00:00
// rsAndPodsWithHashKeySynced returns the RSs and pods the given deployment targets, with pod-template-hash information synced.
2016-02-28 02:13:32 +00:00
func rsAndPodsWithHashKeySynced ( deployment * extensions . Deployment , c clientset . Interface , getRSList rsListFunc , getPodList podListFunc ) ( [ ] extensions . ReplicaSet , * api . PodList , error ) {
2016-02-11 01:49:11 +00:00
namespace := deployment . Namespace
selector , err := unversioned . LabelSelectorAsSelector ( deployment . Spec . Selector )
if err != nil {
2016-02-11 18:57:42 +00:00
return nil , nil , err
2016-02-11 01:49:11 +00:00
}
options := api . ListOptions { LabelSelector : selector }
rsList , err := getRSList ( namespace , options )
if err != nil {
2016-02-11 18:57:42 +00:00
return nil , nil , err
2016-02-11 01:49:11 +00:00
}
syncedRSList := [ ] extensions . ReplicaSet { }
for _ , rs := range rsList {
2016-02-11 18:57:42 +00:00
// Add pod-template-hash information if it's not in the RS.
2016-02-19 18:25:34 +00:00
// Otherwise, new RS produced by Deployment will overlap with pre-existing ones
2016-02-11 18:57:42 +00:00
// that aren't constrained by the pod-template-hash.
syncedRS , err := addHashKeyToRSAndPods ( deployment , c , rs , getPodList )
if err != nil {
return nil , nil , err
2016-02-11 01:49:11 +00:00
}
2016-02-11 18:57:42 +00:00
syncedRSList = append ( syncedRSList , * syncedRS )
2016-02-11 01:49:11 +00:00
}
2016-02-11 18:57:42 +00:00
syncedPodList , err := getPodList ( namespace , options )
2016-02-19 18:25:34 +00:00
if err != nil {
return nil , nil , err
}
2016-02-11 18:57:42 +00:00
return syncedRSList , syncedPodList , nil
2016-02-11 01:49:11 +00:00
}
2016-02-11 18:57:42 +00:00
// addHashKeyToRSAndPods adds pod-template-hash information to the given rs, if it's not already there, with the following steps:
2016-02-19 18:25:34 +00:00
// 1. Add hash label to the rs's pod template, and make sure the controller sees this update so that no orphaned pods will be created
2016-02-18 19:45:24 +00:00
// 2. Add hash label to all pods this rs owns
// 3. Add hash label to the rs's label and selector
2016-02-28 02:13:32 +00:00
func addHashKeyToRSAndPods ( deployment * extensions . Deployment , c clientset . Interface , rs extensions . ReplicaSet , getPodList podListFunc ) ( updatedRS * extensions . ReplicaSet , err error ) {
2016-02-23 22:14:22 +00:00
updatedRS = & rs
2016-02-19 18:25:34 +00:00
// If the rs already has the new hash label in its selector, it's done syncing
2016-02-25 02:09:59 +00:00
if labelsutil . SelectorHasLabel ( rs . Spec . Selector , extensions . DefaultDeploymentUniqueLabelKey ) {
return
}
2016-02-19 18:25:34 +00:00
namespace := deployment . Namespace
2016-02-25 02:09:59 +00:00
meta := rs . Spec . Template . ObjectMeta
meta . Labels = labelsutil . CloneAndRemoveLabel ( meta . Labels , extensions . DefaultDeploymentUniqueLabelKey )
2016-02-19 18:25:34 +00:00
hash := fmt . Sprintf ( "%d" , podutil . GetPodTemplateSpecHash ( api . PodTemplateSpec {
2016-02-25 02:09:59 +00:00
ObjectMeta : meta ,
2016-02-19 18:25:34 +00:00
Spec : rs . Spec . Template . Spec ,
} ) )
2016-03-02 20:52:12 +00:00
rsUpdated := false
2016-02-18 19:45:24 +00:00
// 1. Add hash template label to the rs. This ensures that any newly created pods will have the new label.
2016-02-24 18:19:27 +00:00
if len ( updatedRS . Spec . Template . Labels [ extensions . DefaultDeploymentUniqueLabelKey ] ) == 0 {
2016-03-02 20:52:12 +00:00
updatedRS , rsUpdated , err = rsutil . UpdateRSWithRetries ( c . Extensions ( ) . ReplicaSets ( namespace ) , updatedRS , func ( updated * extensions . ReplicaSet ) {
2016-02-19 18:25:34 +00:00
updated . Spec . Template . Labels = labelsutil . AddLabel ( updated . Spec . Template . Labels , extensions . DefaultDeploymentUniqueLabelKey , hash )
} )
if err != nil {
2016-03-02 20:52:12 +00:00
return nil , fmt . Errorf ( "error updating %s %s/%s pod template label with template hash: %v" , updatedRS . Kind , updatedRS . Namespace , updatedRS . Name , err )
2016-02-19 18:25:34 +00:00
}
2016-03-02 20:52:12 +00:00
if rsUpdated {
// Make sure rs pod template is updated so that it won't create pods without the new label (orphaned pods).
if updatedRS . Generation > updatedRS . Status . ObservedGeneration {
if err = waitForReplicaSetUpdated ( c , updatedRS . Generation , namespace , updatedRS . Name ) ; err != nil {
return nil , fmt . Errorf ( "error waiting for %s %s/%s generation %d observed by controller: %v" , updatedRS . Kind , updatedRS . Namespace , updatedRS . Name , updatedRS . Generation , err )
}
2016-02-24 02:07:04 +00:00
}
2016-03-02 20:52:12 +00:00
glog . V ( 4 ) . Infof ( "Observed the update of %s %s/%s's pod template with hash %s." , rs . Kind , rs . Namespace , rs . Name , hash )
} else {
// If RS wasn't updated but didn't return error in step 1, we've hit a RS not found error.
// Return here and retry in the next sync loop.
return & rs , nil
2016-02-19 18:25:34 +00:00
}
2016-02-18 19:45:24 +00:00
}
// 2. Update all pods managed by the rs to have the new hash label, so they will be correctly adopted.
2016-02-24 18:19:27 +00:00
selector , err := unversioned . LabelSelectorAsSelector ( updatedRS . Spec . Selector )
2016-02-11 01:49:11 +00:00
if err != nil {
2016-03-01 02:28:32 +00:00
return nil , fmt . Errorf ( "error in converting selector to label selector for replica set %s: %s" , updatedRS . Name , err )
2016-02-11 01:49:11 +00:00
}
options := api . ListOptions { LabelSelector : selector }
podList , err := getPodList ( namespace , options )
if err != nil {
2016-03-01 02:28:32 +00:00
return nil , fmt . Errorf ( "error in getting pod list for namespace %s and list options %+v: %s" , namespace , options , err )
2016-02-11 01:49:11 +00:00
}
2016-03-02 20:52:12 +00:00
allPodsLabeled := false
if allPodsLabeled , err = labelPodsWithHash ( podList , updatedRS , c , namespace , hash ) ; err != nil {
2016-03-01 02:28:32 +00:00
return nil , fmt . Errorf ( "error in adding template hash label %s to pods %+v: %s" , hash , podList , err )
2016-02-11 01:49:11 +00:00
}
2016-03-02 20:52:12 +00:00
// If not all pods are labeled but didn't return error in step 2, we've hit at least one pod not found error.
// Return here and retry in the next sync loop.
if ! allPodsLabeled {
return updatedRS , nil
}
2016-02-11 01:49:11 +00:00
2016-02-19 18:25:34 +00:00
// 3. Update rs label and selector to include the new hash label
2016-02-11 01:49:11 +00:00
// Copy the old selector, so that we can scrub out any orphaned pods
2016-03-02 20:52:12 +00:00
if updatedRS , rsUpdated , err = rsutil . UpdateRSWithRetries ( c . Extensions ( ) . ReplicaSets ( namespace ) , updatedRS , func ( updated * extensions . ReplicaSet ) {
2016-02-11 18:57:42 +00:00
updated . Labels = labelsutil . AddLabel ( updated . Labels , extensions . DefaultDeploymentUniqueLabelKey , hash )
2016-02-11 01:49:11 +00:00
updated . Spec . Selector = labelsutil . AddLabelToSelector ( updated . Spec . Selector , extensions . DefaultDeploymentUniqueLabelKey , hash )
2016-02-18 19:45:24 +00:00
} ) ; err != nil {
2016-03-02 20:52:12 +00:00
return nil , fmt . Errorf ( "error updating %s %s/%s label and selector with template hash: %v" , updatedRS . Kind , updatedRS . Namespace , updatedRS . Name , err )
}
if rsUpdated {
glog . V ( 4 ) . Infof ( "Updated %s %s/%s's selector and label with hash %s." , rs . Kind , rs . Namespace , rs . Name , hash )
2016-02-11 01:49:11 +00:00
}
2016-03-02 20:52:12 +00:00
// If the RS isn't actually updated in step 3, that's okay, we'll retry in the next sync loop since its selector isn't updated yet.
2016-02-11 01:49:11 +00:00
2016-02-19 18:25:34 +00:00
// TODO: look for orphaned pods and label them in the background somewhere else periodically
2016-02-18 19:45:24 +00:00
return updatedRS , nil
}
2016-02-19 18:25:34 +00:00
func waitForReplicaSetUpdated ( c clientset . Interface , desiredGeneration int64 , namespace , name string ) error {
return wait . Poll ( 10 * time . Millisecond , 1 * time . Minute , func ( ) ( bool , error ) {
rs , err := c . Extensions ( ) . ReplicaSets ( namespace ) . Get ( name )
if err != nil {
return false , err
}
return rs . Status . ObservedGeneration >= desiredGeneration , nil
} )
}
2016-02-18 19:45:24 +00:00
// labelPodsWithHash labels all pods in the given podList with the new hash label.
2016-03-02 20:52:12 +00:00
// The returned bool value can be used to tell if all pods are actually labeled.
func labelPodsWithHash ( podList * api . PodList , rs * extensions . ReplicaSet , c clientset . Interface , namespace , hash string ) ( bool , error ) {
allPodsLabeled := true
2016-02-11 01:49:11 +00:00
for _ , pod := range podList . Items {
2016-02-18 19:45:24 +00:00
// Only label the pod that doesn't already have the new hash
if pod . Labels [ extensions . DefaultDeploymentUniqueLabelKey ] != hash {
2016-03-02 20:52:12 +00:00
if _ , podUpdated , err := podutil . UpdatePodWithRetries ( c . Core ( ) . Pods ( namespace ) , & pod , func ( podToUpdate * api . Pod ) {
2016-03-01 19:47:53 +00:00
podToUpdate . Labels = labelsutil . AddLabel ( podToUpdate . Labels , extensions . DefaultDeploymentUniqueLabelKey , hash )
2016-02-18 19:45:24 +00:00
} ) ; err != nil {
2016-03-02 20:52:12 +00:00
return false , fmt . Errorf ( "error in adding template hash label %s to pod %+v: %s" , hash , pod , err )
} else if podUpdated {
glog . V ( 4 ) . Infof ( "Labeled %s %s/%s of %s %s/%s with hash %s." , pod . Kind , pod . Namespace , pod . Name , rs . Kind , rs . Namespace , rs . Name , hash )
} else {
// If the pod wasn't updated but didn't return error when we try to update it, we've hit a pod not found error.
// Then we can't say all pods are labeled
allPodsLabeled = false
2016-02-11 01:49:11 +00:00
}
}
2016-03-01 02:28:32 +00:00
}
2016-03-02 20:52:12 +00:00
return allPodsLabeled , nil
2016-02-18 19:45:24 +00:00
}
2016-01-20 00:40:18 +00:00
// Returns the desired PodTemplateSpec for the new ReplicaSet corresponding to the given ReplicaSet.
2016-02-28 02:13:32 +00:00
func GetNewReplicaSetTemplate ( deployment * extensions . Deployment ) api . PodTemplateSpec {
2016-01-20 00:40:18 +00:00
// newRS will have the same template as in deployment spec, plus a unique label in some cases.
newRSTemplate := api . PodTemplateSpec {
2015-09-18 20:35:56 +00:00
ObjectMeta : deployment . Spec . Template . ObjectMeta ,
Spec : deployment . Spec . Template . Spec ,
}
2016-01-20 00:40:18 +00:00
newRSTemplate . ObjectMeta . Labels = labelsutil . CloneAndAddLabel (
2015-10-07 22:28:39 +00:00
deployment . Spec . Template . ObjectMeta . Labels ,
2016-02-05 22:45:05 +00:00
extensions . DefaultDeploymentUniqueLabelKey ,
2016-01-20 00:40:18 +00:00
podutil . GetPodTemplateSpecHash ( newRSTemplate ) )
return newRSTemplate
2015-09-18 20:35:56 +00:00
}
2016-01-20 00:40:18 +00:00
// SetFromReplicaSetTemplate sets the desired PodTemplateSpec from a replica set template to the given deployment.
func SetFromReplicaSetTemplate ( deployment * extensions . Deployment , template api . PodTemplateSpec ) * extensions . Deployment {
2016-01-15 02:04:05 +00:00
deployment . Spec . Template . ObjectMeta = template . ObjectMeta
deployment . Spec . Template . Spec = template . Spec
deployment . Spec . Template . ObjectMeta . Labels = labelsutil . CloneAndRemoveLabel (
deployment . Spec . Template . ObjectMeta . Labels ,
2016-02-05 22:45:05 +00:00
extensions . DefaultDeploymentUniqueLabelKey )
2016-01-15 02:04:05 +00:00
return deployment
}
2016-01-20 00:40:18 +00:00
// Returns the sum of Replicas of the given replica sets.
2016-02-06 02:43:02 +00:00
func GetReplicaCountForReplicaSets ( replicaSets [ ] * extensions . ReplicaSet ) int {
2015-09-29 23:55:06 +00:00
totalReplicaCount := 0
2016-01-20 00:40:18 +00:00
for _ , rs := range replicaSets {
2016-02-25 00:09:20 +00:00
if rs != nil {
totalReplicaCount += rs . Spec . Replicas
}
2015-09-29 23:55:06 +00:00
}
return totalReplicaCount
}
2016-02-22 22:28:28 +00:00
// GetActualReplicaCountForReplicaSets returns the sum of actual replicas of the given replica sets.
func GetActualReplicaCountForReplicaSets ( replicaSets [ ] * extensions . ReplicaSet ) int {
totalReplicaCount := 0
for _ , rs := range replicaSets {
2016-02-25 00:09:20 +00:00
if rs != nil {
totalReplicaCount += rs . Status . Replicas
}
2016-02-22 22:28:28 +00:00
}
return totalReplicaCount
}
2016-01-20 00:40:18 +00:00
// Returns the number of available pods corresponding to the given replica sets.
func GetAvailablePodsForReplicaSets ( c clientset . Interface , rss [ ] * extensions . ReplicaSet , minReadySeconds int ) ( int , error ) {
2016-02-19 23:30:40 +00:00
allPods , err := GetPodsForReplicaSets ( c , rss )
2015-09-29 23:55:06 +00:00
if err != nil {
return 0 , err
}
2015-11-11 23:22:57 +00:00
return getReadyPodsCount ( allPods , minReadySeconds ) , nil
}
func getReadyPodsCount ( pods [ ] api . Pod , minReadySeconds int ) int {
2015-09-29 23:55:06 +00:00
readyPodCount := 0
2015-11-11 23:22:57 +00:00
for _ , pod := range pods {
2016-02-19 23:30:40 +00:00
if IsPodAvailable ( & pod , minReadySeconds ) {
readyPodCount ++
2015-09-29 23:55:06 +00:00
}
}
2015-11-11 23:22:57 +00:00
return readyPodCount
2015-09-29 23:55:06 +00:00
}
2016-02-19 23:30:40 +00:00
func IsPodAvailable ( pod * api . Pod , minReadySeconds int ) bool {
// Check if we've passed minReadySeconds since LastTransitionTime
// If so, this pod is ready
for _ , c := range pod . Status . Conditions {
// we only care about pod ready conditions
2016-02-19 23:53:34 +00:00
if c . Type == api . PodReady && c . Status == api . ConditionTrue {
2016-02-19 23:30:40 +00:00
// 2 cases that this ready condition is valid (passed minReadySeconds, i.e. the pod is ready):
// 1. minReadySeconds <= 0
// 2. LastTransitionTime (is set) + minReadySeconds (>0) < current time
minReadySecondsDuration := time . Duration ( minReadySeconds ) * time . Second
if minReadySeconds <= 0 || ! c . LastTransitionTime . IsZero ( ) && c . LastTransitionTime . Add ( minReadySecondsDuration ) . Before ( time . Now ( ) ) {
return true
}
}
}
return false
}
func GetPodsForReplicaSets ( c clientset . Interface , replicaSets [ ] * extensions . ReplicaSet ) ( [ ] api . Pod , error ) {
2016-02-24 18:59:51 +00:00
allPods := map [ string ] api . Pod { }
2016-01-20 00:40:18 +00:00
for _ , rs := range replicaSets {
2016-02-25 00:09:20 +00:00
if rs != nil {
selector , err := unversioned . LabelSelectorAsSelector ( rs . Spec . Selector )
if err != nil {
return nil , fmt . Errorf ( "invalid label selector: %v" , err )
}
options := api . ListOptions { LabelSelector : selector }
podList , err := c . Core ( ) . Pods ( rs . ObjectMeta . Namespace ) . List ( options )
if err != nil {
return nil , fmt . Errorf ( "error listing pods: %v" , err )
}
for _ , pod := range podList . Items {
allPods [ pod . Name ] = pod
}
2016-02-24 18:59:51 +00:00
}
}
requiredPods := [ ] api . Pod { }
for _ , pod := range allPods {
requiredPods = append ( requiredPods , pod )
2015-09-29 23:55:06 +00:00
}
2016-02-24 18:59:51 +00:00
return requiredPods , nil
2015-09-29 23:55:06 +00:00
}
2016-01-20 23:48:52 +00:00
2016-01-20 00:40:18 +00:00
// Revision returns the revision number of the input replica set
func Revision ( rs * extensions . ReplicaSet ) ( int64 , error ) {
v , ok := rs . Annotations [ RevisionAnnotation ]
2016-01-20 23:48:52 +00:00
if ! ok {
return 0 , nil
}
return strconv . ParseInt ( v , 10 , 64 )
}
2016-02-05 02:05:38 +00:00
func IsRollingUpdate ( deployment * extensions . Deployment ) bool {
return deployment . Spec . Strategy . Type == extensions . RollingUpdateDeploymentStrategyType
}
// NewRSNewReplicas calculates the number of replicas a deployment's new RS should have.
// When one of the followings is true, we're rolling out the deployment; otherwise, we're scaling it.
// 1) The new RS is saturated: newRS's replicas == deployment's replicas
// 2) Max number of pods allowed is reached: deployment's replicas + maxSurge == all RSs' replicas
func NewRSNewReplicas ( deployment * extensions . Deployment , allRSs [ ] * extensions . ReplicaSet , newRS * extensions . ReplicaSet ) ( int , error ) {
switch deployment . Spec . Strategy . Type {
case extensions . RollingUpdateDeploymentStrategyType :
// Check if we can scale up.
2016-02-25 05:03:47 +00:00
maxSurge , err := intstrutil . GetValueFromIntOrPercent ( & deployment . Spec . Strategy . RollingUpdate . MaxSurge , deployment . Spec . Replicas , true )
2016-02-05 02:05:38 +00:00
if err != nil {
return 0 , err
}
// Find the total number of pods
currentPodCount := GetReplicaCountForReplicaSets ( allRSs )
maxTotalPods := deployment . Spec . Replicas + maxSurge
if currentPodCount >= maxTotalPods {
// Cannot scale up.
return newRS . Spec . Replicas , nil
}
// Scale up.
scaleUpCount := maxTotalPods - currentPodCount
// Do not exceed the number of desired replicas.
scaleUpCount = integer . IntMin ( scaleUpCount , deployment . Spec . Replicas - newRS . Spec . Replicas )
return newRS . Spec . Replicas + scaleUpCount , nil
case extensions . RecreateDeploymentStrategyType :
return deployment . Spec . Replicas , nil
default :
return 0 , fmt . Errorf ( "deployment type %v isn't supported" , deployment . Spec . Strategy . Type )
}
}
2016-02-24 04:27:24 +00:00
// Polls for deployment to be updated so that deployment.Status.ObservedGeneration >= desiredGeneration.
// Returns error if polling timesout.
func WaitForObservedDeployment ( getDeploymentFunc func ( ) ( * extensions . Deployment , error ) , desiredGeneration int64 , interval , timeout time . Duration ) error {
// TODO: This should take clientset.Interface when all code is updated to use clientset. Keeping it this way allows the function to be used by callers who have client.Interface.
return wait . Poll ( interval , timeout , func ( ) ( bool , error ) {
deployment , err := getDeploymentFunc ( )
if err != nil {
return false , err
}
return deployment . Status . ObservedGeneration >= desiredGeneration , nil
} )
}