<?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: Jaruwat Panturat</title>
    <description>The latest articles on DEV Community by Jaruwat Panturat (@jaruwat_panturat_4b2c0617).</description>
    <link>https://dev.to/jaruwat_panturat_4b2c0617</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%2F2027341%2Fc079b74e-73d6-4894-9b8a-1da70d73e8e4.jpg</url>
      <title>DEV Community: Jaruwat Panturat</title>
      <link>https://dev.to/jaruwat_panturat_4b2c0617</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jaruwat_panturat_4b2c0617"/>
    <language>en</language>
    <item>
      <title>Open Source Contribution Note #1: Displaying ESO Custom Target Resources on ArgoCD UI</title>
      <dc:creator>Jaruwat Panturat</dc:creator>
      <pubDate>Sat, 04 Apr 2026 07:02:27 +0000</pubDate>
      <link>https://dev.to/jaruwat_panturat_4b2c0617/open-source-contribution-note-1-displaying-eso-custom-target-resources-on-argocd-ui-3c3l</link>
      <guid>https://dev.to/jaruwat_panturat_4b2c0617/open-source-contribution-note-1-displaying-eso-custom-target-resources-on-argocd-ui-3c3l</guid>
      <description>&lt;p&gt;Currently, I'm working as a platform engineer. Kubernetes (and its ecosystem, e.g., External Secrets, ArgoCD) and AWS services, including AWS Secrets Manager and EKS, are things I deal with every day. From my point of view, I am an end user of those CNCF projects and AWS. As I want to go further into the cloud-native path, I decided to start my open source contributor journey just 2 months ago. For this note, I will explain what I did in this contribution, from start, investigation to delivering the PR.&lt;/p&gt;

&lt;p&gt;Normally, The feature requests, bugs, documentation requests, questions are listed on the &lt;a href="https://github.com/external-secrets/external-secrets/issues" rel="noopener noreferrer"&gt;issues&lt;/a&gt; page. This post is about my contribution to the issue number &lt;a href="https://github.com/external-secrets/external-secrets/issues/6104" rel="noopener noreferrer"&gt;#6104&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This contribution is related to 2 CNCF projects: &lt;strong&gt;ArgoCD&lt;/strong&gt; and &lt;strong&gt;External Secrets Operator (ESO)&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  ArgoCD
&lt;/h2&gt;

&lt;p&gt;A CNCF graduated project and a popular GitOps continuous delivery tool for Kubernetes. It watches your Git repository and ensures your cluster state always matches what's defined there. Its UI console gives you a visual overview of all your application resources and their health status. (&lt;a href="https://argo-cd.readthedocs.io/" rel="noopener noreferrer"&gt;https://argo-cd.readthedocs.io/&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  External Secrets Operator (ESO)
&lt;/h2&gt;

&lt;p&gt;A CNCF sandbox project that acts as a bridge between Kubernetes and external secret management systems such as AWS Secrets Manager, HashiCorp Vault, and Google Secret Manager. Instead of hardcoding secrets into your cluster, ESO automatically syncs them into Kubernetes &lt;code&gt;Secret&lt;/code&gt; from your preferred secret provider. (&lt;a href="https://external-secrets.io/" rel="noopener noreferrer"&gt;https://external-secrets.io/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;For ESO, there are 2 main CRDs: &lt;code&gt;SecretStore&lt;/code&gt; and &lt;code&gt;ExternalSecret&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SecretStore&lt;/code&gt; is where we define how to reach the external API, such as AWS Secrets Manager.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ExternalSecret&lt;/code&gt; is where we define which data to retrieve and what to create in your cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you define everything correctly (happy path), the ESO operator will create a Kubernetes native &lt;code&gt;Secret&lt;/code&gt; in your cluster. ArgoCD also shows the relationship between the &lt;code&gt;ExternalSecret&lt;/code&gt; resource and the created &lt;code&gt;Secret&lt;/code&gt;, which looks like this on the ArgoCD console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F2qcb7dxx383vb6yg9bfo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F2qcb7dxx383vb6yg9bfo.png" alt=" " width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem statement
&lt;/h2&gt;

&lt;p&gt;This was something new to me as well. ESO is not only able to create Kubernetes &lt;code&gt;Secret&lt;/code&gt;s, but it can also create other resources in your cluster. For example, if you want ESO to create a Kubernetes &lt;code&gt;ConfigMap&lt;/code&gt; from AWS Parameter Store, this is also doable. This feature is called &lt;a href="https://external-secrets.io/latest/guides/targeting-custom-resources/" rel="noopener noreferrer"&gt;Targeting Custom Resources&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But one key difference between creating &lt;code&gt;Secret&lt;/code&gt; and other resources is that the relationship for resources other than &lt;code&gt;Secret&lt;/code&gt; is not shown on the ArgoCD console.&lt;/p&gt;

&lt;p&gt;So, in the case of non-&lt;code&gt;Secret&lt;/code&gt; resources, it looked like this on the ArgoCD console:&lt;br&gt;
&lt;a href="https://media2.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%2Fvm0nyixe4oain92rqeqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fvm0nyixe4oain92rqeqp.png" alt=" " width="800" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which should have looked like this:&lt;br&gt;
&lt;a href="https://media2.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%2Fswtjsqxlnaqf15htg6ya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fswtjsqxlnaqf15htg6ya.png" alt=" " width="800" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was not the original reporter of this issue (&lt;a href="https://github.com/external-secrets/external-secrets/issues/6104" rel="noopener noreferrer"&gt;#6104&lt;/a&gt;), but I picked it up while exploring this feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  Investigation
&lt;/h2&gt;

&lt;p&gt;So, I tried creating two &lt;code&gt;ExternalSecret&lt;/code&gt; resources, one for a &lt;code&gt;Secret&lt;/code&gt; and another for a &lt;code&gt;ConfigMap&lt;/code&gt;, to see what distinguishes them. This was what I found:&lt;br&gt;
&lt;a href="https://media2.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%2F3xe0bu2e0jnbs517xr5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F3xe0bu2e0jnbs517xr5u.png" alt=" " width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key differences between the two cases were the &lt;code&gt;ownerReferences&lt;/code&gt; attribute and the external-secrets &lt;code&gt;annotations&lt;/code&gt; and &lt;code&gt;labels&lt;/code&gt;. They were added only in the &lt;code&gt;Secret&lt;/code&gt;, not in the &lt;code&gt;ConfigMap&lt;/code&gt; case.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ownerReferences&lt;/code&gt;: This is the Kubernetes native way of indicating which resource owns another resource and ArgoCD relies on this attribute to understand resource relationships and visualize them in the UI. An example of this is the relationship between a &lt;code&gt;Deployment&lt;/code&gt;, &lt;code&gt;ReplicaSet&lt;/code&gt;, and &lt;code&gt;Pod&lt;/code&gt;. When you create a &lt;code&gt;Deployment&lt;/code&gt;, the &lt;code&gt;ReplicaSet&lt;/code&gt; that is created from it will have &lt;code&gt;ownerReferences&lt;/code&gt; that refer to the &lt;code&gt;Deployment&lt;/code&gt;. The same concept applies to the &lt;code&gt;Pod&lt;/code&gt;s created by the &lt;code&gt;ReplicaSet&lt;/code&gt;. (See more &lt;a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/" rel="noopener noreferrer"&gt;detail&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fq6gcjgk3hcuwjx0xh4ch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fq6gcjgk3hcuwjx0xh4ch.png" alt=" " width="800" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The external-secrets annotations and labels: These are required for the ESO controller to reconcile the resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, my plan to fix this was to add the missing &lt;code&gt;ownerReferences&lt;/code&gt; attribute, &lt;code&gt;labels&lt;/code&gt;, and &lt;code&gt;annotations&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;The ESO project is implemented in Go. The main codebase for this fix is in &lt;code&gt;pkg/controllers/externalsecret&lt;/code&gt;, which holds the key logic for how &lt;code&gt;Secret&lt;/code&gt; and generic targets (e.g., &lt;code&gt;ConfigMap&lt;/code&gt;) are created by the controllers.&lt;/p&gt;

&lt;p&gt;In this folder, there are several controllers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/external-secrets/external-secrets/blob/main/pkg/controllers/externalsecret/externalsecret_controller.go" rel="noopener noreferrer"&gt;externalsecret_controller.go&lt;/a&gt;: The main reconcile loop. It routes to the Secret path or the generic target path based on whether &lt;code&gt;spec.target.manifest&lt;/code&gt; is set. The &lt;code&gt;Secret&lt;/code&gt; path's ownership logic (&lt;code&gt;mutationFunc&lt;/code&gt;) already worked correctly which I aimed to replicate for generic targets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/external-secrets/external-secrets/blob/main/pkg/controllers/externalsecret/externalsecret_controller_template.go" rel="noopener noreferrer"&gt;externalsecret_controller_template.go&lt;/a&gt;: Handles templating and metadata for the &lt;code&gt;Secret&lt;/code&gt; path only. Its &lt;code&gt;setMetadata()&lt;/code&gt; function already propagated ExternalSecret &lt;code&gt;labels&lt;/code&gt;/&lt;code&gt;annotations&lt;/code&gt; as a fallback. The generic target path was missing this same behavior.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/external-secrets/external-secrets/blob/main/pkg/controllers/externalsecret/externalsecret_controller_manifest.go" rel="noopener noreferrer"&gt;externalsecret_controller_manifest.go&lt;/a&gt;: Handles create/update/delete for generic targets. The metadata propagation and the ownership logic were missing here.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/external-secrets/external-secrets/blob/main/pkg/controllers/externalsecret/externalsecret_controller_manifest_test.go" rel="noopener noreferrer"&gt;externalsecret_controller_manifest_test.go&lt;/a&gt;: The unit tests for the above controller.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this understanding, the changes came down to two things in &lt;code&gt;externalsecret_controller_manifest.go&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Propagate the ExternalSecret's labels and annotations to the target resource (the same fallback that setMetadata() already did for Secrets)&lt;/li&gt;
&lt;li&gt;Add an &lt;code&gt;applyOwnership()&lt;/code&gt; function to set &lt;code&gt;ownerReferences&lt;/code&gt; on generic targets (replicating the existing &lt;code&gt;mutationFunc&lt;/code&gt; pattern for Secrets). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://github.com/external-secrets/external-secrets/pull/6132" rel="noopener noreferrer"&gt;pull request&lt;/a&gt; was raised for these changes plus the unit tests that cover the proposed changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The PR was finally reviewed and merged. It was my very first contribution to open source software, and I'm quite proud of that. This work was included in &lt;a href="https://github.com/external-secrets/external-secrets/releases/tag/v2.3.0" rel="noopener noreferrer"&gt;version 2.3.0&lt;/a&gt;, It feels really good seeing my name there and I gained several key learnings from this contribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On AI in open source contribution&lt;/strong&gt;&lt;br&gt;
AI is increasingly welcomed in the contribution process. In fact, ESO already has AI in place. For example, ESO's code review bot, which looks very impressive. However, this doesn't mean contributors can rely on AI 100% for their contributions. They are still the ones accountable for their changes.&lt;/p&gt;

&lt;p&gt;From my experience, I used AI to help understand the codebase and code generation. But if you have no foundational knowledge of the project, say, no idea about Kubernetes, ESO, or ArgoCD then AI is not quite useful. It can even be worse as it might suggest hallucinated or unrelated changes that you don't have a chance to challenge.&lt;/p&gt;

&lt;p&gt;My suggestions for using AI in contributions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand the coding standards and conventions of the project before you start.&lt;/li&gt;
&lt;li&gt;Strictly review what AI produces. Try to understand every output and the reasons behind the change.&lt;/li&gt;
&lt;li&gt;Build and test the change locally. Deploy to your local cluster and make sure it works as expected. Don't trust AI-generated code blindly.&lt;/li&gt;
&lt;li&gt;Most importantly, interact with other contributors and maintainers in your own words. Do not copy and paste responses from AI. Always remember that another contributor, maintainer, or issue requester on the other end is a human (at least from what I’ve seen so far 😄). So, treat them with respect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;On the open source experience&lt;/strong&gt;&lt;br&gt;
The maintainers were very supportive throughout the process. They are responsive in reviews and helpful with guidance. It was also great to see the extensive automation in the project especially the GitHub Actions workflows and bots. There's a lot to learn just from working with others in the community and seeing how a well-maintained project operates.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>cloudnative</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
