<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ShandonCodes</title>
    <description>The latest articles on DEV Community by ShandonCodes (@shandoncodes).</description>
    <link>https://dev.to/shandoncodes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1251114%2F116deaa9-4a82-4f13-a2e9-fb5976e70a40.png</url>
      <title>DEV Community: ShandonCodes</title>
      <link>https://dev.to/shandoncodes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shandoncodes"/>
    <language>en</language>
    <item>
      <title>Packer &amp; Proxmox: A Bumpy Road</title>
      <dc:creator>ShandonCodes</dc:creator>
      <pubDate>Tue, 16 Jul 2024 22:30:27 +0000</pubDate>
      <link>https://dev.to/shandoncodes/packer-proxmox-a-bumpy-road-1de2</link>
      <guid>https://dev.to/shandoncodes/packer-proxmox-a-bumpy-road-1de2</guid>
      <description>&lt;p&gt;Several months ago I used Packer to simplify the creation of VM templates on my organization's &lt;a href="https://www.vmware.com/products/vsphere.html" rel="noopener noreferrer"&gt;vSphere&lt;/a&gt; instance. While getting Packer to work was not the most pleasant, the benefits of using it had begun to show and so I decided to begin using Packer in my &lt;a href="https://youtu.be/Dj4YvZvDNJU?si=ZobJqezGocSR55HY" rel="noopener noreferrer"&gt;homelab&lt;/a&gt;. In my homelab I use Proxmox as my hypervisor solution, so I figured there would be a few differences but nothing that would cost me too many cycles. I was wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Issue 1: VM Creation Failure
&lt;/h2&gt;

&lt;p&gt;The first issue I encountered was the failure of the VM creation process. Initially, I attempted to authenticate using an API token created by my admin user, but Packer provided no useful feedback in the terminal. After enabling Packer’s debugging mode, I discovered a &lt;code&gt;501 Not Implemented&lt;/code&gt; error being returned from my Proxmox instance.&lt;/p&gt;

&lt;p&gt;This error led me to explore the Proxmox API reference documentation. Initially, I suspected that my Proxmox version (v8.0.3) was not compatible with the latest Packer plugin for Proxmox. However, the real issue was with the token permissions, not the API endpoint.&lt;/p&gt;

&lt;p&gt;When creating the API token, I had left the &lt;strong&gt;Privilege Separation&lt;/strong&gt; box checked, which required me to manually assign permissions for every resource the token needed to access. I tried adding permissions for each required resource, but I couldn’t find all of them in the UI. Eventually, I created a new token, ensuring that I did not select the &lt;strong&gt;Privilege Separation&lt;/strong&gt; box. This new token inherited all permissions from my admin user, allowing me to create the VM and its resources without issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ld1teevbqqmy7gyj4ch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ld1teevbqqmy7gyj4ch.png" alt="Privilege Separation box shown in Proxmox"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although diagnosing this issue was challenging due to the misleading &lt;strong&gt;501&lt;/strong&gt; error, it could have been avoided if a more appropriate error, such as &lt;code&gt;401 Unauthorized&lt;/code&gt;, had been returned. While this issue originates from the Proxmox API response, an additional warning message in the Packer plugin could help others avoid wasting time on similar problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 2: Subiquity Install  freezing
&lt;/h2&gt;

&lt;p&gt;Ubuntu uses &lt;a href="https://github.com/canonical/subiquity" rel="noopener noreferrer"&gt;Subiquity&lt;/a&gt; as a framework to drive the installation of the OS, it make it very easy to automate installation of both Ubuntu Desktop and Server. During the Subiquity install I noticed the installer started to hang once additional packages were being installed. This was a pretty easy solve as all I needed to do was &lt;a href="https://discuss.hashicorp.com/t/ubuntu-22-04-3-lts-install-hangs-at-subiquity-install-install-postinstall-run-unattended-upgrades-cmd-in-target/63068" rel="noopener noreferrer"&gt;add more RAM&lt;/a&gt; to my configuration. I modified my configuration to use about 4GB of RAM and the installation started without a hitch!&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 3: Packer SSH Connection Failure
&lt;/h2&gt;

&lt;p&gt;After the initial installation was complete Packer was not able to connect to the VM over SSH even though it had the correct credentials (I test the credentials by SSHing in manually while the VM was running). After debugging for a few hours I came across an &lt;a href="https://github.com/hashicorp/packer-plugin-proxmox/issues/91#issuecomment-1139189942" rel="noopener noreferrer"&gt;article&lt;/a&gt; that outlines how Packer gets the VM's IP. It relies on the QEMU guest agent to be running on the machine, without it the IP of the machine was not being communicated back to Packer so the SSH connection kept timing out.&lt;br&gt;
All I had to do was add the &lt;code&gt;qemu-guest-agent&lt;/code&gt; package to the Subiquity installer so that the service would start and report the IP to Packer for the IP connection. &lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 4: VM Template Clone Failures
&lt;/h2&gt;

&lt;p&gt;Once Packer was able to create the VM template correctly, I had one last issue when attempting to use it. During the installation a temporary disk was created to expose the files used Subiquity installer. During the teardown by Packer that temporary image is destroyed, but the VM does not know that when it reboots that image will not longer be available so all VMs created from that template would require that drive to be manually removed before boot. To solve this I set the &lt;code&gt;unmount&lt;/code&gt; key to &lt;code&gt;true&lt;/code&gt; in the image declaration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wucger37lqanezc5kdi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wucger37lqanezc5kdi.png" alt="Screenshot of HCL2 config displaying unmount field"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now before Packer finishes the teardown process it makes sure the temporary image is unmounted from the VM. Without this temporary image being permanently mounted in their template all VMs created from it wold no longer throw an error when booting the OS. &lt;/p&gt;

&lt;h2&gt;
  
  
  Issue 5: Unclear Documentation
&lt;/h2&gt;

&lt;p&gt;This is more a list of gripes rather than a specific issue, but I really do not understand the design behind the Packer documentation (or really Hashicorp's in general). A couple of the issues that stand out to me are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Launch point is good, reference is terrible&lt;/strong&gt;: The base documentation page is actually pretty good, it explains what Packer does and lays out the terminology used in the remainder of the documentation. The tutorials are pretty well defined and provide quite a bit of detail while not completely overloading you along the way. The part I do not like about the documentation is the lack on contrast between section in the "On this page" pane. For example, look at the following screenshot:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8sunk5kdtni0m3w8rha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8sunk5kdtni0m3w8rha.png" alt="Screenshot of Packer documentation page displaying showing tabular issue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "Required" and "Optional" sections should be nested under the "Configuration Reference" parent section. This would help break things but in that side pane visually, also when with option is selected the remaining instances of that selection are highlighted in that pane. So selecting "Required" highlights the other four instances of "Required" in that pane. I am not sure why you would want that functionality at all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example inconsistency:&lt;/strong&gt; Inside of the docs the examples used very often use the JSON format over HCL2. Considering the preferred method by Hashicorp is for everyone to use HCL2 I think it would be best if examples are shown in both languages (considering they are both supported) with HCL2 being noted as the primary method to use. I will say this is not really on Hashicorp alone, as community based plugins (like the one I am using) probably do not have to adhere to the same standards BUT considering this documentation is hosted on the official Packer site I think they can share in some of the fault.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Packer is a powerful tool with significant benefits, but using it on new platforms can present challenges. I encountered issues with VM creation, Subiquity installation, SSH connections, and VM template cloning. Additionally, unclear documentation added to the complexity. However, each problem had a solution, and with some persistence, Packer can be effectively used across different environments.&lt;/p&gt;

</description>
      <category>devops</category>
    </item>
    <item>
      <title>Pylint is...</title>
      <dc:creator>ShandonCodes</dc:creator>
      <pubDate>Wed, 28 Feb 2024 03:00:23 +0000</pubDate>
      <link>https://dev.to/shandoncodes/ci-errors-hard-to-find-harder-to-solve-1kio</link>
      <guid>https://dev.to/shandoncodes/ci-errors-hard-to-find-harder-to-solve-1kio</guid>
      <description>&lt;h3&gt;
  
  
  The Intro
&lt;/h3&gt;

&lt;p&gt;It happens at some point in everyone's career, one minute you are making a small change to your software project and the next thing you know you come across an error. An error that no one else on your team has seen and worse, one you have little to no idea how to debug and find a solution. Strap-in as I walk you through an error that took me an entire workday to solve and most importantly, one that made me re-think the entire way my team operates.&lt;/p&gt;

&lt;p&gt;Let me start by painting the scene, I picked up a maintenance ticket with a simple description: "Migrate Gitlab Runners to new vSphere instance". Sounds simple, enough but I decided to take the time to really improve how we deployed our runners. At the time, it was a very manual process to create new runners for our project, an engineer would need to manually create the VM, install the OS as well as other software, and they would have to remember all of the configuration steps that were used on previous runners. Needless to say this was not ideal, so I decided I would take advantage of tools like &lt;a href="https://www.packer.io/"&gt;Packer&lt;/a&gt; to ease the future creation of VMs. Specifically, I used Packer to create a VM template with all the required configurations (I'll talk about that in the future). Once I had the template all I needed to do was perform a few clicks in vSphere and voilà, a new runner was available to the project. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Error
&lt;/h3&gt;

&lt;p&gt;Now that are runners were "migrated" I began testing our pipelines on one new runner (I decided to start testing with more modest one to simplify debugging). Everything ran just fine until the pipeline began its linting stage. The linting job would fail but there was no output, just a notice that the job failed and there was an exit code of "1"...now if you are thinking what I was you might be wondering why zero changes to the project source code somehow caused a linting error and you would be right to wonder. As far as the linter should be concerned nothing changed, yet it was finding an issue. I ran the linter locally to double-check for errors and none were shown. I doubled checked the pylint version being run in the pipeline vs locally and I confirmed they were exactly the same. While debugging this two main things stood out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The version of pylint was a major version behind (were were using 2.13.9 and the current release version (as of this writing) is 3.0.3&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An error was thrown in the pipeline, but no output was displayed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, I touched base with my team about the outdated version of pylint and they mentioned due to some breaking changes in the newest major version the linter would totally fail in our very large codebase and would require a very large refactor to work. While this was not ideal, I decided to move on from this and focus on the more pressing second issue. The pipeline was failing and I was getting no error. After about an hour or so of digging I learned our pipeline was outputting the pylint output to a file and because the job was failing all artifacts it created, like the linter record) was not saved. So there &lt;em&gt;was&lt;/em&gt; an actual error, but I just could not view it. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Easily enough I changed the job to output to stdout and then I could see the errors. The errors were related to some relative imports in part of the source and while this was great to know I was still puzzled by &lt;em&gt;why&lt;/em&gt; a linting error suddenly came out of seemingly nowhere and &lt;em&gt;why&lt;/em&gt; I could not replicate on my workstation. I mean sure, I could just fix the linter issues found, but how would I or anyone be able to ensure we will not receive linter errors in the pipeline if we cannot test them locally before hand? After scratching my head on this for quite a few hours I realized something (and you may have too), remember earlier when I mentioned I created one &lt;em&gt;modest&lt;/em&gt; runner for testing? Well the exact specs were as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 CPU&lt;/li&gt;
&lt;li&gt;64 GB RAM&lt;/li&gt;
&lt;li&gt;150 SSD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compared to these specs from the now deprecated runners:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4 CPU&lt;/li&gt;
&lt;li&gt;64 GB RAM&lt;/li&gt;
&lt;li&gt;150 HDD&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Notice the CPU count. You see, as I was searching "why are my lint results different from my pipeline" I did not get any exact results, but I stumbled across this &lt;a href="https://github.com/pylint-dev/pylint/issues/374"&gt;bug report&lt;/a&gt;. Basically, the report says when pylint is run with multiple cores some errors may or may not appear even if the do or do not appear when running pylint on a single core. Now in our pipeline and locally we do not specify how many cores to use (i.e. pylint --jobs=0), so by default pylint will use AS MANY cores as possible. So in our pipeline the runner's pylint instance was using &lt;code&gt;--jobs=1&lt;/code&gt; and locally my workstation (4 cores) was using &lt;code&gt;--jobs=4&lt;/code&gt;. To confirm I manually changed my pylint to use &lt;code&gt;--jobs=1&lt;/code&gt; locally and I was finally able to reproduce the errors locally! To complete my testing I updated the runner specs to have 4 CPUs and the pipeline passed with no issues!&lt;/p&gt;

&lt;p&gt;To recap the issue was caused by a known bug in an outdated version of pylint, but it was only found by chance when I began working on a completely unrelated ticket. If I had used more CPUs on my test runner to begin with, I might not have ever found this issue at all (especially if we upgraded pylint in the near future).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Path Forward
&lt;/h3&gt;

&lt;p&gt;So how did I wrap everything up? I created 2 other runners and thanks to the higher performance specs used on the new vSphere cluster our release pipeline is now down from 4 hours to 2, a major win! Also, I did not actually fix the original linter error. I felt that it did not matter as the error was never thrown on multi-core systems and our runners/workstations always have multiple cores. I did however cite this issue in a ticket to update pylint. &lt;/p&gt;

&lt;p&gt;I learned that efforts that might add little to no benefit in the present may drastically save time in the future. If we as a team had prioritized updating our pylint version than I very may well have not spent a full workday chasing bugs and learning more about an outdated version of pylint than I needed to.&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>python</category>
      <category>cicd</category>
      <category>programming</category>
    </item>
    <item>
      <title>Stop using entgo...please</title>
      <dc:creator>ShandonCodes</dc:creator>
      <pubDate>Mon, 08 Jan 2024 03:24:04 +0000</pubDate>
      <link>https://dev.to/shandoncodes/stop-using-entgoplease-5gm5</link>
      <guid>https://dev.to/shandoncodes/stop-using-entgoplease-5gm5</guid>
      <description>&lt;p&gt;If you found this article, than you are probably similar to how I was a few months ago. I started a project in Go that required a SQL backend and I wanted to use any tool that would help me build this backend quickly. I stumbled upon &lt;a href="https://entgo.io/"&gt;entgo&lt;/a&gt; (an ORM for Go) and decided to give it a try.&lt;/p&gt;

&lt;p&gt;Initially it was easy to setup and get started. The code generation seemed to work well and in no time I was using the database in my application. Everything was going well, but I ran into a few issues I could not over look:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto Migration Failures&lt;/li&gt;
&lt;li&gt;"Magical" Queries&lt;/li&gt;
&lt;li&gt;Code Bloat&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Auto Migration Failures
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://entgo.io/docs/migrate/#auto-migration"&gt;auto migration feature&lt;/a&gt; of module seems to have issues processing fields with &lt;code&gt;date/time&lt;/code&gt; values. After I spent hours debugging I reached out to their community to see if anyone had this issue. I received no response in their Discord (does not seem very active) and I only received one response on a &lt;a href="https://github.com/ent/ent/issues/3790"&gt;Github issue I posted&lt;/a&gt; on the topic (after over a month of the issue being posted).&lt;/p&gt;

&lt;h4&gt;
  
  
  "Magical" Queries
&lt;/h4&gt;

&lt;p&gt;During load testing on my application I began noticing performance issues from the database. When I began using the &lt;code&gt;Debug()&lt;/code&gt; mode for the entgo client I observed overly complex queries, even for simple cases (i.e. simple &lt;code&gt;SELECT&lt;/code&gt;s, no &lt;code&gt;JOIN&lt;/code&gt;s. Some of these issues may be resolved by modifying the &lt;a href="https://entgo.io/docs/code-gen#code-generation-options"&gt;code generation&lt;/a&gt; options of the library but that seems like quite a bit of additional effort for an issue that should not exist.&lt;/p&gt;

&lt;h4&gt;
  
  
  Code Bloat
&lt;/h4&gt;

&lt;p&gt;The included &lt;code&gt;ent&lt;/code&gt; code generation tool could use some optimization, reviewing many of the files reveals many included functions and complex logic that (as I understand it) adds little value unless advanced features of the library are being used (GraphQL integration, Custom Hooks, etc). &lt;/p&gt;

&lt;h2&gt;
  
  
  What to use instead?
&lt;/h2&gt;

&lt;p&gt;I just told you all of the reasons why not to use entgo, but what should you use? Honestly, raw SQL or really a raw SQL schema and raw queries with &lt;a href="https://docs.sqlc.dev/en/stable/index.html"&gt;sqlc&lt;/a&gt; for Go code generation. That may sound counter intuitive, but hear me out. &lt;code&gt;sqlc&lt;/code&gt; is not an ORM, you still write your database schema, perform your migrations, and write your own queries. &lt;code&gt;sqlc&lt;/code&gt; handles the generation of Go interfaces to perform those queries in a type-safe fashion within your application. This has a few advantages over &lt;code&gt;entgo&lt;/code&gt; (or any ORM for that matter):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No "Magic" Queries&lt;/li&gt;
&lt;li&gt;No Auto Migration System Failures&lt;/li&gt;
&lt;li&gt;Minimal Code Generation&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  No "Magic" Queries
&lt;/h4&gt;

&lt;p&gt;You write all of the queries yourself, so you can easily make them as efficient as your SQL knowledge allows&lt;/p&gt;

&lt;h4&gt;
  
  
  No Auto Migration System Failures
&lt;/h4&gt;

&lt;p&gt;You perform the database migrations yourself before &lt;code&gt;sqlc&lt;/code&gt; is ever called, it cannot cause migration errors if it does not perform migrations.&lt;/p&gt;

&lt;h4&gt;
  
  
  Minimal Code Generation
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;sqlc&lt;/code&gt; generates 3 (minimal) files total as part of its tooling, regardless of the database schema or number/complexity of queries.&lt;/p&gt;

&lt;p&gt;For all of these reasons above I am officially making the switch from using &lt;code&gt;entgo&lt;/code&gt; to &lt;code&gt;sqlc&lt;/code&gt;. If there is interest I can write about that migration in more detail, let me know what you think about that in the comments.&lt;/p&gt;

&lt;p&gt;I hope you read this and have considered that an ORM like &lt;code&gt;entgo&lt;/code&gt; might not actually be the faster/easier approach to adding SQL queries to your application and that sometimes a simpler approach can be just as good, if not better.&lt;/p&gt;

</description>
      <category>go</category>
      <category>database</category>
      <category>programming</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
