<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[TECHWITHHUZ]]></title><description><![CDATA[My name is Huzefa. I am a seasoned DevOps consultant adept at automating and managing comprehensive infrastructures. Extensive expertise in Kubernetes, OpenShif]]></description><link>https://blog.techwithhuz.com</link><generator>RSS for Node</generator><lastBuildDate>Sun, 19 Apr 2026 15:15:59 GMT</lastBuildDate><atom:link href="https://blog.techwithhuz.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How ArgoCD and Sealed Secret works]]></title><description><![CDATA[In the ever-evolving landscape of Kubernetes and DevOps, orchestrating seamless deployments while maintaining top-notch security is the holy grail. In this comprehensive blog, we'll take you on an in-depth journey into two powerful tools that can ele...]]></description><link>https://blog.techwithhuz.com/how-argocd-and-sealed-secret-works</link><guid isPermaLink="true">https://blog.techwithhuz.com/how-argocd-and-sealed-secret-works</guid><category><![CDATA[ArgoCD]]></category><category><![CDATA[argocd support]]></category><category><![CDATA[Devops]]></category><category><![CDATA[sealed secret]]></category><category><![CDATA[sealedsecret]]></category><dc:creator><![CDATA[TechWithHuz]]></dc:creator><pubDate>Sun, 17 Sep 2023 11:17:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1694949240595/15fed7c4-bb7d-4386-b631-1dee115df795.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the ever-evolving landscape of Kubernetes and DevOps, orchestrating seamless deployments while maintaining top-notch security is the holy grail. In this comprehensive blog, we'll take you on an in-depth journey into two powerful tools that can elevate your Kubernetes game to the next level: ArgoCD and Sealed Secrets.</p>
<h3 id="heading-the-power-of-gitops"><strong>The Power of GitOps</strong></h3>
<ul>
<li><p><em>Understanding GitOps</em>: We'll start by dissecting the concept of GitOps, exploring its origins, and why it has become a game-changer in the Kubernetes ecosystem.</p>
</li>
<li><p><em>ArgoCD Unveiled</em>: A deep dive into ArgoCD, including its architecture, components, and how it manages Kubernetes applications declaratively.</p>
</li>
<li><p><em>Git Repository as the Source of Truth</em>: Learn why your Git repository becomes the ultimate source of truth for your Kubernetes configurations, bringing transparency, accountability, and robust version control to your deployments.</p>
</li>
</ul>
<h2 id="heading-setting-up-argocd"><strong>Setting Up ArgoCD</strong></h2>
<h3 id="heading-installation-and-configuration"><em>Installation and Configuration</em></h3>
<ul>
<li><p>To get started with ArgoCD, you'll first need to install it in your Kubernetes cluster. This can be achieved using a simple Kubernetes manifest or a Helm chart. We will walk you through both methods, providing guidance on choosing the one that best fits your requirements.</p>
<p>  <em>Example</em>:</p>
<pre><code class="lang-bash">  <span class="hljs-comment"># ArgoCD Installation via Helm</span>
  helm repo add argo https://argoproj.github.io/argo-helm
  helm install my-argocd argo/argo-cd
</code></pre>
<p>  Once installed, you'll need to configure ArgoCD to connect to your Git repository where your Kubernetes manifests and ArgoCD Application configurations are stored. We'll show you how to set up these configurations, including connecting to private Git repositories securely.</p>
<h3 id="heading-multi-cluster-support"><em>Multi-Cluster Support</em></h3>
<p>  ArgoCD excels in managing applications across multiple Kubernetes clusters. We'll guide you through the process of configuring ArgoCD to support multi-cluster environments, allowing you to deploy and manage applications seamlessly, no matter how complex your infrastructure.</p>
<h3 id="heading-role-based-access-control-rbac"><em>Role-Based Access Control (RBAC)</em></h3>
<p>  Securing your ArgoCD instance is paramount. We'll provide best practices for implementing Role-Based Access Control (RBAC) within ArgoCD. You'll learn how to create custom roles, define permissions, and manage user access effectively.</p>
<p>  <em>Example</em>:</p>
<pre><code class="lang-bash">  <span class="hljs-comment"># Define a custom Role in ArgoCD</span>
  apiVersion: rbac.authorization.k8s.io/v1
  kind: Role
  metadata:
    name: my-custom-role
  rules:
  - apiGroups: [<span class="hljs-string">""</span>]
    resources: [<span class="hljs-string">"applications"</span>]
    verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"create"</span>, <span class="hljs-string">"update"</span>, <span class="hljs-string">"delete"</span>]
</code></pre>
</li>
</ul>
<h3 id="heading-deploying-applications-with-argocd"><strong>Deploying Applications with ArgoCD</strong></h3>
<p>Now that you have ArgoCD set up, it's time to dive into deploying applications. In this chapter, we'll guide you through defining Application Custom Resource Definitions (CRDs) and deploying your first application.</p>
<h3 id="heading-defining-application-crds"><em>Defining Application CRDs</em></h3>
<p>Applications in ArgoCD are defined using Application Custom Resource Definitions (CRDs). These YAML manifests declare how ArgoCD should deploy and manage your application.</p>
<p><em>Example</em>:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Application CRD for a sample application</span>
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: sample-app
  namespace: argocd
spec:
  <span class="hljs-built_in">source</span>:
    repoURL: https://github.com/your-org/your-repo
    targetRevision: HEAD
    path: apps/sample
  destination:
    server: <span class="hljs-string">'https://kubernetes.default.svc'</span>
    namespace: sample-namespace
  project: default
  syncPolicy:
    automated:
      prune: <span class="hljs-literal">true</span>
      selfHeal: <span class="hljs-literal">true</span>
</code></pre>
<p>Here, we're defining an application named <code>sample-app</code> that resides in a Git repository. ArgoCD will automatically synchronize this application according to the defined sync policy.</p>
<h3 id="heading-syncing-applications"><em>Syncing Applications</em></h3>
<p>ArgoCD continuously monitors your Git repositories for changes. When it detects updates, it syncs applications to match the desired state defined in your Git repository.</p>
<p><em>Example</em>:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Triggering a manual sync for an application</span>
argocd app sync sample-app
</code></pre>
<p>ArgoCD also provides a web-based dashboard for easy application monitoring and manual syncing.</p>
<h3 id="heading-application-rollbacks"><em>Application Rollbacks</em></h3>
<p>Sometimes, things don't go as planned. ArgoCD supports application rollbacks, allowing you to easily revert to a previous version of your application when issues arise.</p>
<p><em>Example</em>:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Rollback an application to a previous version</span>
argocd app rollback sample-app
</code></pre>
<h3 id="heading-manifests-and-application-resources"><strong>Manifests and Application Resources</strong></h3>
<p>In this section, we'll explore how ArgoCD deals with Kubernetes manifests and different application resources. By the end, you'll have a solid understanding of how to manage and deploy complex applications using ArgoCD.</p>
<p><strong><em>Managing Manifests</em></strong></p>
<p>ArgoCD shines when it comes to managing Kubernetes manifests. You can keep your application manifests in a Git repository, and ArgoCD will automatically synchronize them with your Kubernetes clusters.</p>
<p><em>Example</em>: Here's a simplified directory structure for your application's manifests:</p>
<pre><code class="lang-bash">my-app/
├── deployment.yaml
├── service.yaml
├── configmap.yaml
├── route.yaml
</code></pre>
<p>ArgoCD will track changes in this repository and ensure that the application state matches the definitions in these manifests.</p>
<p><strong><em>Resource Dependency</em></strong></p>
<p>Kubernetes applications often consist of multiple resources that depend on each other. For example, a Deployment may depend on a ConfigMap and a Service.</p>
<p><em>Example</em>: In the <code>deployment.yaml</code>, you might reference a ConfigMap:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">my-app-deployment</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">my-app</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">my-app-image</span>
          <span class="hljs-attr">envFrom:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">configMapRef:</span>
                <span class="hljs-attr">name:</span> <span class="hljs-string">my-app-config</span>
</code></pre>
<p>ArgoCD understands these dependencies and ensures that resources are synchronized in the correct order.</p>
<p><strong><em>Application Resources</em></strong></p>
<p>Applications often involve various Kubernetes resources. ArgoCD makes managing these resources straightforward.</p>
<p><em>Example</em>: Consider a YAML manifest for a Service:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">my-app-service</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">my-app</span>
  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">8080</span>
</code></pre>
<p>ArgoCD handles resources like Services, Deployments, ConfigMaps, and Routes seamlessly, deploying and managing them according to your Git repository's state.</p>
<h3 id="heading-deployment-and-synchronization"><em>Deployment and Synchronization</em></h3>
<p>Whenever you push changes to your Git repository, ArgoCD automatically detects the updates and synchronizes your application to match the desired state.</p>
<p><em>Example</em>: After you push an update to your manifests, ArgoCD will sync the application:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Triggering a sync</span>
argocd app sync my-app
</code></pre>
<h2 id="heading-sealed-secrets-with-argocd"><strong>Sealed Secrets with ArgoCD</strong></h2>
<p>In this section, we'll dive deeper into the world of Sealed Secrets and see how ArgoCD can securely simplify the management of secrets.</p>
<h3 id="heading-what-are-sealed-secrets"><em>What are Sealed Secrets?</em></h3>
<p>Sealed Secrets are an innovative way of managing Kubernetes secrets in a GitOps workflow. They work by encrypting your sensitive data and storing it as a SealedSecret custom resource in your Git repository.</p>
<h3 id="heading-how-to-use-sealed-secrets-with-argocd"><em>How to Use Sealed Secrets with ArgoCD</em></h3>
<h3 id="heading-step-1-install-sealed-secrets-controller"><strong>Step 1: Install Sealed Secrets Controller</strong></h3>
<p>The first step is to install the Sealed Secrets controller in your Kubernetes cluster. This controller is responsible for decrypting SealedSecrets objects during runtime.</p>
<pre><code class="lang-yaml"><span class="hljs-string">shellCopy</span> <span class="hljs-string">code#</span> <span class="hljs-string">Install</span> <span class="hljs-string">Sealed</span> <span class="hljs-string">Secrets</span> <span class="hljs-string">Controller</span>
<span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-string">https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.16.0/controller.yaml</span>
</code></pre>
<h3 id="heading-step-2-create-a-sealed-secret"><strong>Step 2: Create a Sealed Secret</strong></h3>
<p>Now, let's create a Sealed Secret from a regular Kubernetes Secret. You'll need to use the <code>kubeseal</code> command-line tool.</p>
<pre><code class="lang-yaml"><span class="hljs-string">shellCopy</span> <span class="hljs-string">code#</span> <span class="hljs-string">Create</span> <span class="hljs-string">a</span> <span class="hljs-string">Sealed</span> <span class="hljs-string">Secret</span> <span class="hljs-string">from</span> <span class="hljs-string">a</span> <span class="hljs-string">regular</span> <span class="hljs-string">Secret</span>
<span class="hljs-string">kubectl</span> <span class="hljs-string">create</span> <span class="hljs-string">secret</span> <span class="hljs-string">generic</span> <span class="hljs-string">my-secret</span> <span class="hljs-string">--dry-run=client</span> <span class="hljs-string">--from-literal=my-key=my-value</span> <span class="hljs-string">-o</span> <span class="hljs-string">json</span> <span class="hljs-string">|</span> <span class="hljs-string">kubeseal</span> <span class="hljs-string">&gt;</span> <span class="hljs-string">my-sealed-secret.yaml</span>
</code></pre>
<p>This command generates a SealedSecret YAML file that you can store in your Git repository.</p>
<h3 id="heading-step-3-add-sealed-secret-to-git-repository"><strong>Step 3: Add Sealed Secret to Git Repository</strong></h3>
<p>Commit the <code>my-sealed-secret.yaml</code> file to your Git repository, just like you would with other Kubernetes manifests.</p>
<h3 id="heading-step-4-deploy-sealed-secret-with-argocd"><strong>Step 4: Deploy Sealed Secret with ArgoCD</strong></h3>
<p>ArgoCD can automatically deploy Sealed Secrets. Create an ArgoCD Application resource for your Sealed Secret, just as you would for any other resource.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">yamlCopy codeapiVersion:</span> <span class="hljs-string">argoproj.io/v1alpha1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Application</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">my-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">project:</span> <span class="hljs-string">default</span>
  <span class="hljs-attr">source:</span>
    <span class="hljs-attr">repoURL:</span> <span class="hljs-string">&lt;URL_TO_YOUR_GIT_REPO&gt;</span>
    <span class="hljs-attr">targetRevision:</span> <span class="hljs-string">HEAD</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">path/to/secrets</span>
  <span class="hljs-attr">destination:</span>
    <span class="hljs-attr">server:</span> <span class="hljs-string">https://kubernetes.default.svc</span>
    <span class="hljs-attr">namespace:</span> <span class="hljs-string">my-namespace</span>
</code></pre>
<p>ArgoCD will handle the deployment of the Sealed Secret to your cluster securely.</p>
<h2 id="heading-benefits-of-using-sealed-secrets-with-argocd"><em>Benefits of Using Sealed Secrets with ArgoCD</em></h2>
<ol>
<li><p><strong>Security</strong>: Sensitive data is encrypted and stored in version control, reducing the risk of exposure.</p>
</li>
<li><p><strong>GitOps</strong>: Sealed Secrets fit perfectly into the GitOps workflow. You define, version, and track your secrets in your Git repository.</p>
</li>
<li><p><strong>Auditing</strong>: All changes to secrets are traceable through Git commits, providing a clear audit trail.</p>
</li>
<li><p><strong>Easy Management</strong>: ArgoCD simplifies the deployment and synchronization of Sealed Secrets, making secret management a breeze.</p>
</li>
</ol>
<h2 id="heading-enhancing-security-with-sealed-secrets"><strong>Enhancing Security with Sealed Secrets</strong></h2>
<ul>
<li><p><em>The Challenge of Secrets</em>: Delve into the critical issue of securing sensitive data within Kubernetes and why traditional Secrets fall short.</p>
</li>
<li><p><em>Sealed Secrets Revealed</em>: Learn about the architecture of Sealed Secrets and how it encrypts your secrets into a safe, Git-friendly format.</p>
</li>
<li><p><em>Public Key Encryption</em>: Understand how Sealed Secrets leverages public key encryption to ensure that only your target cluster can decrypt and utilize the secrets</p>
</li>
</ul>
<h2 id="heading-the-future-of-kubernetes-deployments"><strong>The Future of Kubernetes Deployments</strong></h2>
<ul>
<li><em>Emerging Trends</em>: Get a glimpse into the future of Kubernetes deployments, including the evolution of GitOps, new security paradigms, and the latest tools on the horizon.</li>
</ul>
<p>This blog aims to provide you with a comprehensive understanding of these powerful Kubernetes tools, offering the knowledge and expertise you need to optimize your Kubernetes deployments, enhance security, and stay ahead in the ever-changing world of DevOps. Get ready to embark on a deep dive into the world of ArgoCD and Sealed Secrets.</p>
]]></content:encoded></item><item><title><![CDATA[Prototyping AI APIs using AWS]]></title><description><![CDATA[Prototyping AI APIs using AWS
In the rapidly evolving world of Artificial Intelligence (AI), the ability to analyze and understand text data is becoming increasingly important. Amazon Web Services (AWS) offers a powerful service called Amazon Compreh...]]></description><link>https://blog.techwithhuz.com/prototyping-ai-apis-using-aws</link><guid isPermaLink="true">https://blog.techwithhuz.com/prototyping-ai-apis-using-aws</guid><category><![CDATA[AI]]></category><category><![CDATA[ML]]></category><category><![CDATA[AWS]]></category><category><![CDATA[AWS SDK]]></category><category><![CDATA[Sentiment analysis]]></category><dc:creator><![CDATA[TechWithHuz]]></dc:creator><pubDate>Sat, 02 Sep 2023 10:18:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692539643614/f279fa7e-7d43-405d-9f3a-659337e94399.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Prototyping AI APIs using AWS</p>
<p>In the rapidly evolving world of Artificial Intelligence (AI), the ability to analyze and understand text data is becoming increasingly important. Amazon Web Services (AWS) offers a powerful service called Amazon Comprehend that allows developers to build Natural Language Processing (NLP) applications without the need for extensive machine learning expertise. In this blog post, we will explore the process of prototyping AI APIs using AWS Comprehend to extract valuable insights from text data.</p>
<h2 id="heading-understanding-aws-comprehend"><strong>Understanding AWS Comprehend</strong></h2>
<p>Amazon Comprehend is a fully managed NLP service that enables businesses to extract useful information and relationships from unstructured text data. It can identify entities, key phrases, sentiments, language, and more from a variety of text sources, including documents, social media, and customer feedback.</p>
<p><strong>Getting Started: Setting Up AWS Comprehend:</strong></p>
<ol>
<li><p><strong>AWS Account Setup:</strong> If you don't already have an AWS account, sign up and create one.</p>
</li>
<li><p><strong>Access Amazon Comprehend:</strong> Navigate to the AWS Management Console and locate the Amazon Comprehend service.</p>
</li>
<li><p><strong>Create a Custom IAM Role:</strong> Create an IAM role that grants necessary permissions to Amazon Comprehend.</p>
</li>
</ol>
<h2 id="heading-prototyping-ai-apis-using-aws-comprehend"><strong>Prototyping AI APIs using AWS Comprehend:</strong></h2>
<ol>
<li><p><strong>Text Sentiment Analysis:</strong></p>
<ul>
<li><p>Use the AWS SDK to interact with the Comprehend service programmatically.</p>
</li>
<li><pre><code class="lang-bash">  $ aws comprehend <span class="hljs-built_in">help</span>
  NAME
         comprehend -

  DESCRIPTION
         Amazon Comprehend is an Amazon Web Services service <span class="hljs-keyword">for</span> gaining insight
         into the content of documents. Use these actions to determine the  top-
         ics  contained <span class="hljs-keyword">in</span> your documents, the topics they discuss, the predomi-
         nant sentiment expressed <span class="hljs-keyword">in</span> them, the predominant  language  used,  and
         more.

  AVAILABLE COMMANDS
         o batch-detect-dominant-language

         o batch-detect-entities

         o batch-detect-key-phrases

         o batch-detect-sentiment

         o batch-detect-syntax

         o batch-detect-targeted-sentiment

         o classify-document

         o contains-pii-entities

         o create-dataset

         o create-document-classifier

         o create-endpoint

         o create-entity-recognizer

         o create-flywheel

         o delete-document-classifier

         o delete-endpoint

         o delete-entity-recognizer

         o delete-flywheel

         o delete-resource-policy

         o describe-dataset
</code></pre>
</li>
<li><p>Let's say we want to do the sentiment analysis of text - "I love Python Programming language"</p>
<pre><code class="lang-bash">  $ aws comprehend detect-sentiment --language-code <span class="hljs-string">"en"</span> --text <span class="hljs-string">"I love Python programming language"</span>
  {
      <span class="hljs-string">"Sentiment"</span>: <span class="hljs-string">"POSITIVE"</span>,
      <span class="hljs-string">"SentimentScore"</span>: {
          <span class="hljs-string">"Positive"</span>: 0.9986007809638977,
          <span class="hljs-string">"Negative"</span>: 9.051552478922531e-05,
          <span class="hljs-string">"Neutral"</span>: 0.0011894343188032508,
          <span class="hljs-string">"Mixed"</span>: 0.00011932779307244346
      }
  }
</code></pre>
<p>  We can see the sentiment analysis is POSITIVE which is very true.</p>
</li>
<li><p>Now let's say we want to analyse a document or a data from website which is very simple. We will use lynx command to get the data from website into a variable.</p>
<pre><code class="lang-bash">  $ TEXT=`lynx -dump https://en.wikipedia.org/wiki/Python_\(programming_language\) | head -c 5000`
  $
</code></pre>
</li>
<li><p>Pass the $TEXT variable to a detect-sentiment command</p>
<pre><code class="lang-bash">  $ aws comprehend detect-sentiment --language-code <span class="hljs-string">"en"</span> --text <span class="hljs-string">"<span class="hljs-variable">$TEXT</span>"</span>
  {
      <span class="hljs-string">"Sentiment"</span>: <span class="hljs-string">"NEUTRAL"</span>,
      <span class="hljs-string">"SentimentScore"</span>: {
          <span class="hljs-string">"Positive"</span>: 0.00020325076184235513,
          <span class="hljs-string">"Negative"</span>: 0.0005437986110337079,
          <span class="hljs-string">"Neutral"</span>: 0.999243974685669,
          <span class="hljs-string">"Mixed"</span>: 9.053033863892779e-06
      }
  }
</code></pre>
</li>
<li><p>This is how Comprehend classifies text as positive, negative, neutral, or mixed sentiment.</p>
</li>
</ul>
</li>
</ol>
<ol>
<li><p><strong>Entity Recognition:</strong></p>
<ul>
<li><p>Detects named entities in input text when you use the pre-trained model. Detects custom entities if you have a custom entity recognition model.</p>
<p>  When detecting named entities using the pre-trained model, use plain text as the input.</p>
</li>
<li><p>Let's say we want to analysis our website data and want to know about word count for each word.</p>
<pre><code class="lang-bash">  $ aws comprehend detect-entities --language-code <span class="hljs-string">"en"</span> --text <span class="hljs-string">"<span class="hljs-variable">$TEXT</span>"</span> --output text | cut -f 5 |sort|uniq -c |sort -nr -k 1
        4 Wikipedia
        2 Python
        2 Norsk
        2 English
        2 Bahasa
        2 한국어
        1 粵語
        1 日本語
        1 吴语
        1 中文
        1 සිංහල
        1 မြန်မာဘာသာ
        1 বাংলা
        1 অসমীয়া
        1 മലയാളം
        1 தமிழ்
</code></pre>
</li>
<li><p>Explore how Comprehend can enhance data extraction and organization.</p>
</li>
</ul>
</li>
<li><p><strong>Key Phrase Extraction:</strong></p>
<ul>
<li><p>Detects the key noun phrases found in the text.</p>
<pre><code class="lang-bash">  $ aws comprehend detect-key-phrases --language-code <span class="hljs-string">"en"</span> --text <span class="hljs-string">"<span class="hljs-variable">$TEXT</span>"</span> --output text
  KEYPHRASES      3       6       0.8861472606658936      <span class="hljs-comment">#[1</span>
  KEYPHRASES      7       29      0.7588788866996765      alternate [2]Wikipedia
  KEYPHRASES      35      57      0.8782252073287964      [3]Wikipedia Atom feed
  KEYPHRASES      62      69      0.7381764054298401      [4]Jump
  KEYPHRASES      73      80      0.5808557271957397      content
  KEYPHRASES      85      98      0.5795749425888062      [ ] Main menu
  KEYPHRASES      107     111     0.5609327554702759      menu
  KEYPHRASES      116     122     0.9981253743171692      BUTTON
  KEYPHRASES      141     147     0.9974844455718994      BUTTON
  KEYPHRASES      157     167     0.9399071335792542      Navigation
  KEYPHRASES      175     187     0.8160272836685181      [5]Main page
  KEYPHRASES      195     206     0.7837936878204346      [6]Contents
  KEYPHRASES      215     231     0.7777291536331177      7]Current events
  KEYPHRASES      239     256     0.858264684677124       [8]Random article
  KEYPHRASES      264     267     0.7898728847503662      [9]
  KEYPHRASES      273     282     0.8571096062660217      Wikipedia
  KEYPHRASES      290     294     0.7877490520477295      [10]
  KEYPHRASES      312     337     0.8975982069969177      [11]Donate
</code></pre>
</li>
</ul>
</li>
</ol>
<h2 id="heading-use-case-analyzing-customer-feedback"><strong>Use Case: Analyzing Customer Feedback</strong></h2>
<p>This kind of tool is very helpful, where a company can use AWS Comprehend to analyze customer feedback from various sources. They can improve customer satisfaction by doing sentiment analysis, entity recognition, and key phrase extraction.</p>
]]></content:encoded></item><item><title><![CDATA[Develop a Kubernetes Operator using GoLang]]></title><description><![CDATA[Kubernetes has revolutionized the world of container orchestration, and Kubernetes Operators take it a step further by automating complex application management tasks. In this blog, we'll delve into the realm of Kubernetes Operators, walking you thro...]]></description><link>https://blog.techwithhuz.com/develop-a-kubernetes-operator-using-golang</link><guid isPermaLink="true">https://blog.techwithhuz.com/develop-a-kubernetes-operator-using-golang</guid><category><![CDATA[Devops]]></category><category><![CDATA[operator SDK]]></category><category><![CDATA[Operators]]></category><category><![CDATA[#kubernetes #container ]]></category><category><![CDATA[openshift]]></category><dc:creator><![CDATA[TechWithHuz]]></dc:creator><pubDate>Sat, 02 Sep 2023 10:16:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692190935187/50f38358-dac5-47f0-881c-fa4800571f1d.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Kubernetes has revolutionized the world of container orchestration, and Kubernetes Operators take it a step further by automating complex application management tasks. In this blog, we'll delve into the realm of Kubernetes Operators, walking you through the process of developing one using the Go programming language.</p>
<h2 id="heading-understanding-kubernetes-operators"><strong>Understanding Kubernetes Operators</strong></h2>
<p>Before we dive into the technical details, let's understand what a Kubernetes Operator is. An Operator is an extension of Kubernetes that automates the deployment, management, and scaling of applications. It encapsulates domain-specific knowledge to ensure your applications run seamlessly within a Kubernetes environment.</p>
<h2 id="heading-why-golang-for-developing-operators"><strong>Why GoLang for Developing Operators?</strong></h2>
<p>Go, also known as Golang, has gained immense popularity for its simplicity, efficiency, and performance. It's an ideal choice for building Kubernetes Operators due to its strong concurrency support and suitability for systems programming.</p>
<h2 id="heading-operator-sdk"><strong>Operator SDK</strong></h2>
<p>The <a target="_blank" href="https://sdk.operatorframework.io/">Operator SDK</a> provides building blocks that simplify Operator development. The Operator SDK makes it easier to build Kubernetes native applications, a process that can require deep, application-specific operational knowledge. Use the SDK to create controllers, handlers, and operators that follow best practices and ensure consistency across your project.</p>
<h2 id="heading-start-building-the-operator">Start building the Operator</h2>
<h3 id="heading-prerequisite">Prerequisite</h3>
<p>You should have a working Kubernetes cluster or Openshift cluster.</p>
<ul>
<li><p><a target="_blank" href="https://git-scm.com/downloads">git</a></p>
</li>
<li><p><a target="_blank" href="https://golang.org/dl/">go</a></p>
</li>
<li><p><a target="_blank" href="https://docs.docker.com/install/">docker</a></p>
</li>
<li><p><a target="_blank" href="https://kubernetes.io/docs/tasks/tools/install-kubectl/">kubectl</a> and access to a Kubernetes cluster of a <a target="_blank" href="https://sdk.operatorframework.io/docs/overview#kubernetes-version-compatibility">compatible version</a>.</p>
</li>
</ul>
<h2 id="heading-gihub-repository-used-for-this-tutorial">Gihub Repository used for this tutorial</h2>
<p>Code for this tutorial is here -</p>
<p><a target="_blank" href="https://github.com/techwithhuz/kubernetes-operator/tree/main/techwithhuz-operator">https://github.com/techwithhuz/kubernetes-operator/tree/main/techwithhuz-operator</a></p>
<h3 id="heading-step-1-set-up-your-development-environment"><strong>Step 1: Set Up Your Development Environment</strong></h3>
<p>Start by installing the necessary tools: Go, the Kubernetes command-line tool (<code>kubectl</code>), and the Operator SDK.</p>
<p>Install the <a target="_blank" href="https://sdk.operatorframework.io/docs/installation/">Operator SDK CLI</a>. We will use the GitHub release method to install Operator SDK. For Mac users brew install is available.</p>
<h3 id="heading-install-from-github-release"><strong>Install from GitHub release</strong></h3>
<h4 id="heading-prerequisites"><strong>Prerequisites</strong></h4>
<ul>
<li><p><a target="_blank" href="https://curl.haxx.se/">curl</a></p>
</li>
<li><p><a target="_blank" href="https://gnupg.org/">gpg</a> version 2.0+</p>
</li>
</ul>
<h4 id="heading-1download-the-release-binary"><strong>1.Download the release binary</strong></h4>
<p>Set platform information:</p>
<pre><code class="lang-sh"><span class="hljs-variable">$export</span> ARCH=$(<span class="hljs-keyword">case</span> $(uname -m) <span class="hljs-keyword">in</span> x86_64) <span class="hljs-built_in">echo</span> -n amd64 ;; aarch64) <span class="hljs-built_in">echo</span> -n arm64 ;; *) <span class="hljs-built_in">echo</span> -n $(uname -m) ;; <span class="hljs-keyword">esac</span>)
<span class="hljs-variable">$export</span> OS=$(uname | awk <span class="hljs-string">'{print tolower($0)}'</span>)
</code></pre>
<p>Download the binary for your platform:</p>
<pre><code class="lang-sh"><span class="hljs-variable">$export</span> OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.31.0
<span class="hljs-variable">$curl</span> -LO <span class="hljs-variable">${OPERATOR_SDK_DL_URL}</span>/operator-sdk_<span class="hljs-variable">${OS}</span>_<span class="hljs-variable">${ARCH}</span>
</code></pre>
<h4 id="heading-2-verify-the-downloaded-binary"><strong>2. Verify the downloaded binary</strong></h4>
<p>Import the operator-sdk release GPG key from <a target="_blank" href="http://keyserver.ubuntu.com"><code>keyserver.ubuntu.com</code></a>:</p>
<pre><code class="lang-sh"><span class="hljs-variable">$gpg</span> --keyserver keyserver.ubuntu.com --recv-keys 052996E2A20B5C7E
</code></pre>
<p>Download the checksums file and its signature, then verify the signature:</p>
<pre><code class="lang-sh"><span class="hljs-variable">$curl</span> -LO <span class="hljs-variable">${OPERATOR_SDK_DL_URL}</span>/checksums.txt
<span class="hljs-variable">$curl</span> -LO <span class="hljs-variable">${OPERATOR_SDK_DL_URL}</span>/checksums.txt.asc
<span class="hljs-variable">$gpg</span> -u <span class="hljs-string">"Operator SDK (release) &lt;cncf-operator-sdk@cncf.io&gt;"</span> --verify checksums.txt.asc
</code></pre>
<p>You should see something similar to the following:</p>
<pre><code class="lang-bash">gpg: assuming signed data <span class="hljs-keyword">in</span> <span class="hljs-string">'checksums.txt'</span>
gpg: Signature made Fri 30 Oct 2020 12:15:15 PM PDT
gpg:                using RSA key ADE83605E945FA5A1BD8639C59E5B47624962185
gpg: Good signature from <span class="hljs-string">"Operator SDK (release) &lt;cncf-operator-sdk@cncf.io&gt;"</span> [ultimate]
</code></pre>
<p>Make sure the checksums match:</p>
<pre><code class="lang-bash"><span class="hljs-variable">$grep</span> operator-sdk_<span class="hljs-variable">${OS}</span>_<span class="hljs-variable">${ARCH}</span> checksums.txt | sha256sum -c -
</code></pre>
<p>You should see something similar to the following:</p>
<pre><code class="lang-bash">operator-sdk_linux_amd64: OK
</code></pre>
<ol>
<li><p>Install Go</p>
<p> Download the latest version of Go - <a target="_blank" href="https://go.dev/dl/">https://go.dev/dl</a></p>
<p> Then unzip it.</p>
</li>
</ol>
<pre><code class="lang-bash">$ wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz
Saving to: <span class="hljs-string">'go1.21.0.linux-amd64.tar.gz'</span>

go1.21.0.linux-amd64.tar.gz          100%[===================================================================&gt;]  63.40M  85.2MB/s    <span class="hljs-keyword">in</span> 0.7s    

2023-08-13 08:30:42 (85.2 MB/s) - <span class="hljs-string">'go1.21.0.linux-amd64.tar.gz'</span> saved [66479500/66479500]

<span class="hljs-variable">$tar</span> -xvf go1.21.0.linux-amd64.tar.gz

<span class="hljs-variable">$pwd</span>
/root/operator
</code></pre>
<ol>
<li><p>Set PATH variable for GO</p>
<p> You can set the custom path where you downloaded the GO binary or you can copy the binaries to the default /usr/local path</p>
<p> Here, we will be set to the custom path and check the version</p>
</li>
</ol>
<pre><code class="lang-bash">$ <span class="hljs-built_in">export</span> PATH=/usr/<span class="hljs-built_in">local</span>/sbin:/usr/<span class="hljs-built_in">local</span>/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/operator/go/bin:/snap/bin:/root/operator/go/bin:/snap/bin

$ <span class="hljs-built_in">echo</span> <span class="hljs-variable">$PATH</span>
/usr/<span class="hljs-built_in">local</span>/sbin:/usr/<span class="hljs-built_in">local</span>/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/operator/go/bin:/snap/bin:/root/operator/go/bin:/snap/bin

 $ go version
go version go1.21.0 linux/amd64
</code></pre>
<h3 id="heading-step-2-start-building-the-operator"><strong>Step 2: Start building the Operator</strong></h3>
<p>We will create and deploy one demo application with the name <a target="_blank" href="https://techwithhuz.com/"><strong>techwithhuz</strong></a> through the GoLang operator and we will expose it with a service. The application will be a simple Deployment yaml which will have a nginx image.</p>
<ol>
<li><p>Create a new project</p>
<p> Use the CLI to create a new techwithhuz-operator project</p>
</li>
</ol>
<pre><code class="lang-bash">$ mkdir techwithuz-operator
$ <span class="hljs-built_in">cd</span> techwithuz-operator/

$ operator-sdk init --domain techwithhuz.com --repo github.com/techwithhuz/techwithhuz-operator
Writing kustomize manifests <span class="hljs-keyword">for</span> you to edit...
Writing scaffold <span class="hljs-keyword">for</span> you to edit...
Get controller runtime:
$ go get sigs.k8s.io/controller-runtime@v0.14.1
go: downloading sigs.k8s.io/controller-runtime v0.14.1
go: downloading k8s.io/apimachinery v0.26.0
go: downloading github.com/gogo/protobuf v1.3.2
go: downloading github.com/google/gofuzz v1.1.0
go: downloading github.com/go-logr/logr v1.2.3
go: downloading k8s.io/client-go v0.26.0
go: downloading k8s.io/klog/v2 v2.80.1
go: downloading k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
go: downloading github.com/prometheus/client_golang v1.14.0
go: downloading gopkg.in/inf.v0 v0.9.1
go: downloading sigs.k8s.io/structured-merge-diff/v4 v4.2.3
go: downloading golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10
go: downloading github.com/evanphx/json-patch/v5 v5.6.0
go: downloading github.com/evanphx/json-patch v4.12.0+incompatible
go: downloading golang.org/x/time v0.3.0
go: downloading gomodules.xyz/jsonpatch/v2 v2.2.0
go: downloading k8s.io/api v0.26.0
go: downloading k8s.io/apiextensions-apiserver v0.26.0
go: downloading github.com/imdario/mergo v0.3.6
go: downloading github.com/spf13/pflag v1.0.5
go: downloading golang.org/x/term v0.3.0
go: downloading k8s.io/component-base v0.26.0
go: downloading github.com/prometheus/client_model v0.3.0
go: downloading github.com/prometheus/common v0.37.0
go: downloading github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
go: downloading sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2
go: downloading github.com/json-iterator/go v1.1.12
go: downloading gopkg.in/yaml.v2 v2.4.0
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
go: downloading github.com/pkg/errors v0.9.1
go: downloading k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280
go: downloading github.com/golang/protobuf v1.5.2
go: downloading github.com/google/gnostic v0.5.7-v3refs
go: downloading golang.org/x/sys v0.3.0
go: downloading sigs.k8s.io/yaml v1.3.0
go: downloading github.com/beorn7/perks v1.0.1
go: downloading github.com/cespare/xxhash/v2 v2.1.2
go: downloading github.com/prometheus/procfs v0.8.0
go: downloading google.golang.org/protobuf v1.28.1
go: downloading github.com/matttproud/golang_protobuf_extensions v1.0.2
go: downloading github.com/google/uuid v1.1.2
go: downloading github.com/fsnotify/fsnotify v1.6.0
go: downloading github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
go: downloading github.com/modern-go/reflect2 v1.0.2
go: downloading golang.org/x/text v0.5.0
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading github.com/google/go-cmp v0.5.9
go: downloading google.golang.org/appengine v1.6.7
go: downloading github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
go: downloading github.com/emicklei/go-restful/v3 v3.9.0
go: downloading github.com/go-openapi/swag v0.19.14
go: downloading github.com/go-openapi/jsonreference v0.20.0
go: downloading github.com/mailru/easyjson v0.7.6
go: downloading github.com/go-openapi/jsonpointer v0.19.5
go: downloading github.com/josharian/intern v1.0.0
Update dependencies:
$ go mod tidy
go: downloading github.com/stretchr/testify v1.8.0
go: downloading github.com/onsi/ginkgo/v2 v2.6.0
go: downloading github.com/onsi/gomega v1.24.1
go: downloading github.com/go-logr/zapr v1.2.3
go: downloading go.uber.org/zap v1.24.0
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading go.uber.org/goleak v1.2.0
go: downloading go.uber.org/atomic v1.7.0
go: downloading go.uber.org/multierr v1.6.0
go: downloading gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f
go: downloading github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e
go: downloading github.com/kr/text v0.2.0
go: downloading github.com/benbjohnson/clock v1.1.0
Next: define a resource with:
$ operator-sdk create api

$ <span class="hljs-built_in">pwd</span>
/root/techwithuz-operator
$ ls
Dockerfile  Makefile  PROJECT  README.md  config  go.mod  go.sum  hack  main.go
</code></pre>
<p>The above operator-sdk command creates all the necessary folder structures for us inside the techwithhuz-operator folder.</p>
<ol>
<li>Create the api for techwithhuz</li>
</ol>
<pre><code class="lang-bash">$ operator-sdk create api --group cache --version v1alpha1 --kind Techwithhuz --resource --controller
Writing kustomize manifests <span class="hljs-keyword">for</span> you to edit...
Writing scaffold <span class="hljs-keyword">for</span> you to edit...
api/v1alpha1/techwithhuz_types.go
controllers/techwithhuz_controller.go
Update dependencies:
$ go mod tidy
Running make:
$ make generate
<span class="hljs-built_in">test</span> -s /root/techwithuz-operator/bin/controller-gen &amp;&amp; /root/techwithuz-operator/bin/controller-gen --version | grep -q v0.11.1 || \
GOBIN=/root/techwithuz-operator/bin go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.11.1
go: downloading github.com/spf13/cobra v1.6.1
go: downloading github.com/gobuffalo/flect v0.3.0
go: downloading golang.org/x/tools v0.4.0
go: downloading github.com/fatih/color v1.13.0
go: downloading k8s.io/utils v0.0.0-20221107191617-1a15be271d1d
go: downloading github.com/mattn/go-colorable v0.1.9
go: downloading github.com/mattn/go-isatty v0.0.14
go: downloading golang.org/x/mod v0.7.0
go: downloading golang.org/x/net v0.4.0
/root/techwithuz-operator/bin/controller-gen object:headerFile=<span class="hljs-string">"hack/boilerplate.go.txt"</span> paths=<span class="hljs-string">"./..."</span>
Next: implement your new API and generate the manifests (e.g. CRDs,CRs) with:
$ make manifests
$ 
$ ls
Dockerfile  Makefile  PROJECT  README.md  api  bin  config  controllers  go.mod  go.sum  hack  main.go

$ ls api/v1alpha1/techwithhuz_types.go 
api/v1alpha1/techwithhuz_types.go
</code></pre>
<p>Now we have the API folder created with the above command. Also, we can see we have the types.go file available.</p>
<h3 id="heading-step-3-define-the-api"><strong>Step 3: Define the API</strong></h3>
<p>To begin, we will represent our API by defining the <code>Techwithhuz</code> type, which will have a <code>Techwithhuz.Size</code> and Techwithhuz.ContainerPort field to set the number of our instances (CRs) to be deployed and container port to be used, and a <code>TechwithhuzStatus.Conditions</code> field to store a CR’s <a target="_blank" href="https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties">Conditions</a>.</p>
<p>Define the API for the Techwithhuz Custom Resource(CR) by modifying the Go type definitions at <code>api/v1alpha1/techwithhuz_types.go</code> to have the following spec and status:</p>
<pre><code class="lang-go"><span class="hljs-comment">// TechwithhuzSpec defines the desired state of Techwithhuz</span>
<span class="hljs-keyword">type</span> TechwithhuzSpec <span class="hljs-keyword">struct</span> {
    <span class="hljs-comment">// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster</span>
    <span class="hljs-comment">// Important: Run "make" to regenerate code after modifying this file</span>

    <span class="hljs-comment">// Foo is an example field of Techwithhuz. Edit techwithhuz_types.go to remove/update</span>
    <span class="hljs-comment">//Foo string `json:"foo,omitempty"`</span>
    <span class="hljs-comment">//Add size and containerport property for which values can be passed through Custom Resource(CR) file.</span>
    Size          <span class="hljs-keyword">int32</span> <span class="hljs-string">`json:"size,omitempty"`</span>
    ContainerPort <span class="hljs-keyword">int32</span> <span class="hljs-string">`json:"containerPort,omitempty"`</span>
}

<span class="hljs-keyword">type</span> TechwithhuzStatus <span class="hljs-keyword">struct</span> {
    <span class="hljs-comment">// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster</span>
    <span class="hljs-comment">// Important: Run "make" to regenerate code after modifying this file</span>
    <span class="hljs-comment">// Conditions store the status conditions of the TechWithHuz instances</span>
    <span class="hljs-comment">// +operator-sdk:csv:customresourcedefinitions:type=status</span>
    Conditions []metav1.Condition <span class="hljs-string">`json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`</span>
}
</code></pre>
<p>Now add the <code>+kubebuilder:subresource:status</code> <a target="_blank" href="https://book.kubebuilder.io/reference/generating-crd.html#status">marker</a> to add a <a target="_blank" href="https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#status-subresource">status subresource</a> to the CRD manifest so that the controller can update the CR status without changing the rest of the CR object:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Techwithhuz is the Schema for the techwithhuzs API</span>
<span class="hljs-keyword">type</span> Techwithhuz <span class="hljs-keyword">struct</span> {
    metav1.TypeMeta   <span class="hljs-string">`json:",inline"`</span>
    metav1.ObjectMeta <span class="hljs-string">`json:"metadata,omitempty"`</span>

    Spec   TechwithhuzSpec   <span class="hljs-string">`json:"spec,omitempty"`</span>
    Status TechwithhuzStatus <span class="hljs-string">`json:"status,omitempty"`</span>
}
</code></pre>
<p>After modifying the <code>*_types.go</code> file always runs the following command to update the generated code for that resource type</p>
<pre><code class="lang-bash">$ make generate
<span class="hljs-built_in">test</span> -s /root/kubernetes-operator/techwithhuz-operator/bin/controller-gen &amp;&amp; /root/kubernetes-operator/techwithhuz-operator/bin/controller-gen --version | grep -q v0.11.1 || \
GOBIN=/root/kubernetes-operator/techwithhuz-operator/bin go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.11.1
/root/kubernetes-operator/techwithhuz-operator/bin/controller-gen object:headerFile=<span class="hljs-string">"hack/boilerplate.go.txt"</span> paths=<span class="hljs-string">"./..."</span>
</code></pre>
<p>The above makefile target will invoke the <a target="_blank" href="https://sigs.k8s.io/controller-tools">controller-gen</a> utility to update the <code>api/v1alpha1/zz_generated.deepcopy.go</code> file to ensure our API’s Go type definitions implement the <code>runtime.Object</code> interface that all Kind types must implement.</p>
<h3 id="heading-step-4-generating-crd-manifests"><strong>Step 4: Generating CRD manifests</strong></h3>
<p>Once the API is defined with spec/status fields and CRD validation markers, the CRD manifests can be generated and updated with the following command.</p>
<pre><code class="lang-bash">$ make manifests
<span class="hljs-built_in">test</span> -s /root/kubernetes-operator/techwithhuz-operator/bin/controller-gen &amp;&amp; /root/kubernetes-operator/techwithhuz-operator/bin/controller-gen --version | grep -q v0.11.1 || \
GOBIN=/root/kubernetes-operator/techwithhuz-operator/bin go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.11.1
/root/kubernetes-operator/techwithhuz-operator/bin/controller-gen rbac:roleName=manager-role crd webhook paths=<span class="hljs-string">"./..."</span> output:crd:artifacts:config=config/crd/bases
</code></pre>
<p>This makefile target will invoke <a target="_blank" href="https://sigs.k8s.io/controller-tools">controller-gen</a> to generate the CRD manifests at <code>config/crd/bases/cache.example.com_memcacheds.yaml</code>.</p>
<h3 id="heading-step-5-implement-the-controller"><strong>Step 5: Implement the Controller</strong></h3>
<p>The controller file will contain the reconciliation logic. We have the controller file already generated controller/techwithuz_controller.go. Now we need to write the logic like in our case we want to create a kind Deployment for our application Techwithhuz.</p>
<p>So we need to write the yaml file of kind deployment but in Go lang format.</p>
<p>We will see now how to do that.</p>
<pre><code class="lang-go"><span class="hljs-comment">// deploymentForTechwithhuz returns a Techwithhuz Deployment object</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *TechwithhuzReconciler)</span> <span class="hljs-title">deploymentForTechwithhuz</span><span class="hljs-params">(techwithhuz *cachev1alpha1.Techwithhuz)</span> <span class="hljs-params">(*appsv1.Deployment, error)</span></span> {
    replicas := techwithhuz.Spec.Size
    ls := labelsForTechwithhuz(techwithhuz.Name)

    <span class="hljs-comment">// Get the Operand image</span>
    image := <span class="hljs-string">"nginx:latest"</span>
    dep := &amp;appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name:      techwithhuz.Name,
            Namespace: techwithhuz.Namespace,
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: &amp;replicas,
            Selector: &amp;metav1.LabelSelector{
                MatchLabels: ls,
            },
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{
                    Labels: ls,
                },
                Spec: corev1.PodSpec{
                    SecurityContext: &amp;corev1.PodSecurityContext{
                        RunAsNonRoot: &amp;[]<span class="hljs-keyword">bool</span>{<span class="hljs-literal">true</span>}[<span class="hljs-number">0</span>],
                        SeccompProfile: &amp;corev1.SeccompProfile{
                            Type: corev1.SeccompProfileTypeRuntimeDefault,
                        },
                    },
                    Containers: []corev1.Container{{
                        Image:           image,
                        Name:            <span class="hljs-string">"techwithhuz"</span>,
                        ImagePullPolicy: corev1.PullIfNotPresent,
                        SecurityContext: &amp;corev1.SecurityContext{
                            RunAsNonRoot:             &amp;[]<span class="hljs-keyword">bool</span>{<span class="hljs-literal">true</span>}[<span class="hljs-number">0</span>],
                            RunAsUser:                &amp;[]<span class="hljs-keyword">int64</span>{<span class="hljs-number">1001</span>}[<span class="hljs-number">0</span>],
                            AllowPrivilegeEscalation: &amp;[]<span class="hljs-keyword">bool</span>{<span class="hljs-literal">false</span>}[<span class="hljs-number">0</span>],
                            Capabilities: &amp;corev1.Capabilities{
                                Drop: []corev1.Capability{
                                    <span class="hljs-string">"ALL"</span>,
                                },
                            },
                        },
                        Ports: []corev1.ContainerPort{{
                            ContainerPort: techwithhuz.Spec.ContainerPort,
                            Name:          <span class="hljs-string">"techwithhuz"</span>,
                        }},
                        Command: []<span class="hljs-keyword">string</span>{<span class="hljs-string">"sleep"</span>, <span class="hljs-string">"1000s"</span>},
                    }},
                },
            },
        },
    }
    <span class="hljs-keyword">if</span> err := ctrl.SetControllerReference(techwithhuz, dep, r.Scheme); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }
    <span class="hljs-keyword">return</span> dep, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>As we can see in the above code we have written the logic for Kind Deployment in the Go language.</p>
<p>To know more about how to write the above code and what all functions are available refer to this link - <a target="_blank" href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#deploymentspec-v1-apps">https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#deploymentspec-v1-apps</a> also you can refer to the API document link from GO - <a target="_blank" href="https://pkg.go.dev/k8s.io/api/core/v1">https://pkg.go.dev/k8s.io/api/core/v1</a></p>
<p>Now we have to write the logic for reconciliation like when the user set Size as 3 in the CR file, then our reconcile logic should increase the pod to 3.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *TechwithhuzReconciler)</span> <span class="hljs-title">Reconcile</span><span class="hljs-params">(ctx context.Context, req ctrl.Request)</span> <span class="hljs-params">(ctrl.Result, error)</span></span> {
    <span class="hljs-comment">//_ = log.FromContext(ctx)</span>
    log := log.FromContext(ctx)

    <span class="hljs-comment">// TODO(user): your logic here</span>
    <span class="hljs-comment">// Fetch the Techwithhuz instance</span>
    <span class="hljs-comment">// The purpose is check if the Custom Resource for the Kind Techwithhuz</span>
    <span class="hljs-comment">// is applied on the cluster if not we return nil to stop the reconciliation</span>
    techwithhuz := &amp;cachev1alpha1.Techwithhuz{}
    err := r.Get(ctx, req.NamespacedName, techwithhuz)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">if</span> apierrors.IsNotFound(err) {
            <span class="hljs-comment">// If the custom resource is not found then, it usually means that it was deleted or not created</span>
            <span class="hljs-comment">// In this way, we will stop the reconciliation</span>
            log.Info(<span class="hljs-string">"techwithhuz resource not found. Ignoring since object must be deleted"</span>)
            <span class="hljs-keyword">return</span> ctrl.Result{}, <span class="hljs-literal">nil</span>
        }
        <span class="hljs-comment">// Error reading the object - requeue the request.</span>
        log.Error(err, <span class="hljs-string">"Failed to get techwithhuz"</span>)
        <span class="hljs-keyword">return</span> ctrl.Result{}, err
    }
    <span class="hljs-comment">// Let's just set the status as Unknown when no status are available</span>
    <span class="hljs-keyword">if</span> techwithhuz.Status.Conditions == <span class="hljs-literal">nil</span> || <span class="hljs-built_in">len</span>(techwithhuz.Status.Conditions) == <span class="hljs-number">0</span> {
        meta.SetStatusCondition(&amp;techwithhuz.Status.Conditions, metav1.Condition{Type: typeAvailableTechwithhuz, Status: metav1.ConditionUnknown, Reason: <span class="hljs-string">"Reconciling"</span>, Message: <span class="hljs-string">"Starting reconciliation"</span>})
        <span class="hljs-keyword">if</span> err = r.Status().Update(ctx, techwithhuz); err != <span class="hljs-literal">nil</span> {
            log.Error(err, <span class="hljs-string">"Failed to update techwithhuz status"</span>)
            <span class="hljs-keyword">return</span> ctrl.Result{}, err
        }

        <span class="hljs-comment">// Let's re-fetch the techwithhuz Custom Resource after update the status</span>
        <span class="hljs-comment">// so that we have the latest state of the resource on the cluster and we will avoid</span>
        <span class="hljs-comment">// raise the issue "the object has been modified, please apply</span>
        <span class="hljs-comment">// your changes to the latest version and try again" which would re-trigger the reconciliation</span>
        <span class="hljs-comment">// if we try to update it again in the following operations</span>
        <span class="hljs-keyword">if</span> err := r.Get(ctx, req.NamespacedName, techwithhuz); err != <span class="hljs-literal">nil</span> {
            log.Error(err, <span class="hljs-string">"Failed to re-fetch techwithhuz"</span>)
            <span class="hljs-keyword">return</span> ctrl.Result{}, err
        }
    }
    <span class="hljs-comment">// Let's add a finalizer. Then, we can define some operations which should</span>
    <span class="hljs-comment">// occurs before the custom resource to be deleted.</span>
    <span class="hljs-comment">// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers</span>
    <span class="hljs-keyword">if</span> !controllerutil.ContainsFinalizer(techwithhuz, techwithhuzFinalizer) {
        log.Info(<span class="hljs-string">"Adding Finalizer for techwithhuz"</span>)
        <span class="hljs-keyword">if</span> ok := controllerutil.AddFinalizer(techwithhuz, techwithhuzFinalizer); !ok {
            log.Error(err, <span class="hljs-string">"Failed to add finalizer into the custom resource"</span>)
            <span class="hljs-keyword">return</span> ctrl.Result{Requeue: <span class="hljs-literal">true</span>}, <span class="hljs-literal">nil</span>
        }

        <span class="hljs-keyword">if</span> err = r.Update(ctx, techwithhuz); err != <span class="hljs-literal">nil</span> {
            log.Error(err, <span class="hljs-string">"Failed to update custom resource to add finalizer"</span>)
            <span class="hljs-keyword">return</span> ctrl.Result{}, err
        }
    }
    <span class="hljs-comment">// Check if the Techwithhuz instance is marked to be deleted, which is</span>
    <span class="hljs-comment">// indicated by the deletion timestamp being set.</span>
    isTechwithhuzMarkedToBeDeleted := techwithhuz.GetDeletionTimestamp() != <span class="hljs-literal">nil</span>
    <span class="hljs-keyword">if</span> isTechwithhuzMarkedToBeDeleted {
        <span class="hljs-keyword">if</span> controllerutil.ContainsFinalizer(techwithhuz, techwithhuzFinalizer) {
            log.Info(<span class="hljs-string">"Performing Finalizer Operations for techwithhuz before delete CR"</span>)

            <span class="hljs-comment">// Let's add here an status "Downgrade" to define that this resource begin its process to be terminated.</span>
            meta.SetStatusCondition(&amp;techwithhuz.Status.Conditions, metav1.Condition{Type: typeDegradedTechwithhuz,
                Status: metav1.ConditionUnknown, Reason: <span class="hljs-string">"Finalizing"</span>,
                Message: fmt.Sprintf(<span class="hljs-string">"Performing finalizer operations for the custom resource: %s "</span>, techwithhuz.Name)})

            <span class="hljs-keyword">if</span> err := r.Status().Update(ctx, techwithhuz); err != <span class="hljs-literal">nil</span> {
                log.Error(err, <span class="hljs-string">"Failed to update techwithhuz status"</span>)
                <span class="hljs-keyword">return</span> ctrl.Result{}, err
            }

            <span class="hljs-comment">// Perform all operations required before remove the finalizer and allow</span>
            <span class="hljs-comment">// the Kubernetes API to remove the custom resource.</span>
            r.doFinalizerOperationsForTechwithhuz(techwithhuz)

            <span class="hljs-comment">// Re-fetch the techwithhuz Custom Resource before update the status</span>
            <span class="hljs-comment">// so that we have the latest state of the resource on the cluster and we will avoid</span>
            <span class="hljs-comment">// raise the issue "the object has been modified, please apply</span>
            <span class="hljs-comment">// your changes to the latest version and try again" which would re-trigger the reconciliation</span>
            <span class="hljs-keyword">if</span> err := r.Get(ctx, req.NamespacedName, techwithhuz); err != <span class="hljs-literal">nil</span> {
                log.Error(err, <span class="hljs-string">"Failed to re-fetch techwithhuz"</span>)
                <span class="hljs-keyword">return</span> ctrl.Result{}, err
            }

            meta.SetStatusCondition(&amp;techwithhuz.Status.Conditions, metav1.Condition{Type: typeDegradedTechwithhuz,
                Status: metav1.ConditionTrue, Reason: <span class="hljs-string">"Finalizing"</span>,
                Message: fmt.Sprintf(<span class="hljs-string">"Finalizer operations for custom resource %s name were successfully accomplished"</span>, techwithhuz.Name)})

            <span class="hljs-keyword">if</span> err := r.Status().Update(ctx, techwithhuz); err != <span class="hljs-literal">nil</span> {
                log.Error(err, <span class="hljs-string">"Failed to update Techwithhuz status"</span>)
                <span class="hljs-keyword">return</span> ctrl.Result{}, err
            }

            log.Info(<span class="hljs-string">"Removing Finalizer for Techwithhuz after successfully perform the operations"</span>)
            <span class="hljs-keyword">if</span> ok := controllerutil.RemoveFinalizer(techwithhuz, techwithhuzFinalizer); !ok {
                log.Error(err, <span class="hljs-string">"Failed to remove finalizer for techwithhuz"</span>)
                <span class="hljs-keyword">return</span> ctrl.Result{Requeue: <span class="hljs-literal">true</span>}, <span class="hljs-literal">nil</span>
            }

            <span class="hljs-keyword">if</span> err := r.Update(ctx, techwithhuz); err != <span class="hljs-literal">nil</span> {
                log.Error(err, <span class="hljs-string">"Failed to remove finalizer for techwithhuz"</span>)
                <span class="hljs-keyword">return</span> ctrl.Result{}, err
            }
        }
        <span class="hljs-keyword">return</span> ctrl.Result{}, <span class="hljs-literal">nil</span>
    }
    <span class="hljs-comment">// Check if the deployment already exists, if not create a new one</span>
    found := &amp;appsv1.Deployment{}
    err = r.Get(ctx, types.NamespacedName{Name: techwithhuz.Name, Namespace: techwithhuz.Namespace}, found)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> &amp;&amp; apierrors.IsNotFound(err) {
        <span class="hljs-comment">// Define a new deployment</span>
        dep, err := r.deploymentForTechwithhuz(techwithhuz)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            log.Error(err, <span class="hljs-string">"Failed to define new Deployment resource for techwithhuz"</span>)

            <span class="hljs-comment">// The following implementation will update the status</span>
            meta.SetStatusCondition(&amp;techwithhuz.Status.Conditions, metav1.Condition{Type: typeAvailableTechwithhuz,
                Status: metav1.ConditionFalse, Reason: <span class="hljs-string">"Reconciling"</span>,
                Message: fmt.Sprintf(<span class="hljs-string">"Failed to create Deployment for the custom resource (%s): (%s)"</span>, techwithhuz.Name, err)})

            <span class="hljs-keyword">if</span> err := r.Status().Update(ctx, techwithhuz); err != <span class="hljs-literal">nil</span> {
                log.Error(err, <span class="hljs-string">"Failed to update techwithhuz status"</span>)
                <span class="hljs-keyword">return</span> ctrl.Result{}, err
            }

            <span class="hljs-keyword">return</span> ctrl.Result{}, err
        }

        log.Info(<span class="hljs-string">"Creating a new Deployment"</span>,
            <span class="hljs-string">"Deployment.Namespace"</span>, dep.Namespace, <span class="hljs-string">"Deployment.Name"</span>, dep.Name)
        <span class="hljs-keyword">if</span> err = r.Create(ctx, dep); err != <span class="hljs-literal">nil</span> {
            log.Error(err, <span class="hljs-string">"Failed to create new Deployment"</span>,
                <span class="hljs-string">"Deployment.Namespace"</span>, dep.Namespace, <span class="hljs-string">"Deployment.Name"</span>, dep.Name)
            <span class="hljs-keyword">return</span> ctrl.Result{}, err
        }

        <span class="hljs-comment">// Deployment created successfully</span>
        <span class="hljs-comment">// We will requeue the reconciliation so that we can ensure the state</span>
        <span class="hljs-comment">// and move forward for the next operations</span>
        <span class="hljs-keyword">return</span> ctrl.Result{RequeueAfter: time.Minute}, <span class="hljs-literal">nil</span>
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Error(err, <span class="hljs-string">"Failed to get Deployment"</span>)
        <span class="hljs-comment">// Let's return the error for the reconciliation be re-trigged again</span>
        <span class="hljs-keyword">return</span> ctrl.Result{}, err
    }

    <span class="hljs-comment">// The CRD API is defining that the techwithhuz type, have a TechwithhuzSpec.Size field</span>
    <span class="hljs-comment">// to set the quantity of Deployment instances is the desired state on the cluster.</span>
    <span class="hljs-comment">// Therefore, the following code will ensure the Deployment size is the same as defined</span>
    <span class="hljs-comment">// via the Size spec of the Custom Resource which we are reconciling.</span>
    size := techwithhuz.Spec.Size
    <span class="hljs-keyword">if</span> *found.Spec.Replicas != size {
        found.Spec.Replicas = &amp;size
        <span class="hljs-keyword">if</span> err = r.Update(ctx, found); err != <span class="hljs-literal">nil</span> {
            log.Error(err, <span class="hljs-string">"Failed to update Deployment"</span>,
                <span class="hljs-string">"Deployment.Namespace"</span>, found.Namespace, <span class="hljs-string">"Deployment.Name"</span>, found.Name)

            <span class="hljs-comment">// Re-fetch the techwithhuz Custom Resource before update the status</span>
            <span class="hljs-comment">// so that we have the latest state of the resource on the cluster and we will avoid</span>
            <span class="hljs-comment">// raise the issue "the object has been modified, please apply</span>
            <span class="hljs-comment">// your changes to the latest version and try again" which would re-trigger the reconciliation</span>
            <span class="hljs-keyword">if</span> err := r.Get(ctx, req.NamespacedName, techwithhuz); err != <span class="hljs-literal">nil</span> {
                log.Error(err, <span class="hljs-string">"Failed to re-fetch techwithhuz"</span>)
                <span class="hljs-keyword">return</span> ctrl.Result{}, err
            }

            <span class="hljs-comment">// The following implementation will update the status</span>
            meta.SetStatusCondition(&amp;techwithhuz.Status.Conditions, metav1.Condition{Type: typeAvailableTechwithhuz,
                Status: metav1.ConditionFalse, Reason: <span class="hljs-string">"Resizing"</span>,
                Message: fmt.Sprintf(<span class="hljs-string">"Failed to update the size for the custom resource (%s): (%s)"</span>, techwithhuz.Name, err)})

            <span class="hljs-keyword">if</span> err := r.Status().Update(ctx, techwithhuz); err != <span class="hljs-literal">nil</span> {
                log.Error(err, <span class="hljs-string">"Failed to update techwithhuz status"</span>)
                <span class="hljs-keyword">return</span> ctrl.Result{}, err
            }

            <span class="hljs-keyword">return</span> ctrl.Result{}, err
        }

        <span class="hljs-comment">// Now, that we update the size we want to requeue the reconciliation</span>
        <span class="hljs-comment">// so that we can ensure that we have the latest state of the resource before</span>
        <span class="hljs-comment">// update. Also, it will help ensure the desired state on the cluster</span>
        <span class="hljs-keyword">return</span> ctrl.Result{Requeue: <span class="hljs-literal">true</span>}, <span class="hljs-literal">nil</span>
    }

    <span class="hljs-comment">// The following implementation will update the status</span>
    meta.SetStatusCondition(&amp;techwithhuz.Status.Conditions, metav1.Condition{Type: typeAvailableTechwithhuz,
        Status: metav1.ConditionTrue, Reason: <span class="hljs-string">"Reconciling"</span>,
        Message: fmt.Sprintf(<span class="hljs-string">"Deployment for custom resource (%s) with %d replicas created successfully"</span>, techwithhuz.Name, size)})

    <span class="hljs-keyword">if</span> err := r.Status().Update(ctx, techwithhuz); err != <span class="hljs-literal">nil</span> {
        log.Error(err, <span class="hljs-string">"Failed to update techwithhuz status"</span>)
        <span class="hljs-keyword">return</span> ctrl.Result{}, err
    }
    <span class="hljs-keyword">return</span> ctrl.Result{}, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>Lastly, we have to update the SetupWithManager function and tell him to monitor our kind deployment.</p>
<pre><code class="lang-go"><span class="hljs-comment">// SetupWithManager sets up the controller with the Manager.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *TechwithhuzReconciler)</span> <span class="hljs-title">SetupWithManager</span><span class="hljs-params">(mgr ctrl.Manager)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">return</span> ctrl.NewControllerManagedBy(mgr).
        For(&amp;cachev1alpha1.Techwithhuz{}).
        Owns(&amp;appsv1.Deployment{}).
        Complete(r)
}
</code></pre>
<p>Above <a target="_blank" href="https://github.com/techwithhuz/kubernetes-operator/blob/feature/techwithhuz-operator/techwithhuz-operator/controllers/techwithhuz_controller.go">controller/techwithhuz_controller.go</a> file can be referred from here - <a target="_blank" href="https://github.com/techwithhuz/kubernetes-operator/blob/feature/techwithhuz-operator/techwithhuz-operator/controllers/techwithhuz_controller.go">https://github.com/techwithhuz/kubernetes-operator/blob/feature/techwithhuz-operator/techwithhuz-operator/controllers/techwithhuz_controller.go</a></p>
<h3 id="heading-step-6-to-run-the-operator"><strong>Step 6: To run the operator</strong></h3>
<p>To run and test the operator code we need to first test it locally before creating the Docker image and deploying the operator in an environment.</p>
<p>To test the operator locally run this command</p>
<pre><code class="lang-go">$ <span class="hljs-built_in">make</span> install run
test -s /root/kubernetes-operator/techwithhuz-operator/bin/controller-gen &amp;&amp; /root/kubernetes-operator/techwithhuz-operator/bin/controller-gen --version | grep -q v0<span class="hljs-number">.11</span><span class="hljs-number">.1</span> || \
GOBIN=/root/kubernetes-operator/techwithhuz-operator/bin <span class="hljs-keyword">go</span> install sigs.k8s.io/controller-tools/cmd/controller-gen@v0<span class="hljs-number">.11</span><span class="hljs-number">.1</span>
/root/kubernetes-operator/techwithhuz-operator/bin/controller-gen rbac:roleName=manager-role crd webhook paths=<span class="hljs-string">"./..."</span> output:crd:artifacts:config=config/crd/bases
/root/kubernetes-operator/techwithhuz-operator/bin/kustomize build config/crd | kubectl apply -f -
customresourcedefinition.apiextensions.k8s.io/techwithhuzs.cache.techwithhuz.com created
/root/kubernetes-operator/techwithhuz-operator/bin/controller-gen object:headerFile=<span class="hljs-string">"hack/boilerplate.go.txt"</span> paths=<span class="hljs-string">"./..."</span>
<span class="hljs-keyword">go</span> fmt ./...
<span class="hljs-keyword">go</span> vet ./...
<span class="hljs-keyword">go</span>: downloading github.com/onsi/ginkgo/v2 v2<span class="hljs-number">.6</span><span class="hljs-number">.0</span>
<span class="hljs-keyword">go</span>: downloading github.com/onsi/gomega v1<span class="hljs-number">.24</span><span class="hljs-number">.1</span>

<span class="hljs-keyword">go</span> run ./main.<span class="hljs-keyword">go</span>
<span class="hljs-number">2023</span><span class="hljs-number">-08</span><span class="hljs-number">-16</span>T12:<span class="hljs-number">41</span>:<span class="hljs-number">36</span>Z    INFO    controller-runtime.metrics      Metrics server is starting to listen    {<span class="hljs-string">"addr"</span>: <span class="hljs-string">":8080"</span>}
<span class="hljs-number">2023</span><span class="hljs-number">-08</span><span class="hljs-number">-16</span>T12:<span class="hljs-number">41</span>:<span class="hljs-number">36</span>Z    INFO    setup   starting manager
<span class="hljs-number">2023</span><span class="hljs-number">-08</span><span class="hljs-number">-16</span>T12:<span class="hljs-number">41</span>:<span class="hljs-number">36</span>Z    INFO    Starting server {<span class="hljs-string">"path"</span>: <span class="hljs-string">"/metrics"</span>, <span class="hljs-string">"kind"</span>: <span class="hljs-string">"metrics"</span>, <span class="hljs-string">"addr"</span>: <span class="hljs-string">"[::]:8080"</span>}
<span class="hljs-number">2023</span><span class="hljs-number">-08</span><span class="hljs-number">-16</span>T12:<span class="hljs-number">41</span>:<span class="hljs-number">36</span>Z    INFO    Starting server {<span class="hljs-string">"kind"</span>: <span class="hljs-string">"health probe"</span>, <span class="hljs-string">"addr"</span>: <span class="hljs-string">"[::]:8081"</span>}
<span class="hljs-number">2023</span><span class="hljs-number">-08</span><span class="hljs-number">-16</span>T12:<span class="hljs-number">41</span>:<span class="hljs-number">36</span>Z    INFO    Starting EventSource    {<span class="hljs-string">"controller"</span>: <span class="hljs-string">"techwithhuz"</span>, <span class="hljs-string">"controllerGroup"</span>: <span class="hljs-string">"cache.techwithhuz.com"</span>, <span class="hljs-string">"controllerKind"</span>: <span class="hljs-string">"Techwithhuz"</span>, <span class="hljs-string">"source"</span>: <span class="hljs-string">"kind source: *v1alpha1.Techwithhuz"</span>}
<span class="hljs-number">2023</span><span class="hljs-number">-08</span><span class="hljs-number">-16</span>T12:<span class="hljs-number">41</span>:<span class="hljs-number">36</span>Z    INFO    Starting EventSource    {<span class="hljs-string">"controller"</span>: <span class="hljs-string">"techwithhuz"</span>, <span class="hljs-string">"controllerGroup"</span>: <span class="hljs-string">"cache.techwithhuz.com"</span>, <span class="hljs-string">"controllerKind"</span>: <span class="hljs-string">"Techwithhuz"</span>, <span class="hljs-string">"source"</span>: <span class="hljs-string">"kind source: *v1.Deployment"</span>}
<span class="hljs-number">2023</span><span class="hljs-number">-08</span><span class="hljs-number">-16</span>T12:<span class="hljs-number">41</span>:<span class="hljs-number">36</span>Z    INFO    Starting Controller     {<span class="hljs-string">"controller"</span>: <span class="hljs-string">"techwithhuz"</span>, <span class="hljs-string">"controllerGroup"</span>: <span class="hljs-string">"cache.techwithhuz.com"</span>, <span class="hljs-string">"controllerKind"</span>: <span class="hljs-string">"Techwithhuz"</span>}
<span class="hljs-number">2023</span><span class="hljs-number">-08</span><span class="hljs-number">-16</span>T12:<span class="hljs-number">41</span>:<span class="hljs-number">36</span>Z    INFO    Starting workers        {<span class="hljs-string">"controller"</span>: <span class="hljs-string">"techwithhuz"</span>, <span class="hljs-string">"controllerGroup"</span>: <span class="hljs-string">"cache.techwithhuz.com"</span>, <span class="hljs-string">"controllerKind"</span>: <span class="hljs-string">"Techwithhuz"</span>, <span class="hljs-string">"worker count"</span>: <span class="hljs-number">1</span>}
</code></pre>
<p>If it throws any error related to variables or functions then first fix those errors.</p>
<p>Successful logs will look like the above.</p>
<h3 id="heading-step-7-deploy-the-custom-resource-definitioncrd-file"><strong>Step 7: Deploy the Custom Resource Definition(CRD) file</strong></h3>
<p>Now open another tab or take another ssh of your server where our operator is running and deploy the Custom Resource Definition file.</p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">pwd</span>   
/root/kubernetes-operator/techwithhuz-operator/config/crd/bases

$ kubectl apply -f cache.techwithhuz.com_techwithhuzs.yaml 
customresourcedefinition.apiextensions.k8s.io/techwithhuzs.cache.techwithhuz.com configured

$ kubectl get crds techwithhuzs.cache.techwithhuz.com 
NAME                                 CREATED AT
techwithhuzs.cache.techwithhuz.com   2023-08-16T12:39:16Z
</code></pre>
<p>Step 8: Deploy the Custom Resource (CR) file</p>
<p>Now the last step is to deploy the CR file of Kind Techwithhuz and see the magic.</p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">pwd</span>
/root/kubernetes-operator/techwithhuz-operator/config/samples

$ cat techwithhuz-cr.yaml 
apiVersion: cache.techwithhuz.com/v1alpha1 
kind: Techwithhuz
metadata:
  name: techwithhuz-sample
spec:
  size: 1
  containerPort: 8080

$ kubectl get techwithhuz
NAME                 AGE
techwithhuz-sample   3m22s

$ kubectl get pods
No resources found <span class="hljs-keyword">in</span> default namespace.

$ kubectl apply -f techwithhuz-cr.yaml 
techwithhuz.cache.techwithhuz.com/techwithhuz-sample created
</code></pre>
<p>Go to the previous console where our operator code is already running</p>
<pre><code class="lang-bash">2023-08-16T12:47:39Z    INFO    Adding Finalizer <span class="hljs-keyword">for</span> techwithhuz        {<span class="hljs-string">"controller"</span>: <span class="hljs-string">"techwithhuz"</span>, <span class="hljs-string">"controllerGroup"</span>: <span class="hljs-string">"cache.techwithhuz.com"</span>, <span class="hljs-string">"controllerKind"</span>: <span class="hljs-string">"Techwithhuz"</span>, <span class="hljs-string">"Techwithhuz"</span>: {<span class="hljs-string">"name"</span>:<span class="hljs-string">"techwithhuz-sample"</span>,<span class="hljs-string">"namespace"</span>:<span class="hljs-string">"default"</span>}, <span class="hljs-string">"namespace"</span>: <span class="hljs-string">"default"</span>, <span class="hljs-string">"name"</span>: <span class="hljs-string">"techwithhuz-sample"</span>, <span class="hljs-string">"reconcileID"</span>: <span class="hljs-string">"674549cf-7a73-437e-a6cd-fd4805db6651"</span>}
2023-08-16T12:47:40Z    INFO    Creating a new Deployment       {<span class="hljs-string">"controller"</span>: <span class="hljs-string">"techwithhuz"</span>, <span class="hljs-string">"controllerGroup"</span>: <span class="hljs-string">"cache.techwithhuz.com"</span>, <span class="hljs-string">"controllerKind"</span>: <span class="hljs-string">"Techwithhuz"</span>, <span class="hljs-string">"Techwithhuz"</span>: {<span class="hljs-string">"name"</span>:<span class="hljs-string">"techwithhuz-sample"</span>,<span class="hljs-string">"namespace"</span>:<span class="hljs-string">"default"</span>}, <span class="hljs-string">"namespace"</span>: <span class="hljs-string">"default"</span>, <span class="hljs-string">"name"</span>: <span class="hljs-string">"techwithhuz-sample"</span>, <span class="hljs-string">"reconcileID"</span>: <span class="hljs-string">"674549cf-7a73-437e-a6cd-fd4805db6651"</span>, <span class="hljs-string">"Deployment.Namespace"</span>: <span class="hljs-string">"default"</span>, <span class="hljs-string">"Deployment.Name"</span>: <span class="hljs-string">"techwithhuz-sample"</span>}
</code></pre>
<p>We can see in the logs that the operator has detected that a CR is applied with kind Techwithhuz with spec.Size as 1 pod and spec.ContainerPort as 8080. It will then run the reconcile loop and deploy our Deployment Go code and create a new deployment.</p>
<pre><code class="lang-bash">$ kubectl get deployments
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
techwithhuz-sample   0/1     1            0           70s

$ kubectl get pods
NAME                                  READY   STATUS              RESTARTS   AGE
techwithhuz-sample-6f6b66478b-jnbqg   0/1     ContainerCreating   0          85s
</code></pre>
<p>So now we have the deployment created and our <strong>application - techwithhuz is managed by our Go Lang operator.</strong></p>
<p>Similar to how deployment kind is created, other Kubernetes objects like services, secrets, routes, configmaps, pvc etc. can also be created following the same logic.</p>
<p>This approach of running the operator is local but for production, a Docker Image of the Operator needs to be created. Dockerfile is already present in the repository which get generated by default when we run the operator sdk command.</p>
<p><strong>That's it for this blog post.</strong> We can make the code more modular and generic so that we can deploy and manage multiple applications and make our work and life easier.</p>
<p><strong>Lastly, I would like to say building the Golang operator is a little challenging and complicated but once you have it ready then it is very very easy to manage and also it is very stable for use in a production environment.</strong></p>
<p><strong>Feel free to put in the comment box if any queries.</strong></p>
]]></content:encoded></item></channel></rss>