<s> import logging <EOL> import math <EOL> import time <EOL> from . objects import Pod <EOL> from . exceptions import KubernetesError <EOL> logger = logging . getLogger ( __name__ ) <EOL> class RollingUpdater ( object ) : <EOL> def __init__ ( self , api , old_rc , new_rc , ** kwargs ) : <EOL> self . api = api <EOL> self . old_rc = old_rc <EOL> self . new_rc = new_rc <EOL> self . update_period = kwargs . get ( "<STR_LIT>" , <NUM_LIT:10> ) <EOL> self . max_unavailable = kwargs . get ( "<STR_LIT>" , <NUM_LIT:0> ) <EOL> self . max_surge = kwargs . get ( "<STR_LIT>" , <NUM_LIT:1> ) <EOL> def update ( self ) : <EOL> desired = self . new_rc . replicas <EOL> original = self . old_rc . replicas <EOL> max_unavailable = extract_max_value ( self . max_unavailable , "<STR_LIT>" , desired ) <EOL> max_surge = extract_max_value ( self . max_surge , "<STR_LIT>" , desired ) <EOL> min_available = original - max_unavailable <EOL> if self . new_rc . exists ( ) : <EOL> logger . info ( "<STR_LIT>" . format ( self . new_rc . name ) ) <EOL> return False <EOL> new_selector = self . new_rc . obj [ "<STR_LIT>" ] [ "<STR_LIT>" ] <EOL> old_selector = self . old_rc . obj [ "<STR_LIT>" ] [ "<STR_LIT>" ] <EOL> if new_selector == old_selector : <EOL> raise KubernetesError ( <EOL> "<STR_LIT>" . format ( <EOL> self . new_rc . name , <EOL> self . old_rc . name <EOL> ) ) <EOL> new_labels = self . new_rc . obj [ "<STR_LIT>" ] [ "<STR_LIT>" ] [ "<STR_LIT>" ] [ "<STR_LIT>" ] <EOL> if new_selector != new_labels : <EOL> raise KubernetesError ( <EOL> "<STR_LIT>" . format ( <EOL> self . new_rc . name , <EOL> new_selector , <EOL> new_labels ) ) <EOL> self . create_rc ( self . new_rc ) <EOL> logger . info ( "<STR_LIT>" . format ( self . new_rc . name ) ) <EOL> new_rc , old_rc = self . new_rc , self . old_rc <EOL> logger . info ( <EOL> "<STR_LIT>" . format ( <EOL> new_rc . name , <EOL> new_rc . replicas , <EOL> desired , <EOL> old_rc . name , <EOL> old_rc . replicas , <EOL> min_available , <EOL> original + max_surge <EOL> ) , <EOL> ) <EOL> while new_rc . replicas != desired or old_rc . replicas != <NUM_LIT:0> : <EOL> scaled_rc = self . scale_up ( <EOL> new_rc , old_rc , <EOL> original , desired , <EOL> max_surge , max_unavailable , <EOL> ) <EOL> new_rc = scaled_rc <EOL> time . sleep ( self . update_period ) <EOL> scaled_rc = self . scale_down ( <EOL> new_rc , old_rc , <EOL> desired , <EOL> min_available , max_surge , <EOL> ) <EOL> old_rc = scaled_rc <EOL> logger . info ( "<STR_LIT>" . format ( old_rc . name ) ) <EOL> self . cleanup ( old_rc , new_rc ) <EOL> def scale_up ( self , new_rc , old_rc , original , desired , max_surge , max_unavailable ) : <EOL> if new_rc . replicas == desired : <EOL> return new_rc <EOL> increment = ( original + max_surge ) - ( old_rc . replicas + new_rc . replicas ) <EOL> if old_rc . replicas == <NUM_LIT:0> : <EOL> increment = desired - new_rc . replicas <EOL> if increment <= <NUM_LIT:0> : <EOL> return new_rc <EOL> new_rc . replicas = min ( desired , new_rc . replicas + increment ) <EOL> logger . info ( "<STR_LIT>" . format ( new_rc . name , new_rc . replicas ) ) <EOL> new_rc . scale ( ) <EOL> return new_rc <EOL> def scale_down ( self , new_rc , old_rc , desired , min_available , max_surge ) : <EOL> if old_rc . replicas == <NUM_LIT:0> : <EOL> return old_rc <EOL> _ , new_available = self . poll_for_ready_pods ( old_rc , new_rc ) <EOL> decrement = old_rc . replicas + new_available - min_available <EOL> if decrement <= <NUM_LIT:0> : <EOL> return old_rc <EOL> old_rc . replicas = max ( <NUM_LIT:0> , old_rc . replicas - decrement ) <EOL> if new_rc . replicas == desired and new_available == desired : <EOL> old_rc . replicas = <NUM_LIT:0> <EOL> logger . info ( "<STR_LIT>" . format ( old_rc . name , old_rc . replicas ) ) <EOL> old_rc . scale ( ) <EOL> return old_rc <EOL> def cleanup ( self , old_rc , new_rc ) : <EOL> old_rc . delete ( ) <EOL> def poll_for_ready_pods ( self , old_rc , new_rc ) : <EOL> controllers = [ old_rc , new_rc ] <EOL> old_ready = <NUM_LIT:0> <EOL> new_ready = <NUM_LIT:0> <EOL> any_ready = False <EOL> while True : <EOL> for controller in controllers : <EOL> pods = Pod . objects ( self . api ) . filter ( <EOL> namespace = controller . namespace , <EOL> selector = controller . obj [ "<STR_LIT>" ] [ "<STR_LIT>" ] , <EOL> ) <EOL> for pod in pods : <EOL> if pod . ready : <EOL> if controller . name == old_rc . name : <EOL> old_ready += <NUM_LIT:1> <EOL> elif controller . name == new_rc . name : <EOL> new_ready += <NUM_LIT:1> <EOL> any_ready = True <EOL> if any_ready : <EOL> break <EOL> time . sleep ( <NUM_LIT:1> ) <EOL> return old_ready , new_ready <EOL> def create_rc ( self , rc ) : <EOL> rc . replicas = <NUM_LIT:0> <EOL> rc . create ( ) <EOL> def extract_max_value ( field , name , value ) : <EOL> assert type ( field ) in { int , str } , "<STR_LIT>" . format ( type ( field ) ) <EOL> if isinstance ( field , int ) : <EOL> assert field >= <NUM_LIT:0> , "<STR_LIT>" . format ( name ) <EOL> return field <EOL> if isinstance ( field , str ) : <EOL> v = int ( field . replace ( "<STR_LIT:%>" , "<STR_LIT>" ) ) <EOL> assert v >= <NUM_LIT:0> , "<STR_LIT>" . format ( name ) <EOL> return math . ceil ( float ( value ) * ( float ( v ) / <NUM_LIT> ) ) </s>
