<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://micarlise.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://micarlise.github.io/" rel="alternate" type="text/html" /><updated>2025-06-16T01:54:56+00:00</updated><id>https://micarlise.github.io/feed.xml</id><title type="html">Notes</title><subtitle>programming languages and systems</subtitle><entry><title type="html">sorts up to quicksort</title><link href="https://micarlise.github.io/sorting/" rel="alternate" type="text/html" title="sorts up to quicksort" /><published>2022-01-02T00:00:00+00:00</published><updated>2022-01-02T00:00:00+00:00</updated><id>https://micarlise.github.io/sorting</id><content type="html" xml:base="https://micarlise.github.io/sorting/"><![CDATA[<p>Popular sorting algorithms all build from two facts.  1. That any element is 
out of place if it’s larger than any other element with a larger index. 2. We 
grow a sorted subarray by swapping out of order elements.</p>

<h3 id="we-need-a-swap-fn">We need a swap fn()</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">swap</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">):</span>
    <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">a</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
</code></pre></div></div>

<h2 id="selection-sort-grow-the-sorted-subarray-in-the-front">selection sort: Grow the sorted subarray in the front</h2>

<p>Our sorted subarray starts at index 0 and grows to n-1.  At each instance,
find the smallest element and swap element in place.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">selectionsort</span><span class="p">(</span><span class="n">a</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">sorted_array_idx</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)):</span>
        <span class="n">i</span> <span class="o">=</span> <span class="n">sorted_array_idx</span>
        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)):</span>
            <span class="k">if</span> <span class="n">a</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
                <span class="n">i</span> <span class="o">=</span> <span class="n">j</span>
        <span class="n">swap</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="insertion-sort-being-more-clever-about-growing-the-sorted-subarray-from-the-front">insertion sort: Being more clever about growing the sorted subarray from the front</h2>

<p>At any element <code class="language-plaintext highlighter-rouge">j</code>, we loop from <code class="language-plaintext highlighter-rouge">j</code> to 0 while the loop invariant 
<code class="language-plaintext highlighter-rouge">array[j] &lt; array[j-1]</code> continues to be true.  When the invariant is false, the
previous swap() puts element <code class="language-plaintext highlighter-rouge">j</code> in place in the sorted subarray.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">insertionsort</span><span class="p">(</span><span class="n">a</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">array</span><span class="p">)):</span>
        <span class="n">j</span> <span class="o">=</span> <span class="n">i</span>
        <span class="k">while</span> <span class="n">j</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">array</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">array</span><span class="p">[</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
            <span class="n">swap</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
            <span class="n">j</span> <span class="o">-=</span> <span class="mi">1</span>
</code></pre></div></div>

<h2 id="bubblesort-grow-the-sorted-subarray-at-the-back">bubblesort: Grow the sorted subarray at the back</h2>

<p>For any element <code class="language-plaintext highlighter-rouge">i</code>, we can swap with <code class="language-plaintext highlighter-rouge">i</code>+1 if <code class="language-plaintext highlighter-rouge">a[i] &gt; a[i+1]</code>. If we loop 
over an array and swap at each of these conditions, then the last element will 
be the array’s largest value.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">bubblesort</span><span class="p">(</span><span class="n">a</span><span class="p">):</span>
  <span class="n">SORTED</span> <span class="o">=</span> <span class="bp">False</span>
  <span class="n">sorted_array_idx</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span>

  <span class="k">while</span> <span class="ow">not</span> <span class="n">SORTED</span><span class="p">:</span>
    <span class="n">SORTED</span> <span class="o">=</span> <span class="bp">True</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">sorted_array_idx</span><span class="p">):</span>
      <span class="k">if</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span>
        <span class="n">swap</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
        <span class="n">SORTED</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="n">sorted_array_idx</span> <span class="o">-=</span> <span class="mi">1</span>
</code></pre></div></div>

<h2 id="quicksort-find-the-absolute-index-of-the-first-element">Quicksort: find the absolute index of the first element</h2>

<p>All the previous sorting algorithms depend on a sorted subarray index.  This is
the index we continue to swap until the correct value is in place.</p>

<p>Quicksort uses the unsorted part of an array to find the absolute final place 
of the sorted subarray index (now called a pivot index).  Because the pivot 
index is in it’s final place. We recurse over the unsorted left/right subarrays.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">quicksort</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">end</span> <span class="o">&lt;=</span> <span class="n">start</span><span class="p">:</span>
        <span class="k">return</span>

    <span class="n">pivot_idx</span> <span class="o">=</span> <span class="n">start</span>
    <span class="n">left</span> <span class="o">=</span> <span class="n">start</span><span class="o">+</span><span class="mi">1</span>
    <span class="n">right</span> <span class="o">=</span> <span class="n">end</span>

    <span class="k">while</span> <span class="n">right</span> <span class="o">&gt;=</span> <span class="n">left</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">a</span><span class="p">[</span><span class="n">left</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">a</span><span class="p">[</span><span class="n">pivot_idx</span><span class="p">]</span> 
            <span class="ow">and</span> <span class="n">a</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">a</span><span class="p">[</span><span class="n">pivot_idx</span><span class="p">]:</span>
                <span class="n">swap</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">a</span><span class="p">[</span><span class="n">left</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">a</span><span class="p">[</span><span class="n">pivot_idx</span><span class="p">]:</span>
            <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">if</span> <span class="n">a</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">a</span><span class="p">[</span><span class="n">pivot_idx</span><span class="p">]:</span>
            <span class="n">j</span> <span class="o">-=</span> <span class="mi">1</span>
    <span class="c1"># this swap places the correct value of j
</span>    <span class="c1"># into the pivot index.
</span>    <span class="n">swap</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">pivot_idx</span><span class="p">,</span> <span class="n">j</span><span class="p">)</span>

    <span class="n">quicksort</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">right</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">quicksort</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">right</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span>
</code></pre></div></div>]]></content><author><name></name></author><summary type="html"><![CDATA[Popular sorting algorithms all build from two facts. 1. That any element is out of place if it’s larger than any other element with a larger index. 2. We grow a sorted subarray by swapping out of order elements.]]></summary></entry><entry><title type="html">adopt existing helm resources</title><link href="https://micarlise.github.io/adopt-existing-helm-resources/" rel="alternate" type="text/html" title="adopt existing helm resources" /><published>2021-10-30T00:00:00+00:00</published><updated>2021-10-30T00:00:00+00:00</updated><id>https://micarlise.github.io/adopt-existing-helm-resources</id><content type="html" xml:base="https://micarlise.github.io/adopt-existing-helm-resources/"><![CDATA[<p>Every once in a while,  you need to install a helm chart that corresponds with 
already existing resources.  Usually, when trying to use a helm chart to have 
more control over a kube-system daemonset or operator: aws-node, coreDNS, or
kube-proxy for instance.  But if you try to install their helm chart you can
 get <code class="language-plaintext highlighter-rouge">Error: INSTALLATION FAILED: rendered manifests contain a resource that 
 already exists.</code>  You can get helm to adopt resources by using the correct
 annotation/labels.</p>

<h2 id="tldr">tldr</h2>

<p>For each pre-existing resource run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  kubectl annotate <span class="nv">$OBJ</span> meta.helm.sh/release-namespace<span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>
  kubectl annotate <span class="nv">$OBJ</span> meta.helm.sh/release-name<span class="o">=</span><span class="k">${</span><span class="nv">RELEASE_NAME</span><span class="k">}</span>
  kubectl label <span class="nv">$OBJ</span> app.kubernetes.io/managed-by<span class="o">=</span>Helm
</code></pre></div></div>

<p>then run <code class="language-plaintext highlighter-rouge">helm install</code> or <code class="language-plaintext highlighter-rouge">helm upgrade</code></p>

<h2 id="setting-the-stage">Setting the stage</h2>

<p>Let’s create an example deployment and service.  Using kubernetes yaml.
Then we will attempt to wrap these resources into a helm chart.</p>

<p><em>http-echo.yaml</em></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">http-echo</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">http-echo</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">http-echo</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">http-echo</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">echo</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">hashicorp/http-echo</span>
        <span class="na">args</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s2">"</span><span class="s">-text=success"</span>
<span class="nn">---</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">echo-service</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">http-echo</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">LoadBalancer</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">http-echo</span>
  <span class="na">ports</span><span class="pi">:</span>
  <span class="c1"># Default port used by the image</span>
  <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="m">5678</span>
</code></pre></div></div>

<p>Apply and verify that everything works</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl apply -f http-echo.yaml
kubectl get pods,deployment,service -l app=http-echo
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NAME                             READY   STATUS    RESTARTS   AGE
pod/http-echo-58c8855bc6-62zjv   1/1     Running   0          5m
pod/http-echo-58c8855bc6-rbgqm   1/1     Running   0          5m
pod/http-echo-58c8855bc6-vrd2d   1/1     Running   0          5m

NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/http-echo   3/3     3            3           5m

NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP
service/echo-service   LoadBalancer   10.96.182.49   172.18.0.150
</code></pre></div></div>

<h3 id="wrapping-in-a-helm-chart">Wrapping in a helm chart</h3>

<p>We can use a minimal Chart.yaml file for this example.</p>

<p><em>Chart.yaml</em></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">http-echo</span>
<span class="na">version</span><span class="pi">:</span> <span class="s">0.1.0</span>
</code></pre></div></div>

<p>And setup a basic helm chart directory</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http-echo/
├── Chart.yaml
└── templates
    └── http-echo.yaml

1 directory, 2 files
</code></pre></div></div>

<p><strong>The problem is that all of the resources that are specified in 
templetes/http-echo.yaml already exist.</strong>  When we try to install this chart
 via <code class="language-plaintext highlighter-rouge">helm install http-echo http-echo/</code> we get met with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Error: INSTALLATION FAILED: rendered manifests contain a resource 
that already exists. Unable to continue with install: Service 
"echo-service" in namespace "default" exists and cannot be 
imported into the current release: invalid ownership metadata; 
label validation error: missing key 
"app.kubernetes.io/managed-by": must be set to "Helm"; annotation
validation error: missing key "meta.helm.sh/release-name": must
be set to "http-echo"; annotation validation error: missing key
"meta.helm.sh/release-namespace": must be set to "default" 
</code></pre></div></div>
<h2 id="annotate-and-label-existing-resources">Annotate and label existing resources</h2>

<p>Each resource needs two annoations and a label to be adopted by Helm.</p>

<p><em>annotations</em></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">meta.helm.sh/release-namespace</code></li>
  <li><code class="language-plaintext highlighter-rouge">meta.helm.sh/release-name</code></li>
</ul>

<p><em>label</em></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">app.kubernetes.io/managed-by</code></li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for </span>OBJ <span class="k">in </span>deployment/http-echo service/echo-service
<span class="k">do
  </span>kubectl annotate <span class="nv">$OBJ</span> meta.helm.sh/release-namespace<span class="o">=</span>default
  kubectl annotate <span class="nv">$OBJ</span> meta.helm.sh/release-name<span class="o">=</span>http-echo
  kubectl label <span class="nv">$OBJ</span> app.kubernetes.io/managed-by<span class="o">=</span>Helm
<span class="k">done</span>
</code></pre></div></div>

<p>Run <code class="language-plaintext highlighter-rouge">helm install --dry-run http-echo http-echo/</code> to show the error no longer 
exist; without actually installing the chart.</p>

<h2 id="more-information">More Information</h2>

<h3 id="this-doesnt-work-with-crd-objects">This doesn’t work with CRD objects</h3>

<p>It does! You need to annotate/label the CRDs as usual.  But then set
 <code class="language-plaintext highlighter-rouge">--skip-crds</code>.  Most charts have a crd.create = false setting.</p>

<h3 id="can-i-see-the-code-that-makes-this-adopt-thing-work">Can I see the code that makes this adopt thing work?</h3>

<p><a href="https://github.com/helm/helm/pull/7649/files">helm issue #7649</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[Every once in a while, you need to install a helm chart that corresponds with already existing resources. Usually, when trying to use a helm chart to have more control over a kube-system daemonset or operator: aws-node, coreDNS, or kube-proxy for instance. But if you try to install their helm chart you can get Error: INSTALLATION FAILED: rendered manifests contain a resource that already exists. You can get helm to adopt resources by using the correct annotation/labels.]]></summary></entry><entry><title type="html">controller runtime client basics</title><link href="https://micarlise.github.io/controller-runtime-client/" rel="alternate" type="text/html" title="controller runtime client basics" /><published>2021-09-06T00:00:00+00:00</published><updated>2021-09-06T00:00:00+00:00</updated><id>https://micarlise.github.io/controller-runtime-client</id><content type="html" xml:base="https://micarlise.github.io/controller-runtime-client/"><![CDATA[<p><a href="https://github.com/kubernetes-sigs/controller-runtime">controller-runtime</a> is a
SIG project that makes building Controllers (or other native k8s apps) easier.
But their client isn’t completely automagic like client-go.  Below is an outline
of the basic CRUD and useful links for finding the missing info faster.</p>

<h2 id="minimal-client-setup">Minimal client setup</h2>

<p>All you need is a <a href="https://pkg.go.dev/k8s.io/client-go/rest#Config">rest.Config</a>.  The simplest way to generate one is using <code class="language-plaintext highlighter-rouge">config.GetConfigOrDie()</code> which will traverse usual locations of kubeconfig files (i.e. $HOME/.kube).</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">(</span>
    <span class="s">"sigs.k8s.io/controller-runtime/pkg/client"</span>
    <span class="s">"sigs.k8s.io/controller-runtime/pkg/client/config"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">c</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">GetConfigOrDie</span><span class="p">(),</span> <span class="n">client</span><span class="o">.</span><span class="n">Options</span><span class="p">{})</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="using-the-client">Using the client</h2>

<p>All CRUD operations are docuemnted on the <a href="https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/client#Client">client docs page</a>.  Depending on the call signature, we need up to three pieces:</p>

<ul>
  <li><a href="#where-do-objects-come-from"><code class="language-plaintext highlighter-rouge">Object</code> or <code class="language-plaintext highlighter-rouge">ObjectList</code></a></li>
  <li><a href="#using-objectkey"><code class="language-plaintext highlighter-rouge">ObjectKey</code></a></li>
  <li><a href="#generating-listoptions"><code class="language-plaintext highlighter-rouge">ListOptions</code></a></li>
</ul>

<h3 id="where-do-objects-come-from">Where do objects come from</h3>

<p>The canonical reference is the <a href="https://github.com/kubernetes/api">api repo</a>. 
 The Objects are in the <code class="language-plaintext highlighter-rouge">types.go</code> file of their respective API group.</p>

<p>As an example.  The Nodes object are in core/v1 group, so the Nodes struct is 
in <a href="https://github.com/kubernetes/api/blob/master/core/v1/types.go">core/v1/types.go</a>.  And can be used as:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">(</span>
    <span class="n">corev1</span> <span class="s">"k8s.io/api/core/v1"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">node</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">corev1</span><span class="o">.</span><span class="n">Node</span><span class="p">{}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="using-objectkey">Using ObjectKey</h3>

<p><code class="language-plaintext highlighter-rouge">Get()</code> takes an ObjectKey object.  The ObjectKey is an indirection to the <a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/types#NamespacedName">types.NamespacedName Object</a>.  This object can be generated on the fly as there are only two keys: <code class="language-plaintext highlighter-rouge">Namespace</code> and <code class="language-plaintext highlighter-rouge">Name</code>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">_</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">TODO</span><span class="p">(),</span> <span class="n">client</span><span class="o">.</span><span class="n">ObjectKey</span><span class="p">{</span>
    <span class="n">Name</span><span class="o">:</span> <span class="s">"Name"</span><span class="p">,</span>
<span class="p">},</span> <span class="n">node</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="generating-listoptions">Generating ListOptions</h3>

<p>controller-runtime has a convienant way to construct ListOptions objects using <code class="language-plaintext highlighter-rouge">MatchingLabels</code> or <code class="language-plaintext highlighter-rouge">MatchingFields</code>.  Both are string maps to strings, and makes it very simple to list objects with specific label or field values.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
    <span class="s">"fmt"</span>
    <span class="s">"context"</span>

    <span class="n">corev1</span> <span class="s">"k8s.io/api/core/v1"</span>
    <span class="s">"sigs.k8s.io/controller-runtime/pkg/client"</span>
    <span class="s">"sigs.k8s.io/controller-runtime/pkg/client/config"</span>
<span class="p">)</span>


<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>

    <span class="n">c</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">GetConfigOrDie</span><span class="p">(),</span> <span class="n">client</span><span class="o">.</span><span class="n">Options</span><span class="p">{})</span>
    <span class="n">nodes</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">corev1</span><span class="o">.</span><span class="n">NodeList</span><span class="p">{}</span>
    <span class="n">filter</span> <span class="o">:=</span> <span class="n">client</span><span class="o">.</span><span class="n">MatchingLabels</span><span class="p">{</span>
        <span class="s">"node-role.kubernetes.io/control-plane"</span><span class="o">:</span> <span class="s">""</span>
        <span class="p">}</span>

    <span class="n">_</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">List</span><span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">TODO</span><span class="p">(),</span> <span class="n">nodes</span><span class="p">,</span> <span class="n">filter</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">node</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">nodes</span><span class="o">.</span><span class="n">Items</span> <span class="p">{</span>
        <span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">ObjectMeta</span><span class="o">.</span><span class="n">GetName</span><span class="p">())</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>]]></content><author><name></name></author><summary type="html"><![CDATA[controller-runtime is a SIG project that makes building Controllers (or other native k8s apps) easier. But their client isn’t completely automagic like client-go. Below is an outline of the basic CRUD and useful links for finding the missing info faster.]]></summary></entry><entry><title type="html">use a local chrome profile for automation scripts</title><link href="https://micarlise.github.io/local-chrome-profile/" rel="alternate" type="text/html" title="use a local chrome profile for automation scripts" /><published>2021-08-22T00:00:00+00:00</published><updated>2021-08-22T00:00:00+00:00</updated><id>https://micarlise.github.io/local-chrome-profile</id><content type="html" xml:base="https://micarlise.github.io/local-chrome-profile/"><![CDATA[<p>When automating tasks with python scripts, you can hit a wall with SSO and
oauth2.  Creating a local chrome profile in your XDG data directory can allow
one SSO login per work day and your python scripts can re-use the sessions.</p>

<h2 id="tldr">tldr</h2>

<p>Set your chrome options user-data-dir and profile-directory to a local directory
under your control to persist SSO/sessions across script runs.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">selenium</span> <span class="kn">import</span> <span class="n">webdriver</span>
<span class="kn">from</span> <span class="nn">selenium.webdriver.chrome.options</span> <span class="kn">import</span> <span class="n">Options</span>

<span class="n">home_dir</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">getenv</span><span class="p">(</span><span class="s">'HOME'</span><span class="p">)</span>
<span class="n">profile_dir</span> <span class="o">=</span> <span class="n">home_dir</span> <span class="o">+</span> <span class="s">'/.local/share/chrome/profile'</span>

<span class="n">chrome_options</span> <span class="o">=</span> <span class="n">Options</span><span class="p">()</span>
<span class="n">chrome_options</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="sa">r</span><span class="s">'user-data-dir='</span> <span class="o">+</span> <span class="n">profile_dir</span><span class="p">)</span>
<span class="n">chrome_options</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="sa">r</span><span class="s">'profile-directory=Automation'</span><span class="p">)</span>

<span class="n">driver</span> <span class="o">=</span> <span class="n">webdriver</span><span class="p">.</span><span class="n">Chrome</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">chrome_options</span><span class="p">)</span>

<span class="c1"># do some driver/DOM automation
</span><span class="n">driver</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="a-simple-view-of-chrome-profiles">A simple view of chrome profiles</h3>

<p><img src="../images/chrome-profiles.png" alt="image of chrome profile" /></p>

<p>The profile path is where chrome saves passwords, sessions, history, etc.</p>

<h3 id="building-a-profile-for-autmation-scripts">Building a profile for autmation scripts</h3>

<p>We can build a chrome profile for chromedriver and selenium to (re-)use
generated session tokens, etc.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">selenium.webdriver.chrome.options</span> <span class="kn">import</span> <span class="n">Options</span>

<span class="n">home_dir</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">getenv</span><span class="p">(</span><span class="s">'HOME'</span><span class="p">)</span>
<span class="n">profile_dir</span> <span class="o">=</span> <span class="n">home_dir</span> <span class="o">+</span> <span class="s">'/.local/share/chrome/profile'</span>

<span class="n">chrome_options</span> <span class="o">=</span> <span class="n">Options</span><span class="p">()</span>
<span class="n">chrome_options</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="sa">r</span><span class="s">'user-data-dir='</span> <span class="o">+</span> <span class="n">profile_dir</span><span class="p">)</span>
<span class="n">chrome_options</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="sa">r</span><span class="s">'profile-directory=Automation'</span><span class="p">)</span>
</code></pre></div></div>

<p>This setups a profile directory at ./local/share/chrome/profile in our home
directory.  Ideally, you can make this directory more dynamic based on your
needs.  Check out <a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG Base Directory
Specs</a>
for some cross-platform inspiration.</p>

<h3 id="using-our-profile-with-our-chromedriver">Using our profile with our chromedriver</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">selenium</span> <span class="kn">import</span> <span class="n">webdriver</span>

<span class="n">driver</span> <span class="o">=</span> <span class="n">webdriver</span><span class="p">.</span><span class="n">Chrome</span><span class="p">(</span><span class="n">options</span><span class="o">=</span><span class="n">chrome_options</span><span class="p">)</span>

<span class="c1"># do some driver/DOM automation
</span>
<span class="n">driver</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="why-cant-we-use-my-normal-chrome-profile">Why can’t we use my normal chrome profile</h3>

<p>Unfortunately, as far as I can tell, two chrome processes can’t use the same
profile at the same time.  Furthermore, having chromedriver/selenium use your
regular chrome profile can cause problems.  I made one of my profiles unusuable
trying to do this.</p>

<h4 id="debugging-notes">Debugging Notes</h4>

<p>If SSO/Sessions do not seem to persist across python script runs.  A simple way
to check that the profile path is correct is to not close the driver
automatically.  And visit <code class="language-plaintext highlighter-rouge">chrome://version</code> in the spawned chrome window.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[When automating tasks with python scripts, you can hit a wall with SSO and oauth2. Creating a local chrome profile in your XDG data directory can allow one SSO login per work day and your python scripts can re-use the sessions.]]></summary></entry></feed>