<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="http://gustavohidalgo.com/feed.xml" rel="self" type="application/atom+xml" /><link href="http://gustavohidalgo.com/" rel="alternate" type="text/html" /><updated>2024-02-26T13:56:42+00:00</updated><id>http://gustavohidalgo.com/feed.xml</id><title type="html">Gustavo’s Blog</title><subtitle>Doing the thing, doing it well.</subtitle><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><entry><title type="html">5-minutes to Python packaging</title><link href="http://gustavohidalgo.com/python,/tools/2024/02/04/5-mins-for-python.html" rel="alternate" type="text/html" title="5-minutes to Python packaging" /><published>2024-02-04T12:34:23+00:00</published><updated>2024-02-04T12:34:23+00:00</updated><id>http://gustavohidalgo.com/python,/tools/2024/02/04/5-mins-for-python</id><content type="html" xml:base="http://gustavohidalgo.com/python,/tools/2024/02/04/5-mins-for-python.html"><![CDATA[<p>Collection of trivial Python package ecosystem tidbits.</p>

<h1 id="whath-is-the-module-resolution-process">Whath is the module resolution process?</h1>
<p><a href="https://docs.python.org/3/tutorial/modules.html#the-module-search-path">See here</a>.</p>

<h1 id="what-are-site-packages">What are <code class="language-plaintext highlighter-rouge">site-packages</code>?</h1>
<p>One of the module resolution locations is a <code class="language-plaintext highlighter-rouge">site-packages</code> directory.
If you don’t use a venv, this is basically a global install location for packages on your system.</p>

<h1 id="how-can-i-find-where-a-package-is-on-disk">How can I find where a package is on disk?</h1>
<p>Modules have a <code class="language-plaintext highlighter-rouge">__path__</code> property with their location on disk:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ python
Python 3.11.7 (main, Dec  4 2023, 18:10:11) [Clang 15.0.0 (clang-1500.1.0.2.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
&gt;&gt;&gt; import requests
&gt;&gt;&gt; requests.__path__
['/Users/gustavo/code/ghidalgo3.github.io/.venv/lib/python3.11/site-packages/requests']
</code></pre></div></div>
<p>In this case, I am using a <code class="language-plaintext highlighter-rouge">venv</code> so <code class="language-plaintext highlighter-rouge">site-packages</code> is local and NOT the sytem python on macOS.</p>

<h1 id="what-is-an-__init__py">What is an <code class="language-plaintext highlighter-rouge">__init__.py</code>?</h1>

<p>This file is implicitly executed when a module package is imported for the first time.</p>

<h1 id="how-do-i-build-a-wheel">How do I build a Wheel?</h1>
<p>Have a “frontend” like <a href="https://pypi.org/project/build/">build</a> and run it on your <code class="language-plaintext highlighter-rouge">pyproject.toml</code> file.
For example, run <code class="language-plaintext highlighter-rouge">python -m build</code> in the directory where a <code class="language-plaintext highlighter-rouge">pyproject.toml</code> file exists to build a wheel, many other CLI arguments available.</p>

<h1 id="im-a-repo-with-several-python-packages-how-do-i-express-a-dependency-between-them-without-having-to-build-wheels">I’m a repo with several python packages, how do I express a dependency between them without having to build wheels?</h1>
<p>More context, I’m used to the .NET model where repos contain several projects, MSBuild understands <code class="language-plaintext highlighter-rouge">ProjectReferences</code> and building at the top-level understands the topology of the project graph. 
At work, we have repos like <a href="https://github.com/microsoft/planetary-computer-tasks">PCTasks</a> that contain several python projects and my understanding is that Python doesn’t “understand” this dependency “graph”.
In that, modifying source code in the same repo doesn’t necessarilly mean that it will affect what you want it to affect.</p>

<p>UPDATE: This works the way I want it to work. The trick is to:</p>
<ol>
  <li>Use a venv in the repo</li>
  <li>Install packages as <em>editable installs</em> with <code class="language-plaintext highlighter-rouge">pip install -e</code></li>
</ol>

<p>I confirmed that’s actually what happens in the <a href="https://github.com/microsoft/planetary-computer-tasks/blob/main/scripts/install">install</a> script of planetary-computer-tasks.</p>

<h1 id="how-do-i-get-deterministic-pip-installs">How do I get deterministic <code class="language-plaintext highlighter-rouge">pip installs</code>?</h1>
<p>Use <a href="https://pypi.org/project/pip-tools/">pip-tools</a> and compile a <code class="language-plaintext highlighter-rouge">requirements.txt</code>.</p>

<h1 id="references">References</h1>

<ol>
  <li><a href="https://docs.python.org/3/reference/import.html#regular-packages">Importing regular/module packages</a></li>
  <li><a href="https://setuptools.pypa.io/en/latest/userguide/development_mode.html">Editable installations</a></li>
</ol>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="python," /><category term="tools" /><summary type="html"><![CDATA[Collection of trivial Python package ecosystem tidbits.]]></summary></entry><entry><title type="html">Kubernetes Day 1</title><link href="http://gustavohidalgo.com/aks/2024/02/04/doing-k8s.html" rel="alternate" type="text/html" title="Kubernetes Day 1" /><published>2024-02-04T12:34:23+00:00</published><updated>2024-02-04T12:34:23+00:00</updated><id>http://gustavohidalgo.com/aks/2024/02/04/doing-k8s</id><content type="html" xml:base="http://gustavohidalgo.com/aks/2024/02/04/doing-k8s.html"><![CDATA[<p>Enough cribbing about tooling, let’s run some containers!</p>

<h1 id="archetype">Archetype</h1>
<p>At $DAYJOB I maintain a lot of Python code, so to make everything transferrable I’m going to create a Python package called <code class="language-plaintext highlighter-rouge">archetype</code> which is your archetypical Azure Python REST service.
The only design constraint is that <code class="language-plaintext highlighter-rouge">archetype</code> must not do anything interesting! 
WYSIWYG, so to speak.
Its source code can be found <a href="https://github.com/ghidalgo3/ghidalgo3.github.io/tree/main/k8s">here</a>, and for my sanity I’ll document a little of its structure here:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">scripts</code> directory holds the project scripts according to the <a href="https://github.com/github/scripts-to-rule-them-all">scripts-to-rule-them-all</a> pattern.</li>
  <li><code class="language-plaintext highlighter-rouge">pyproject.toml</code> defines metadata for the project. This file is involved when packaging this into a wheel (a python package) and if I was publishing this on PyPI then this file would drive the metadata that others see about this package.</li>
  <li><code class="language-plaintext highlighter-rouge">src</code> holds the source code of the project. By default, the build backend Hatchling will use either <a href="https://hatch.pypa.io/1.9/plugins/builder/wheel/#default-file-selection">these files for wheels</a>, and <a href="https://hatch.pypa.io/1.9/plugins/builder/sdist/#default-file-selection">these files for source distributions</a>. This might be very important to understand if I was publishing on PyPI but for deploying services it seems not too important.</li>
  <li><code class="language-plaintext highlighter-rouge">requirements.txt</code> and <code class="language-plaintext highlighter-rouge">dev-requirements.txt</code> are generated by <code class="language-plaintext highlighter-rouge">pip-compile</code> using <code class="language-plaintext highlighter-rouge">pyproject.toml</code> and are not meant to be edited by hand. They are checked in and modified whenever the dependency set of the project changes.</li>
  <li><code class="language-plaintext highlighter-rouge">Dockerfile</code> simply copies the code, runs <code class="language-plaintext highlighter-rouge">setup</code> to install dependencies and install the package as an editable install, and then runs the project using <code class="language-plaintext highlighter-rouge">uvicorn</code> as the server.</li>
</ol>

<p>This is the output I see when running the project:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ ./scripts/server 
INFO:     Will watch for changes in these directories: ['/Users/gustavo/code/ghidalgo3.github.io/k8s']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [39552] using StatReload
Importing archetype module!
INFO:     Started server process [39554]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
</code></pre></div></div>

<h1 id="dockerizing-archetype">Dockerizing Archetype</h1>
<p>Dockerizing this project is pretty straight forward:</p>
<div class="language-Dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> python:3.11-bookworm</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">COPY</span><span class="s"> . /app</span>
<span class="k">RUN </span>scripts/setup
<span class="k">ENTRYPOINT</span><span class="s"> [ "uvicorn", "archetype.main:app", "--host=0.0.0.0"]</span>
</code></pre></div></div>

<p>Line by line:</p>
<ol>
  <li>Start with <a href="https://hub.docker.com/_/python">an image with Python pre-installed</a>, <code class="language-plaintext highlighter-rouge">python:3.11-bookwork</code></li>
  <li>Create a working directory <code class="language-plaintext highlighter-rouge">/app</code></li>
  <li>Copy everything under the Dockerfile’s path to <code class="language-plaintext highlighter-rouge">/app</code></li>
  <li>Run the same <code class="language-plaintext highlighter-rouge">scripts/setup</code> scripts inside the container. This install our dependencies!</li>
  <li>Leave a declared entry point for <code class="language-plaintext highlighter-rouge">uvicorn</code> to run its server.</li>
</ol>

<p>Then I publish this container to a private ACR with <code class="language-plaintext highlighter-rouge">docker push -t "gustavo2acr.azurecr.io/archetype:latest"</code>. Pretty simple!</p>

<h2 id="arm">ARM?</h2>
<p>Eventually I’ll come to find out that docker files are built for specific ISAs!
When I build images on my Mac, the image is an ARM build, not an x86 build.
If I want the image as is to run on the Kubernetes nodes I created, I would have needed to create a cluster with ARM CPU VMs. 
The solution is simply to ask docker to build an x86 image with <code class="language-plaintext highlighter-rouge">--platform=linux/amd64</code> in the <code class="language-plaintext highlighter-rouge">docker build</code> call.</p>

<h1 id="deploy-this-application-to-aks">Deploy this application to AKS.</h1>
<p>I’ll do this in the most verbose way possible first to motivate using Helm.
Verbose mandates tool purity, so <code class="language-plaintext highlighter-rouge">kubectl</code> is my only choice.
To authenticate againt the cluster, I simply run:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az aks get-credentials \
    --resource-group $(terraform output -raw resource_group_name) \
    --name $(terraform output -raw k8s_cluster_name)
</code></pre></div></div>
<p>Double checking that worked…</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ kubectl get services
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   192.168.0.1   &lt;none&gt;        443/TCP   20h
</code></pre></div></div>
<p>Cool, I have a configured <code class="language-plaintext highlighter-rouge">kubectl</code>.</p>

<p>Building this from the bottom up, my understanding is that I need 3 kubernetes resources:</p>
<ol>
  <li>A <code class="language-plaintext highlighter-rouge">Deployment</code> object</li>
  <li>A <code class="language-plaintext highlighter-rouge">Service</code> object</li>
  <li>A <code class="language-plaintext highlighter-rouge">Ingress</code> object</li>
</ol>

<h2 id="deployment">Deployment</h2>
<p>According to the docs, a <a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/">Deployment</a> resource declaratively controls <a href="https://kubernetes.io/docs/concepts/workloads/pods/">Pods</a> and <a href="https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/">ReplicaSets</a>.
I think since 90% of all kubenetes applications just need the cluster to make sure that <em>N</em> replicas of the application are running, deployments were created to satisfy this common use case.</p>

<p>Here’s our deployment definition:</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">archetype-deployment</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">archetype</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">2</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">archetype</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">archetype</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">archetype</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">gustavo2acr.azurecr.io/archetype:latest-amd64</span>
        <span class="na">ports</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8000</span>
        <span class="na">resources</span><span class="pi">:</span>
          <span class="na">requests</span><span class="pi">:</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s2">"</span><span class="s">64Mi"</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s2">"</span><span class="s">250m"</span>
          <span class="na">limits</span><span class="pi">:</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s2">"</span><span class="s">128Mi"</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s2">"</span><span class="s">500m"</span>
</code></pre></div></div>
<p>Then after applying it:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; kubectl describe deployment
Name:                   archetype-deployment
Namespace:              default
CreationTimestamp:      Sun, 25 Feb 2024 12:35:33 -0500
Labels:                 app=archetype
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=archetype
Replicas:               2 desired | 2 updated | 2 total | 0 available | 2 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=archetype
  Containers:
   archetype:
    Image:      gustavo2acr.azurecr.io/archetype:latest-amd64
    Port:       8000/TCP
    Host Port:  0/TCP
    Limits:
      cpu:     500m
      memory:  128Mi
    Requests:
      cpu:        250m
      memory:     64Mi
    Environment:  &lt;none&gt;
    Mounts:       &lt;none&gt;
  Volumes:        &lt;none&gt;
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      False   MinimumReplicasUnavailable
  Progressing    True    ReplicaSetUpdated
OldReplicaSets:  &lt;none&gt;
NewReplicaSet:   archetype-deployment-75cc984b68 (2/2 replicas created)
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  3m23s  deployment-controller  Scaled up replica set archetype-deployment-75cc984b68 to 2
</code></pre></div></div>
<h3 id="resource-limits">Resource Limits</h3>
<p>I’m cheap, so my cluster only has 1 node and it’s a little 2-core Azure VM.
It’s remarkable how many pods are running by default in a cluster doing nothing:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ kubectl describe node
Name:               aks-default-42366886-vmss000000
Roles:              agent
Labels:             agentpool=default
                    beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/instance-type=Standard_DS2_v2
                    beta.kubernetes.io/os=linux
                    failure-domain.beta.kubernetes.io/region=eastus2
                    failure-domain.beta.kubernetes.io/zone=0
                    kubernetes.azure.com/agentpool=default
                    kubernetes.azure.com/cluster=MC_gustavo2-rg_gustavo2-aks-cluster_eastus2
                    kubernetes.azure.com/consolidated-additional-properties=91882a8a-d34a-11ee-bd45-2a30a704d842
                    kubernetes.azure.com/kubelet-identity-client-id=abb742dc-7a98-47b0-8d03-cc3d662c02c6
                    kubernetes.azure.com/mode=system
                    kubernetes.azure.com/node-image-version=AKSUbuntu-2204gen2containerd-202402.07.0
                    kubernetes.azure.com/nodepool-type=VirtualMachineScaleSets
                    kubernetes.azure.com/os-sku=Ubuntu
                    kubernetes.azure.com/role=agent
                    kubernetes.azure.com/storageprofile=managed
                    kubernetes.azure.com/storagetier=Premium_LRS
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=aks-default-42366886-vmss000000
                    kubernetes.io/os=linux
                    kubernetes.io/role=agent
                    node-role.kubernetes.io/agent=
                    node.kubernetes.io/instance-type=Standard_DS2_v2
                    storageprofile=managed
                    storagetier=Premium_LRS
                    topology.disk.csi.azure.com/zone=
                    topology.kubernetes.io/region=eastus2
                    topology.kubernetes.io/zone=0
Annotations:        csi.volume.kubernetes.io/nodeid:
                      {"disk.csi.azure.com":"aks-default-42366886-vmss000000","file.csi.azure.com":"aks-default-42366886-vmss000000"}
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Sat, 24 Feb 2024 14:28:09 -0500
Taints:             &lt;none&gt;
Unschedulable:      false
Lease:
  HolderIdentity:  aks-default-42366886-vmss000000
  AcquireTime:     &lt;unset&gt;
  RenewTime:       Sun, 25 Feb 2024 13:06:03 -0500
Conditions:
  Type                          Status  LastHeartbeatTime                 LastTransitionTime                Reason                          Message
  ----                          ------  -----------------                 ------------------                ------                          -------
  ContainerRuntimeProblem       False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   ContainerRuntimeIsUp            container runtime service is up
  VMEventScheduled              False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   NoVMEventScheduled              VM has no scheduled event
  FrequentDockerRestart         False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   NoFrequentDockerRestart         docker is functioning properly
  ReadonlyFilesystem            False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   FilesystemIsNotReadOnly         Filesystem is not read-only
  FrequentKubeletRestart        False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   NoFrequentKubeletRestart        kubelet is functioning properly
  KubeletProblem                False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   KubeletIsUp                     kubelet service is up
  FilesystemCorruptionProblem   False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   FilesystemIsOK                  Filesystem is healthy
  KernelDeadlock                False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   KernelHasNoDeadlock             kernel has no deadlock
  FrequentUnregisterNetDevice   False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   NoFrequentUnregisterNetDevice   node is functioning properly
  FrequentContainerdRestart     False   Sun, 25 Feb 2024 13:02:16 -0500   Sat, 24 Feb 2024 14:29:40 -0500   NoFrequentContainerdRestart     containerd is functioning properly
  MemoryPressure                False   Sun, 25 Feb 2024 13:04:21 -0500   Sat, 24 Feb 2024 14:28:09 -0500   KubeletHasSufficientMemory      kubelet has sufficient memory available
  DiskPressure                  False   Sun, 25 Feb 2024 13:04:21 -0500   Sat, 24 Feb 2024 14:28:09 -0500   KubeletHasNoDiskPressure        kubelet has no disk pressure
  PIDPressure                   False   Sun, 25 Feb 2024 13:04:21 -0500   Sat, 24 Feb 2024 14:28:09 -0500   KubeletHasSufficientPID         kubelet has sufficient PID available
  Ready                         True    Sun, 25 Feb 2024 13:04:21 -0500   Sat, 24 Feb 2024 14:28:10 -0500   KubeletReady                    kubelet is posting ready status. AppArmor enabled
Addresses:
  InternalIP:  10.0.1.4
  Hostname:    aks-default-42366886-vmss000000
Capacity:
  cpu:                2
  ephemeral-storage:  129886128Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             7097684Ki
  pods:               30
Allocatable:
  cpu:                1900m
  ephemeral-storage:  119703055367
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             4652372Ki
  pods:               30
System Info:
  Machine ID:                 91d02b7297d74d2290142148194c2bec
  System UUID:                e0c121d3-1932-4eb1-b50f-fbec6960eccd
  Boot ID:                    36431874-31af-4b99-84d5-11c14da820a8
  Kernel Version:             5.15.0-1054-azure
  OS Image:                   Ubuntu 22.04.3 LTS
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://1.7.7-1
  Kubelet Version:            v1.27.9
  Kube-Proxy Version:         v1.27.9
ProviderID:                   azure:///subscriptions/394dccd4-6f06-4003-8c9a-671ba0d665c7/resourceGroups/mc_gustavo2-rg_gustavo2-aks-cluster_eastus2/providers/Microsoft.Compute/virtualMachineScaleSets/aks-default-42366886-vmss/virtualMachines/0
Non-terminated Pods:          (16 in total)
  Namespace                   Name                                     CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                     ------------  ----------  ---------------  -------------  ---
  app-routing-system          nginx-5d4cbcf56b-qwkn4                   500m (26%)    0 (0%)      127Mi (2%)       0 (0%)         76m
  app-routing-system          nginx-5d4cbcf56b-rkrx4                   500m (26%)    0 (0%)      127Mi (2%)       0 (0%)         77m
  default                     archetype-deployment-69945d5cf9-klrff    0 (0%)        0 (0%)      0 (0%)           0 (0%)         109s
  default                     archetype-deployment-69945d5cf9-wnh7p    0 (0%)        0 (0%)      0 (0%)           0 (0%)         3m28s
  kube-system                 azure-ip-masq-agent-lbpkh                100m (5%)     500m (26%)  50Mi (1%)        250Mi (5%)     22h
  kube-system                 cloud-node-manager-qbxww                 50m (2%)      0 (0%)      50Mi (1%)        512Mi (11%)    22h
  kube-system                 coredns-789789675-nn9qz                  100m (5%)     3 (157%)    70Mi (1%)        500Mi (11%)    22h
  kube-system                 coredns-789789675-ss2hf                  100m (5%)     3 (157%)    70Mi (1%)        500Mi (11%)    22h
  kube-system                 coredns-autoscaler-649b947bbd-xr74t      20m (1%)      200m (10%)  10Mi (0%)        500Mi (11%)    22h
  kube-system                 csi-azuredisk-node-69clt                 30m (1%)      0 (0%)      60Mi (1%)        400Mi (8%)     22h
  kube-system                 csi-azurefile-node-mwcpv                 30m (1%)      0 (0%)      60Mi (1%)        600Mi (13%)    22h
  kube-system                 konnectivity-agent-7565b9c9b8-bmjb2      20m (1%)      1 (52%)     20Mi (0%)        1Gi (22%)      22h
  kube-system                 konnectivity-agent-7565b9c9b8-t55pk      20m (1%)      1 (52%)     20Mi (0%)        1Gi (22%)      22h
  kube-system                 kube-proxy-nct72                         100m (5%)     0 (0%)      0 (0%)           0 (0%)         22h
  kube-system                 metrics-server-5bd48455f4-6cmbn          50m (2%)      145m (7%)   85Mi (1%)        355Mi (7%)     22h
  kube-system                 metrics-server-5bd48455f4-n8km8          50m (2%)      145m (7%)   85Mi (1%)        355Mi (7%)     22h
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                1670m (87%)  8990m (473%)
  memory             834Mi (18%)  6020Mi (132%)
  ephemeral-storage  0 (0%)       0 (0%)
  hugepages-1Gi      0 (0%)       0 (0%)
  hugepages-2Mi      0 (0%)       0 (0%)
Events:              &lt;none&gt;
</code></pre></div></div>

<p>The interesting bit is that the VM has a CPU capacity of <code class="language-plaintext highlighter-rouge">1900m</code> (about 2 cores) and the current requested CPU consumption is <code class="language-plaintext highlighter-rouge">1670m</code> CPU from all the pods (that I didn’t create BTW!).
The deployment requests to deploy 2 <code class="language-plaintext highlighter-rouge">archetype</code> pods that require <code class="language-plaintext highlighter-rouge">500m</code> CPU each so naturally Kubernetes cannot schedule them because <code class="language-plaintext highlighter-rouge">1670m + 2 * 500m &gt; 1900m</code>.
I could create more VMs to increase cluster resourcing but I’m cheap so instead I’ll remove the resource request and limits from the pod spec.
Maybe this is dangerous in production but for my learning purposes I don’t really care about violating resource constraints.</p>

<h2 id="service">Service</h2>
<p>The service definition looks like this:</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">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">archetype-service</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ClusterIP</span>
  <span class="na">ports</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="m">80</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8000</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">archetype</span>
</code></pre></div></div>
<p>Note that the <code class="language-plaintext highlighter-rouge">spec.selector.app</code> of the service matches <code class="language-plaintext highlighter-rouge">spec.template.metadata.labels.app</code> of the deployment.
This is how kubernetes finds <code class="language-plaintext highlighter-rouge">archetype</code> pods to expose as the <code class="language-plaintext highlighter-rouge">archetype-service</code>.
If the labels don’t match, then the pods simply aren’t exposed!</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ kubectl describe service archetype-service
Name:              archetype-service
Namespace:         default
Labels:            &lt;none&gt;
Annotations:       &lt;none&gt;
Selector:          app=archetype
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                192.168.90.238
IPs:               192.168.90.238
Port:              &lt;unset&gt;  80/TCP
TargetPort:        8000/TCP
Endpoints:         &lt;none&gt;
Session Affinity:  None
Events:            &lt;none&gt;
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">ClusterIP</code> services are only available inside the cluster.
That means, a pod within the cluster can communicate with the service either at IP address <code class="language-plaintext highlighter-rouge">192.168.90.238</code> or at address <code class="language-plaintext highlighter-rouge">archetype</code>.
The outside world cannot reach this service!
For that we need to define an <code class="language-plaintext highlighter-rouge">Ingress</code> object.</p>

<h2 id="ingress">Ingress</h2>
<p>This is where the <a href="https://learn.microsoft.com/en-us/azure/aks/app-routing?tabs=default%2Cdeploy-app-default">Azure documentation</a> falls apart.</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">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Ingress</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">archetype-ingress</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">ingressClassName</span><span class="pi">:</span> <span class="s">webapprouting.kubernetes.azure.com</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">host</span><span class="pi">:</span> <span class="s">archetype.gustavohidalgo.com</span>
    <span class="na">http</span><span class="pi">:</span>
      <span class="na">paths</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">backend</span><span class="pi">:</span>
          <span class="na">service</span><span class="pi">:</span>
            <span class="na">name</span><span class="pi">:</span> <span class="s">archetype-service</span>
            <span class="na">port</span><span class="pi">:</span>
              <span class="na">number</span><span class="pi">:</span> <span class="m">80</span>
        <span class="na">path</span><span class="pi">:</span> <span class="s">/</span>
        <span class="na">pathType</span><span class="pi">:</span> <span class="s">Prefix</span>
</code></pre></div></div>
<p>Looking at the <a href="https://learn.microsoft.com/en-us/azure/aks/app-routing?tabs=default%2Cdeploy-app-default#limitations">limitations</a>:</p>

<blockquote>
  <p>All global Azure DNS zones integrated with the add-on have to be in the same resource group.</p>
</blockquote>

<p>Ok that’s kind of a stupid limitation.
I already have a DNS Zone resource that I created long ago for <code class="language-plaintext highlighter-rouge">gustavohidalgo.com</code> in a different resource group and I don’t know if I need to or should create a new DNS zone.</p>

<p>Maybe we don’t need to create a DNS zone, because actually this worked:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ curl 4.152.12.223/ -H "Host: archetype.gustavohidalgo.com"
{"Hello":"NAMELESS!"}
</code></pre></div></div>
<p>And the address <code class="language-plaintext highlighter-rouge">4.152.12.223</code> was allocated either when I enabled the app routing feature or when I created the ingress.
Who knows, AKS definitely doesn’t tell you when new billable resources are created under your nose!</p>

<h1 id="conclusion">Conclusion</h1>
<p>After all of this, I have a dockerized Python application running in a Kubenetes cluster.
Cool, but I still feel like AKS is overly complicated.</p>

<h1 id="reference">Reference</h1>
<ol>
  <li><a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#writing-pyproject-toml">Python Project TOML</a></li>
  <li><a href="https://packaging.python.org/en/latest/tutorials/packaging-projects/">Python Packaging</a></li>
  <li><a href="https://www.bitecode.dev/p/relieving-your-python-packaging-pain">Relieving Python packaging pain</a></li>
  <li><a href="https://paulyu.dev/article/managed-ingress-on-aks/">AKS Ingress Options</a></li>
  <li><a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors">Kubernetes Label Selectors</a></li>
</ol>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="aks" /><summary type="html"><![CDATA[Enough cribbing about tooling, let’s run some containers!]]></summary></entry><entry><title type="html">Terraform, please don’t be dumb</title><link href="http://gustavohidalgo.com/terraform,/aks,/tools/2024/02/04/terraform-secrets.html" rel="alternate" type="text/html" title="Terraform, please don’t be dumb" /><published>2024-02-04T12:34:23+00:00</published><updated>2024-02-04T12:34:23+00:00</updated><id>http://gustavohidalgo.com/terraform,/aks,/tools/2024/02/04/terraform-secrets</id><content type="html" xml:base="http://gustavohidalgo.com/terraform,/aks,/tools/2024/02/04/terraform-secrets.html"><![CDATA[<p><a href="https://martinfowler.com/bliki/TwoHardThings.html">Cache invalidation</a> is one of the hardest things to solve in computer science.
Cloud DevOps is also really hard.
Why terraform decided that you needed to cache cloud provider state is beyond me.</p>

<p>In their words:</p>
<blockquote>
  <p>Terraform must store state about your managed infrastructure and configuration. This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.</p>
</blockquote>

<p>Must it?</p>
<ol>
  <li>Tracking real-world resources: Your cloud provider is already tracking all your resources buddy, that is how they bill you.</li>
  <li>Tracking metadata: I assume they mean things like resource properties. This one is tricky in Azure because of <a href="https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md#api-versioning">Azure’s REST Guidelines</a>. If you use <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/">Azure Resource Templates or Bicep files</a> you are always keenly aware of exactly what properties you are setting in your resource because 1) you must declare an exact <code class="language-plaintext highlighter-rouge">api-version</code> and 2) the property name matches exactly what the REST API says with no translations to lower_case_underscores. So the problem then becomes: when Terraform creates or updates a resource you don’t know exactly what API version the provider used and therefore you don’t know exactly what request was sent to Azure and therefore you don’t know exactly what your resource actually looks like. This only makes cloud DevOps harder than it needs to be IMO.</li>
  <li>Improve performance: Yea maybe, if Terraform’s error messages and error recovery were good then I wouldn’t have a problem with this one. However when Terraform can’t delete a resource because it can’t figure out the dependencies between things because it’s “intelligently” trying to delete things in reverse topological order, then I really don’t care about performance.</li>
</ol>

<h1 id="is-that-it">Is that it?</h1>
<p>NO! The Azure Terraform provider also stores secrets in its lock file.
It looks like a bearer token, not sure why it does this.
Thankfully GitHub alerted me that I had commited a secret <a href="https://github.com/ghidalgo3/ghidalgo3.github.io/commit/d4405e4b50df522dc30ddb534ccba352eee51f5a#diff-af426e3f6e243f3640b78016eeb13738ba1d41e53dc1f46e08bc240308cbccb4">here</a> and I immediately went to delete the resources the secrets reference.</p>

<p>HashiCorp tries to prevent this by asking you to use <a href="https://developer.hashicorp.com/terraform/language/state/remote">remote state</a> which is just a stepping stone to upselling you to <a href="https://www.hashicorp.com/products/terraform">Terraform Cloud</a>.
Following the classic tactic of selling you a tool that creates a problem that only they have a solution for, I’ll keep thinking Terraform is over-engineered.</p>

<h1 id="lessons-learned">Lessons Learned</h1>
<p>Deeply review your commit before you push them.
If you don’t trust yourself or your team, <a href="https://microsoft.github.io/code-with-engineering-playbook/continuous-integration/dev-sec-ops/secret-management/recipes/detect-secrets/">use technology</a>.</p>

<h1 id="references">References</h1>
<ol>
  <li><a href="https://developer.hashicorp.com/terraform/language/state">Terraform State</a></li>
  <li><a href="https://stackoverflow.com/questions/38486335/should-i-commit-tfstate-files-to-git">Should I commit .tfstate files?</a></li>
</ol>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="terraform," /><category term="aks," /><category term="tools" /><summary type="html"><![CDATA[Cache invalidation is one of the hardest things to solve in computer science. Cloud DevOps is also really hard. Why terraform decided that you needed to cache cloud provider state is beyond me.]]></summary></entry><entry><title type="html">New Job</title><link href="http://gustavohidalgo.com/life/2024/01/26/new-job.html" rel="alternate" type="text/html" title="New Job" /><published>2024-01-26T16:34:23+00:00</published><updated>2024-01-26T16:34:23+00:00</updated><id>http://gustavohidalgo.com/life/2024/01/26/new-job</id><content type="html" xml:base="http://gustavohidalgo.com/life/2024/01/26/new-job.html"><![CDATA[<p>It’s been about 3 weeks since I started at Microsoft again, and I think it’s important to share some internet memes about it.
These memes do not necessariy represent any truth or lies about Microsoft and they are entirely my opinion and not necessarily Microsoft’s.</p>

<h1 id="kubernetes-memes">Kubernetes memes</h1>
<p><a href="https://medium.com/skale-5/19-memes-about-kubernetes-86d4ee87ba1b">Someone beat me to the punch</a>.</p>

<h1 id="python-memes">Python memes</h1>
<p><img src="https://imgs.xkcd.com/comics/python_environment.png" alt="Environments" /></p>

<h1 id="misc-cosmic-horrors">Misc cosmic horrors</h1>
<p>Did you know Terraform for AWS has a boatload more effor put into it than Terraform for Azure?
Don’t just believe me, compare the <a href="https://github.com/hashicorp/terraform-provider-aws">AWS provider</a> with the <a href="https://github.com/hashicorp/terraform-provider-azurerm">Azure provider</a>.</p>

<p><img src="https://miro.medium.com/v2/resize:fit:1154/format:webp/0*BWTwPe-hON4bPJ6c" alt="Title" /></p>

<p>I’m not saying ARM templates or Bicep files are “good” but at least I am literally just defining an easy to reason about DAG of idempotent REST calls.</p>

<h2 id="references">References</h2>
<ol>
  <li>https://medium.com/driven-by-code/the-terrors-and-joys-of-terraform-88bbd1aa4359</li>
</ol>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="life" /><summary type="html"><![CDATA[It’s been about 3 weeks since I started at Microsoft again, and I think it’s important to share some internet memes about it. These memes do not necessariy represent any truth or lies about Microsoft and they are entirely my opinion and not necessarily Microsoft’s.]]></summary></entry><entry><title type="html">Can’t avoid Kubernetes anymore</title><link href="http://gustavohidalgo.com/kubernetes,/tools/2024/01/25/kubernetes-gets-everyone.html" rel="alternate" type="text/html" title="Can’t avoid Kubernetes anymore" /><published>2024-01-25T16:34:23+00:00</published><updated>2024-01-25T16:34:23+00:00</updated><id>http://gustavohidalgo.com/kubernetes,/tools/2024/01/25/kubernetes-gets-everyone</id><content type="html" xml:base="http://gustavohidalgo.com/kubernetes,/tools/2024/01/25/kubernetes-gets-everyone.html"><![CDATA[<p>Time comes for us all, and this time it came for me.
I can’t avoid learning Kubernetes anymore so here we go!!!</p>

<h1 id="fake-news">Fake News</h1>
<p>Kubernetes is so great and fast moving that all of the old documentation for it is now deprecated.
I was reading a book that talked about <a href="https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/">ReplicationControllers</a> but if you click on that link you see that <a href="https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/"><em>actually ReplicaSets are the recommended way of doing this</em></a>. I get it, software changes, I’m not mad about that.
I’m mad that I wasn’t told ahead of time to check the publish date and ignore anything that’s 3+ years old 🤣.</p>

<h1 id="pick-a-cloud-any-cloud">Pick a cloud, any cloud</h1>
<p>You want to run a local kubernetes cluster? 
I hope you don’t mind jank because <a href="https://minikube.sigs.k8s.io/docs/start/">minikube</a> and <a href="https://kind.sigs.k8s.io">kind</a> are janky. For starters, you can’t just <code class="language-plaintext highlighter-rouge">docker build</code> an image and <a href="https://iximiuz.com/en/posts/kubernetes-kind-load-docker-image/">expect a local kind cluster to use it</a>.
Writing this made me look up that there are <a href="https://minikube.sigs.k8s.io/docs/handbook/pushing/#1-pushing-directly-to-the-in-cluster-docker-daemon-docker-env">8 ways to do this</a> 🤣.</p>

<p>The minikube team failed here.
By default minikube should look for images built by the local docker host and if it can’t find any images then it should look for them in remote container registries.
Of course if you had <em>just</em> pushed an image to docker hub or GCP or Azure or AWS or whatever then you would be perfect!
How dare I assume that I should be able to use the computer I already paid for and not a cloud service…</p>

<p>Fine, I’ll play your game Kubernetes. Let’s get cloudy.</p>

<h1 id="i-have-to-have-my-tools">I have to have my tools</h1>
<p>I installed:</p>
<ol>
  <li><a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a>: To build Docker images</li>
  <li><a href="https://learn.microsoft.com/en-us/cli/azure/install-azure-cli">Azure CLI</a>: To interact with the Azure control plane and authenticate.</li>
  <li><a href="https://kubernetes.io/docs/tasks/tools/">kubectl</a>: To interact with the k8s control plane.</li>
  <li><a href="https://developer.hashicorp.com/terraform/install">Terraform</a>: This is what we use at work and I need to get good at it.</li>
  <li><a href="https://helm.sh/docs/intro/install/">Helm</a>: We use this at work so I need to get good at it.</li>
  <li><a href="https://k9scli.io">k9s</a>: This is a basically a GUI frontend for <code class="language-plaintext highlighter-rouge">kubectl</code>. It is not necessary but it makes inspecting clusters a little more fun.</li>
</ol>

<p>OK enough tooling, let’s create a cluster!</p>

<h2 id="whats-in-a-cluster">What’s in a cluster?</h2>
<p>A k8s cluster is a set of machines (k8s calls them <em>nodes</em>) running applications (losely defined, a <em>pod</em> is an instance of an application) managed by control plane processes which are usually running in dedicated controller nodes and communicate with “agents” that are running on worker node.</p>

<p>In the following image, we see a cluster with 2 nodes, and a control plane node.
<img src="https://kubernetes.io/images/docs/kubernetes-cluster-architecture.svg" alt="k8s" /></p>

<p>When you run <code class="language-plaintext highlighter-rouge">kubectl</code> to administer your cluster, you are making requests to <code class="language-plaintext highlighter-rouge">kube-api-server</code> in the control plane.
<code class="language-plaintext highlighter-rouge">kube-api-server</code> does some validation on your request and stores it in <code class="language-plaintext highlighter-rouge">etcd</code>, a kind of simple distributed database for your control plane nodes.
Then the control plane and the kubelets (agents) running on the nodes coordinate to instantiate the right kind and number of pods that the cluster should be running.</p>

<h1 id="terraforming-it-up">Terraforming it up</h1>
<p>Since we’re in Azure land, let’s write a terraform file to deploy an <a href="https://learn.microsoft.com/en-us/azure/aks/">Azure Kubernetes Service</a> cluster.
Basically an AKS cluster is a <a href="https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/overview">VM Scale Set</a> with the <code class="language-plaintext highlighter-rouge">kubelet</code> and <code class="language-plaintext highlighter-rouge">kube-proxy</code> agents configured to talk to a central kubernetes control plane run by the AKS service.
You “own” the worker nodes and Azure “owns” the the control plane nodes.
The documentation puts it like this:</p>

<blockquote>
  <p>When you create an AKS cluster, a control plane is automatically created and configured. This control plane is provided at no cost as a managed Azure resource abstracted from the user. You only pay for and manage the nodes attached to the AKS cluster.</p>
</blockquote>

<p>Ok let’s write this file:</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">resource</span> <span class="s2">"azurerm_kubernetes_cluster"</span> <span class="s2">"aks_cluster"</span> <span class="p">{</span>
  <span class="nx">name</span>                <span class="p">=</span> <span class="s2">"</span><span class="k">${</span><span class="kd">var</span><span class="p">.</span><span class="nx">prefix</span><span class="k">}</span><span class="s2">-aks-cluster"</span>
  <span class="nx">location</span>            <span class="p">=</span> <span class="s2">"eastus2"</span>
  <span class="nx">resource_group_name</span> <span class="p">=</span> <span class="nx">azurerm_resource_group</span><span class="p">.</span><span class="nx">rg</span><span class="p">.</span><span class="nx">name</span>
  <span class="nx">dns_prefix</span>          <span class="p">=</span> <span class="s2">"</span><span class="k">${</span><span class="kd">var</span><span class="p">.</span><span class="nx">prefix</span><span class="k">}</span><span class="s2">aks"</span>
  <span class="nx">network_profile</span> <span class="p">{</span>
    <span class="nx">network_plugin</span> <span class="p">=</span> <span class="s2">"azure"</span>
    <span class="nx">dns_service_ip</span> <span class="p">=</span> <span class="s2">"192.168.255.254"</span>
    <span class="nx">service_cidrs</span>  <span class="p">=</span> <span class="p">[</span><span class="s2">"192.168.0.0/16"</span><span class="p">]</span>
  <span class="p">}</span>

  <span class="nx">default_node_pool</span> <span class="p">{</span>
    <span class="nx">name</span>           <span class="p">=</span> <span class="s2">"default"</span>
    <span class="nx">node_count</span>     <span class="p">=</span> <span class="mi">1</span>
    <span class="nx">vnet_subnet_id</span> <span class="p">=</span> <span class="k">data</span><span class="p">.</span><span class="nx">azurerm_subnet</span><span class="p">.</span><span class="nx">node_subnet</span><span class="p">.</span><span class="nx">id</span>
    <span class="nx">vm_size</span>        <span class="p">=</span> <span class="s2">"Standard_DS2_v2"</span>
  <span class="p">}</span>

  <span class="nx">identity</span> <span class="p">{</span>
    <span class="nx">type</span> <span class="p">=</span> <span class="s2">"SystemAssigned"</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="errors">Errors</h2>
<p>What happens when you get this error?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Code="ServiceCidrOverlapExistingSubnetsCidr" Message="The specified service CIDR 10.0.0.0/16 is conflicted with an existing subnet CIDR 10.0.1.0/24" Target="networkProfile.serviceCIDR"
</code></pre></div></div>

<p><a href="https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/error-code-servicecidroverlapexistingsubnetscidr">This helpful troubleshooting page</a> doesn’t actually address the problem, but here’s what I understand: there are 2 address spaces at play here, Azure’s and Kubernetes’.
They cannot overlap because otherwise <code class="language-plaintext highlighter-rouge">kube-proxy</code> <a href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#pod-network">could not configure unambiguous routing rules between pods</a>.
By default (and the error message tells us this but I can’t find this actually documented anywhere within Microsoft’s documenation), AKS clusters take 10.0.0.0/16.
Not exactly true, I did find it mentioned on <a href="https://learn.microsoft.com/en-us/azure/aks/azure-cni-overlay?tabs=kubectl#deploy-a-dual-stack-aks-cluster">this page</a> talking about dual-stack IPv4/6 deployments.
Anyway, I want my virtual network to use 10.0.0.0/16 so AKS needs to bend-the-knee to <a href="https://datatracker.ietf.org/doc/html/rfc1918">RFC1918</a> with this <code class="language-plaintext highlighter-rouge">network_profile</code>:</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">network_profile</span> <span class="p">{</span>
    <span class="nx">network_plugin</span> <span class="p">=</span> <span class="s2">"azure"</span>
    <span class="nx">dns_service_ip</span> <span class="p">=</span> <span class="s2">"192.168.255.254"</span>
    <span class="nx">service_cidrs</span> <span class="p">=</span> <span class="p">[</span> <span class="s2">"192.168.0.0/16"</span> <span class="p">]</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here are some more constraints on these address spaces <a href="https://learn.microsoft.com/en-us/azure/aks/azure-cni-overlay?tabs=kubectl#ip-address-planning">you oughta know</a>.</p>

<p>Sidebar about <a href="https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/error-code-servicecidroverlapexistingsubnetscidr">this page</a>, when your customers encounter this problem the first solution shouldn’t be to go and delete the virtual network.
If someone is deploying nodes into a virtual network they are doing that on purpose.
Don’t ask them to go delete it and start over, the first solution should have been explaining how to change the cluster service CIDR range.</p>

<h1 id="conclusion">Conclusion</h1>
<p>Ok that’s enough for one day, I now have an AKS cluster with one VM up and running.
Next time I’ll actually deploy an application to it an explore the world of <code class="language-plaintext highlighter-rouge">kubectl</code>, <code class="language-plaintext highlighter-rouge">helm</code>, and Ingress controllers.</p>

<h1 id="references">References</h1>
<ol>
  <li><a href="https://etcd.io">etcd</a></li>
  <li><a href="https://github.com/kubernetes/kubernetes/">Kubernetes source code</a></li>
  <li><a href="https://www.reddit.com/r/kubernetes/comments/10n7a9t/how_does_control_plane_kubelet_communication_work/">Who talks to whom?</a></li>
  <li><a href="https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/kube-proxy">kube-proxy source code</a></li>
</ol>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="kubernetes," /><category term="tools" /><summary type="html"><![CDATA[Time comes for us all, and this time it came for me. I can’t avoid learning Kubernetes anymore so here we go!!!]]></summary></entry><entry><title type="html">My C# project structure</title><link href="http://gustavohidalgo.com/c%23/2023/06/30/my-csharp-projects.html" rel="alternate" type="text/html" title="My C# project structure" /><published>2023-06-30T16:34:23+00:00</published><updated>2023-06-30T16:34:23+00:00</updated><id>http://gustavohidalgo.com/c%23/2023/06/30/my-csharp-projects</id><content type="html" xml:base="http://gustavohidalgo.com/c%23/2023/06/30/my-csharp-projects.html"><![CDATA[<p>Writing C# code involves using <code class="language-plaintext highlighter-rouge">msbuild</code> as the build system that resolves dependencies, orders projects topologically, compiles your codes, gathers your output files, creates publishable artifacts, etc.
I learned to author <code class="language-plaintext highlighter-rouge">msbuild</code> project files quite well at Microsoft, and I got a few opinions out of it.
In this post, I will summarize the best practices I have learned working with <code class="language-plaintext highlighter-rouge">msbuild</code> for C# development since 2016.</p>

<h1 id="vocabulary">Vocabulary</h1>
<p>Words are important, especially with a build system. In one sentence:</p>
<blockquote>
  <p><code class="language-plaintext highlighter-rouge">msbuild</code> builds <em>projects</em> by sequencing <em>targets</em> which execute <em>tasks</em> with <em>properties</em> and <em>item groups</em> as inputs.</p>
</blockquote>

<p>Each word deserves a good definition:</p>
<ol>
  <li>Project: The XML file that <code class="language-plaintext highlighter-rouge">msbuild</code> reads and builds.</li>
  <li>Target: A named <em>step</em> in a build. Targets only sequence build actions, they are not the actions themselves.</li>
  <li>Task: The “functions” that <code class="language-plaintext highlighter-rouge">msbuild</code> calls during the build. Tasks can be built-in to <code class="language-plaintext highlighter-rouge">msbuild</code>, code that is evaluated at build time, or calls to arbitrary process.</li>
  <li>Property: Simple named values that influence the build. All properties are strings ultimately.</li>
  <li>Item Groups: Named collections of items, often files but not necessarily.</li>
</ol>

<p>A good example is walking through what happens when you build a C# project. 
Every file of source code is put into the <code class="language-plaintext highlighter-rouge">Compile</code> item group.
The <code class="language-plaintext highlighter-rouge">Build</code> target eventually calls the <code class="language-plaintext highlighter-rouge">Csc</code> task (that’s the C# compiler task) and passed every file as an argument to the task.
The property <code class="language-plaintext highlighter-rouge">Configuration</code> is also passed to the <code class="language-plaintext highlighter-rouge">Csc</code> task to control release and debug builds.</p>

<h1 id="calling-msbuild">Calling MSBuild</h1>
<p>If you use Visual Studio, you need to use the <code class="language-plaintext highlighter-rouge">msbuild.exe</code> that ships with the version of Visual Studio you are using.
If you don’t match <code class="language-plaintext highlighter-rouge">msbuild.exe</code> versions with Visual Studio versions, undefined things will happen when someone or something else tries to build the project on a different machine.
To guarantee this, you need to use the Developer Command Prompt (or Developer PowerShell) for the version of Visual Studio you use.
Calling that program will put the right <code class="language-plaintext highlighter-rouge">msbuild.exe</code> on your <code class="language-plaintext highlighter-rouge">PATH</code>, along with a host of other Visual Studio tools..</p>

<p>If you use the <code class="language-plaintext highlighter-rouge">dotnet</code> CLI, <code class="language-plaintext highlighter-rouge">msbuild</code> is implicitly invoked when you call <code class="language-plaintext highlighter-rouge">dotnet build</code>, but you can unleash the beast by calling <code class="language-plaintext highlighter-rouge">dotnet msbuild</code> instead. 
That command will call the appropriate version of <code class="language-plaintext highlighter-rouge">msbuild</code> that ships with the .NET SDK version you are using and forward any command line arguments to <code class="language-plaintext highlighter-rouge">msbuild</code>.</p>

<p>Do one of those two and you should not have any issues.</p>

<h1 id="controlling-net-sdk-versions">Controlling .NET SDK versions</h1>
<p>It is important to ensure that developer machines and CI/CD machines all use the same version of the .NET SDK.
That can be controlled by using a <code class="language-plaintext highlighter-rouge">global.json</code> file.
The full documentation for <code class="language-plaintext highlighter-rouge">global.json</code> <a href="https://learn.microsoft.com/en-us/dotnet/core/tools/global-json">can be found here</a>, but basically I always roll with something like this:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"sdk"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"7.0.102"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"rollForward"</span><span class="p">:</span><span class="w"> </span><span class="s2">"latestFeature"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h1 id="put-common-build-settings-in-a-directorybuildprops">Put common build settings in a <code class="language-plaintext highlighter-rouge">Directory.Build.props</code></h1>
<p>Since <code class="language-plaintext highlighter-rouge">msbuild</code> operates on XML files, you can re-use build settings by <code class="language-plaintext highlighter-rouge">Import</code>-ing XML files <a href="https://learn.microsoft.com/en-us/visualstudio/msbuild/import-element-msbuild?view=vs-2022">like this</a>.
For along time, this was the only way to organize and re-use build configuration.
In newer versions of <code class="language-plaintext highlighter-rouge">msbuild</code>, a project’s build will search the file system for a file named <code class="language-plaintext highlighter-rouge">Directory.Build.props</code> and automatically <em>import</em> that file before building the project.
This means that properties and item groups defined this file will be available to any project that needs to know about them.</p>

<p>The full documentation <a href="https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2022">can be found here</a>, but this is what I roll with:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Project&gt;</span>
  <span class="nt">&lt;PropertyGroup&gt;</span>
    <span class="nt">&lt;Platform</span> <span class="na">Condition=</span><span class="s">" '$(Platform)' == '' "</span><span class="nt">&gt;</span>x64<span class="nt">&lt;/Platform&gt;</span>
    <span class="nt">&lt;Configuration</span> <span class="na">Condition=</span><span class="s">" '$(DOTNET_WATCH)' == '1' "</span><span class="nt">&gt;</span>Debug<span class="nt">&lt;/Configuration&gt;</span>
    <span class="nt">&lt;Configuration</span> <span class="na">Condition=</span><span class="s">" '$(Configuration)' == '' "</span><span class="nt">&gt;</span>Debug<span class="nt">&lt;/Configuration&gt;</span>
    <span class="nt">&lt;TreatWarningsAsErrors</span> <span class="na">Condition=</span><span class="s">" '$(Configuration)' == 'Release' "</span><span class="nt">&gt;</span>true<span class="nt">&lt;/TreatWarningsAsErrors&gt;</span>
  <span class="nt">&lt;/PropertyGroup&gt;</span>
<span class="nt">&lt;/Project&gt;</span>
</code></pre></div></div>
<p>Here’s <a href="https://github.com/ClosedXML/ClosedXML/blob/develop/Directory.Build.props">another good</a> <code class="language-plaintext highlighter-rouge">Directory.Build.props</code> that I like to reference.</p>

<h1 id="put-common-build-targets-and-tasks-in-a-directorybuildtargets">Put common build targets and tasks in a <code class="language-plaintext highlighter-rouge">Directory.Build.targets</code></h1>
<p>The sister file to <code class="language-plaintext highlighter-rouge">Directory.Build.props</code> should define target definitions or redefinitions because it is imported <em>after</em> a project is evaluated.
It is highly likely that you will never need write a custom targets.
With any luck you will never need to understand why or actually write a <code class="language-plaintext highlighter-rouge">Directory.Build.targets</code>, but in case you do please <a href="https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2022#choose-between-adding-properties-to-a-props-or-targets-file">read through this</a>, familiarize yourself with the <code class="language-plaintext highlighter-rouge">/pp</code> command-line flag to <code class="language-plaintext highlighter-rouge">msbuild</code> and use your favorite text editor to examine massive XML files :).</p>

<p>Here is an example one I had to write last year:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Project&gt;</span>
<span class="c">&lt;!-- 
    [REDACTED] uses packages.config instead of PackageReference and they have
    static relative paths in their project files to their assembly references.
    Since we cannot modify their project files, we will tell MSBuild to rewrite
    the HintPath attribute at build time!
--&gt;</span>
  <span class="nt">&lt;UsingTask</span>
    <span class="na">TaskName=</span><span class="s">"PackageFolderRedirect"</span>
    <span class="na">TaskFactory=</span><span class="s">"CodeTaskFactory"</span>
    <span class="na">AssemblyFile=</span><span class="s">"$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll"</span> <span class="nt">&gt;</span>
    <span class="nt">&lt;ParameterGroup&gt;</span>
      <span class="nt">&lt;References</span> <span class="na">ParameterType=</span><span class="s">"Microsoft.Build.Framework.ITaskItem[]"</span> <span class="na">Required=</span><span class="s">"true"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/ParameterGroup&gt;</span>
    <span class="nt">&lt;Task&gt;</span>
      <span class="nt">&lt;Using</span> <span class="na">Namespace=</span><span class="s">"System"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;Using</span> <span class="na">Namespace=</span><span class="s">"System.IO"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;Code</span> <span class="na">Type=</span><span class="s">"Fragment"</span> <span class="na">Language=</span><span class="s">"cs"</span><span class="nt">&gt;</span>
<span class="cp">&lt;![CDATA[
if (References.Length &gt;</span> 0)
{
  for (int i = 0; i <span class="nt">&lt; References.Length</span><span class="err">;</span> <span class="err">i++)</span>
  <span class="err">{</span>
    <span class="err">ITaskItem</span> <span class="na">item =</span> <span class="s">References[i];</span>
    <span class="err">string</span> <span class="na">path =</span> <span class="s">item.GetMetadata("HintPath");</span>
    <span class="err">if</span> <span class="err">(!string.IsNullOrWhiteSpace(path)</span> <span class="err">&amp;&amp;</span> <span class="err">!File.Exists(path))</span>
    <span class="err">{</span>
      <span class="err">string</span> <span class="na">newPath =</span> <span class="s">"..\\"</span> <span class="err">+</span> <span class="err">path;</span>
      <span class="err">Log.LogMessage(MessageImportance.High,</span> <span class="err">"Redirecting</span> <span class="err">HintPath</span> <span class="err">to</span> <span class="err">"</span> <span class="err">+</span> <span class="err">newPath);</span>
      <span class="err">References[i].SetMetadata("HintPath",</span> <span class="err">newPath);</span>
    <span class="err">}</span>
    <span class="err">else</span>
    <span class="err">{</span>
      <span class="err">Log.LogMessage(MessageImportance.Low,</span> <span class="err">"Valid</span> <span class="err">HintPath");</span>
    <span class="err">}</span>
  <span class="err">}</span>
<span class="err">}</span>
<span class="err">]]</span><span class="nt">&gt;</span>
      <span class="nt">&lt;/Code&gt;</span>
    <span class="nt">&lt;/Task&gt;</span>
  <span class="nt">&lt;/UsingTask&gt;</span>

  <span class="nt">&lt;Target</span>
    <span class="na">Name=</span><span class="s">"FixHintPath"</span>
    <span class="na">Condition=</span><span class="s">"Exists('$(MSBuildProjectDirectory)/packages.config') == 'true'"</span>
    <span class="na">BeforeTargets=</span><span class="s">"ResolveAssemblyReferences"</span> <span class="nt">&gt;</span>
    <span class="nt">&lt;PackageFolderRedirect</span> <span class="na">References=</span><span class="s">"@(Reference)"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/Target&gt;</span>

<span class="nt">&lt;/Project&gt;</span>
</code></pre></div></div>

<h1 id="why-and-when-to-create-a-new-project">Why and When to create a new project</h1>
<p>Code organization and architecture are imporant things all software engineers should think about.
I’ve seen code bases where well-intentioned developers used <code class="language-plaintext highlighter-rouge">msbuild</code> projects to structure code, and this can be problematic if taken too far.
For example, the first team I joined essentially shipped code out of one repo to 4 locations:</p>

<ol>
  <li>The backend code.</li>
  <li>The client code.</li>
  <li>The integration/unit tests.</li>
  <li>CLI tools.</li>
</ol>

<p>This means there were at 4 <code class="language-plaintext highlighter-rouge">msbuild</code> projects (one for each deployment target) and at least 1 “common” project that housed the shared code.
This N + 1 arrangement is the <em>simplest</em> possible architecture for projects, that is N projects that control deployment specific settings and 1 common library that produces a library assembly.
There is the case where you have no shared assemblies, then you only have one project.</p>

<p>In reality, that team had at least 20 intermediate projects forming their own little internal dependency graph within the repo.
There are many downsides to this including:</p>

<ol>
  <li>Long build times if you modify foundational projects.</li>
  <li>High chance for an intermediate project to introduce some form of <a href="https://en.wikipedia.org/wiki/Dependency_hell">dependency hell</a>.</li>
  <li>Bloating build output sizes because each intermediate project will copy its assembly and all of its dependencies to its build output. This redundant file copying is often the <em>primary</em> cause for long build times, especially on Window’s NTFS.</li>
</ol>

<h1 id="conclusion">Conclusion</h1>
<p>That’s it for now, I’ll come back here and update these as I collect more nuggets of best practices.</p>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="C#" /><summary type="html"><![CDATA[Writing C# code involves using msbuild as the build system that resolves dependencies, orders projects topologically, compiles your codes, gathers your output files, creates publishable artifacts, etc. I learned to author msbuild project files quite well at Microsoft, and I got a few opinions out of it. In this post, I will summarize the best practices I have learned working with msbuild for C# development since 2016.]]></summary></entry><entry><title type="html">Hello LLDB</title><link href="http://gustavohidalgo.com/tools/2022/07/20/lldb.html" rel="alternate" type="text/html" title="Hello LLDB" /><published>2022-07-20T16:34:23+00:00</published><updated>2022-07-20T16:34:23+00:00</updated><id>http://gustavohidalgo.com/tools/2022/07/20/lldb</id><content type="html" xml:base="http://gustavohidalgo.com/tools/2022/07/20/lldb.html"><![CDATA[<h1 id="introduction">Introduction</h1>
<p>I am reading through Crafting Interpreters by Robert Nystom and one of the first exercises asks you to write and debug a doubly-linked list in C.
Regretfully I confess I never actually learned how to use GDB, ever, and on macOS I need to use the LLVM toolchain which means using LLDB instead of GDB.
In this post I will write using LLDB to debug a simple C program.</p>
<h1 id="reference-program">Reference program</h1>

<p>Throughout this post, I will reference a simple implementation of a doubly-linked list in C.
Here is the single-file C program I will be debugging:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">LinkedListNode</span> <span class="p">{</span>
    <span class="k">struct</span> <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">next</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">prev</span><span class="p">;</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span> <span class="n">LinkedListNode</span><span class="p">;</span>

<span class="n">LinkedListNode</span><span class="o">*</span> <span class="nf">initList</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">head</span> <span class="o">=</span> <span class="p">(</span><span class="n">LinkedListNode</span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">LinkedListNode</span><span class="p">));</span>
    <span class="n">head</span><span class="o">-&gt;</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
    <span class="n">head</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">head</span><span class="o">-&gt;</span><span class="n">prev</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">head</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">append</span><span class="p">(</span><span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">list</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">new</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">current</span> <span class="o">=</span> <span class="n">list</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">size</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">current</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">current</span> <span class="o">=</span> <span class="n">current</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
        <span class="n">size</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">tail</span> <span class="o">=</span> <span class="n">initList</span><span class="p">(</span><span class="n">new</span><span class="p">);</span>
    <span class="n">tail</span><span class="o">-&gt;</span><span class="n">prev</span> <span class="o">=</span> <span class="n">current</span><span class="p">;</span>
    <span class="n">current</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">tail</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">size</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">printList</span><span class="p">(</span><span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">list</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">current</span> <span class="o">=</span> <span class="n">list</span><span class="p">;</span>
    <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">current</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="n">current</span> <span class="o">=</span> <span class="n">current</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">,</span> <span class="n">index</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"[%d]: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="n">current</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="n">LinkedListNode</span><span class="o">*</span> <span class="nf">find</span><span class="p">(</span><span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">list</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">current</span> <span class="o">=</span> <span class="n">list</span><span class="p">;</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">current</span> <span class="o">!=</span> <span class="nb">NULL</span> <span class="o">&amp;&amp;</span> <span class="n">strcmp</span><span class="p">(</span><span class="n">current</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// printf("Comparing %s and %s\n", current-&gt;value, value);</span>
        <span class="n">current</span> <span class="o">=</span> <span class="n">current</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">current</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">current</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">LinkedListNode</span><span class="o">*</span> <span class="nf">delete</span><span class="p">(</span><span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">list</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">toDelete</span> <span class="o">=</span> <span class="n">find</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">toDelete</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">toDelete</span><span class="o">-&gt;</span><span class="n">prev</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">toDelete</span><span class="o">-&gt;</span><span class="n">prev</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">toDelete</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">toDelete</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">toDelete</span><span class="o">-&gt;</span><span class="n">next</span><span class="o">-&gt;</span><span class="n">prev</span> <span class="o">=</span> <span class="n">toDelete</span><span class="o">-&gt;</span><span class="n">prev</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="n">toDelete</span><span class="o">-&gt;</span><span class="n">prev</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">toDelete</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">toDelete</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
    <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">list</span> <span class="o">=</span> <span class="n">initList</span><span class="p">(</span><span class="s">"A"</span><span class="p">);</span>
    <span class="n">append</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="s">"B"</span><span class="p">);</span>
    <span class="n">append</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="s">"C"</span><span class="p">);</span>
    <span class="n">append</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="s">"D"</span><span class="p">);</span>
    <span class="n">printList</span><span class="p">(</span><span class="n">list</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">LinkedListNode</span><span class="o">*</span> <span class="n">c</span> <span class="o">=</span> <span class="n">find</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="s">"C"</span><span class="p">);</span>
    <span class="n">printList</span><span class="p">(</span><span class="n">c</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">delete</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="s">"B"</span><span class="p">);</span>
    <span class="n">printList</span><span class="p">(</span><span class="n">list</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And the Makefile that builds this:</p>

<pre><code class="language-Makefile"># Thanks to Job Vranish (https://spin.atomicobject.com/2016/08/26/makefile-c-projects/)
TARGET_EXEC := llist

BUILD_DIR := ./build
SRC_DIRS := ./src

# Find all the C and C++ files we want to compile
# Note the single quotes around the * expressions. Make will incorrectly expand these otherwise.
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')

# String substitution for every C/C++ file.
# As an example, hello.cpp turns into ./build/hello.cpp.o
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)

# String substitution (suffix version without %).
# As an example, ./build/hello.cpp.o turns into ./build/hello.cpp.d
DEPS := $(OBJS:.o=.d)

# Every folder in ./src will need to be passed to GCC so that it can find header files
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
# Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))

# The -MMD and -MP flags together generate Makefiles for us!
# These files will have .d instead of .o as the output.
CPPFLAGS := $(INC_FLAGS) -MMD -MP

CFLAGS := -g

# The final build step.
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
	$(CC) $(OBJS) -o $@ $(LDFLAGS)

# Build step for C source
$(BUILD_DIR)/%.c.o: %.c
	mkdir -p $(dir $@)
	$(CC) $(CPPFLAGS) $(CFLAGS) -c $&lt; -o $@

# Build step for C++ source
$(BUILD_DIR)/%.cpp.o: %.cpp
	mkdir -p $(dir $@)
	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $&lt; -o $@


.PHONY: clean
clean:
	rm -r $(BUILD_DIR)

# Include the .d makefiles. The - at the front suppresses the errors of missing
# Makefiles. Initially, all the .d files will be missing, and we don't want those
# errors to show up.
-include $(DEPS) 
</code></pre>

<blockquote>
  <p>Your program must be compiled with debug symbols to fully work with LLDB
For Apple’s <code class="language-plaintext highlighter-rouge">cc</code> C compiler, debug symbols generation uses the <code class="language-plaintext highlighter-rouge">-g</code> flag passed to the compiler.</p>
</blockquote>

<h1 id="invoking-lldb">Invoking LLDB</h1>
<p>Starting simple, LLDB can be invoked by passing it an executable file.
Compilation of our sample code produces a <code class="language-plaintext highlighter-rouge">llist</code> executable, so to debug it you simply invoke <code class="language-plaintext highlighter-rouge">lldb llist</code>
This launches the debugger and drops you in a REPL prompt, waiting for a command.</p>

<p>The simplest thing to do is to run the program, which can be done by sending the <code class="language-plaintext highlighter-rouge">run</code> command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; lldb llist
(lldb) target create "llist"
Current executable set to '/Users/gustavo/code/craftinginterpreters/prelude/build/llist' (x86_64).
(lldb) run
Process 7890 launched: '/Users/gustavo/code/craftinginterpreters/prelude/build/llist' (x86_64)
[0]: A
[1]: B
[2]: C
[3]: D

[0]: C
[1]: D

[0]: A
[1]: C
[2]: D
Process 7890 exited with status = 0 (0x00000000)
</code></pre></div></div>
<p>Here we can see that the <code class="language-plaintext highlighter-rouge">printf</code> statements from the C program work, and the process exits with the expected 0 status code.
You can run the program again with <code class="language-plaintext highlighter-rouge">run</code>, and for a full list of commands you can run <code class="language-plaintext highlighter-rouge">help</code> at the debugger REPL.</p>

<p>Pretty neat!</p>

<p>LLDB and GDB both support running commands identified by an unambiguous prefix match.
This means that instead of typing <code class="language-plaintext highlighter-rouge">run&lt;Enter&gt;</code> you can just type <code class="language-plaintext highlighter-rouge">r&lt;Enter&gt;</code> (or <code class="language-plaintext highlighter-rouge">ru&lt;Enter&gt;</code>) because there is no other command that begins with <code class="language-plaintext highlighter-rouge">r</code>.
While great for experienced debuggers, use of the terse commands for beginners just causes them to have to learn shortcuts at the same time as learning to use the debugger.
In this post I will use the full command names, but just know that you can always use the shorter names.</p>

<h1 id="setting-breakpoints">Setting breakpoints</h1>
<p>Because we passed the <code class="language-plaintext highlighter-rouge">-g</code> flag to <code class="language-plaintext highlighter-rouge">cc</code> to generate debug symbols, LLDB has full knowledge of the source code of our executable.</p>

<p>Let’s set a breakpoint right at the start of <code class="language-plaintext highlighter-rouge">main</code> then run the code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; lldb llist 
(lldb) target create "llist"
Current executable set to '/Users/gustavo/code/craftinginterpreters/prelude/build/llist' (x86_64).
(lldb) breakpoint set --name main
Breakpoint 1: where = llist`main + 15 at llist.c:67:28, address = 0x0000000100003ebf
(lldb) run
Process 2805 launched: '/Users/gustavo/code/craftinginterpreters/prelude/build/llist' (x86_64)
Process 2805 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003ebf llist`main(argc=1, argv=0x00007ff7bfeff4b0) at llist.c:67:28
   64   }
   65  
   66   int main(int argc, char* argv[]) {
-&gt; 67       LinkedListNode* list = initList("A");
   68       append(list, "B");
   69       append(list, "C");
   70       append(list, "D");
Target 0: (llist) stopped.
(lldb) 
</code></pre></div></div>

<p>Let’s break that down:</p>

<ol>
  <li>Invoke LLDB with <code class="language-plaintext highlighter-rouge">lldb llist</code></li>
  <li>Set a breakpoint at <code class="language-plaintext highlighter-rouge">main</code> with <code class="language-plaintext highlighter-rouge">breakpoint set --name main</code></li>
  <li>Run the program with <code class="language-plaintext highlighter-rouge">run</code></li>
  <li>Watch our breakpoint be hit at <code class="language-plaintext highlighter-rouge">llist.c</code> line 67 with a source code print out.</li>
</ol>

<p>Neat!
A debugger should allow you to inspect the values of local variables, and you can do that with <code class="language-plaintext highlighter-rouge">var</code>.
Within <code class="language-plaintext highlighter-rouge">main</code>, we should see local variables for the signature of <code class="language-plaintext highlighter-rouge">main</code> and the 2 <code class="language-plaintext highlighter-rouge">LinkedListNode*</code> declared inside:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) var
(int) argc = 1
(char **) argv = 0x00007ff7bfeff4b0
(LinkedListNode *) list = 0x000000010001a883
(LinkedListNode *) c = 0x00007ff7bfeff370
</code></pre></div></div>

<p>A debugger should allow you to step through your code line by line, and there are 2 commands to know:</p>
<ol>
  <li><code class="language-plaintext highlighter-rouge">step</code>: Executes the next thing, stepping <em>into</em> calls.</li>
  <li><code class="language-plaintext highlighter-rouge">next</code>: Executes the next thing, stepping <em>over</em> calls.</li>
</ol>

<p>Our breakpoint at <code class="language-plaintext highlighter-rouge">main</code> stopped right at a call to <code class="language-plaintext highlighter-rouge">initList</code>.
Let’s step inside <code class="language-plaintext highlighter-rouge">initList</code> with <code class="language-plaintext highlighter-rouge">step</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) step
Process 2805 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x0000000100003c5c llist`initList(value="A") at llist.c:12:45
   9    } LinkedListNode;
   10  
   11   LinkedListNode* initList(char* value) {
-&gt; 12       LinkedListNode* head = (LinkedListNode*)malloc(sizeof(LinkedListNode));
   13       head-&gt;value = value;
   14       head-&gt;next = NULL;
   15       head-&gt;prev = NULL;
Target 0: (llist) stopped.
</code></pre></div></div>

<p>Now we’re inside of <code class="language-plaintext highlighter-rouge">initList</code> and we could set more breakpoints or inspect locals.</p>

<p>One final basic command, <code class="language-plaintext highlighter-rouge">finish</code>.
<code class="language-plaintext highlighter-rouge">finish</code> allows the current stack frame to run to completion, and then breaks when program execution returns to the caller frame.
This is a good command to run whenever you want to just finish the current function call and return to debugging your caller.</p>

<p>If we call <code class="language-plaintext highlighter-rouge">finish</code> while we’re debugging <code class="language-plaintext highlighter-rouge">initList</code>, we’ll go back to debugging <code class="language-plaintext highlighter-rouge">main</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) finish
Process 2805 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step out
Return value: (LinkedListNode *) $0 = 0x00000001003044a0

    frame #0: 0x0000000100003ecb llist`main(argc=1, argv=0x00007ff7bfeff4b0) at llist.c:67:21
   64   }
   65  
   66   int main(int argc, char* argv[]) {
-&gt; 67       LinkedListNode* list = initList("A");
   68       append(list, "B");
   69       append(list, "C");
   70       append(list, "D");
Target 0: (llist) stopped.
</code></pre></div></div>

<p>Finally, you can always terminate the debugging session with <code class="language-plaintext highlighter-rouge">exit</code>.</p>

<h1 id="programmatically-setting-breakpoints">Programmatically setting breakpoints.</h1>
<p>Think about the experience of using an IDE: you set breakpoints on lines and the breakpoints <em>move</em> as you edit code to approximately stay with the line you originally set the breakpoint on.
You can add several breakpoints, run the program, and the breakpoints <em>persist</em> across runs.
In a CLI debugger, you can achieve the same thing by writing a file with commands that gets run when lldb runs a program.
For example, let’s say you knew you wanted to debug <code class="language-plaintext highlighter-rouge">printList</code>, and you wanted to spare having to write <code class="language-plaintext highlighter-rouge">breakpoint set --name printList</code> and <code class="language-plaintext highlighter-rouge">run</code> every time you invoked <code class="language-plaintext highlighter-rouge">lldb</code>.
First, write a file containing the commands you want to run, one command per line:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>breakpoint set --name printList
run
</code></pre></div></div>

<p>Then invoke <code class="language-plaintext highlighter-rouge">lldb</code> with the <code class="language-plaintext highlighter-rouge">--source</code> (short <code class="language-plaintext highlighter-rouge">-s</code>) command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; lldb llist -s ../debug
(lldb) target create "llist"
Current executable set to '/Users/gustavo/code/craftinginterpreters/prelude/build/llist' (x86_64).
(lldb) command source -s 0 '../debug'
Executing commands in '/Users/gustavo/code/craftinginterpreters/prelude/debug'.
(lldb) breakpoint set --name printList
Breakpoint 1: where = llist`printList + 12 at llist.c:33:31, address = 0x0000000100003d2c
(lldb) run
Process 3648 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003d2c llist`printList(list=0x00000001003041c0) at llist.c:33:31
   30   }
   31  
   32   void printList(LinkedListNode* list) {
-&gt; 33       LinkedListNode* current = list;
   34       for(int index = 0; current != NULL; current = current-&gt;next, index++) {
   35           printf("[%d]: %s\n", index, current-&gt;value);
   36       }
Target 0: (llist) stopped.
Process 3648 launched: '/Users/gustavo/code/craftinginterpreters/prelude/build/llist' (x86_64)
</code></pre></div></div>

<p>This debugger command file therefore allows us to configure the debugger on launch with an arbitrarily complex command list, which means you can script the process of getting your program into the error condition you need to debug 😈.</p>

<blockquote>
  <p>Don’t confuse <code class="language-plaintext highlighter-rouge">-s|--source</code> with <code class="language-plaintext highlighter-rouge">-S|--source-before-file</code>! The latter runs the commands <em>before</em> your target executable is loaded, which means your debugger commands will fail!</p>
</blockquote>

<h1 id="conclusion">Conclusion</h1>
<p>This post wraps a simple introduction to <code class="language-plaintext highlighter-rouge">lldb</code>, the LLVM equivalent to GCC’s <code class="language-plaintext highlighter-rouge">gdb</code>. 
We saw how to compile, run, and debug a simple C program with <code class="language-plaintext highlighter-rouge">lldb</code>.
Then we saw how to script <code class="language-plaintext highlighter-rouge">lldb</code> to automatically set breakpoints.
I am happy with <code class="language-plaintext highlighter-rouge">lldb</code>’s documentation and its UX, I will use it to debug my C programs.</p>

<!-- ## How do the breakpoints work?
The `-g` flag adds additional information to the final assembly.
```
prelude git:(master) tree .
.
├── Makefile
├── build
│   ├── llist
│   └── src
│       ├── llist.c.d
│       └── llist.c.o
└── src
    └── llist.c

3 directories, 5 files
```
The `llist.c.*` files contain references to the source code files on the machine.

To see for yourself, run `objdump -S llist.c.o` on macOS. -->

<h1 id="references">References</h1>
<p>https://lldb.llvm.org/index.html</p>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="tools" /><summary type="html"><![CDATA[Introduction I am reading through Crafting Interpreters by Robert Nystom and one of the first exercises asks you to write and debug a doubly-linked list in C. Regretfully I confess I never actually learned how to use GDB, ever, and on macOS I need to use the LLVM toolchain which means using LLDB instead of GDB. In this post I will write using LLDB to debug a simple C program. Reference program]]></summary></entry><entry><title type="html">Rust Day 2: Linked Lists</title><link href="http://gustavohidalgo.com/rust/2021/08/11/rust-linked-list.html" rel="alternate" type="text/html" title="Rust Day 2: Linked Lists" /><published>2021-08-11T16:34:23+00:00</published><updated>2021-08-11T16:34:23+00:00</updated><id>http://gustavohidalgo.com/rust/2021/08/11/rust-linked-list</id><content type="html" xml:base="http://gustavohidalgo.com/rust/2021/08/11/rust-linked-list.html"><![CDATA[<p>Today I was inspired to continue learning Rust by implementing what every CS student eventually has to know backwards and forwards to get a job: a singly linked list.
This shouldn’t be too hard, my list will support:</p>
<ol>
  <li>Appending elements to the list one at a time</li>
  <li>Iterating over elements</li>
  <li>Determining the length of the list by iterating from head to tail</li>
  <li>Deallocating the list</li>
  <li>Using Rust modules (just to learn how they work)</li>
</ol>

<p>Every linked list needs a good recursive <code class="language-plaintext highlighter-rouge">linked_list_node</code> definition with a nice <code class="language-plaintext highlighter-rouge">car</code> and <code class="language-plaintext highlighter-rouge">cdr</code> erm I mean <code class="language-plaintext highlighter-rouge">value</code> and <code class="language-plaintext highlighter-rouge">next</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">LinkedListNode</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">value</span> <span class="p">:</span> <span class="n">T</span><span class="p">,</span>
    <span class="n">next</span> <span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">LinkedListNode</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>It’s obvious to me why <code class="language-plaintext highlighter-rouge">next</code> has to be a reference to a <code class="language-plaintext highlighter-rouge">LinkedListNode</code>, but the lifetime parameter <code class="language-plaintext highlighter-rouge">'a</code> requires some explaining. That will be explained later.
Now obviously we need to support creating an empty list and adding elements to it one at a time.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">struct</span> <span class="n">List</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">head</span> <span class="p">:</span> <span class="o">&amp;</span><span class="n">LinkedListNode</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">List</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">new</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// except Rust doesn't have a null!</span>
        <span class="n">List</span> <span class="p">{</span> <span class="n">head</span> <span class="p">:</span> <span class="n">null</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now here is where I ran into my first C#-induced headache, because I would have loved to use <code class="language-plaintext highlighter-rouge">null</code> to represent a the end of a list.
Rust has no nulls, and some Googling immediately pointed me toward using an <code class="language-plaintext highlighter-rouge">Option</code> and a <a href="https://rust-unofficial.github.io/too-many-lists/first-layout.html">great book</a> doing exactly what I’m trying to do!
In short, we can use Rust <code class="language-plaintext highlighter-rouge">enum</code>s to create a <a href="https://en.wikipedia.org/wiki/Tagged_union">tagged union</a>(or what F# would call a discriminated union) to define a type that can be one of several values (one of which is Null or Nil) and whose values can themselves be more complex types.
Something like this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">enum</span> <span class="n">ListNode</span> <span class="p">{</span>
    <span class="n">Empty</span><span class="p">,</span>
    <span class="nf">ListNode</span><span class="p">(</span><span class="nb">i32</span><span class="p">,</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="n">ListNode</span><span class="o">&gt;</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I read that like this: A <code class="language-plaintext highlighter-rouge">ListNode</code> is either the <code class="language-plaintext highlighter-rouge">ListNode::Empty</code> value, or it is a <code class="language-plaintext highlighter-rouge">ListNode(i32, Box&lt;ListNode&gt;)</code> which is an <code class="language-plaintext highlighter-rouge">i32</code> paired with a pointer to a heap-allocated <code class="language-plaintext highlighter-rouge">ListNode</code>.</p>
<h1 id="conclusion">Conclusion</h1>
<p>I wouldn’t say that starting to write this post was a waste of time because I discovered a great book to read through.
Sometimes starting and failing still results in a victory.</p>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="rust" /><summary type="html"><![CDATA[Today I was inspired to continue learning Rust by implementing what every CS student eventually has to know backwards and forwards to get a job: a singly linked list. This shouldn’t be too hard, my list will support: Appending elements to the list one at a time Iterating over elements Determining the length of the list by iterating from head to tail Deallocating the list Using Rust modules (just to learn how they work)]]></summary></entry><entry><title type="html">Fun with jq</title><link href="http://gustavohidalgo.com/tools/2021/07/24/fun-with-jq.html" rel="alternate" type="text/html" title="Fun with jq" /><published>2021-07-24T16:34:23+00:00</published><updated>2021-07-24T16:34:23+00:00</updated><id>http://gustavohidalgo.com/tools/2021/07/24/fun-with-jq</id><content type="html" xml:base="http://gustavohidalgo.com/tools/2021/07/24/fun-with-jq.html"><![CDATA[<h1 id="what-is-jq">What is <code class="language-plaintext highlighter-rouge">jq</code>?</h1>
<p><a href="https://stedolan.github.io/jq/tutorial/"><code class="language-plaintext highlighter-rouge">jq</code></a> is a wonderful tool for processing JSON in the command line. 
The prevalence of JSON in web APIs and data dumps makes <code class="language-plaintext highlighter-rouge">jq</code> a very imporant tool for interactively manipulating JSON.
The <a href="https://stedolan.github.io/jq/tutorial/">tutorial</a> is good enough to get you started with basic functionality, but I found that real usage quickly outgrew the scope of the tutorial.
In this post I will show examples of using <code class="language-plaintext highlighter-rouge">jq</code> in slightly more complex scenarios than the tutorial.</p>

<p>The data in this post comes from <a href="https://www.yelp.com/dataset/documentation/main">Yelp’s Dataset Challenge</a>, and I will be focusing on the business data file (that is <code class="language-plaintext highlighter-rouge">yelp_academic_dataset_business.json</code> if you’re following at home).</p>

<h1 id="formatting">Formatting</h1>

<p>Usually I like to look at one row of a dataset to understand the data schema.
The Yelp data contains one entry per line, so we can use <code class="language-plaintext highlighter-rouge">head -n 1</code> argument to get the first line of the data file and inspect the schema.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; head -n1 yelp_academic_dataset_business.json 
{"business_id":"6iYb2HFDywm3zjuRg0shjw","name":"Oskar Blues Taproom","address":"921 Pearl St","city":"Boulder","state":"CO","postal_code":"80302","latitude":40.0175444,"longitude":-105.2833481,"stars":4.0,"review_count":86,"is_open":1,"attributes":{"RestaurantsTableService":"True","WiFi":"u'free'","BikeParking":"True","BusinessParking":"{'garage': False, 'street': True, 'validated': False, 'lot': False, 'valet': False}","BusinessAcceptsCreditCards":"True","RestaurantsReservations":"False","WheelchairAccessible":"True","Caters":"True","OutdoorSeating":"True","RestaurantsGoodForGroups":"True","HappyHour":"True","BusinessAcceptsBitcoin":"False","RestaurantsPriceRange2":"2","Ambience":"{'touristy': False, 'hipster': False, 'romantic': False, 'divey': False, 'intimate': False, 'trendy': False, 'upscale': False, 'classy': False, 'casual': True}","HasTV":"True","Alcohol":"'beer_and_wine'","GoodForMeal":"{'dessert': False, 'latenight': False, 'lunch': False, 'dinner': False, 'brunch': False, 'breakfast': False}","DogsAllowed":"False","RestaurantsTakeOut":"True","NoiseLevel":"u'average'","RestaurantsAttire":"'casual'","RestaurantsDelivery":"None"},"categories":"Gastropubs, Food, Beer Gardens, Restaurants, Bars, American (Traditional), Beer Bar, Nightlife, Breweries","hours":{"Monday":"11:0-23:0","Tuesday":"11:0-23:0","Wednesday":"11:0-23:0","Thursday":"11:0-23:0","Friday":"11:0-23:0","Saturday":"11:0-23:0","Sunday":"11:0-23:0"}}
</code></pre></div></div>
<p>😅 A little hard to read. Let’s pipe that into <code class="language-plaintext highlighter-rouge">jq</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; head -n1 yelp_academic_dataset_business.json | jq
{
  "business_id": "6iYb2HFDywm3zjuRg0shjw",
  "name": "Oskar Blues Taproom",
  "address": "921 Pearl St",
  "city": "Boulder",
  "state": "CO",
  "postal_code": "80302",
  "latitude": 40.0175444,
  "longitude": -105.2833481,
  "stars": 4,
  "review_count": 86,
  "is_open": 1,
  "attributes": {
    "RestaurantsTableService": "True",
    "WiFi": "u'free'",
    "BikeParking": "True",
    "BusinessParking": "{'garage': False, 'street': True, 'validated': False, 'lot': False, 'valet': False}",
    "BusinessAcceptsCreditCards": "True",
    "RestaurantsReservations": "False",
    "WheelchairAccessible": "True",
    "Caters": "True",
    "OutdoorSeating": "True",
    "RestaurantsGoodForGroups": "True",
    "HappyHour": "True",
    "BusinessAcceptsBitcoin": "False",
    "RestaurantsPriceRange2": "2",
    "Ambience": "{'touristy': False, 'hipster': False, 'romantic': False, 'divey': False, 'intimate': False, 'trendy': False, 'upscale': False, 'classy': False, 'casual': True}",
    "HasTV": "True",
    "Alcohol": "'beer_and_wine'",
    "GoodForMeal": "{'dessert': False, 'latenight': False, 'lunch': False, 'dinner': False, 'brunch': False, 'breakfast': False}",
    "DogsAllowed": "False",
    "RestaurantsTakeOut": "True",
    "NoiseLevel": "u'average'",
    "RestaurantsAttire": "'casual'",
    "RestaurantsDelivery": "None"
  },
  "categories": "Gastropubs, Food, Beer Gardens, Restaurants, Bars, American (Traditional), Beer Bar, Nightlife, Breweries",
  "hours": {
    "Monday": "11:0-23:0",
    "Tuesday": "11:0-23:0",
    "Wednesday": "11:0-23:0",
    "Thursday": "11:0-23:0",
    "Friday": "11:0-23:0",
    "Saturday": "11:0-23:0",
    "Sunday": "11:0-23:0"
  }
}
</code></pre></div></div>
<p>Much easier to read!</p>
<h2 id="format-the-whole-file">Format the whole file</h2>
<p>To completely format a file, we can pipe all lines into <code class="language-plaintext highlighter-rouge">jq</code> and then redirect the result to a file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; cat yelp_academic_dataset_business.json | jq &gt; yelp_academic_dataset_business_formatted.json
</code></pre></div></div>
<blockquote>
  <p>Caution, this file is not valid JSON! It is only useful for reading through the data in a text editor.</p>
</blockquote>

<h1 id="filtering">Filtering</h1>

<p>Let’s say we wanted to find all of the restaurants with ratings &gt;= 4 and with more than 5 reviews.
We can do it like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; cat yelp_academic_dataset_business.json | jq --compact-output 'select(.review_count &gt; 5 and .stars &gt;= 4)' &gt; high_ratings.json
</code></pre></div></div>
<blockquote>
  <p>Note that I used <code class="language-plaintext highlighter-rouge">--compact-output</code> to preserve the 1-object-per-line structure of the original file. Without this option, <code class="language-plaintext highlighter-rouge">high_ratings.json</code> would contain nicely formatted JSON.</p>
</blockquote>

<p>The syntax is very easy: <code class="language-plaintext highlighter-rouge">jq 'select(&lt;boolean expression&gt;)'</code> will filter objects where the boolean expression returns false.</p>

<h1 id="selecting">Selecting</h1>
<p>Or sometimes called projections, I will show 2 cases building on the previous filter example.</p>
<h2 id="select-json-to-value">Select JSON to value</h2>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat </span>yelp_academic_dataset_business.json | jq <span class="nt">--compact-output</span> <span class="s1">'select(.review_count &gt; 5 and .stars &gt;= 4) | .business_id'</span> <span class="o">&gt;</span> high_ratings.json
</code></pre></div></div>

<p>After the <code class="language-plaintext highlighter-rouge">select</code> call, I pipe the resulting object into a property access <code class="language-plaintext highlighter-rouge">.business_id</code>. 
This produces a file that looks like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"6iYb2HFDywm3zjuRg0shjw"
"tCbdrRPZA0oiIYSmHG3J0w"
"bvN78flM8NLprQ1a1y5dRg"
"PE9uqAjdw0E4-8mjGl3wVA"
</code></pre></div></div>

<h2 id="select-json-to-json">Select JSON to JSON</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat yelp_academic_dataset_business.json | jq --compact-output 'select(.review_count &gt; 5 and .stars &gt;= 4) | {business_id}' &gt; high_ratings.json
</code></pre></div></div>

<p>Note that the syntax <code class="language-plaintext highlighter-rouge">| {business_id}</code> is a shortcut for <code class="language-plaintext highlighter-rouge">| {"business_id": .business_id}</code>, the latter case can be useful if you want to rename a property or product a new property from different values.
This produces a file that looks like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{"business_id":"6iYb2HFDywm3zjuRg0shjw"}
{"business_id":"tCbdrRPZA0oiIYSmHG3J0w"}
{"business_id":"bvN78flM8NLprQ1a1y5dRg"}
{"business_id":"PE9uqAjdw0E4-8mjGl3wVA"}
</code></pre></div></div>

<h1 id="json-to-ctsv">JSON to [C|T]SV</h1>
<p>JSON is a very self-documenting format for data, which is great for humans but not very useful for computers.
If I wanted to do some basic analysis on this data, the first thing I would do is load it into a spreadsheet program and start generating some graphs.
To do that, we need to transform this file into a comma-separated value file or a tab-separated value file.</p>

<p>Unfortunately any command to transform JSON to CSV on anything but the most basic JSON has to make choices about what to do with object and array properties (recursively) so there is no easy answer.
The <code class="language-plaintext highlighter-rouge">jq</code> filter we write depends on the source data and how we want the CSV to look like.</p>

<p>Or does it?
Denizens of the internet have graciously provided a <code class="language-plaintext highlighter-rouge">jq</code> filter to perform this for arbitrary JSON.
This filter takes advantage of <code class="language-plaintext highlighter-rouge">jq</code>’s programming features: that’s right, <code class="language-plaintext highlighter-rouge">jq</code> is its own little programming language!
Gaze your eyes on this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def json2header:
  [paths(scalars)];

def json2array($header):
  [$header[] as $p | getpath($p)];

# given an array of conformal objects, produce "CSV" rows, with a header row:
def json2csv:
  (.[0] | json2header) as $h
  | ([$h[]|join(".")], (.[] | json2array($h))) 
  | @csv ;

# `main`
json2csv
</code></pre></div></div>

<p>Put this into a file called <code class="language-plaintext highlighter-rouge">json2csv.jq</code> and invoke it like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jq -rf json2csv.jq yelp_academic_dataset_business.json
</code></pre></div></div>

<p>Don’t forget to turn the json file into an array of objects first with something like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; head -n 3 yelp_academic_dataset_business.json | jq -s '.' &gt; high_ratings.json
</code></pre></div></div>

<p>Big thanks to <a href="https://stackoverflow.com/questions/32960857/how-to-convert-arbitrary-simple-json-to-csv-using-jq">this</a> and <a href="https://stackoverflow.com/questions/57242240/jq-object-cannot-be-csv-formatted-only-array">this</a> StackOverflow question.</p>

<h1 id="conclusion">Conclusion</h1>
<p><code class="language-plaintext highlighter-rouge">jq</code> is great!</p>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="tools" /><summary type="html"><![CDATA[What is jq? jq is a wonderful tool for processing JSON in the command line. The prevalence of JSON in web APIs and data dumps makes jq a very imporant tool for interactively manipulating JSON. The tutorial is good enough to get you started with basic functionality, but I found that real usage quickly outgrew the scope of the tutorial. In this post I will show examples of using jq in slightly more complex scenarios than the tutorial.]]></summary></entry><entry><title type="html">Swagger from C# code</title><link href="http://gustavohidalgo.com/c%23/2021/07/11/swagger-from-code.html" rel="alternate" type="text/html" title="Swagger from C# code" /><published>2021-07-11T16:34:23+00:00</published><updated>2021-07-11T16:34:23+00:00</updated><id>http://gustavohidalgo.com/c%23/2021/07/11/swagger-from-code</id><content type="html" xml:base="http://gustavohidalgo.com/c%23/2021/07/11/swagger-from-code.html"><![CDATA[<h1 id="problem">Problem</h1>
<p>At work we write a lot of ASP.NET REST controllers.
Some of the controllers have their API documented in the <a href="https://github.com/Azure/azure-rest-api-specs">Azure REST API</a> specs in the form of a <a href="https://swagger.io/specification/v2/">Swagger</a> specification, and some are consumed internally by a system that requires us to expose a Swagger specification. This is our problem: say we want to write a controller for <code class="language-plaintext highlighter-rouge">Widget</code>s that supports CRUD operations. A developer has to:</p>
<ol>
  <li>Write the C# code.</li>
  <li>Write the Swagger JSON necessary.</li>
  <li>Ensure (erm, try really hard) to make sure the swagger and the C# stay consistent with each other.</li>
  <li>Ship the Swagger to the clients.</li>
</ol>

<p>There are existing tools like <a href="https://github.com/RicoSuter/NSwag">NSwag</a> and <a href="https://github.com/domaindrivendev/Swashbuckle.AspNetCore">Swashbuckle</a> that are almost tailor made for this but they assume you’re going to generate perfectly legal OpenAPIv2/3 documents, which I’m not because this internal swagger has some… <em>customizations</em>. We can easily adapt a proper Swagger document into our custom format, ultimately we are trying to generate a swagger document at build time from our source code which we will then transform some more (again, at build time!).</p>

<h1 id="solution">Solution</h1>
<p>Thankfully NSwag has a component that does almost exactly what we need: <a href="https://github.com/RicoSuter/NSwag/wiki/NSwag.MSBuild">NSwag.MSBuild</a>. With this package, we can generate a Swagger document for some or all of the controllers in an assembly, and then we can modify the document to fit our custom Swagger format. Let’s see how it works:</p>

<p>First I generate a small project with:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> dotnet new webapi
</code></pre></div></div>
<p>At this time, this template contains one controller with a GET method on <code class="language-plaintext highlighter-rouge">/WeatherForecast</code> that returns an object with weather forecast data.</p>

<p>Then I add a dependency on NSwag with</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> dotnet add package NSwag.AspNetCore
<span class="o">&gt;</span> dotnet add package NSwag.MSBuild 
</code></pre></div></div>

<p>This is my <code class="language-plaintext highlighter-rouge">Startup.cs</code> with the call to <code class="language-plaintext highlighter-rouge">AddSwaggerDocument</code>, this is <strong>necessary</strong> for the generator to discover controllers in the assembly:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Startup</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="nf">Startup</span><span class="p">(</span><span class="n">IConfiguration</span> <span class="n">configuration</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Configuration</span> <span class="p">=</span> <span class="n">configuration</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="n">IConfiguration</span> <span class="n">Configuration</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">ConfigureServices</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">services</span><span class="p">.</span><span class="nf">AddControllers</span><span class="p">();</span>
        <span class="n">services</span><span class="p">.</span><span class="nf">AddSwaggerDocument</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IApplicationBuilder</span> <span class="n">app</span><span class="p">,</span> <span class="n">IWebHostEnvironment</span> <span class="n">env</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">env</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
        <span class="p">{</span>
            <span class="n">app</span><span class="p">.</span><span class="nf">UseDeveloperExceptionPage</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>
        <span class="n">app</span><span class="p">.</span><span class="nf">UseRouting</span><span class="p">();</span>
        <span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
        <span class="n">app</span><span class="p">.</span><span class="nf">UseEndpoints</span><span class="p">(</span><span class="n">endpoints</span> <span class="p">=&gt;</span>
        <span class="p">{</span>
            <span class="n">endpoints</span><span class="p">.</span><span class="nf">MapControllers</span><span class="p">();</span>
        <span class="p">});</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And finally I add a target in the project file to call the Swagger generator executable. Note that I am on a Mac so I have to call the generator with the property for .NET 5.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Target</span> <span class="na">Name=</span><span class="s">"NSwag"</span> <span class="na">AfterTargets=</span><span class="s">"Build"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;Exec</span> 
    <span class="na">EnvironmentVariables=</span><span class="s">"ASPNETCORE_ENVIRONMENT=Development"</span>
    <span class="na">Command=</span><span class="s">"$(NSwagExe_Net50) aspnetcore2openapi /assembly:$(TargetDir)$(MSBuildProjectName).dll /output:swagger.json"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/Target&gt;</span>
</code></pre></div></div>

<p>Some nice build output:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Microsoft (R) Build Engine version 16.8.0+126527ff1 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
  swaggergen -&gt; /Users/gustavo/code/swaggergen/bin/Debug/net5.0/swaggergen.dll
  NSwag command line tool for .NET Core Net50, toolchain v13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))
  Visit http://NSwag.org for more information.
  NSwag bin directory: /Users/gustavo/.nuget/packages/nswag.msbuild/13.11.3/tools/Net50
  Code has been successfully written to file.
  
  Duration: 00:00:00.8377024

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:03.55
</code></pre></div></div>

<p>And finally, a <code class="language-plaintext highlighter-rouge">swagger.json</code> file is produced next to our project file. 
Let’s take a look at it:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"x-generator"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NSwag v13.11.3.0 (NJsonSchema v10.4.4.0 (Newtonsoft.Json v12.0.0.0))"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"swagger"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"info"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My Title"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.0"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"produces"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"text/plain"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"application/json"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"text/json"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"paths"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"/WeatherForecast"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"get"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"tags"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="s2">"WeatherForecast"</span><span class="w">
        </span><span class="p">],</span><span class="w">
        </span><span class="nl">"operationId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"WeatherForecast_Get"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"responses"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"200"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"x-nullable"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
            </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
            </span><span class="nl">"schema"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
              </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"array"</span><span class="p">,</span><span class="w">
              </span><span class="nl">"items"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="nl">"$ref"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#/definitions/WeatherForecast"</span><span class="w">
              </span><span class="p">}</span><span class="w">
            </span><span class="p">}</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"definitions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"WeatherForecast"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"object"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"required"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"date"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"temperatureC"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"temperatureF"</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"date"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"format"</span><span class="p">:</span><span class="w"> </span><span class="s2">"date-time"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"temperatureC"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"integer"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"format"</span><span class="p">:</span><span class="w"> </span><span class="s2">"int32"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"temperatureF"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"integer"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"format"</span><span class="p">:</span><span class="w"> </span><span class="s2">"int32"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"summary"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Pretty good! The generator:</p>
<ol>
  <li>Found the controller in the assembly.</li>
  <li>Generated an object in <code class="language-plaintext highlighter-rouge">paths</code> for each controller method.</li>
  <li>Generated definitions for the objects the controller returns.</li>
</ol>

<p>For a very simple controller, that Swagger document is a whopping 141 lines long!
I will grant that it is a full Swagger document with all of the ceremony of Swagger, but still even if it was only 70 lines that would be a huge change to review.
If the author tells me “The swagger is automatically generated from the code”, then I’ll spend exactly 0 seconds looking at it and instead focus on reviewing the code that produced it.</p>

<h1 id="conclusion">Conclusion</h1>

<p>Generating Swagger files from C# code is extremely easy with NSwag.
Armed with these tools, you can reduce the amount of time you spend toiling at the Swagger mines.</p>]]></content><author><name>Gustavo Hidalgo</name><email>zambrano.hidalgo@gmail.com</email></author><category term="c#" /><summary type="html"><![CDATA[Problem At work we write a lot of ASP.NET REST controllers. Some of the controllers have their API documented in the Azure REST API specs in the form of a Swagger specification, and some are consumed internally by a system that requires us to expose a Swagger specification. This is our problem: say we want to write a controller for Widgets that supports CRUD operations. A developer has to: Write the C# code. Write the Swagger JSON necessary. Ensure (erm, try really hard) to make sure the swagger and the C# stay consistent with each other. Ship the Swagger to the clients.]]></summary></entry></feed>