Jekyll2023-05-27T01:28:31+00:00https://ftclausen.github.io/feed.xmlFriedrich’s “Fred” NotesJust some random notes of random things I am working on/studying. A stream of consciousness if you will.Friedrich "Fred" Clausenftclausen@gmail.comJosephus - generalised binary recurrence unfolding2023-03-28T16:00:00+00:002023-03-28T16:00:00+00:00https://ftclausen.github.io/mathematics/josephus---generalised-binary-recurrence-unfolding<p>I have some confusion around the meaning of $b_m$ and $b_j$. I <em>think</em> it is as follows:</p>
<ul>
<li>$\beta_j$ - In the rewritten 1.15 where $\beta_0 = \beta = -1$ and $\beta_1 = \gamma = 1$</li>
<li>$\beta_m$ - In the binary representation or expansion. I.e. $(\beta_m \beta_{m-1} … \beta_1 \beta_0)_2$. As in
$\beta_m$ is either a $0$ or a $1$.</li>
</ul>
<p>Note on book context: Page 15, from just before equation 1.15.</p>
<h1 id="random-ramblings-on-above">Random Ramblings on Above</h1>
<p>About re-using the cyclical bit-shifting “magical solution” to the Josephus problem in a more generalised
context. Firstly, the magical solution they are talking about:</p>
\[J((b_mb_{m-1}...b_1b_0)_2) = (b_{m-1}...b_1b_0b_m)_2\]
<p>They then ask</p>
<blockquote>
<p>Does the generalised Josephus recurrence admit of such magic? Sure, why not?</p>
</blockquote>
<p>They then go on to, very cleverly, rewrite generalised equation in a new form that allows us to look at it in a
different way. Firstly, the original generalised equation 1.11:</p>
\[\begin{align}
f(1) &= \alpha \\
f(2n) &= 2f(n) + \beta\text{,} \ \ \ \ \ \ \text{for } n \geq 1\text{;} \\
f(2n+1) &= 2f(n) + \gamma\text{,} \ \ \ \ \ \ \text{for } n \geq 1\text{.} \
\end{align}\]
<p>Then the rewritten one as 1.15</p>
\[\begin{align}
f(1) &= \alpha; \\
f(2n + j) &= 2f(n) + \beta, \ \ \ \ \ \ \text{for}\ j=0,1\ \text{and}\ n\geq 1 \\
\end{align}\]
<p>Now they say</p>
<blockquote>
<p>If we let $\beta_0 = \beta$ and $\beta_1 = \gamma$</p>
</blockquote>
<p>Which <strong>I think</strong> means: if we encounter a binary $0$ ($\beta_0)$ in the number then we substitute the $\beta = -1$
<em>from the generalised form 1.11</em> equation. Otherwise, if we encounter a binary $1$ ($\beta_1$) then we substitute the
$\gamma = 1$ also from the generalised equation 1.11.</p>
<p>It is unfortunate that they overload $\beta$ in the generalised equation 1.11, rewritten generalised equation 1.15,
and in the expansion notation $\beta_m$ where $m$ is the position/power of two in the binary number. I think that is the
root of my confusion.</p>
<p>The table presented on page 16 (of the physical book, second edition) is very enlightening in demonstrating the
rewritten (1.15) equation is used:</p>
\[\begin{array}
n &= &(&1 &1 &0 &0 &1 &0 &0 &)_2 &= 100
\\
\hline
f(n) &= &(&1 &1 &-1 &-1 &1 &-1 &-1 &)_2
\\
\end{array}\]
<p>Where:</p>
<ul>
<li>$\beta = -1$ is applied to $\beta_0$ i.e. binary $0$.</li>
<li>$\gamma = 1$ is applied to $\beta_1$ i.e. binary $1$.</li>
</ul>
<p>Applying above wherever we find a binary 1 convert to decimal with the appropriate power of two $m$ as in $2^m$ then
multiply by $1$. Likewise, wherever we find a binary 0, convert to decimal with appropriate power of two $m$ as in $2^m$
then multiply by $-1$. Doing the above leads us to:</p>
\[+64 +32 -16 -8 +4 -2 -1 = 73\]
<p>So J(100) = 73</p>Friedrich "Fred" Clausenftclausen@gmail.comI have some confusion around the meaning of $b_m$ and $b_j$. I think it is as follows:Azure Dev Ops Boards - How to get and update a Work Item using typed-rest-client2023-03-18T20:24:00+00:002023-03-18T20:24:00+00:00https://ftclausen.github.io/jenkins/cicd/azure-dev-ops-boards---how-to-get-and-update-a-work-item-using-typed-rest-client<p>This post outlines what worked for me to update an Azure Dev Ops (ADO) Boards Work Item. I initially tried the following
prior to settling on <a href="https://github.com/microsoft/typed-rest-client">typed-rest-client</a>:</p>
<ul>
<li><a href="https://github.com/microsoft/azure-devops-node-api">azure-devops-node-api</a> - This looked promising but ultimately I
could not get it to work <a href="https://stackoverflow.com/questions/75763159/using-azure-devops-node-api-to-update-patch-a-ado-work-item">for updating work
items</a>.
Judging by the silence in my Stackoverflow question it seems updating Work Items via azure-devops-node-api is not
something anyone has tried?</li>
<li><a href="https://github.com/microsoft/azure-devops-extension-api">azure-devops-extension-api</a> - This one seemed promising but
has no README nor examples. It was too deep a rabbit hole to go down right now.</li>
</ul>
<p>Thus I settled on typed-rest-client which seemed relatively straight forward to use.</p>
<p>I’ll show some snippets of how I fetched a Work Item and then how to update a custom field (or any field really). For my
use case I just created a toy type to store just what I was interested in rather than try and pull down an official type
from the monster <code class="language-plaintext highlighter-rouge">azure-devops*</code> repos above (🐇). If I find a more “official” way of doing it then I’ll update this
post.</p>
<h1 id="step-0---minimal-types-for-representing-work-item">Step 0 - Minimal Types for Representing Work Item</h1>
<p>Given the labynth of types azure-devops-node-api and azure-devops-extension-api have I decided to just make my own as
follows:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kr">interface</span> <span class="nx">ADOWorkItemLite</span> <span class="p">{</span>
<span class="k">readonly</span> <span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span>
<span class="k">readonly</span> <span class="nx">fields</span><span class="p">:</span> <span class="nx">ADOWorkItemFields</span>
<span class="p">}</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ADOWorkItemFields</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">Custom.SomeField</span><span class="dl">"</span><span class="p">:</span> <span class="kr">string</span> <span class="c1">// Can also be another, non-custom field</span>
<span class="p">}</span>
</code></pre></div></div>
<h1 id="fetching-a-work-item">Fetching a Work Item</h1>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Note: I can probably get the PAT handler from type-rest-client as well</span>
<span class="k">import</span> <span class="o">*</span> <span class="k">as</span> <span class="nx">ado</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">azure-devops-node-api</span><span class="dl">'</span>
<span class="k">import</span> <span class="o">*</span> <span class="k">as</span> <span class="nx">restClient</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">typed-rest-client</span><span class="dl">'</span>
<span class="kd">const</span> <span class="nx">authHandler</span> <span class="o">=</span> <span class="nx">ado</span><span class="p">.</span><span class="nx">getPersonalAccessTokenHandler</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">PAT</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">client</span><span class="p">:</span> <span class="nx">restClient</span><span class="p">.</span><span class="nx">RestClient</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">restClient</span>
<span class="p">.</span><span class="nx">RestClient</span><span class="p">(</span><span class="dl">'</span><span class="s1">example-user-agent</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">https://dev.azure.com</span><span class="dl">'</span><span class="p">,</span> <span class="p">[</span><span class="nx">authHandler</span><span class="p">])</span>
<span class="cm">/**
* Where:
* myorg - Your organisation name
* project - Your URL escaped project name
* 1234 - The ADO ticket being requested
* 7.0 - The ADO API version (7.0 as of March 2023)
*/</span>
<span class="kd">const</span> <span class="nx">location</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">/myorg/project/_apis/wit/workitems/1234?api-version=7.0</span><span class="dl">'</span>
<span class="k">return</span> <span class="nx">client</span><span class="p">.</span><span class="kd">get</span><span class="o"><</span><span class="nx">ADOWorkItemLite</span><span class="o">></span><span class="p">(</span><span class="nx">location</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">workItem</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">result</span> <span class="k">as</span> <span class="nx">ADOWorkItemLite</span>
<span class="k">return</span> <span class="nx">workItem</span>
<span class="p">})</span>
</code></pre></div></div>
<h1 id="updating-a-work-item-field">Updating a Work Item Field</h1>
<p>For this I introduced a new type, the HTTP PATCH update payload:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kr">interface</span> <span class="nx">ADOWorkItemUpdatePayload</span> <span class="p">{</span>
<span class="cm">/**
* What we're updating, usually "add"
*/</span>
<span class="nl">op</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
<span class="cm">/**
* The Work Item path e.g. /fields/SomeField
*/</span>
<span class="nx">path</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
<span class="cm">/**
* The value given to the item on the path
*/</span>
<span class="nx">value</span><span class="p">:</span> <span class="kr">string</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Then the initial parts are the same as getting a work item above but, after that, we apply the update:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="o">*</span> <span class="k">as</span> <span class="nx">ado</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">azure-devops-node-api</span><span class="dl">'</span>
<span class="k">import</span> <span class="o">*</span> <span class="k">as</span> <span class="nx">restClient</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">typed-rest-client</span><span class="dl">'</span>
<span class="kd">const</span> <span class="nx">authHandler</span> <span class="o">=</span> <span class="nx">ado</span><span class="p">.</span><span class="nx">getPersonalAccessTokenHandler</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">PAT</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">client</span><span class="p">:</span> <span class="nx">restClient</span><span class="p">.</span><span class="nx">RestClient</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">restClient</span>
<span class="p">.</span><span class="nx">RestClient</span><span class="p">(</span><span class="dl">'</span><span class="s1">example-user-agent</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">https://dev.azure.com</span><span class="dl">'</span><span class="p">,</span> <span class="p">[</span><span class="nx">authHandler</span><span class="p">])</span>
<span class="kd">const</span> <span class="nx">location</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">/myorg/project/_apis/wit/workitems/1234?api-version=7.0</span><span class="dl">'</span>
<span class="kd">const</span> <span class="nx">patchContents</span><span class="p">:</span> <span class="nx">ADOWorkItemUpdatePayload</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">op</span><span class="p">:</span> <span class="dl">"</span><span class="s2">add</span><span class="dl">"</span><span class="p">,</span>
<span class="na">path</span><span class="p">:</span> <span class="dl">"</span><span class="s2">/fields/Custom.SomeField</span><span class="dl">"</span><span class="p">,</span>
<span class="na">value</span><span class="p">:</span> <span class="nx">updatedWorkItem</span><span class="p">.</span><span class="nx">fields</span><span class="p">[</span><span class="dl">'</span><span class="s1">Custom.SomeField</span><span class="dl">'</span><span class="p">]</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">client</span><span class="p">.</span><span class="nx">update</span><span class="o"><</span><span class="nx">ADOWorkItemUpdatePayload</span><span class="o">></span><span class="p">(</span><span class="nx">location</span><span class="p">,</span> <span class="p">[</span><span class="nx">patchContents</span><span class="p">],</span> <span class="p">{</span>
<span class="na">additionalHeaders</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">Content-Type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json-patch+json</span><span class="dl">'</span>
<span class="p">}</span>
<span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="nx">results</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">results</span><span class="p">.</span><span class="nx">statusCode</span><span class="p">}</span><span class="s2"> - Updated work item </span><span class="p">${</span><span class="nx">updatedWorkItem</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="s2">`</span><span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>
<p>For me, this worked. As mentioned I should use the auth handler from type-rest-client to remove the dependency on
azure-devops-node-api.</p>Friedrich "Fred" Clausenftclausen@gmail.comThis post outlines what worked for me to update an Azure Dev Ops (ADO) Boards Work Item. I initially tried the following prior to settling on typed-rest-client:How to solve it - Reductio ad absurdum example2022-11-04T15:35:00+00:002022-11-04T15:35:00+00:00https://ftclausen.github.io/mathematics/how-to-solve-it---reductio-ad-absurdum-example<p>As before, this is from George Pólya’s <a href="https://amzn.to/3S8cHr3">“How to Solve
It”</a> book. I am reading this because I
want to sharpen my problem solving skills and, eventually, be able to make derivations like this in Concrete
Mathematics.</p>
<p>In this example they propose the problem of</p>
<blockquote>
<p>Write numbers using each of the ten digits exactly once so that the sum of the numbers is exactly 100</p>
</blockquote>
<p>Then the book goes through some attempts where we relax the conditions.</p>
<h2 id="relax-that-they-must-add-up-to-100">Relax that they must add up to 100</h2>
<p>Here are some examples from the book where they try to get close by relaxing the condition that they need to add up to
100:</p>
\[19 + 28 + 37 + 46 + 50 = 180\]
<p>or</p>
\[19 + 28 + 30 + 7 + 6 + 5 + 4 = 99\]
<p>close but no cigar.</p>
<h2 id="relax-that-the-numbers-should-be-unique">Relax that the numbers should be unique</h2>
\[19 + 28 + 31 + 7 + 6 + 5 + 4 = 100\]
<p>Here we get 100 but we are repeating some numbers. It would seem it is not possibles to satisfy both conditions
(uniqueness and adding up to 100) at once.</p>
<p>We need to <em>prove that it is impossible to solve both conditions at the same time</em>.</p>
<h2 id="setting-about-trying-to-satisfy-both-conditions">Setting about trying to satisfy both conditions</h2>
<p>Now we go about being optimistic and try to satisfy both conditions. The book does this by being very clever I thought;
simply pretend both conditions <em>are</em> true even though we suspect they are not.</p>
<p>Some ideas:</p>
<ul>
<li>There are ten figures, 0 to 9, and each one must appear at least once</li>
<li>The sum of just those figures is $0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9$ is $45$</li>
</ul>
<p>Now comes the bit I found confusing:</p>
<blockquote>
<p>Some of these figures denote units and some of them tens</p>
</blockquote>
<p>I thought they meant in the above some of single figures. They were all units but then I realised the author meant
<em>while adding to get 100</em> not in the sum to make $45$. Continuing:</p>
<blockquote>
<p>It takes a little sagacity to hit upon the idea that the <em>sum of the figures denoting tens</em> may be of some
importance.</p>
</blockquote>
<p>This also caused me some confusion (a common state for me to be in) but using the examples above:</p>
\[19 + 28 + 30 + 7 + 6 + 5 + 4 = 99\]
<p>Here the sum of the tens figures are</p>
\[10 + 20 + 30\]
<p>From the numbers 19, 28, 30. Another example from above:</p>
\[19 + 28 + 31 + 7 + 6 + 5 + 4 = 100\]
<p>Where the tens are, again,</p>
\[10 + 20 + 30\]
<p>We can see these give us the <em>most progress</em> to 100 (i.e. to 60). The others (part of the 45) get us the rest of the
way. Now $60 + 45 = 105$ which is obviously too much so we need to subtract the ones we’ve already used to get to 60.
This, then, finally brings us to:</p>
<blockquote>
<p>It takes a little sagacity to hit upon the idea that the <em>sum of the figures denoting tens</em> may be of some
importance.
<em>In fact, let $t$ stand for this sum</em>. Then the sum of the remaining figures, denoting units, is $45 - t$. Therefore
the numbers in the set must be</p>
</blockquote>
\[10t + (45 - t) = 100\]
<p>Breaking that down</p>
<ul>
<li>$10t$ - This is the 60 talked about above. Or, $6 * 10$ because as represented here.</li>
<li>$45 - t$ - This is the remaining units figured</li>
</ul>
<p>So, altogether, this should add to 100.</p>
<h2 id="solving-for-t---the-sum-of-tens">Solving for $t$ - the sum of tens</h2>
<p>Solving for $t$ we get</p>
\[t = \frac{55}{9}\]
<p>Here we get the absurdum beacause, of course, $t$ should be an integer otherwise it won’t add to 100. Thus we can’t
satisfy both conditions at once.</p>Friedrich "Fred" Clausenftclausen@gmail.comAs before, this is from George Pólya’s “How to Solve It” book. I am reading this because I want to sharpen my problem solving skills and, eventually, be able to make derivations like this in Concrete Mathematics.How to Solve It - Chains of Auxilliary Problems2022-08-22T10:22:00+00:002022-08-22T10:22:00+00:00https://ftclausen.github.io/mathematics/how-to-solve-it---part-3---section-7---chains-of-auxilliary-problems<p>This is from George Pólya’s <a href="https://amzn.to/3S8cHr3">“How to Solve
It”</a> book. I am reading this because I
want to sharpen my problem solving skills and, eventually, be able to make derivations like this in Concrete
Mathematics.</p>
<p>In How to Solve It they go through a series of equivalent problems to find the eventual solution. So we may not know the
solution to our original problem A but we can go through B, C, D, etc. until we find a solution we know or that can be
directly “seen”.</p>
<p>The book goes through solving this equation with the unknown $x$</p>
\[x^4 - 13x^2 + 36 = 0\]
<p>Since I am bootstrapping my mathematical knowledge I found the book’s treatment a bit terse without many explanations of
how they got to each step. I eventually figured out that they are trying to massage the equation into a state where you
can apply the perfect square rule. This makes things much simpler to reason about and thus find the solution.</p>
<h1 id="step-a---the-original-equation">Step A - The original equation.</h1>
<p>As mentioned above this is</p>
\[x^4 - 13x^2 + 36 = 0\]
<p>And our task is to find $x$. Or, more specifically, where the graph intercepts the $x$ axis at $0$. The steps are
essentially a set of quadratic equation equivalence conversions.</p>
<h1 id="step-b---multiple-by-powers-of-two">Step B - Multiple by powers of two</h1>
<p>My slow self needed to ponder this one for a while. In any case, B ends up being</p>
\[(2x^2)^2 - 2(2x^2)13 + 144 = 0\]
<p>This had me baffled for a while but I, eventually, saw that they are multiplying by increasing powers of two. I don’t
know if this technique has a name or how they arrived at doing this first step but, to write it another way,</p>
\[\begin{align}
2^0\cdot(2x^2)^2 - 2^1\cdot(2x^2)13 + 2^2\cdot36 &= 0 \\
(2x^2)^2 - 2(2x^2)13 + 144 &= 0
\end{align}\]
<h1 id="step-c---add-25-to-each-side">Step C - Add 25 to each side</h1>
<p>For those who know quadratics quite well can probably see where they are going with these steps but, anyway, I was still
baffled at this stage but at least it could see the manipulation easily enough:</p>
\[\begin{align}
(2x^2)^2 - 2(2x^2)13 + 144 + 25 &= 0 + 25 \\
(2x^2)^2 - 2(2x^2)13 + 169 &= 25
\end{align}\]
<h1 id="step-d---make-it-short">Step D - Make it short!</h1>
<p>This makes things much more compact through, as I eventually figured out, the perfect square rule! To remind the
forgetful like me it is: $(a - b)^2 = a^2 - 2ab + b^2$. There is also a <a href="https://www.khanacademy.org/math/algebra/x2f8bb11595b61c86:quadratics-multiplying-factoring/x2f8bb11595b61c86:factor-perfect-squares/a/factoring-quadratics-perfect-squares">$a + b$
version</a>.</p>
\[\begin{align}
(2x^2)^2 - 2(2x^2)13 + 169 &= 25 \\
(2x^2 - 13)^2 &= 25
\end{align}\]
<p>The more compact form gives us a better handle on things. This is a key step to solve this more easily. The following
steps are using this step to find the four solutions.</p>
<h1 id="step-e---partial-solution">Step E - Partial “solution”</h1>
<p>Here we take the square root and find a sort-of solution that still needs some work. Let’s call it the IKEA solution:</p>
\[\begin{align}
(2x^2 - 13)^2 &= 25 \\
2x^2 - 13 &= \pm5
\end{align}\]
<h1 id="step-f---re-arrange">Step F - Re-arrange</h1>
<p>Let’s isolate the $x^2$ like it has covid:</p>
\[\begin{align}
2x^2 - 13 &= \pm5 \\
x^2 &= \frac{13\pm5}{2}
\end{align}\]
<h1 id="step-g---almost-there">Step G - Almost there!</h1>
<p>Now we take the square root remembering our $\pm$:</p>
\[\begin{align}
x^2 &= \frac{13\pm5}{2} \\
x &= \pm\sqrt{\frac{13\pm5}{2}}
\end{align}\]
<h1 id="step-h---the-answers">Step H - The answers!</h1>
<p>Now can see what the concrete answers are: $x = 3$ or $-3$ or $2$ or $-2$.</p>
<p><img src="../../images/x_to_the_4_graph.png" alt="../../images/x_to_the_4_graph.png" /></p>Friedrich "Fred" Clausenftclausen@gmail.comThis is from George Pólya’s “How to Solve It” book. I am reading this because I want to sharpen my problem solving skills and, eventually, be able to make derivations like this in Concrete Mathematics.Steps for importing PEM files into JKS keystore stored within Kubernetes secrets2022-06-20T15:01:00+00:002022-06-20T15:01:00+00:00https://ftclausen.github.io/jenkins/java/renew-cert-in-jks-in-k8s<p>This post goes through the steps required specifically for creating or updating
a certificate + key for use with Tomcat running in Kubernetes. There are some
oddities because most certificates are distributed as PEM files (containing
x509 cert/key representation). However Java uses its own keystore either as JKS
or PKCS12.</p>
<p>High level the steps are</p>
<ul>
<li>Generate key or use old key again (not shown in this post)</li>
<li>Create CSR and get back signed certificate (not shown in this post)</li>
<li>Also get CA intermediate certificate - usually comes back with signed certificate (this step might be optional)</li>
<li>Concatenate your certificate with CA intermediate and all root certs</li>
<li>Create PKCS12 file</li>
<li>Create Java keystore file</li>
<li>Import as Kubernetes secret</li>
</ul>
<h1 id="concatenate-your-certificate-with-ca-intermediate-and-all-root-certs">Concatenate your certificate with CA intermediate and all root certs</h1>
<p>Usually an up to date ca-certificates package will have a file containing all root certs. This package is available on
Homebrew or on Linux. E.g. it could be in:</p>
<ul>
<li>Homebrew - <code class="language-plaintext highlighter-rouge"><homebrew base>var/homebrew/linked/ca-certificates/share/ca-certificates/cacert.pem</code></li>
<li>Linux - Commonly somewhere like <code class="language-plaintext highlighter-rouge">/etc/ssl/certs/ca-certificates.crt</code></li>
<li>Or get it from Firefox or some other browser</li>
</ul>
<p>Once you have the ca-certificates run</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat CA.crt /usr/local/homebrew/var/homebrew/linked/ca-certificates/share/ca-certificates/cacert.pem > allcerts.crt
</code></pre></div></div>
<p>Where <code class="language-plaintext highlighter-rouge">CA.crt</code> is your CA intermediate certificate, be sure to also check the
path to the <code class="language-plaintext highlighter-rouge">ca-certificates</code> full CA certificate list. It will probably be
different to above.</p>
<h1 id="create-pkcs12-file">Create PKCS12 file</h1>
<p>Then create a PKCS12 file containing your own cert as well as the CA chain:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openssl pkcs12
-export -in example.com.crt \
-inkey ~/somewhere/safe/example.key \
-out example.p12 \
-name <some alias> \
-CAfile allcerts.crt -caname root \
-chain
</code></pre></div></div>
<p>Replace the example names as appropriate and also <code class="language-plaintext highlighter-rouge"><some alias></code> to something meaningful. E.g. if using Tomcat this is commonly set to <code class="language-plaintext highlighter-rouge">tomcat</code>.</p>
<h1 id="create-java-keystore-file">Create java keystore file</h1>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>keytool -deststoretype JKS \
-importkeystore \
-srckeystore example.p12 \
-srcstoretype PKCS12 \
-srcstorepass changeme \
-alias tomcat # Or any other alias that makes sense for your use case \
-deststorepass changeme \
-destkeypass changeme \
-destkeystore example.keystore
</code></pre></div></div>
<p>Note - <code class="language-plaintext highlighter-rouge">keytool</code> complains if you use JKS due to it being deprecated but it has the greatest compatibility e.g. with Gradle.</p>
<h1 id="import-as-a-kubernetes-secret">Import as a Kubernetes Secret</h1>
<h2 id="save-current-secret">Save current secret</h2>
<p>First save the current secret (if there is one) so that it can be referred to later if need be</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get -o yaml secret example-keystore > ~/somewhere/safe
</code></pre></div></div>
<h2 id="delete-current-secret">Delete current secret</h2>
<p>Then delete the current secret (coordinating as appropriate if this is a heavily used one)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl delete secret example-keystore
</code></pre></div></div>
<p>Because you cannot update secrets in-place.</p>
<h2 id="re-create-secret-with-new-cert--key--certificate-chain">Re-create secret with new cert + key + certificate chain</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl create secret generic example-keystore --from-file example.keystore
</code></pre></div></div>
<p>Once this is done systems (like services in K8s) that mount in that secret volume will be able to use the updated cert. Deployments may need scaling down and then up again or just delete the pods so they are recreated.</p>
<h1 id="credits">Credits</h1>
<p>Thanks to these for helping me along my way:</p>
<ul>
<li>https://stackoverflow.com/a/8224863/1300307</li>
<li>https://superuser.com/questions/1142555/openssl-p12-generation-failing-with-ca-bundle-chain-option</li>
</ul>Friedrich "Fred" Clausenftclausen@gmail.comThis post goes through the steps required specifically for creating or updating a certificate + key for use with Tomcat running in Kubernetes. There are some oddities because most certificates are distributed as PEM files (containing x509 cert/key representation). However Java uses its own keystore either as JKS or PKCS12.Concrete Mathematics: Notes on Josephus Problem Induction2022-04-07T12:00:00+00:002022-04-07T12:00:00+00:00https://ftclausen.github.io/mathematics/concrete-mathematics/josephus-worked-induction<p>The explanations in Concrete Mathematics are very good if a bit terse. So, <a href="../mathematics/lines-in-plaine-worked-example-induction/">as
before</a>, I will add some extra detail in a blog post to help
cement (🥁) my knowledge. I am also shouting it into the void in case it helps someone else.</p>
<p>See the book for full context; I’ll just cover the induction related steps.</p>
<h1 id="the-recurrences-and-closed-form">The Recurrences and Closed Form</h1>
<p>The recurrence comes in two flavours: odd and even. The complete recurrences are thus:</p>
\[\begin{align}
J(1) &= 1;\\
J(2n) &= 2J(n) - 1, \ \ \ \ \text{for n} \geq 1\\
J(2n+1) &= 2J(n) + 1, \ \ \ \ \text{for n} \geq 1\\
\end{align}\]
<p>And the closed form is as follows</p>
\[J(2^m+\ell)=2\ell+1\]
<h1 id="even-induction">Even Induction</h1>
<p>The book goes on to give an example of the even induction</p>
<blockquote>
<p>$J(2^m+\ell)=2J(2^{m-1} + \ell/2) - 1 = 2(2\ell/2 + 1) - 1 = 2\ell + 1$</p>
<p>by (1.8) and the induction hypothesis; this is exactly what we want.</p>
</blockquote>
<p>What confused me at first was the leap</p>
\[2J(2^{m-1} + \ell/2) - 1 = 2(2\ell/2 + 1) - 1\]
<p>This is the key induction step - this is not done via algebraic manipulation. We are substituting the closed form that
is equivalent to $J(n)$. Our closed form proposal is actually for $2J(n)-1=2l+1$ so we need to refit it to be for $J(n)$. Thus
$l$ is in the previous power of two block (remember $J(n)$ not $2J(n)$) and half as big: $l/2$. So the $J(n)$ closed form is $2l/2 +1$.</p>
<p>So we plug that in and then he next step, however, <em>is done</em> via algebra:</p>
\[2(2\ell/2 + 1) - 1 = 2\ell + 1\]
<p>Completing the even induction.</p>
<h1 id="odd-induction">Odd Induction</h1>
<p>Credit: I was stuck on this until helped out by the ever helpful Brian M. Scott on <a href="https://math.stackexchange.com/a/3743359/109665">Math
Stackexchange</a>. I will repeat
that here and then do my own (simplified) take on thereafter. First Brian’s
reply:</p>
<blockquote>
<p>Your’re not applying the recurrence for the odd case correctly. Suppose that
$2n + 1 = 2^m + \ell$, where $0 \leq \ell \leq 2^m$. The recurrence is $J(2n +
1) = 2J(n) + 1$, and <em>n</em> here is $\frac12(2^m + \ell - 1)$, so</p>
\[\begin{align}
J(2^m + \ell) &= 2\left(2^{m-1} + \frac{\ell - 1}{2}\right) + 1 \\
&= 2\left(\frac{2(\ell - 1)}{2} + 1 \right) + 1 \\
&= 2\ell + 1
\end{align}\]
<p>as desired</p>
</blockquote>
<p>I will do some colour highlighting and ever more verbose explanation for my
future self here. The core is we want to convert $2n + 1$ into $n$ hence
dividing by two and subtracting one:</p>
\[\frac{2(n + 1)}{2} - 1 = n\]
<p>We apply these operations during the induction step to turn $2\ell + 1$ into
$\frac{2(\ell -1)}{2} + 1$. Notice the division by two and subtraction by one?
And now with gusto and in context of the induction step where we “insert” the
closed form representing <em>n</em> into the recurrence “framework” (substitution
highlighted in blue):</p>
\[\begin{align}
J(2^m + \ell) &= 2\left(\color{blue}\frac{2(\ell - 1)}{2} + 1\color{#3d4144}\right) + 1 \\
&= 2\ell + 1
\end{align}\]
<p>Giving us what we want.</p>Friedrich "Fred" Clausenftclausen@gmail.comThe explanations in Concrete Mathematics are very good if a bit terse. So, as before, I will add some extra detail in a blog post to help cement (🥁) my knowledge. I am also shouting it into the void in case it helps someone else.Josephus Problem - Recurrence - Revisited - Unrolled2022-02-12T14:09:00+00:002022-02-12T14:09:00+00:00https://ftclausen.github.io/mathematics/josephus-problem-revisited<p>I am restarting Concrete Mathematics (Graham, Knuth, Patashnik) due to a forced
break due to a neurological problem called <a href="https://en.wikipedia.org/wiki/Anti-NMDA_receptor_encephalitis">Anti-NMDA receptor
encephalitis</a>. I
include this detail because it is a relatively newly discovered disease and
misdiagnosis is common so, in a very small way, raising awareness.</p>
<p>Maths is my friend in the recovery process even though it is challenging. Perhaps because it is challenging.</p>
<p>This post goes over the <em>recurrence only</em> because I found that challenging. For
some reason I find recursion harder than it should be 😅</p>
<h1 id="setting-the-scene-with-some-examples">Setting the Scene with Some Examples</h1>
<p>While the concept of shooting each <em>second</em> remaining person sounds obvious I
found it useful to step through it on pen and paper which I will attempt to
reproduce here in text; really an animation is also useful to look at such as
<a href="https://www.youtube.com/watch?v=uCsD3ZGzMgE">this one</a>. I won’t show a diagram
because all you see is the end result which is not so useful</p>
<ul>
<li>$n = 1$ - Survivor is $1$ obviously because Josephus does not want to kill
himself.</li>
<li>$n = 2$
<ul>
<li>1 shoots 2</li>
<li>1 survives</li>
</ul>
</li>
<li>$n = 3$
<ul>
<li>1 shoots 2</li>
<li>3 shoots 1</li>
<li>3 survives</li>
</ul>
</li>
<li>$n = 4$
<ul>
<li>1 shoots 2</li>
<li>3 shoots 4</li>
<li>1 shoots 3</li>
<li>1 survives</li>
</ul>
</li>
<li>… and so on - see above linked
<a href="https://www.youtube.com/watch?v=uCsD3ZGzMgE">video</a> for a great
explanation.</li>
</ul>
<p>I was accidentally thinking the dead person was also shooting someone (😅) which
meant I was messing up above. Here is a tabular form of the above as presented in
the book:</p>
\[\begin{array}{|c|c|}
\hline
n & 1\ \ 2\ \ 3\ \ 4\ \ 5\ \ 6\
\\
\hline
J(n) & 1\ \ 1\ \ 3\ \ 1\ \ 3\ \ 5\
\\
\hline
\end{array}\]
<p>I think the point the book is trying to make is that, at each go around, we are
essentially at the same point as the round before except there are <em>half</em> as
many people _and their numbers (aka “label” or “position”) have changed. Or, <a href="https://en.wikipedia.org/wiki/Josephus_problem">as
Wikipedia puts it</a>:</p>
<blockquote>
<p>The first time around the circle, all of the even-numbered people die. The
second time around the circle, the new 2nd person dies, then the new 4th
person, etc.; it is as though there were no first time around the circle.</p>
</blockquote>
<h1 id="the-recurrence">The Recurrence</h1>
<p>This refers heavily to <a href="https://math.stackexchange.com/a/4069794/109665">this
post</a> by, the ever helpful,
Brian M. Scott. I am only adding even more details for my addled brain to help
it understand.</p>
<p>First the recurrences (for base case, odd, and even) which is the notational
expression of the above sentences:</p>
\[\begin{align}
J(1) &= 1 \\
J(2n) &= 2J(n) - 1, \ \ \ \ \ \ \text{for} \ n \ge 1; \\
J(2n + 1) &= 2J(n) + 1, \ \ \ \ \ \ \text{for}\ n \ge 1; \\
\end{align}\]
<h2 id="unrolling">Unrolling</h2>
<p>The <a href="https://math.stackexchange.com/a/4069794/109665">post</a> by Brian M. Scott
shows the case for $n = 21$ but I found it useful to step through the small
cases as well.</p>
<p>Formatting note: I am using a comma followed by some notes. The font for the
<code class="language-plaintext highlighter-rouge">\text{...}</code> is not beautiful but serves the purpose.</p>
<h3 id="n--1">n = 1</h3>
<p>This is the base case so $J(1) = 1$</p>
<h3 id="n--2">n = 2</h3>
\[\begin{align}
J(2) &= J(1\cdot2), \quad \text{expand} \\
&= 2J(1) - 1, \quad \text{recurse} \\
&= 2\cdot1 - 1 = 1, \quad \text{base case and finish}
\end{align}\]
<h3 id="n--3">n = 3</h3>
\[\begin{align}
J(3) &= J(1\cdot2 + 1), \quad \text{expand} \\
&= 2J(1) + 1, \quad \text{recurse}\\
&= 2\cdot1 + 1 = 1, \quad \text{base case and finish}\\
\end{align}\]
<h3 id="n--4">n = 4</h3>
\[\begin{align}
J(4) &= J(2\cdot2), \quad \text{expand}\\
&= 2J(2) - 1, \quad \text{recurse} \\
&= 2J(1\cdot2) - 1, \quad \text{expand} \\
&= 2(2J(1) - 1) - 1, \quad \text{recurse} \\
&= 4J(1) - 3, \quad \text{simplify}\\
&= 4\cdot1 - 3 = 1, \quad \text{base case and finish}
\end{align}\]
<h3 id="n--5">n = 5</h3>
\[\begin{align}
J(5) &= J(2\cdot2 + 1), \quad \text{expand} \\
&= 2J(2) + 1, \quad \text{recurse} \\
&= 2J(1.2) + 1, \quad \text{expand} \\
&= 2(2J(1) - 1 ) + 1, \quad \text{recurse} \\
&= 4J(1) - 2 + 1, \quad \text{simplify} \\
&= 4\cdot1 - 1 = 3, \quad \text{base case and finish}
\end{align}\]
<h3 id="n--6">n = 6</h3>
\[\begin{align}
J(6) &= J(2\cdot3), \quad \text{expand} \\
&= 2J(3) - 1, \quad \text{recurse} \\
&= 2J(1\cdot3) - 1, \quad \text{expand} \\
&= 2(2J(1) + 1) - 1, \quad \text{recurse} \\
&= 2(2\cdot1 + 1) - 1 = 4 + 2 - 1 = 5, \quad \text{base case and finish}
\end{align}\]
<h1 id="conclusion">Conclusion</h1>
<p>Doing this manual unrolling really helps understand the nature of the problem
when expressed in math notation. We can see how powers of two play a role which
is useful in the closed for solution(s).</p>Friedrich "Fred" Clausenftclausen@gmail.comI am restarting Concrete Mathematics (Graham, Knuth, Patashnik) due to a forced break due to a neurological problem called Anti-NMDA receptor encephalitis. I include this detail because it is a relatively newly discovered disease and misdiagnosis is common so, in a very small way, raising awareness.Concrete Mathematics Notes: Summation by Parts2021-08-17T20:34:00+00:002021-08-17T20:34:00+00:00https://ftclausen.github.io/mathematics/concrete-mathematics-notes-summation-by-parts<p>Just a quick note to more verbosely explain some things that took a while to sink in. This post primarily concerns page
55 of Concrete Mathematics (Knuth, Graham, Patashnick) where they derive <em>Summation by Parts</em>.</p>
<h2 id="applying-difference-operator-to-product-of-two-functions">Applying Difference Operator to Product of Two Functions</h2>
<p>This is equation 2.54 and, at first, I did not understand the second step especially the section highlighted in blue</p>
\[\begin{align}
\Delta(u(x)v(x)) &= u(x+1)v(x+1) - u(x)v(x) \\
&= u(x+1)v(x+1) \boldsymbol{\color{blue}{- u(x)v(x+1) + u(x)v(x+1)}}\color{#3d4144} - u(x)v(x) \\
&= u(x)\Delta v(x) + v(x+1)\Delta u(x)
\end{align}\]
<p>My initial reaction was: wtf is the new terms above? Had me stumped for a while I must confess.. Then I realised they
were just cancelling out extras conjured into existence like a
quickly annihilating virtual particle/anti-particle in the quantum vacuum. These “nothing terms” are used for factoring to arrive at our
difference-of-a-product form at 2.54 in the book. So, more verbosely, the factoring step is in blue this time:</p>
\[\begin{align}
\Delta(u(x)v(x)) &= u(x+1)v(x+1) - u(x)v(x) \\
&= u(x+1)v(x+1) - u(x)v(x+1) + u(x)v(x+1) - u(x)v(x) \\
&= \boldsymbol{\color{blue}v(x+1)(u(x+1) - u(x)) + u(x)(v(x+1) - v(x))\color{#3d4144}} \\
&= v(x+1)\Delta u(x) + u(x)\Delta v(x) \\
&= u(x)\Delta v(x) + v(x+1)\Delta u(x)
\end{align}\]
<p>Last two lines just swapped to end up where the book does. This seemed kind of magical to me but, I suppose, it works
due to the commutative and associative laws of addition/multiplication. I even tried it out with actual numbers and it works :D</p>
<h2 id="deriving-actual-summation-by-parts">Deriving Actual Summation by Parts</h2>
<p>My next moment of confusion came when deriving the actual summation by parts. We start with the compact rule for
difference of a product using the $E$ “nuisance” (quoting the book here :) ):</p>
\[\Delta(uv) = u\Delta v + Ev\Delta u\]
<p>Then they say:</p>
<blockquote>
<p>Taking the indefinite sum on both sites of this equation, and rearranging its terms, yields the advertised rule for
summation by parts:</p>
</blockquote>
\[\sum u\Delta v = uv - \sum Ev\Delta u\]
<p>I get the rearrangement of terms but how $\Delta(uv)$ becomes $uv$ was a bit of a mystery. After the requisite amount of
time overthinking it, it is just literally following through the steps of turning $\Delta(uv)$ into $-\Delta(uv)$ when
re-arranging; then shaking things out. I find it useful to expand everything and go from there:</p>
\[\begin{align}
-\Delta(uv) &= -\Delta(u(x)v(x)) \\
&= -(u(x+1)v(x+1) - u(x)v(x)) \\
&= -u(x+1)v(x+1) + u(x)v(x) \\
&= -u(x)v(x) \\
&= -uv \\
\end{align}\]
<p>Then, in 2.56 in the book, the rearrangment part makes it positive:</p>
\[\begin{align}
\Delta(uv) &= u\Delta v + Ev\Delta u \\
-u\Delta v &= -\Delta(uv) + Ev\Delta u \\
u\Delta v &= \Delta(uv) - Ev\Delta u
\end{align}\]
<p>Finally they take the indefinite sum on both sides giving us the summation by parts rule</p>
\[\sum u\Delta v = \Delta(uv) - \sum Ev\Delta u\]Friedrich "Fred" Clausenftclausen@gmail.comJust a quick note to more verbosely explain some things that took a while to sink in. This post primarily concerns page 55 of Concrete Mathematics (Knuth, Graham, Patashnick) where they derive Summation by Parts.Using JobDSL and Jenkinsfiles to fully automate Jenkins job management2021-08-09T14:48:00+00:002021-08-09T14:48:00+00:00https://ftclausen.github.io/jenkins/using-jobdsl-and-jenkinsfiles-to-fully-automate-jenkins-job-management<p>By using JobDSL and Jenkinsfiles we can fully automate all aspects of Jenkins job management. The two components play the following roles</p>
<ul>
<li><em>JobDSL</em>: The skeletal framework of a job that “registers” it in the Jenkins UI but does not contain any core job logic</li>
<li><em>Jenkinsfiles</em>: Contains all the project specific steps to accomplish the project goals such as building or deploying code</li>
</ul>
<p><img src="/images/sandwhich.png" alt="Sandwhich DSL goodness" height="600px" width="600px" /></p>
<p>It does not have to be JobDSL vs. Jenkinsfiles but, rather, JobDSL <strong>and</strong> Jenkinsfiles.</p>
<h1 id="jobdsl">JobDSL</h1>
<p>The code statements in JobDSL map directly to what is in the Jenkins UI as shown below</p>
<p><img src="/images/job_dsl.png" alt="Yummy JobDSL FRED_AI" /></p>
<h1 id="jenkinsfiles">Jenkinsfiles</h1>
<p>The “meat” of the job, that is, the core logic and all logic specific to the project in question is contained in the
Jenkinsfile. Quite often this is just called “Jenkinsfile” or “Jenkinsfile.groovy” (if you want to clue in your
editor/IDE) but it can be called anything.</p>
<p>Below are some Jenkinsfile snippets showing, roughly, how the correspond to what you might see in the Jenkins UI.</p>
<p><img src="/images/jenkinsfile.png" alt="Yummy Jenkinsfiles" /></p>Friedrich "Fred" Clausenftclausen@gmail.comBy using JobDSL and Jenkinsfiles we can fully automate all aspects of Jenkins job management. The two components play the following rolesBatch cancelling stuck Jenkins queued jobs2021-07-08T09:43:00+00:002021-07-08T09:43:00+00:00https://ftclausen.github.io/jenkins/batch-cancelling-stuck-jenkins-jobs<p>We use Kubernetes to run Jenkins jobs on disposable pods and, if Jenkins is restarted while jobs are running, the pods
go away and Jenkins attempts to restart the jobs on the now missing pods. This results in stuck jobs in the build queue with
the following status</p>
<blockquote>
<p>There are no nodes with the label <some label></p>
<p>Waiting for <some long period></p>
</blockquote>
<p>There is a handy
<a href="https://javadoc.jenkins-ci.org/hudson/model/Queue.Item.html#isStuck--">hudson.model.Queue.Item#isStuck()</a> method for
this however, in our Kubernetes environment, we find that it returns very newly created tasks are also marked as “stuck”
even when they have only been in the queue for milliseconds. So I’m using <code class="language-plaintext highlighter-rouge">isStuck()</code> as a pre-filter and then further
checking by our own criteria.</p>
<p>Without further ado, here is a <a href="https://www.jenkins.io/doc/book/managing/script-console/">Groovy script console</a> snippet
to clean up jobs queued for more than 6 hours. Tweak for your environment and use at your own risk:</p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">hudson.model.*</span>
<span class="kn">import</span> <span class="nn">groovy.time.TimeCategory</span>
<span class="kt">def</span> <span class="n">q</span> <span class="o">=</span> <span class="n">Jenkins</span><span class="o">.</span><span class="na">instance</span><span class="o">.</span><span class="na">queue</span>
<span class="kt">def</span> <span class="n">now</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Date</span><span class="o">();</span>
<span class="kt">def</span> <span class="n">maxAgeHours</span> <span class="o">=</span> <span class="mi">6</span>
<span class="n">q</span><span class="o">.</span><span class="na">items</span><span class="o">.</span><span class="na">findAll</span> <span class="o">{</span> <span class="n">it</span><span class="o">.</span><span class="na">stuck</span> <span class="o">}.</span><span class="na">each</span> <span class="o">{</span>
<span class="kt">def</span> <span class="n">taskStart</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Date</span><span class="o">(</span><span class="n">it</span><span class="o">.</span><span class="na">inQueueSince</span><span class="o">)</span>
<span class="kt">def</span> <span class="n">age</span> <span class="o">=</span> <span class="n">TimeCategory</span><span class="o">.</span><span class="na">minus</span><span class="o">(</span><span class="n">now</span><span class="o">,</span> <span class="n">taskStart</span><span class="o">)</span>
<span class="k">if</span> <span class="o">(</span><span class="n">age</span><span class="o">.</span><span class="na">hours</span> <span class="o">></span> <span class="n">maxAgeHours</span><span class="o">)</span> <span class="o">{</span>
<span class="n">println</span> <span class="s2">"${it.task.name} is stuck for more than $maxAgeHours hours (${age.hours} hours), cancelling."</span>
<span class="n">q</span><span class="o">.</span><span class="na">cancel</span><span class="o">(</span><span class="n">it</span><span class="o">.</span><span class="na">task</span><span class="o">)</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="s2">"DONE"</span>
</code></pre></div></div>Friedrich "Fred" Clausenftclausen@gmail.comWe use Kubernetes to run Jenkins jobs on disposable pods and, if Jenkins is restarted while jobs are running, the pods go away and Jenkins attempts to restart the jobs on the now missing pods. This results in stuck jobs in the build queue with the following status