11package org .csanchez .jenkins .plugins .kubernetes ;
22
3- import edu .umd .cs .findbugs .annotations .SuppressFBWarnings ;
3+ import java .util .HashMap ;
4+ import java .util .Map ;
5+ import java .util .logging .Level ;
6+ import java .util .logging .Logger ;
7+
8+ import javax .annotation .Nonnull ;
9+
10+ import org .kohsuke .accmod .Restricted ;
11+ import org .kohsuke .accmod .restrictions .NoExternalUse ;
12+
413import hudson .Extension ;
514import hudson .ExtensionList ;
615import hudson .init .InitMilestone ;
1019import jenkins .metrics .api .Metrics ;
1120import jenkins .model .Jenkins ;
1221import jenkins .model .NodeListener ;
13- import org .kohsuke .accmod .Restricted ;
14- import org .kohsuke .accmod .restrictions .NoExternalUse ;
15-
16- import javax .annotation .Nonnull ;
17- import java .util .Collections ;
18- import java .util .HashMap ;
19- import java .util .Map ;
20- import java .util .concurrent .atomic .AtomicInteger ;
21- import java .util .logging .Level ;
22- import java .util .logging .Logger ;
2322
2423/**
2524 * Implements provisioning limits for clouds and pod templates
@@ -31,26 +30,28 @@ public final class KubernetesProvisioningLimits {
3130 /**
3231 * Tracks current number of kubernetes agents per pod template
3332 */
34- private final Map <String , AtomicInteger > podTemplateCounts = Collections . synchronizedMap ( new HashMap <>() );
33+ private final Map <String , Integer > podTemplateCounts = new HashMap <>();
3534
3635 /**
3736 * Tracks current number of kubernetes agents per kubernetes cloud
3837 */
39- private final Map <String , AtomicInteger > cloudCounts = Collections . synchronizedMap ( new HashMap <>() );
38+ private final Map <String , Integer > cloudCounts = new HashMap <>();
4039
4140 @ Initializer (after = InitMilestone .SYSTEM_CONFIG_LOADED )
4241 public static void init () {
4342 // We don't want anything to be provisioned while we do the initial count.
4443 Queue .withLock (() -> {
4544 final KubernetesProvisioningLimits instance = get ();
46- Jenkins .get ().getNodes ()
47- .stream ()
48- .filter (KubernetesSlave .class ::isInstance )
49- .map (KubernetesSlave .class ::cast )
50- .forEach (node -> {
51- instance .getGlobalCount (node .getCloudName ()).addAndGet (node .getNumExecutors ());
52- instance .getPodTemplateCount (node .getTemplateId ()).addAndGet (node .getNumExecutors ());
53- });
45+ synchronized (instance ) {
46+ Jenkins .get ().getNodes ()
47+ .stream ()
48+ .filter (KubernetesSlave .class ::isInstance )
49+ .map (KubernetesSlave .class ::cast )
50+ .forEach (node -> {
51+ instance .cloudCounts .put (node .getCloudName (), instance .getGlobalCount (node .getCloudName ()) + node .getNumExecutors ());
52+ instance .podTemplateCounts .put (node .getTemplateId (), instance .getPodTemplateCount (node .getTemplateId ()) + node .getNumExecutors ());
53+ });
54+ }
5455 });
5556 }
5657
@@ -67,26 +68,24 @@ public static KubernetesProvisioningLimits get() {
6768 * @param podTemplate the pod template used to schedule the agent
6869 * @param numExecutors the number of executors (pretty much always 1)
6970 */
70- @ SuppressFBWarnings (value ="JLM_JSR166_UTILCONCURRENT_MONITORENTER" , justification = "Trust me here" )
71- public boolean register (@ Nonnull KubernetesCloud cloud , @ Nonnull PodTemplate podTemplate , int numExecutors ) {
72- AtomicInteger globalCount = getGlobalCount (cloud .name );
73- AtomicInteger podTemplateCount = getPodTemplateCount (podTemplate .getId ());
74- synchronized (globalCount ) {
75- synchronized (podTemplateCount ) {
76- if (globalCount .get () + numExecutors <= cloud .getContainerCap ()) {
77- if (podTemplateCount .get () + numExecutors <= podTemplate .getInstanceCap ()) {
78- int g = globalCount .addAndGet (numExecutors );
79- int p = podTemplateCount .addAndGet (numExecutors );
80- LOGGER .log (Level .FINEST , () -> cloud .name + " global limit: " + g + "/" + cloud .getContainerCap ());
81- LOGGER .log (Level .FINEST , () -> podTemplate .getName () + " template limit: " + p + "/" + podTemplate .getInstanceCap ());
82- return true ;
83- } else {
84- Metrics .metricRegistry ().counter (MetricNames .REACHED_POD_CAP ).inc ();
85- }
86- } else {
87- Metrics .metricRegistry ().counter (MetricNames .REACHED_GLOBAL_CAP ).inc ();
88- }
71+ public synchronized boolean register (@ Nonnull KubernetesCloud cloud , @ Nonnull PodTemplate podTemplate , int numExecutors ) {
72+ int newGlobalCount = getGlobalCount (cloud .name ) + numExecutors ;
73+ if (newGlobalCount <= cloud .getContainerCap ()) {
74+ int newPodTemplateCount = getPodTemplateCount (podTemplate .getId ()) + numExecutors ;
75+ if (newPodTemplateCount <= podTemplate .getInstanceCap ()) {
76+ cloudCounts .put (cloud .name , newGlobalCount );
77+ LOGGER .log (Level .FINEST , () -> cloud .name + " global limit: " + newGlobalCount + "/" + cloud .getContainerCap ());
78+
79+ podTemplateCounts .put (podTemplate .getId (), newPodTemplateCount );
80+ LOGGER .log (Level .FINEST , () -> podTemplate .getName () + " template limit: " + newPodTemplateCount + "/" + podTemplate .getInstanceCap ());
81+ return true ;
82+ } else {
83+ LOGGER .log (Level .FINEST , () -> podTemplate .getName () + " template limit reached: " + getPodTemplateCount (podTemplate .getId ()) + "/" + podTemplate .getInstanceCap () + ". Cannot add " + numExecutors + " more!" );
84+ Metrics .metricRegistry ().counter (MetricNames .REACHED_POD_CAP ).inc ();
8985 }
86+ } else {
87+ LOGGER .log (Level .FINEST , () -> cloud .name + " global limit reached: " + getGlobalCount (cloud .name ) + "/" + cloud .getContainerCap () + ". Cannot add " + numExecutors + " more!" );
88+ Metrics .metricRegistry ().counter (MetricNames .REACHED_GLOBAL_CAP ).inc ();
9089 }
9190 return false ;
9291 }
@@ -97,40 +96,32 @@ public boolean register(@Nonnull KubernetesCloud cloud, @Nonnull PodTemplate pod
9796 * @param podTemplate the pod template used to schedule the agent
9897 * @param numExecutors the number of executors (pretty much always 1)
9998 */
100- @ SuppressFBWarnings (value ="JLM_JSR166_UTILCONCURRENT_MONITORENTER" , justification = "Trust me here" )
101- public void unregister (@ Nonnull KubernetesCloud cloud , @ Nonnull PodTemplate podTemplate , int numExecutors ) {
102- AtomicInteger globalCount = getGlobalCount (cloud .name );
103- AtomicInteger podTemplateCount = getPodTemplateCount (podTemplate .getId ());
104- synchronized (globalCount ) {
105- synchronized (podTemplateCount ) {
106- int newGlobalCount = globalCount .addAndGet (numExecutors * -1 );
107- int newPodTemplateCount = podTemplateCount .addAndGet (numExecutors * -1 );
108- if (newGlobalCount < 0 ) {
109- LOGGER .log (Level .WARNING , "Global count for " + cloud .name + " went below zero. There is likely a bug in kubernetes-plugin" );
110- globalCount .set (0 );
111- } else {
112- LOGGER .log (Level .FINEST , () -> cloud .name + " global limit: " + newGlobalCount + "/" + cloud .getContainerCap ());
113- }
114- if (newPodTemplateCount < 0 ) {
115- LOGGER .log (Level .WARNING , "Pod template count for " + podTemplate .getId () + " went below zero. There is likely a bug in kubernetes-plugin" );
116- podTemplateCount .set (0 );
117- } else {
118- LOGGER .log (Level .FINEST , () -> podTemplate .getName () + " template limit: " + newPodTemplateCount + "/" + podTemplate .getInstanceCap ());
119- }
120- }
99+ public synchronized void unregister (@ Nonnull KubernetesCloud cloud , @ Nonnull PodTemplate podTemplate , int numExecutors ) {
100+ int newGlobalCount = getGlobalCount (cloud .name ) - numExecutors ;
101+ if (newGlobalCount < 0 ) {
102+ LOGGER .log (Level .WARNING , "Global count for " + cloud .name + " went below zero. There is likely a bug in kubernetes-plugin" );
103+ }
104+ cloudCounts .put (cloud .name , Math .max (0 , newGlobalCount ));
105+ LOGGER .log (Level .FINEST , () -> cloud .name + " global limit: " + Math .max (0 , newGlobalCount ) + "/" + cloud .getContainerCap ());
106+
107+ int newPodTemplateCount = getPodTemplateCount (podTemplate .getId ()) - numExecutors ;
108+ if (newPodTemplateCount < 0 ) {
109+ LOGGER .log (Level .WARNING , "Pod template count for " + podTemplate .getName () + " went below zero. There is likely a bug in kubernetes-plugin" );
121110 }
111+ podTemplateCounts .put (podTemplate .getId (), Math .max (0 , newPodTemplateCount ));
112+ LOGGER .log (Level .FINEST , () -> podTemplate .getName () + " template limit: " + Math .max (0 , newPodTemplateCount ) + "/" + podTemplate .getInstanceCap ());
122113 }
123114
124115 @ Nonnull
125116 @ Restricted (NoExternalUse .class )
126- AtomicInteger getGlobalCount (String name ) {
127- return cloudCounts .computeIfAbsent ( name , k -> new AtomicInteger () );
117+ int getGlobalCount (String cloudName ) {
118+ return cloudCounts .getOrDefault ( cloudName , 0 );
128119 }
129120
130121 @ Nonnull
131122 @ Restricted (NoExternalUse .class )
132- AtomicInteger getPodTemplateCount (String id ) {
133- return podTemplateCounts .computeIfAbsent ( id , k -> new AtomicInteger () );
123+ int getPodTemplateCount (String podTemplate ) {
124+ return podTemplateCounts .getOrDefault ( podTemplate , 0 );
134125 }
135126
136127 @ Extension
0 commit comments