<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2023-05-10T22:18:05+00:00</updated><id>/feed.xml</id><title type="html">bit bland</title><subtitle>a blog for the unexcitable</subtitle><entry><title type="html">Debugging wandering IPs with arping</title><link href="/metallb/kubernetes/2023/05/05/arping-metallb-madness.html" rel="alternate" type="text/html" title="Debugging wandering IPs with arping" /><published>2023-05-05T07:57:23+00:00</published><updated>2023-05-05T07:57:23+00:00</updated><id>/metallb/kubernetes/2023/05/05/arping-metallb-madness</id><content type="html" xml:base="/metallb/kubernetes/2023/05/05/arping-metallb-madness.html">&lt;p&gt;We have a number of on premises &lt;a href=&quot;https://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt; clusters on virtual hardware which utilise &lt;a href=&quot;https://metallb.org/&quot;&gt;MetalLB&lt;/a&gt; for load balancing. New &lt;a href=&quot;https://metallb.org/configuration/_advanced_l2_configuration/&quot;&gt;configuration options&lt;/a&gt; enables the coupling of specific IP address pools to named Kubernetes nodes, this functionality prompted an upgrade.&lt;/p&gt;

&lt;p&gt;During the upgrade of a specific cluster our services started flapping. We noticed IPs in the range were constantly being reassigned to different nodes whilst also intermittently failing, here demonstrated by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sshing&lt;/code&gt; into the node via the IP assigned to it by MetalLB and retrieving the hostname:&lt;/p&gt;

&lt;p&gt;(code examples are for the &lt;a href=&quot;https://fishshell.com/]&quot;&gt;fish shell&lt;/a&gt;)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;ip &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;host vip-pool.hostname | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;address | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $4}'&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ip&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;   &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ssh &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ConnectTimeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UserKnownHostsFile=/dev/null&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ERROR root@&lt;span class=&quot;nv&quot;&gt;$ahost&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cat /etc/hostname&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
end&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
10.34.11.190   kubenode001
10.34.11.191   kubenode002
10.34.11.192   ssh: connect to host 10.34.11.192 port 22: Connection timed out
10.34.11.193   ssh: connect to host 10.34.11.193 port 22: Connection timed out
10.34.11.194   ssh: connect to host 10.34.11.194 port 22: Connection timed out
10.34.11.195   kubenode003
10.34.11.196   kubenode004
10.34.11.197   kubenode005
10.34.11.198   ssh: connect to host 10.34.11.198 port 22: Connection timed out
10.34.11.199   kubenode006

&lt;span class=&quot;c&quot;&gt;# and again&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;ip &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;host vip-pool.hostname | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;address | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $4}'&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ip&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;   &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ssh &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ConnectTimeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UserKnownHostsFile=/dev/null&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ERROR root@&lt;span class=&quot;nv&quot;&gt;$ahost&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cat /etc/hostname&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                                    
end&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
10.34.11.190   ssh: connect to host 10.34.11.190 port 22: Connection timed out
10.34.11.191   ssh: connect to host 10.34.11.191 port 22: Connection timed out
10.34.11.192   kubenode001
10.34.11.193   kubenode002
10.34.11.194   ssh: connect to host 10.34.11.194 port 22: Connection timed out
10.34.11.195   kubenode003
10.34.11.196   ssh: connect to host 10.34.11.196 port 22: Connection timed out
10.34.11.197   kubenode004
10.34.11.198   ssh: connect to host 10.34.11.198 port 22: Connection timed out
10.34.11.199   ssh: connect to host 10.34.11.199 port 22: Connection timed out&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It seems IP addresses are constantly being reassigned to different nodes, and this should happen, according to the &lt;a href=&quot;https://metallb.org/concepts/layer2/#load-balancing-behavior&quot;&gt;docs&lt;/a&gt; because&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;. . . the leader node [has] failed for some reason, failover is [then] automatic: the failed node is detected using memberlist, at which point new nodes take over ownership of the IP addresses from the failed node.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s investigate further with &lt;a href=&quot;https://github.com/ThomasHabets/arping&quot;&gt;arping&lt;/a&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;suffix &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;190 199&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;arping &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; ens192 &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 1 10.34.11.&lt;span class=&quot;nv&quot;&gt;$suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
ARPING 10.34.11.190 from 10.34.11.230 ens192
Unicast reply from 10.34.11.190 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:47:53]  0.695ms
Unicast reply from 10.34.11.190 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:62:40]  0.730ms
Unicast reply from 10.34.11.190 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:D4:6D]  0.740ms
Unicast reply from 10.34.11.190 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:89:59]  0.837ms
Unicast reply from 10.34.11.190 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:7A:9F]  0.872ms
Sent 1 probes &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1 broadcast&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
Received 5 response&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
ARPING 10.34.11.191 from 10.34.11.230 ens192
Unicast reply from 10.34.11.191 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:62:40]  0.700ms
Unicast reply from 10.34.11.191 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:47:53]  0.745ms
Unicast reply from 10.34.11.191 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:D4:6D]  0.753ms
Unicast reply from 10.34.11.191 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:89:59]  0.827ms
Unicast reply from 10.34.11.191 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:86:6B]  0.852ms
Sent 1 probes &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1 broadcast&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
Received 5 response&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
ARPING 10.34.11.192 from 10.34.11.230 ens192
Unicast reply from 10.34.11.192 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:47:53]  0.643ms
Unicast reply from 10.34.11.192 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:62:40]  0.666ms
Unicast reply from 10.34.11.192 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:D4:6D]  0.732ms
Unicast reply from 10.34.11.192 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:89:59]  0.800ms
Unicast reply from 10.34.11.192 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:38:AC]  0.981ms
Unicast reply from 10.34.11.192 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;00:50:56:AB:A4:FC]  1.034ms 
...&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here we have responses coming from multiple interfaces for the same IP. Grepping for the above MAC addresses in the output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ip a&lt;/code&gt; on the cluster nodes revealed many were for unconfigured virtual interfaces, no wonder our services were flapping. By default MetalLB will attempt to broadcast on every interface, &lt;a href=&quot;https://www.reddit.com/r/kubernetes/comments/p2uwfw/comment/h8mo70a&quot;&gt;up or down&lt;/a&gt;. To disable the default behaviour we need to augment our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L2Advertisement&lt;/code&gt; Custom Resource configuration to specify the interfaces &lt;a href=&quot;https://metallb.org/configuration/_advanced_l2_configuration/#specify-network-interfaces-that-lb-ip-can-be-announced-from&quot;&gt;we want to use&lt;/a&gt;, resulting in a stable service:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;ip &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;host vip-pool.hostname | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;address | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $4}'&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ip&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;   &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ssh &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ConnectTimeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UserKnownHostsFile=/dev/null&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ERROR root@&lt;span class=&quot;nv&quot;&gt;$ahost&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cat /etc/hostname&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
end&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
10.34.11.190   kubenode001
10.34.11.191   kubenode002
10.34.11.192   kubenode003
10.34.11.193   kubenode004
10.34.11.194   kubenode005
10.34.11.195   kubenode006
10.34.11.196   kubenode007
10.34.11.197   kubenode008
10.34.11.198   kubenode009
10.34.11.199   kubenode010&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content><author><name></name></author><category term="metallb" /><category term="kubernetes" /><summary type="html">We have a number of on premises Kubernetes clusters on virtual hardware which utilise MetalLB for load balancing. New configuration options enables the coupling of specific IP address pools to named Kubernetes nodes, this functionality prompted an upgrade.</summary></entry><entry><title type="html">Simulating –dry-run and –patch in Hashicorp Vault</title><link href="/vault/2022/06/27/vault-hacks.html" rel="alternate" type="text/html" title="Simulating –dry-run and –patch in Hashicorp Vault" /><published>2022-06-27T14:32:31+00:00</published><updated>2022-06-27T14:32:31+00:00</updated><id>/vault/2022/06/27/vault-hacks</id><content type="html" xml:base="/vault/2022/06/27/vault-hacks.html">&lt;p&gt;Hashicorp &lt;a href=&quot;https://www.hashicorp.com/products/vault&quot;&gt;Vault&lt;/a&gt; is a great resource for keeping secrets secret. However version 1 of the &lt;a href=&quot;https://www.vaultproject.io/api-docs/secret/kv/kv-v1&quot;&gt;KV secrets engine&lt;/a&gt; lacked the ability to patch a particular endpoint which made modifying existing values fraught with danger. We mitigate this by utilising the ever wonderful &lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;jq&lt;/a&gt; alongside a little used Vault flag to perform inline modifcations before similuating writes.&lt;/p&gt;

&lt;p&gt;Let’s write some test data and read it back again in sequentially more useful ways:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;vault write /secret/test/smchm &lt;span class=&quot;nv&quot;&gt;testkey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;testvalue
Success! Data written to: secret/test/smchm

vault &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; /secret/test/smchm
Key                 Value
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;                 &lt;span class=&quot;nt&quot;&gt;-----&lt;/span&gt;
refresh_interval    768h
testkey             testvalue

vault &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;json /secret/test/smchm | jq &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;request_id&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;087490c9-ccad-e225-cd17-9f88df12abd5&quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;lease_id&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;lease_duration&quot;&lt;/span&gt;: 2764800,
  &lt;span class=&quot;s2&quot;&gt;&quot;renewable&quot;&lt;/span&gt;: &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt;: &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;testkey&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;testvalue&quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;warnings&quot;&lt;/span&gt;: null
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

vault &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;json /secret/test/smchm | jq .data
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;testkey&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;testvalue&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now lets &lt;em&gt;patch&lt;/em&gt; the data with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq&lt;/code&gt;, the resultant patched data is not yet stored in Vault:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;vault &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;json /secret/test/smchm | jq &lt;span class=&quot;s1&quot;&gt;'.data += { &quot;testkey&quot;: &quot;newvalue&quot; }'&lt;/span&gt; | jq .data
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;testkey&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;newvalue&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We can also add values:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;vault &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;json /secret/test/smchm | jq &lt;span class=&quot;s1&quot;&gt;'.data += { &quot;testkey&quot;: &quot;newvalue&quot;, &quot;testkey2&quot;: &quot;testvalue2&quot; }'&lt;/span&gt; | jq .data
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;testkey&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;newvalue&quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;testkey2&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;testvalue2&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we have the values we want in our JSON object we can write them back to our Vault endpoint. However first we can &lt;em&gt;simulate&lt;/em&gt; the write by performing a dry-run using the Vault flag &lt;em&gt;–output-curl-string&lt;/em&gt; to make sure everything is as we expect:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;vault &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;json /secret/test/smchm | jq &lt;span class=&quot;s1&quot;&gt;'.data += { &quot;testkey&quot;: &quot;newvalue&quot;, &quot;testkey2&quot;: &quot;testvalue2&quot; }'&lt;/span&gt; | jq .data | vault write &lt;span class=&quot;nt&quot;&gt;-output-curl-string&lt;/span&gt; /secret/test/smchm - 
curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; PUT &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;X-Vault-Token: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;vault print token&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{&quot;testkey&quot;:&quot;newvalue&quot;,&quot;testkey2&quot;:&quot;testvalue2&quot;}'&lt;/span&gt; https://ourvaulthost:8200/v1/secret/test/smchm&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;which can be a little difficult to grok for endpoints with many values, so lets parse the output with &lt;em&gt;sed&lt;/em&gt; and jq:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;vault &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;json /secret/test/smchm | jq &lt;span class=&quot;s1&quot;&gt;'.data += { &quot;testkey&quot;: &quot;newvalue&quot;, &quot;testkey2&quot;: &quot;testvalue2&quot; }'&lt;/span&gt; | jq .data | vault write &lt;span class=&quot;nt&quot;&gt;-output-curl-string&lt;/span&gt; /secret/test/smchm - | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/^.*{/{/'&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/}.*/}/'&lt;/span&gt; | jq &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;testkey&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;newvalue&quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;testkey2&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;testvalue2&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;looks good, so finally lets stream our values back to Vault to patch our endpoint and validate the results:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;vault &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;json /secret/test/smchm | jq &lt;span class=&quot;s1&quot;&gt;'.data += { &quot;testkey&quot;: &quot;newvalue&quot;, &quot;testkey2&quot;: &quot;testvalue2&quot; }'&lt;/span&gt; | jq .data | vault write /secret/test/smchm -
Success! Data written to: secret/test/smchm

vault &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;json /secret/test/smchm | jq .data
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;testkey&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;newvalue&quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;testkey2&quot;&lt;/span&gt;: &lt;span class=&quot;s2&quot;&gt;&quot;testvalue2&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Of course this doesn’t prevent us writing to the wrong endpoint!&lt;/p&gt;</content><author><name></name></author><category term="vault" /><summary type="html">Hashicorp Vault is a great resource for keeping secrets secret. However version 1 of the KV secrets engine lacked the ability to patch a particular endpoint which made modifying existing values fraught with danger. We mitigate this by utilising the ever wonderful jq alongside a little used Vault flag to perform inline modifcations before similuating writes.</summary></entry><entry><title type="html">All your passwords are Troy’s</title><link href="/java/spring/junit5/2022/06/07/password-hygiene.html" rel="alternate" type="text/html" title="All your passwords are Troy’s" /><published>2022-06-07T07:57:23+00:00</published><updated>2022-06-07T07:57:23+00:00</updated><id>/java/spring/junit5/2022/06/07/password-hygiene</id><content type="html" xml:base="/java/spring/junit5/2022/06/07/password-hygiene.html">&lt;p&gt;Our institution is re-opening the age old debate about password hygiene. One of the topics under discussion is whether to introduce expiry conditions to limit password longevity. A &lt;a href=&quot;https://www.ncsc.gov.uk/collection/passwords/updating-your-approach&quot;&gt;bad idea&lt;/a&gt;. Instead store the hashes and periodically check Troy Hunt’s excellent database of &lt;a href=&quot;https://haveibeenpwned.com/Passwords&quot;&gt;compromised credentials&lt;/a&gt; via the &lt;a href=&quot;https://haveibeenpwned.com/API/v3#PwnedPasswords&quot;&gt;API&lt;/a&gt;. The example below generates the hash from the passed argument so not something which can be used in production:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;PASSWD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SHAPREFIX&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PASSWD&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sha1sum&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c1-5&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SHASUFFIX&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PASSWD&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sha1sum&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 6- | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PWNED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  curl &lt;span class=&quot;nt&quot;&gt;-Ss&lt;/span&gt; https://api.pwnedpasswords.com/range/&lt;span class=&quot;nv&quot;&gt;$SHAPREFIX&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SHASUFFIX&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PWNED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;then &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;pwned&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;not pwned&quot;&lt;/span&gt;  
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and to use:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;smchm@home ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;have-i-been-pwned.sh &lt;span class=&quot;s1&quot;&gt;'passwd'&lt;/span&gt;
pwned
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;smchm@home ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;have-i-been-pwned.sh &lt;span class=&quot;s1&quot;&gt;'not-in-the-database-as-it-is-too-long-but-reasonably-easy-to-remember'&lt;/span&gt;
not pwned
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;smchm@home ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;have-i-been-pwned.sh &lt;span class=&quot;s1&quot;&gt;'password1234567890'&lt;/span&gt;
pwned&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;or perhaps more interestingly:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;smchm@home ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;have-i-been-pwned.sh &lt;span class=&quot;s1&quot;&gt;'password'&lt;/span&gt;
pwned
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;smchm@home ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;have-i-been-pwned.sh &lt;span class=&quot;s1&quot;&gt;'password123'&lt;/span&gt;
pwned
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;smchm@home ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;have-i-been-pwned.sh &lt;span class=&quot;s1&quot;&gt;'password123123'&lt;/span&gt;
pwned
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;smchm@home ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;have-i-been-pwned.sh &lt;span class=&quot;s1&quot;&gt;'password123123123'&lt;/span&gt;
pwned
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;smchm@home ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;have-i-been-pwned.sh &lt;span class=&quot;s1&quot;&gt;'password123123123123'&lt;/span&gt;
pwned
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;smchm@home ~]&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;have-i-been-pwned.sh &lt;span class=&quot;s1&quot;&gt;'password123123123123123'&lt;/span&gt;
not pwned&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content><author><name></name></author><category term="java" /><category term="spring" /><category term="junit5" /><summary type="html">Our institution is re-opening the age old debate about password hygiene. One of the topics under discussion is whether to introduce expiry conditions to limit password longevity. A bad idea. Instead store the hashes and periodically check Troy Hunt’s excellent database of compromised credentials via the API. The example below generates the hash from the passed argument so not something which can be used in production:</summary></entry><entry><title type="html">JUnit5 test exclusions based on Spring profile names</title><link href="/java/spring/junit5/2022/06/01/junit5-test-exclusions-by-spring-profile.html" rel="alternate" type="text/html" title="JUnit5 test exclusions based on Spring profile names" /><published>2022-06-01T13:30:48+00:00</published><updated>2022-06-01T13:30:48+00:00</updated><id>/java/spring/junit5/2022/06/01/junit5-test-exclusions-by-spring-profile</id><content type="html" xml:base="/java/spring/junit5/2022/06/01/junit5-test-exclusions-by-spring-profile.html">&lt;p&gt;To prevent some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JUnit5&lt;/code&gt; tests from running when specific &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Spring Profiles&lt;/code&gt; are active simply implement the &lt;a href=&quot;https://junit.org/junit5/docs/5.0.0/api/org/junit/jupiter/api/extension/ExecutionCondition.html&quot;&gt;ExecutionCondition&lt;/a&gt; interface and interrogate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spring.profiles.active&lt;/code&gt; system property;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.example.project.config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.jupiter.api.extension.ConditionEvaluationResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.jupiter.api.extension.ExecutionCondition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.jupiter.api.extension.ExtensionContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DisabledInProductionExtension&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExecutionCondition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConditionEvaluationResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;evaluateExecutionCondition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ExtensionContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensionContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;springProfileKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;spring.profiles.active&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;containsKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;springProfileKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;springProfileKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;integration-test-production&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConditionEvaluationResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Test disabled in production environment&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConditionEvaluationResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Test enabled in non-production environment&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConditionEvaluationResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;No profile active, test enabled&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And to use the Extension&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.example.project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.jupiter.api.Test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.jupiter.api.extension.ExtendWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.example.project.config.DisabledInProductionExtension&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@ExtendWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DisabledInProductionExtension&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IntegrationTestForNonProductionEnvironment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To define multiple Extensions for different profiles we can create complimentary &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExecutionCondition&lt;/code&gt; implementations. The framework will chain the invocations &lt;em&gt;until one evaluates to disabled&lt;/em&gt; and exclude the annotated test accordingly.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.example.project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.jupiter.api.Test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.jupiter.api.extension.ExtendWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.example.project.config.DisabledInProductionExtension&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.example.project.config.DisabledInStagingExtension&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@ExtendWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DisabledInProductionExtension&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DisabledInStagingExtension&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IntegrationTestForDevelopmentEnvironment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content><author><name></name></author><category term="java" /><category term="spring" /><category term="junit5" /><summary type="html">To prevent some JUnit5 tests from running when specific Spring Profiles are active simply implement the ExecutionCondition interface and interrogate the spring.profiles.active system property;</summary></entry></feed>