<?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>Forem: Steven Murawski</title>
    <description>The latest articles on Forem by Steven Murawski (@smurawski).</description>
    <link>https://forem.com/smurawski</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%2F554254%2F099635c7-ea4e-40e2-8bc6-10e0e1798e9c.jpeg</url>
      <title>Forem: Steven Murawski</title>
      <link>https://forem.com/smurawski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/smurawski"/>
    <language>en</language>
    <item>
      <title>ClickOps to GitOps (an Azure Marketplace story)</title>
      <dc:creator>Steven Murawski</dc:creator>
      <pubDate>Mon, 25 Sep 2023 16:32:35 +0000</pubDate>
      <link>https://forem.com/azure/clickops-to-gitops-an-azure-marketplace-story-1353</link>
      <guid>https://forem.com/azure/clickops-to-gitops-an-azure-marketplace-story-1353</guid>
      <description>&lt;h2&gt;
  
  
  Our GitOps Journey So Far
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Has Come Before
&lt;/h3&gt;

&lt;p&gt;Our team has dug into some of the basics of GitOps over the past week. So far, we've talked about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/JustEnoughGit" rel="noopener noreferrer"&gt;Just Enough Git for GitOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/WhatReallyIsGitOps" rel="noopener noreferrer"&gt;What Really Is GitOps?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/GitGoingWithGitOps" rel="noopener noreferrer"&gt;Git going with GitOps on AKS: A Step-by-Step Guide using FluxCD AKS Extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/joshduffney/status/1704922340751032647" rel="noopener noreferrer"&gt;What is GitOps: A Twitter Space&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/ImageAutomationWithFluxCD" rel="noopener noreferrer"&gt;Automating Image Updates with FluxCD on AKS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Quick Recap
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Principles of GitOps
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;1) Declarative&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A system managed by GitOps must have it's state expressed declaratively.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2) Versioned and Immutable&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Desired state is stored in a way that enforces immutability, versioning and retains a complete version history.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3) Pulled Automatically&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Software agents automatically pull the desired state declarations from the source.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;4) Continuously Reconciled&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Software agents continuously observe actual system state and attempt to apply the desired state.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Application of the Principles
&lt;/h4&gt;

&lt;p&gt;GitOps tools boil down to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A Git repository&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An artifact repository (by default, a branch in the Git repo)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An agent that is responsible for ensuring that the state of the cluster matches the configuration defined in the artifact repository (over time)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Applying GitOps
&lt;/h3&gt;

&lt;p&gt;Applying these principles to the use of the tools identified, we can effectively manage our Kubernetes clusters and workloads therein with auditable, automated changes and maintain a consistent configuration despite unintentional configuration drift.&lt;/p&gt;
&lt;h2&gt;
  
  
  Our Next GitOps Tool Exploration - ArgoCD
&lt;/h2&gt;

&lt;p&gt;Now, it's time to dig in to the next GitOps tool in the GitOps ecosystem - &lt;a href="https://argoproj.github.io/cd/" rel="noopener noreferrer"&gt;Argo CD&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Argo CD is part of the Argo ecosystem of tools. Argo CD is a GitOps engine and continuous delivery tool. Argo CD is a Kubernetes controller that monitors the applications running inside your cluster and ensures that it matches the desired state (as represented by manifests or Helm charts in your target repository).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fargo-cd.readthedocs.io%2Fen%2Fstable%2Fassets%2Fargocd_architecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fargo-cd.readthedocs.io%2Fen%2Fstable%2Fassets%2Fargocd_architecture.png" alt="Argo CD architectural overview"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Reducing Time to Impact with the Azure Marketplace
&lt;/h2&gt;

&lt;p&gt;The Azure Marketplace is integrated into our Azure Kubernetes Service (AKS) clusters and provides a way for open source and commercial offerings that are container-based to be easily deployed into AKS clusters.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing ArgoCD from the Azure Marketplace
&lt;/h2&gt;

&lt;p&gt;Argo CD is offered through the Azure Marketplace. Marketplace offers are installed as extensions into AKS.&lt;/p&gt;

&lt;p&gt;We'll start in the Azure Portal on the AKS Cluster view.&lt;/p&gt;
&lt;h3&gt;
  
  
  Extensions + Applications
&lt;/h3&gt;

&lt;p&gt;We'll navigate down the left-hand side of the page to the &lt;em&gt;Extensions + applications&lt;/em&gt; item under &lt;em&gt;Settings&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_overview.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_overview.png" alt="Azure portal, AKS view, selecting Extensions + applications"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Add an Application
&lt;/h3&gt;

&lt;p&gt;Next, we'll select Add.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_extensions.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_extensions.png" alt="AKS Extensions + applications screen with Add highlighted"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll put &lt;code&gt;argocd&lt;/code&gt; in the search box and select the &lt;em&gt;Argo CD packaged by Bitnami&lt;/em&gt; offering. &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_install.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_install.png" alt="Install a Kubernetes Application with ArgoCD in the search box"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The search will return two options, one a container and one a Kubernetes App. We'll select the Kubernetes App and click &lt;em&gt;Create&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_install_select.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_install_select.png" alt="Selecting the Kubernetes App for Argo CD"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will open up the &lt;em&gt;Create Argo CD packaged by Bitnami&lt;/em&gt; page. We specify a subscription and resource group, as well as whether or not we need a new AKS cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_create_argo_basics.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_create_argo_basics.png" alt="Create Argo CD packaged by Bitnami - basics tab with subscription, resource group, and whether or not to create a new AKS cluster."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step will be moving to the &lt;em&gt;Kubernetes Cluster (AKS) Details&lt;/em&gt; tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_create_argo_cluster.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_create_argo_cluster.png" alt="Create Argo CD packaged by Bitnami - Kubernetes Cluster (AKS) Details with dropdown to select a cluster from within the targeted resource group."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After we specified the cluster details, we move to the &lt;em&gt;Application Details&lt;/em&gt; tab. Here, we add a resource name (which has to be lowercase letters or numbers - no symbols - of between 6 and 30 characters). We can customize the namespace, but we'll take the default. And we can decide if we want the extension to be able to automatically deploy minor version updates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_create_argo_appdetails.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_create_argo_appdetails.png" alt="Create Argo CD packaged by Bitnami - Application Details tab, with the extension resource name, installation namespace, and whether or not to allow auto upgrades for minor versions."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last step of this process is to go to the &lt;em&gt;Review + create&lt;/em&gt; tab. Here we'll see the versions of the application and deployment resources, as well as any potential cost (for the packaged application specifically) and other details like terms of use. We'll click &lt;em&gt;Create&lt;/em&gt; here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_create_argo_create.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_create_argo_create.png" alt="Create Argo CD packaged by Bitnami - Review + create tab."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking &lt;em&gt;Create&lt;/em&gt; kicks of the deployment process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_deploy_argo_progress.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_deploy_argo_progress.png" alt="Deployment process in the Azure portal for Argo CD"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the deployment completes, we'll have &lt;em&gt;argocd&lt;/em&gt; listed in our &lt;em&gt;Extension + applications&lt;/em&gt; section of our AKS Cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_extensions_installed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fsmurawski%2Fdevto%2Fmain%2Fposts%2Fgitops%2Fassets%2Fazure_portal_aks_extensions_installed.png" alt="View of the deployed application in the AKS Cluster view in the Azure portal"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Access the Argo CD Server
&lt;/h3&gt;

&lt;p&gt;We'll retrieve the Argo CD Server password with &lt;code&gt;kubectl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get secret argocd-secret -o jsonpath='{.data.clearPassword}' | base64 --decode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(or if you are like me and using PowerShell)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get secret argocd-secret -o jsonpath='{.data.clearPassword}'  | % {[system.text.encoding]::utf8.getstring([system.convert]::FromBase64String($_))}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we need to expose the Argo CD server so we can access it from our workstation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl port-forward svc/argocd-argo-cd-server 30443:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can access the Argo CD Server at &lt;code&gt;https://localhost:30443&lt;/code&gt; with the user name &lt;code&gt;admin&lt;/code&gt; and the password we retrieved in the previous step.&lt;/p&gt;

&lt;p&gt;From there, we could jump into &lt;a href="https://argo-cd.readthedocs.io/en/stable/getting_started/#6-create-an-application-from-a-git-repository" rel="noopener noreferrer"&gt;the sample in the Argo CD documentation&lt;/a&gt; and carry on from there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The AKS Marketplace provides a convenient way to installed a somewhat managed experience and quickly get started with new applications.&lt;/p&gt;

&lt;p&gt;Getting Argo CD up and running is pretty simple and we can start exploring it in greater depth in our AKS environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continue the conversation
&lt;/h3&gt;

&lt;p&gt;Leave your questions in the comments or come over to the Microsoft Open Source Discord and chat with me and my team in the &lt;em&gt;cloud-native&lt;/em&gt; channel!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/JoinOSSDiscord" rel="noopener noreferrer"&gt;Join the Microsoft Open Source Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/DiscordChannel" rel="noopener noreferrer"&gt;Meet us in the Cloud Native Channel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And check back with our team tomorrow, as &lt;a href="https://dev.to/pauldotyu"&gt;Paul&lt;/a&gt; digs into &lt;a href="https://fluxcd.io/flagger/" rel="noopener noreferrer"&gt;Flagger&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>gitops</category>
      <category>cloudnative</category>
      <category>beginners</category>
    </item>
    <item>
      <title>My Git and VS Code Settings</title>
      <dc:creator>Steven Murawski</dc:creator>
      <pubDate>Wed, 20 Sep 2023 19:06:36 +0000</pubDate>
      <link>https://forem.com/smurawski/my-git-and-vs-code-settings-4d6</link>
      <guid>https://forem.com/smurawski/my-git-and-vs-code-settings-4d6</guid>
      <description>&lt;p&gt;I work regularly with several different project types and languages across Windows, Mac, and Linux.&lt;/p&gt;

&lt;p&gt;I find some basic settings for Git and VS Code smooth out some headaches.&lt;/p&gt;

&lt;p&gt;My user settings include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"files.eol": "\n", // Windows usually doesn't care, but Linux/Mac tooling is way more sensitive
"editor.trimAutoWhitespace": true,
"editor.fontSize": 16,
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"window.zoomLevel": 0,
"[powershell]": {
    "editor.tabSize": 4
},
"[ruby]": {
    "editor.tabSize": 2
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My git config consists of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[credential]
    helper = wincred
[user]
    email = &amp;lt;my email here&amp;gt;
    name = Steven Murawski
    signingkey = &amp;lt;my gpg signing key here&amp;gt;
[diff]
    tool = default-difftool
[difftool "default-difftool"]
    cmd = code --wait --diff $LOCAL $REMOTE
[alias]
    commit = commit -S -s
[core]
    editor = code --wait
    eol = lf
    autocrlf = input
    excludesfile = ~/.gitignore
[difftool "winmerge"]
    cmd = /c/Program\\ Files\\ \\(x86\\)/WinMerge/WinMergeU.exe
[gpg]
    program = c:/Program Files (x86)/GNU/GnuPG/gpg2.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since I work on a number of projects that require a &lt;a href="https://developercertificate.org/"&gt;DCO signoff&lt;/a&gt;, I just default to signing off my commits (&lt;code&gt;alias.commit&lt;/code&gt;) and I GPG sign them because I like how they look in the GitHub UI. ;)&lt;/p&gt;

&lt;p&gt;I've set git to only use Linux line feeds for new clones/fetches (&lt;code&gt;core.eol&lt;/code&gt;) and change anything with CRLFs on upload (&lt;code&gt;core.autocrlf&lt;/code&gt;). To aid in this, my editor config has line endings set to &lt;code&gt;\n&lt;/code&gt; as well.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>git</category>
      <category>vscode</category>
    </item>
    <item>
      <title>What Really is GitOps?</title>
      <dc:creator>Steven Murawski</dc:creator>
      <pubDate>Tue, 19 Sep 2023 15:59:54 +0000</pubDate>
      <link>https://forem.com/azure/what-really-is-gitops-4n2d</link>
      <guid>https://forem.com/azure/what-really-is-gitops-4n2d</guid>
      <description>&lt;h2&gt;
  
  
  Welcome
&lt;/h2&gt;

&lt;p&gt;Welcome to the second post in our series on GitOps. &lt;a href="https://aka.ms/cloudnative/JustEnoughGit" rel="noopener noreferrer"&gt;You'll find the first post - Just Enough Git for GitOps - here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;GitOps describes the process and tooling around managing the state of a Kubernetes cluster based on content in a Git repository. We aren't going to get into specific tooling in this post - that'll start tomorrow with a post on FluxCD (with lots more to follow over the course of the next week and a half).&lt;/p&gt;

&lt;h2&gt;
  
  
  GitOps is ...
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Defining GitOps
&lt;/h3&gt;

&lt;p&gt;There are a number of definitions for GitOps (kinda like how we had a bajillion definitions for DevOps).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/buchatech" rel="noopener noreferrer"&gt;Steve Buchanan&lt;/a&gt; (of [bucahtech.com] and a teammate here at Microsoft) defines GitOps as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GitOps is an operating model pattern for cloud native applications storing application &amp;amp; declarative infrastructure code in Git as the source of truth used for automated continuous delivery.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Principles of GitOps
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://opengitops.dev/" rel="noopener noreferrer"&gt;OpenGitOps standards group&lt;/a&gt; defines GitOps using a set of four principles:&lt;/p&gt;

&lt;h4&gt;
  
  
  1) Declarative
&lt;/h4&gt;

&lt;p&gt;A system managed by GitOps must have it's state expressed declaratively.&lt;/p&gt;

&lt;h4&gt;
  
  
  2) Versioned and Immutable
&lt;/h4&gt;

&lt;p&gt;Desired state is stored in a way that enforces immutability, versioning and retains a complete version history.&lt;/p&gt;

&lt;h4&gt;
  
  
  3) Pulled Automatically
&lt;/h4&gt;

&lt;p&gt;Software agents automatically pull the desired state declarations from the source.&lt;/p&gt;

&lt;h4&gt;
  
  
  4) Continuously Reconciled
&lt;/h4&gt;

&lt;p&gt;Software agents continuously observe actual system state and attempt to apply the desired state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elements of a GitOps Environment
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Bare Minimum
&lt;/h4&gt;

&lt;p&gt;GitOps, at a minimum, requires a Git repository and an agent (usually an operator) in your Kubernetes cluster. The agent's role is to poll for configuration changes between the state of configuration files in a particular branch in the Git repo and the state of the cluster.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Farchitecture%2Fexample-scenario%2Fgitops-aks%2Fmedia%2Fgitops-flux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Farchitecture%2Fexample-scenario%2Fgitops-aks%2Fmedia%2Fgitops-flux.png" alt="Basic architecture for GitOps with Flux"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  More Realistic Environment
&lt;/h4&gt;

&lt;p&gt;The basic example ignores the container build process and image deployment. A more realistic example may look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Farchitecture%2Fexample-scenario%2Fgitops-aks%2Fmedia%2Fgitops-ci-cd-flux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fazure%2Farchitecture%2Fexample-scenario%2Fgitops-aks%2Fmedia%2Fgitops-ci-cd-flux.png" alt="More complete architecture for GitOps with Flux"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Essence of GitOps
&lt;/h3&gt;

&lt;p&gt;The GitOps principles defined by the OpenGitOps group drills down to the core of what GitOps is:&lt;/p&gt;

&lt;p&gt;A Git repository with a specified branch protected from change by technical measures and process.&lt;/p&gt;

&lt;p&gt;An agent (operator) that monitors both the cluster configuration and the specified branch to detect change and remediate configuration differences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why GitOps?
&lt;/h2&gt;

&lt;p&gt;Why would we want to use GitOps? I'll call back to the &lt;a href="https://opengitops.dev/about" rel="noopener noreferrer"&gt;OpenGitOps group and what they enumerate as the potential benefits of using GitOps tools and practices&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased Developer &amp;amp; Operational Productivity&lt;/li&gt;
&lt;li&gt;Enhanced Developer Experience&lt;/li&gt;
&lt;li&gt;Improved Stability&lt;/li&gt;
&lt;li&gt;Higher Reliability&lt;/li&gt;
&lt;li&gt;Consistency and Standardization&lt;/li&gt;
&lt;li&gt;Stronger Security Guarantees&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Is GitOps Different from DevOps?
&lt;/h3&gt;

&lt;p&gt;GitOps seems to have a lot in common with traditional DevOps practices.&lt;/p&gt;

&lt;p&gt;If we go back to the &lt;a href="https://www.continuousdelivery.com/" rel="noopener noreferrer"&gt;Continuous Delivery&lt;/a&gt; concept, we see one of the foundations is Configuration Management - which is where GitOps tooling and process fall.&lt;/p&gt;

&lt;p&gt;GitOps is for Kubernetes what Chef/Puppet/PowerShell DSC/CF Engine/and others were for server configuration management.&lt;/p&gt;

&lt;p&gt;So, GitOps isn't a replacement or different concept from DevOps - it's tooling and process specialized for working with Kubernetes environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Like about GitOps
&lt;/h2&gt;

&lt;p&gt;Coming from an infrastructure as code background, I really like the concept of the GitOps operator performing consistency checks and remediation of configuration drift over my infrastructure.&lt;/p&gt;

&lt;p&gt;Using a source control driven process also makes me happy (though not completely... see below). There are tremendous benefits (see all the State of DevOps reports) to operational stability and reductions in mean time to recovery when your infrastructure configuration is a version-able artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Problem(s) with GitOps
&lt;/h2&gt;

&lt;p&gt;I have one philosophical problem with GitOps and one operational problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Operational Problem with GitOps
&lt;/h3&gt;

&lt;p&gt;Let's start with the operational problem, as that's more of an observation based on many years in the infrastructure as code space.&lt;/p&gt;

&lt;p&gt;Typically, most teams I've worked with have a very difficult time giving control of environmental and operational changes to an agent. One of the reasons tools like Ansible or active deployment by CD tools were so popular was that it was a "push" process where some operator or developer action was required to initiate the process to deploy or change configuration.&lt;/p&gt;

&lt;p&gt;When folks attempted to operate agent-based configuration management tools where the agent ran on some schedule and performed a "test and repair" process, the concern was often "what if someone made a manual change?". While this isn't a technical problem with the tooling, it is a people and process problem. Folks had a difficult time (either personally or organizationally) allowing the agent to take control and then having to trust (and monitor) the status of those agent operations.&lt;/p&gt;

&lt;p&gt;Some of this boiled down to a lack of trust in the configuration management tooling (not the agent, but the instructions the agents had to follow). This trust could be built by quality testing - which was often neglected.&lt;/p&gt;

&lt;p&gt;Again, this isn't a technical problem with GitOps, but it is a potential blocker if teams cannot buy into funneling all changes through the agent and staying away from the &lt;code&gt;kubectl&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Philosophical Problem with GitOps
&lt;/h3&gt;

&lt;p&gt;One of the core principles of GitOps is that the configuration is "versioned and immutable" and this versioning provides an accurate audit log.&lt;/p&gt;

&lt;p&gt;This pushes the responsibility of being an artifact repository to the Git repository, which is not the purpose of the tooling. We have to rely on hosting solutions and process to enforce process to make sure the repository can achieve those goals.&lt;/p&gt;

&lt;p&gt;Additionally, managing application configuration and secrets requires special handling and forces us to build process outside of the GitOps workflow to ensure auditability and versioning for those values.&lt;/p&gt;

&lt;p&gt;Fortunately, there are options to move "GitOps" artifacts to actual artifact stores (like OCI artifacts), I just wish that was the default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;GitOps is an umbrella for practices and tooling to use Git-based configuration elements to manage our Kubernetes environment via an agent that manages the cluster configuration.&lt;/p&gt;

&lt;p&gt;Adopting GitOps practices and tools can improve your operational capabilities but requires some forethought and buy in from your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continue the conversation
&lt;/h3&gt;

&lt;p&gt;Leave your questions in the comments or come over to the Microsoft Open Source Discord and chat with me and my team in the &lt;em&gt;cloud-native&lt;/em&gt; channel!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/JoinOSSDiscord" rel="noopener noreferrer"&gt;Join the Microsoft Open Source Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/DiscordChannel" rel="noopener noreferrer"&gt;Meet us in the Cloud Native Channel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Join us tomorrow to &lt;a href="https://aka.ms/cloudnative/GitGoingWithGitOps" rel="noopener noreferrer"&gt;take a look at Flux&lt;/a&gt; with &lt;a href="https://dev.to/pauldotyu"&gt;Paul Yu&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>cloudnative</category>
      <category>azure</category>
      <category>devops</category>
    </item>
    <item>
      <title>Just Enough Git for GitOps</title>
      <dc:creator>Steven Murawski</dc:creator>
      <pubDate>Mon, 18 Sep 2023 16:47:35 +0000</pubDate>
      <link>https://forem.com/azure/just-enough-git-for-gitops-3ok8</link>
      <guid>https://forem.com/azure/just-enough-git-for-gitops-3ok8</guid>
      <description>&lt;h2&gt;
  
  
  GitOps Series
&lt;/h2&gt;

&lt;p&gt;This week and next, my team and I are going to be exploring different facets of GitOps and some of the tools in the space. Watch our social media for new posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Josh Duffney: &lt;a href="https://twitter.com/joshduffney"&gt;twitter&lt;/a&gt;|&lt;a href="https://www.linkedin.com/in/joshduffney/"&gt;linkedin&lt;/a&gt;|&lt;a href="https://github.com/duffney"&gt;github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Paul Yu: &lt;a href="https://twitter.com/pauldotyu"&gt;twitter&lt;/a&gt;|&lt;a href="https://www.linkedin.com/in/yupaul/"&gt;linkedin&lt;/a&gt;|&lt;a href="https://github.com/pauldotyu"&gt;github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Jorge Arteiro: &lt;a href="https://twitter.com/JorgeArteiro"&gt;twitter&lt;/a&gt;|&lt;a href="https://www.linkedin.com/in/jorgearteiro/"&gt;linkedin&lt;/a&gt;|&lt;a href="https://github.com/jorgearteiro"&gt;github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Yosh Wuyts: &lt;a href="https://twitter.com/yoshuawuyts"&gt;twitter&lt;/a&gt;|&lt;a href="https://www.linkedin.com/in/yoshuawuyts/"&gt;linkedin&lt;/a&gt;|&lt;a href="https://github.com/yoshuawuyts"&gt;github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Steven Murawski (me): &lt;a href="https://twitter.com/stevenmurawski"&gt;twitter&lt;/a&gt;|&lt;a href="https://www.linkedin.com/in/usepowershell/"&gt;linkedin&lt;/a&gt;|&lt;a href="https://github.com/smurawski"&gt;github&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of the topics we'll cover include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is GitOps?&lt;/li&gt;
&lt;li&gt;How do Secure Supply Chain tools fit in a GitOps workflow?&lt;/li&gt;
&lt;li&gt;FluxCD&lt;/li&gt;
&lt;li&gt;ArgoCD&lt;/li&gt;
&lt;li&gt;Dealing with secrets in GitOps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What else would you want to see? Leave a comment below with topics you'd like to see covered and we'll see what we can do!&lt;/p&gt;

&lt;p&gt;Let's get started with some concepts about Git and then cover a basic set of commands that will get you ready to work with GitOps tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git Concepts
&lt;/h2&gt;

&lt;p&gt;Git is a distributed version control system that allows multiple developers to collaboratively manage and track changes to source code, enabling efficient code development, and review.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/JlpRfMRWNY8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Distributed
&lt;/h3&gt;

&lt;p&gt;The distributed nature of Git means that you can have multiple independent copies of a repository with some shared history. That shared history makes it possible to push or pull changes from other copies effectively.&lt;/p&gt;

&lt;p&gt;What this effectively means is that Git does not, by design, have the concept of a central repository. All copies of a repository are just as valid as any other. We, as developers and operations personnel, enforce authority to one particular copy of a repository by pattern and practice. Tools and services like GitHub, GitLab, Azure Repos, and others help facilitate that practice.&lt;/p&gt;

&lt;p&gt;For the purposes of GitOps, this means that we enforce the source of truth for our environment by convention and process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branches
&lt;/h3&gt;

&lt;p&gt;In Git, branches are separate workspaces that allow developers to work on features, bug fixes, or experiments independently from other changes.&lt;/p&gt;

&lt;p&gt;This makes it easier to manage and merge changes into the project when they are ready.&lt;/p&gt;

&lt;p&gt;By convention, the primary branch in a repository is &lt;code&gt;main&lt;/code&gt;. Older projects may use the &lt;code&gt;master&lt;/code&gt;, though that terminology is perceived as culturally insensitive or offensive and the industry has shifted towards other, equally accurate terminology. Another term you may see for the &lt;code&gt;main&lt;/code&gt; branch is &lt;code&gt;trunk&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/rv8HFYNmHG8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Repository
&lt;/h3&gt;

&lt;p&gt;Repositories in Git are represented as the directory structure of the project under control. There is a hidden &lt;code&gt;.git&lt;/code&gt; directory that handles all the change tracking and content across branches and tags represented in the repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo&lt;/strong&gt; is a common shorthand version of repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Special files
&lt;/h3&gt;

&lt;p&gt;As I previously mentioned, a Git repository has a special directory for tracking Git related operations - the &lt;code&gt;.git&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;One other key file to be aware of at the start is the &lt;code&gt;.gitignore&lt;/code&gt; file. This file allows you to define specific files or groups of files (wildcards supported) that Git should ignore. This is commonly used to keep Git from tracking specific binary files, intermediate build artifacts, local settings files, and editor specific files - though it can be used for any file or set of files you do not want tracked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remotes
&lt;/h3&gt;

&lt;p&gt;Remotes are repositories that are in a physically different location (either on the local filesystem or available over a network). Git can track named references to these other locations.&lt;/p&gt;

&lt;p&gt;By convention, if you copy (clone) a repository from a remote source, that original source is tracked as a remote named &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/3Brg_l0vHBo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Commands
&lt;/h2&gt;

&lt;p&gt;These are the commands, in order of use, that I think will be helpful as you get started with Git and GitOps. There are more complicated operations - let me know in the comments below if you'd like to see more specific scenarios with Git.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone
&lt;/h3&gt;

&lt;p&gt;We'll often be starting with a source repository from a central server. One common location is from GitHub. To create a local copy of a repository from a remote source, we use the &lt;code&gt;git clone&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Git can operate over local file system paths or over a network using HTTPS or SSH. We provide a remote source to &lt;code&gt;git clone&lt;/code&gt; and Git will track that method and use it for future operations with that remote repository.&lt;/p&gt;

&lt;p&gt;The cloned remote repository will be tracked as a remote named &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;HTTPS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/Azure-Samples/aks-store-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SSH&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone git@github.com:Azure-Samples/aks-store-demo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Init
&lt;/h3&gt;

&lt;p&gt;If you aren't starting from a remote Git repository, you can create a new git repository from just about any directory structure. &lt;code&gt;git init&lt;/code&gt; will create the tracking data required for the repository (the &lt;code&gt;.git&lt;/code&gt; folder). There will be no &lt;code&gt;origin&lt;/code&gt; or other remotes defined.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; mkdir init_sample

    Directory: C:\Users\stmuraws\source

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----           9/18/2023 10:10 AM                init_sample
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; cd init_sample
&amp;gt; git init .
Initialized empty Git repository in C:/Users/stmuraws/source/init_sample/.git/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Status
&lt;/h3&gt;

&lt;p&gt;The most common command I run inside a Git repo is &lt;code&gt;git status&lt;/code&gt;. This will tell you the current state of changes. You'll see your current branch, status compared to the same branch against any origin you are tracking against (usually &lt;code&gt;origin&lt;/code&gt;), as well as any pending file changes &lt;strong&gt;staged&lt;/strong&gt; by &lt;code&gt;git add&lt;/code&gt; (described next).&lt;/p&gt;

&lt;p&gt;Example (from the &lt;code&gt;init_sample&lt;/code&gt; repository):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add
&lt;/h3&gt;

&lt;p&gt;Regardless of how you got your Git repository locally (whether you cloned an existing repo or used &lt;code&gt;git init&lt;/code&gt; to create a new one), when you add new, remove existing, or change existing files, you need a way to tell Git what you want to track.&lt;/p&gt;

&lt;p&gt;Git is not like &lt;em&gt;autosave&lt;/em&gt;. It does not continuously track changes. We'll tell Git specifically which changes to track.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git add&lt;/code&gt; is what we'll use to tell Git which changes to &lt;strong&gt;stage&lt;/strong&gt; as part of a &lt;strong&gt;commit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example (from the &lt;code&gt;init_sample&lt;/code&gt; repository):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; 'some content' &amp;gt; sample_file.txt
&amp;gt; git add sample_file.txt
&amp;gt; git status

On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached &amp;lt;file&amp;gt;..." to unstage)
        new file:   sample_file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git add&lt;/code&gt; does not create a new &lt;strong&gt;commit&lt;/strong&gt; by itself. We'll use it in conjunction with the next command, &lt;code&gt;git commit&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commit
&lt;/h3&gt;

&lt;p&gt;Once we have some changes &lt;strong&gt;staged&lt;/strong&gt;, we can commit them to our repository. Each &lt;strong&gt;commit&lt;/strong&gt; is identified by a hash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commits&lt;/strong&gt; require a message by default. If you do not pass one at the command line or use a flag to allow an empty message, then an editor will open to allow you to type a new commit message. I'll typically use the &lt;code&gt;-m&lt;/code&gt; parameter to supply a message at the command line.&lt;/p&gt;

&lt;p&gt;Example (from the &lt;code&gt;init_sample&lt;/code&gt; repository):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; git commit -m "First commit to the sample repository."

[main (root-commit) 01e9e34] First commit to the sample repository
 1 file changed, 1 insertion(+)
 create mode 100644 sample_file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally, each &lt;strong&gt;commit&lt;/strong&gt; would be a set of related changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Push
&lt;/h3&gt;

&lt;p&gt;Now that we've made a change, if we are working with a shared remote repository, we can push our changes to that repository.&lt;/p&gt;

&lt;p&gt;If we started by cloning a remote repository, then we'll already have a remote configured.&lt;/p&gt;

&lt;p&gt;For this example, let's use the repository we used for &lt;code&gt;git init&lt;/code&gt; (so we don't make random changes to something we might really be using). To use this repository, we'll need to do a bit of setup.&lt;/p&gt;

&lt;p&gt;Setup (from the &lt;code&gt;init_sample&lt;/code&gt; repository):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; cd ..
&amp;gt; mkdir push_sample
&amp;gt; cd push_sample
&amp;gt; # Initialize a new git repo as bare (no working directory)
&amp;gt; git init --bare .
&amp;gt; cd ../init_sample
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we are back in the &lt;code&gt;init_sample&lt;/code&gt; directory, we'll configure the new repository as a remote.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; git remote add origin ../push_sample
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we are ready to push our changes. The remote setup usually only happens once for the repository (and if we cloned a repository, it'll be setup for us already). We'll specify the remote we are pushing to (&lt;code&gt;origin&lt;/code&gt;) and the branch we are pushing (&lt;code&gt;main&lt;/code&gt;). The &lt;code&gt;-u&lt;/code&gt; is only needed once. It tells Git that the &lt;code&gt;main&lt;/code&gt; branch of my local repository and my remote repository should be associated (&lt;strong&gt;tracked&lt;/strong&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; git push origin main -u

Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 254 bytes | 254.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To ../push_sample
 * [new branch]      main -&amp;gt; main
branch 'main' set up to track 'origin/main'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fetch
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git fetch&lt;/code&gt; retrieves the contents and &lt;strong&gt;commits&lt;/strong&gt;. It does not change anything in your &lt;strong&gt;working directory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I prefer to use &lt;code&gt;git fetch&lt;/code&gt;, in combination with the next command &lt;code&gt;git merge&lt;/code&gt;, rather than &lt;code&gt;git pull&lt;/code&gt; to retrieve changes from a remote repository. This puts me in greater control of the changes that will happen to the files I'm currently working on. &lt;code&gt;git pull&lt;/code&gt; is convenient until it bites you (which can be a rare occurrence).&lt;/p&gt;

&lt;p&gt;Example (from our &lt;code&gt;init_sample&lt;/code&gt; repository):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; cd ..
&amp;gt; mkdir fetch_sample
&amp;gt; cd fetch_sample
&amp;gt; git init .
&amp;gt; git remote add origin ../push_sample
&amp;gt; git fetch origin

remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 234 bytes | 21.00 KiB/s, done.
From ../push_sample
 * [new branch]      main       -&amp;gt; origin/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Merge
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git merge&lt;/code&gt; allows you to bring changes from one branch into your current branch. The source branches can be local or from a remote (which have been &lt;code&gt;fetch&lt;/code&gt;ed locally).&lt;/p&gt;

&lt;p&gt;Now that we have changes from a remote repository in our local Git repository (from the &lt;code&gt;git fetch&lt;/code&gt;), we can merge changes from the remote branch to our local branch.&lt;/p&gt;

&lt;p&gt;Example (from the &lt;code&gt;fetch_sample&lt;/code&gt; repository):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; # Verify the directory is empty (other than the hidden .git directory)
&amp;gt; ls
&amp;gt; git merge origin/main
&amp;gt; ls

    Directory: C:\Users\stmuraws\source\fetch_sample

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           9/18/2023 11:09 AM             13 sample_file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checkout
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git checkout&lt;/code&gt; allows us to switch between branches inside our repository. This allows us to more easily keep our work isolated from other ongoing changes. &lt;code&gt;git checkout&lt;/code&gt; has a handy switch, &lt;code&gt;-b&lt;/code&gt;, that lets us create a new branch from the current branch (otherwise, you can use the &lt;code&gt;git branch&lt;/code&gt; command, but I almost never use that to create new branches since &lt;code&gt;git checkout&lt;/code&gt; is so convenient).&lt;/p&gt;

&lt;p&gt;Ideally, we create a branch for every set of related changes we want to make.&lt;/p&gt;

&lt;p&gt;Example (from the &lt;code&gt;fetch_sample&lt;/code&gt; repository):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; git checkout -b 'new_branch'
Switched to a new branch 'new_branch'
&amp;gt; git checkout main
Switched to branch 'main'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reset
&lt;/h3&gt;

&lt;p&gt;Sometimes, we can get our repositories all messed up. &lt;code&gt;git reset&lt;/code&gt; can help us. (There are other uses too, but early on this is the most common.)&lt;/p&gt;

&lt;p&gt;There are two modes for &lt;code&gt;git reset&lt;/code&gt;, soft and hard.&lt;/p&gt;

&lt;h4&gt;
  
  
  Soft resets
&lt;/h4&gt;

&lt;p&gt;A soft reset leaves the changes from commits after the specified commit in your working directory, allowing you to retain the changes you made in those commits as uncommitted modifications. Any of the previously &lt;strong&gt;staged&lt;/strong&gt; changes (things we added with &lt;code&gt;git add&lt;/code&gt;) will be no longer be staged, but those changes will still be present as uncommitted modifications.&lt;/p&gt;

&lt;p&gt;Use a soft reset when you want to rework and recommit some changes from the commits after the specified commit. Or if you accidentally committed something but want to include those changes in a different commit.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hard resets
&lt;/h4&gt;

&lt;p&gt;A hard reset resets both the &lt;strong&gt;staging&lt;/strong&gt; (things we added with &lt;code&gt;git add&lt;/code&gt;) area and the working directory (any current changes in files) to match the state of the specified &lt;strong&gt;commit&lt;/strong&gt;. This means any changes made in &lt;strong&gt;commits&lt;/strong&gt; after the specified &lt;strong&gt;commit&lt;/strong&gt; are completely removed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: A hard reset can and will remove/lose changes that are not present in target &lt;strong&gt;commit&lt;/strong&gt; you are resetting to.&lt;/p&gt;

&lt;p&gt;Use a hard reset when you want to completely discard all changes made in &lt;strong&gt;commits&lt;/strong&gt; after a certain point and start fresh.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebase
&lt;/h3&gt;

&lt;p&gt;The last command I'm going to introduce here is &lt;code&gt;git rebase&lt;/code&gt;. There are two modes of &lt;code&gt;git rebase&lt;/code&gt; that I commonly use, but we'll focus on the most critical use case.&lt;/p&gt;

&lt;p&gt;We can use rebasing to bring our branch history current to the state of whatever branch we want to target. This will make it easier to bring our changes into that target branch and let us solve any conflicts in our working branch locally.&lt;/p&gt;

&lt;p&gt;For example, I'm &lt;a href="https://github.com/smurawski/aks-store-demo/tree/helm"&gt;working on some Helm charts&lt;/a&gt; for the &lt;a href="https://github.com/Azure-Samples/aks-store-demo"&gt;AKS Store Demo project&lt;/a&gt;. My working branch is currently behind the state of the project source. I can bring my project current with &lt;code&gt;git rebase&lt;/code&gt; and make it easier for the project maintainers to accept my contribution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; git clone https://github.com/smurawski/aks-store-demo
&amp;gt; cd aks-store-demo
&amp;gt; git checkout origin/helm
&amp;gt; git checkout -b helm
&amp;gt; git remote add upstream https://github.com/Azure-Samples/aks-store-demo
&amp;gt; git fetch upstream
&amp;gt; git rebase upstream/main
Successfully rebased and updated refs/heads/helm.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What about merging the main branch back into my working branch? You can do that, but it can make the history pretty messy and, in some cases, make reviews more difficult (especially when there are a lot of files modified or moved around).&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This was a very brief introduction to a &lt;strong&gt;lot&lt;/strong&gt; of commands, but it should be enough to get started. For a more hands-on exploration of Git, check out &lt;a href="https://learn.microsoft.com/training/paths/intro-to-vc-git/"&gt;this learning path on Microsoft Learn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We didn't cover anything about dealing with merge conflicts, but we get into that a bit in this video or &lt;a href="https://learn.microsoft.com/training/modules/branch-merge-git"&gt;you can get hands on with Microsoft Learn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/rv8HFYNmHG8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Continue the conversation
&lt;/h3&gt;

&lt;p&gt;Leave your questions in the comments or come over to the Microsoft Open Source Discord and chat with me and my team in the &lt;em&gt;cloud-native&lt;/em&gt; channel!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/JoinOSSDiscord"&gt;Join the Microsoft Open Source Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/cloudnative/DiscordChannel"&gt;Meet us in the Cloud Native Channel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Watch for a post from our team tomorrow covering what GitOps is!&lt;/p&gt;

</description>
      <category>git</category>
      <category>gitops</category>
      <category>cloudnative</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Getting Started with Helm</title>
      <dc:creator>Steven Murawski</dc:creator>
      <pubDate>Fri, 08 Sep 2023 17:52:31 +0000</pubDate>
      <link>https://forem.com/azure/getting-started-with-helm-4g1d</link>
      <guid>https://forem.com/azure/getting-started-with-helm-4g1d</guid>
      <description>&lt;p&gt;Over the past few days, my team and I have looked into tools that can help you deploy containerized workloads into Kubernetes more easily.&lt;/p&gt;

&lt;p&gt;Here's a quick roundup of those posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Video: &lt;a href="https://aka.ms/cloudnative/DeployingToACA"&gt;Deploying to Azure Container Apps&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lab: &lt;a href="https://aka.ms/cloudnative/DeployingToACALab"&gt;Deploying to Azure Container Apps&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog post: &lt;a href="https://aka.ms/cloudnative/BuildingWithDraft"&gt;Jump Start Your Applications with Draft&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog post: &lt;a href="https://aka.ms/cloudnative/DraftAndAcorn"&gt;Effortlessly Deploy to AKS with Open Source Tools Draft and Acorn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lab: &lt;a href="https://aka.ms/cloudnative/DeployingToAKSLab"&gt;Deploying Apps to AKS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog post: &lt;a href="https://aka.ms/cloudnative/draftdeepdive"&gt;Deep Dive into Using Draft&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog post: &lt;a href="https://aka.ms/cloudnative/BuildingMultiArchContainers"&gt;Building Multi-Architecture Container Images&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog post: &lt;a href="https://aka.ms/cloudnative/SecureSupplyChainTools"&gt;Level-up Container Security: 4 Open-Source Tools for Secure Software Supply Chain&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog post: &lt;a href="https://aka.ms/cloudnative/PushingMultiArchContainers"&gt;Pushing Multi-Architecture Container Images&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding Helm
&lt;/h2&gt;

&lt;p&gt;Today, we are going to look at [Helm]. Helm can seem like a mystical tool, taking a bunch of marked up files, putting together a bunch of data about your environment, and then making a service appear in your cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Helm?
&lt;/h3&gt;

&lt;p&gt;Helm is a package manager for Kubernetes. Helm provides a way to create templates for Kubernetes configuration files, apply those configurations with local data specific to your cluster, and manage updates, rollbacks, and other release concerns. We'll focus on the packaging and deployment aspects of Helm in this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Helm
&lt;/h2&gt;

&lt;p&gt;Getting started with Helm is pretty straight forward. &lt;a href="https://helm.sh/docs/intro/install/"&gt;Installing a local copy of Helm&lt;/a&gt; is the first step.&lt;/p&gt;

&lt;p&gt;Helm will also require you to have &lt;a href="https://kubernetes.io/docs/tasks/tools/"&gt;&lt;code&gt;kubectl&lt;/code&gt; installed&lt;/a&gt; and configured to access a Kubernetes cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding Helm Charts
&lt;/h3&gt;

&lt;p&gt;The templates that Helm uses are called Charts. Helm allows you to find and install charts from repositories. Artifact Hub can help you find repositories (&lt;a href="https://artifacthub.io/packages/search?kind=0"&gt;Artifact Hub&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Step one is to add the chart repository. We'll use my fork of the &lt;a href="https://github.com/smurawski/aks-store-demo"&gt;AKS Store Demo&lt;/a&gt; as an example. We'll use the &lt;code&gt;helm repo add&lt;/code&gt; command and provide it a (local) name for the repository, as well as the URL to the published chart repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aks-store-demo ❯ helm repo add aks-demo https://raw.githubusercontent.com/smurawski/aks-store-demo/helm/packages/
"aks-demo" has been added to your repositories
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can inspect what charts are available from the repository with &lt;code&gt;helm search repo&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aks-store-demo ❯ helm search repo aks-demo
NAME                    CHART VERSION   APP VERSION     DESCRIPTION
aks-demo/aks-store-demo 0.1.0           1.16.0          A Helm chart for Kubernetes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installing a Helm Chart
&lt;/h3&gt;

&lt;p&gt;The simplest way to install a Helm chart, with no customization, is with &lt;code&gt;helm install&lt;/code&gt; with a name for the installation (called a release) and the specific chart to deploy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aks-store-demo ❯ helm install aks-store-demo aks-demo/aks-store-demo
NAME: aks-store-demo
LAST DEPLOYED: Fri Sep  8 11:59:57 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing aks-store-demo.

Your release is named aks-store-demo.

To learn more about the release, try:

  $ helm status aks-store-demo
  $ helm get all aks-store-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can list the Helm releases with &lt;code&gt;helm list&lt;/code&gt;. These are namespace specific.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aks-store-demo ❯ helm list
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION
aks-store-demo  default         1               2023-09-08 11:59:57.0813261 -0500 CDT   deployed        aks-store-demo-0.1.0    1.16.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can remove the release with &lt;code&gt;helm uninstall&lt;/code&gt; followed by the name of the release.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aks-store-demo ❯ helm uninstall aks-store-demo
release "aks-store-demo" uninstalled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Customizing the Installation
&lt;/h4&gt;

&lt;p&gt;Helm charts typically have Values, which are default settings, that can be customized.&lt;/p&gt;

&lt;p&gt;We can find the various options by using &lt;code&gt;helm show values&lt;/code&gt; and providing the chart name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aks-store-demo ❯ helm show values aks-demo/aks-store-demo
[LOTS OF YAML follows]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can redirect this to a file and customize any of the values there. Then, when we install the helm chart, we can point to our custom &lt;code&gt;values.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aks-store-demo ❯ helm show values aks-demo/aks-store-demo &amp;gt; local_values.yaml
aks-store-demo ❯ # make a few changes in local_values.yaml (maybe change some replica counts)
aks-store-demo ❯ helm install aks-store-demo aks-demo/aks-store-demo -f local_values.yaml
... release details ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the quick and dirty guide to getting up and running with Helm charts. Now, we'll look into what it takes to convert our Kubernetes manifests into a Helm charts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Helm Chart
&lt;/h2&gt;

&lt;p&gt;Helm offers a "quick" way to get started creating your own Helm charts with &lt;code&gt;helm create&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir charts
cd charts
helm create aks-store-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's going to create a sample Chart with in the &lt;code&gt;aks-store-demo&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;In the directory will be an (empty) charts directory, a directory of templates (delete everything inside except for the &lt;code&gt;Notes.txt&lt;/code&gt; file), a &lt;code&gt;.helmignore&lt;/code&gt; file, a &lt;code&gt;Chart.yaml&lt;/code&gt; file, and a &lt;code&gt;values.yaml&lt;/code&gt; file. You can delete the contents of the &lt;code&gt;values.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;In our template directory, we can drop any and all of our manifests. In my example, I broke up the &lt;a href="https://github.com/smurawski/aks-store-demo/blob/helm/aks-store-all-in-one.yaml"&gt;all-in-one.yaml&lt;/a&gt; into &lt;a href="https://github.com/smurawski/aks-store-demo/tree/helm/charts/aks-store-demo/templates"&gt;individual manifests&lt;/a&gt;. To me, it was easier to experiment with the templating engine with smaller files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Converting a Manifest into a Template
&lt;/h3&gt;

&lt;p&gt;Any of the manifests in the &lt;code&gt;templates&lt;/code&gt; directory will be evaulated and applied as part of the &lt;code&gt;helm install&lt;/code&gt;. If a file does not have any templating features used, it'll just be applied as it exists. For example, the &lt;code&gt;config_map_rabbitmq.yaml&lt;/code&gt; doesn't have any values I want to change, so it just exists as it did in the &lt;code&gt;aks-store-all-in-one.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first thing I wanted to be able to customize is what container image to pull for each of the deployments. I'll walk through how I changed one of the deployments, and you can explore the repo and files to see the changes made for the other deployments.&lt;/p&gt;

&lt;p&gt;Let's use the &lt;code&gt;store-front&lt;/code&gt; as our example. I started out by identifying the default container image specified, which was &lt;code&gt;ghcr.io/azure-samples/aks-store-demo/store-front:latest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, I opened my &lt;code&gt;values.yaml&lt;/code&gt;, which should be empty (since we deleted all the contents earlier). I then added the following to the file and saved it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;store_front:
  image:
    repository: ghcr.io/azure-samples/aks-store-demo/store-front
    tag: latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;values.yaml&lt;/code&gt; is just a YAML file with whatever structure of data you feel will map effectively into your configs.&lt;/p&gt;

&lt;p&gt;Then, I modified the &lt;code&gt;image&lt;/code&gt; element of the deployment manifest to look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image: "{{ .Values.store_front.image.repository }}:{{ .Values.store_front.image.tag }}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I wanted to control the number of replicas for my deployment. I updated &lt;code&gt;values.yaml&lt;/code&gt; to look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;store_front:
  replicaCount: 1
  image:
    repository: ghcr.io/azure-samples/aks-store-demo/store-front
    tag: latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I updated the deployment manifest with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replicas: {{ .Values.store_front.replicaCount }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since everything for this deployment is namespaced under &lt;code&gt;store_front&lt;/code&gt; in my &lt;code&gt;values.yaml&lt;/code&gt;, I can simplify this a bit by using &lt;code&gt;with&lt;/code&gt;, which allows me to set a context from the &lt;code&gt;values.yaml&lt;/code&gt; to use when referencing elements to apply to the template.&lt;/p&gt;

&lt;p&gt;I added &lt;code&gt;{{ with .Values.store_front }}&lt;/code&gt; to the top of the manifest and &lt;code&gt;{{ end }}&lt;/code&gt; to the bottom. Then my two existing references need to be updated to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image: "{{ .image.repository }}:{{ .image.tag }}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replicas: {{ .replicaCount }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, that looks much simpler. :)&lt;/p&gt;

&lt;p&gt;The last element I wanted to be able to customize is the resource requests and limits. I moved those elements to the &lt;code&gt;values.yaml&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;store_admin:
  replicaCount: 1
  image:
    repository: ghcr.io/azure-samples/aks-store-demo/store-admin
    tag: latest
  resources:
    requests:
      cpu: 1m
      memory: 200Mi
    limits:
      cpu: 1000m
      memory: 512Mi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then I got to use my &lt;code&gt;with&lt;/code&gt; again, along with the &lt;code&gt;toYaml&lt;/code&gt; function to basically copy the YAML keys and values from the &lt;code&gt;values.yaml&lt;/code&gt; into my template. You'll notice there's a new element in the template, the &lt;code&gt;-&lt;/code&gt;. This tells the templating engine to only render that section if there's content for it from the &lt;code&gt;values.yaml&lt;/code&gt;. If there's no resources defined there, it'll leave that section out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        {{- with .resources }}
        resources:
          {{- toYaml . | nindent 10 }}
        {{- end }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing Template Rendering
&lt;/h3&gt;

&lt;p&gt;Once we've made a few updates to our templates, we can test to see how they come out.&lt;/p&gt;

&lt;p&gt;From the root of the repository, you can run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;helm install aks-store-demo ./charts/aks-store-demo --dry-run --debug
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will evaluate and render the templates to the console, applying any values from the &lt;code&gt;values.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Another way to make sure you have valid templates is to use &lt;code&gt;helm lint&lt;/code&gt;. You'll have to be in the directory with the chart when the command executes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pushd ./charts/aks-store-demo
helm lint
popd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Notes.txt
&lt;/h3&gt;

&lt;p&gt;I haven't mentioned it until now, but the &lt;code&gt;Notes.txt&lt;/code&gt; gets rendered by the templating engine and written to the console upon the installation of a release of the chart. You can include any information that folks may find useful in using what was installed by your chart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;I hope this quick intro to Helm helps. Please drop me a comment with any questions or feedback!&lt;/p&gt;

&lt;p&gt;You'll find &lt;a href="https://github.com/smurawski/aks-store-demo/tree/helm"&gt;my updates to the demo repository here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cloudnative</category>
      <category>beginners</category>
      <category>opensource</category>
      <category>azure</category>
    </item>
    <item>
      <title>Deep Dive into Working with Draft</title>
      <dc:creator>Steven Murawski</dc:creator>
      <pubDate>Fri, 01 Sep 2023 17:51:24 +0000</pubDate>
      <link>https://forem.com/azure/deep-dive-into-working-with-draft-23nh</link>
      <guid>https://forem.com/azure/deep-dive-into-working-with-draft-23nh</guid>
      <description>&lt;h2&gt;
  
  
  What is Draft and what does it do?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/azure/draft"&gt;Draft&lt;/a&gt; exists to help folks get up and running on Kubernetes more easily.&lt;/p&gt;

&lt;p&gt;This command line tool is run in your project's working directory and attempts to identify the type of project in the repository.&lt;/p&gt;

&lt;p&gt;From there, Draft will ask you some questions and generate all the files you need to containerize the application and deploy into a Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;In addition to the Dockerfile, Draft can provide &lt;code&gt;Helm&lt;/code&gt; charts, &lt;code&gt;kustomize&lt;/code&gt; configuration files, or plain old Kubernetes manifests.&lt;/p&gt;

&lt;p&gt;Once your project has those configuration files, Draft can help you set up GitHub actions to build, package, and deploy your application into a Kubernetes cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Draft Work?
&lt;/h2&gt;

&lt;p&gt;Draft has several subcommands that we'll look at in depth.&lt;/p&gt;

&lt;p&gt;First is &lt;code&gt;draft create&lt;/code&gt; which will generate the Dockerfile and any supporting configuration files necessary.&lt;/p&gt;

&lt;p&gt;Second is &lt;code&gt;draft setup-gh&lt;/code&gt; which configures &lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure"&gt;GitHub OpenID Connect&lt;/a&gt; with Microsoft Entra ID (formerly Azure Active Directory).&lt;/p&gt;

&lt;p&gt;The third subcommand is &lt;code&gt;draft generate-workflow&lt;/code&gt; which creates GitHub workflow files to build, package, and deploy your application into Kubernetes.&lt;/p&gt;

&lt;p&gt;The fourth main subcommand is &lt;code&gt;draft update&lt;/code&gt; which can update your configuration files to allow your application to be accessible from outside the cluster.&lt;/p&gt;

&lt;p&gt;There are a few other minor features that we'll review as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up an Azure Environment
&lt;/h3&gt;

&lt;p&gt;To create a basic environment to test with Draft, we can use the Bicep template that I've included in &lt;a href="https://github.com/smurawski/drafttesting"&gt;my sample repo&lt;/a&gt;. It will create a resource group called &lt;code&gt;drafttesting&lt;/code&gt; with an AKS cluster and Azure Container Registry in the region you specify.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/smurawski/drafttesting
cd drafttesting

# I deployed to eastus, but pick a region that fits for you.
az deployment sub create --template-file ./deploy/main.bicep --location eastus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting up a test project
&lt;/h3&gt;

&lt;p&gt;To start, I'm going to create a simple Go project. (or feel free to copy it from &lt;a href="https://github.com/smurawski/drafttesting"&gt;my sample repo&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir DraftTesting
cd DraftTesting
go mod init DraftTesting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add a file &lt;code&gt;main.go&lt;/code&gt; (which I borrowed from &lt;a href="https://learn.microsoft.com/azure/app-service/quickstart-golang"&gt;learn.microsoft.com&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main
import (
    "fmt"
    "net/http"
)
func main() {
    http.HandleFunc("/", HelloServer)
    http.ListenAndServe(":8080", nil)
}
func HelloServer(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Getting Started with &lt;code&gt;draft create&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;After &lt;a href="https://github.com/Azure/draft/#installation"&gt;installing Draft&lt;/a&gt;, while still in the &lt;code&gt;DraftTesting&lt;/code&gt; directory, I'll run &lt;code&gt;draft create&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dockerfile generation
&lt;/h4&gt;

&lt;p&gt;Draft detects that I've got a Go project and asks if I want to use Go Modules. Other languages will have appropriate questions asked for their specific needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/source/DraftTesting ❯  draft create
[Draft] --- Detecting Language ---
v yes
[Draft] --&amp;gt; Draft detected Go (88.475836%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the language is identified, Draft moves on to create a Dockerfile. You'll be asked for the port to expose in the Dockerfile. My app is listening on 8080, so I'll use that. Then it asks for the version of Go my module requires, which for me was 1.21.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Draft] --- Dockerfile Creation ---
Please enter the port exposed in the application (default: 80): 8080
Please enter the version of go used by the application (default: 1.18): 1.21.1
[Draft] --&amp;gt; Creating Dockerfile...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I have a Dockerfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM golang:1.21
ENV PORT 8080
EXPOSE 8080

WORKDIR /go/src/app
COPY . .

RUN go mod vendor
RUN go build -v -o app
RUN mv ./app /go/bin/

CMD ["app"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Generating Kubernetes Manifests
&lt;/h4&gt;

&lt;p&gt;Draft continues to ask me which type of deployment I want to use. I'll keep it simple and pick &lt;code&gt;manifests&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Draft] --- Deployment File Creation ---
v manifests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In one of the more annoying bits of the workflow, I get asked for the exposed port again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please enter the port exposed in the application: 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Draft prompts me to enter a name for the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please enter the name of the application: drafttesting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Continuing on with Draft's fascination with ports, it asks which port should be used to expose my application outside the cluster, though it makes the default my previously selected port.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please enter the port the service uses to make the application accessible from outside the cluster (default: 8080):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I'm asked for the namespace to deploy the application into. I took the default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please enter  the namespace to place new resources in (default: default):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This leads to the first question where things could start to go REALLY wrong if we aren't paying attention. Draft wants to know the image name for the deployment. Here's where we'll need to know what container registry our application is going to be deployed to.&lt;/p&gt;

&lt;p&gt;Thanks to the Bicep template we ran earlier, I do have an Azure Container Registry to which I can deploy my container. We need to prefix our container image name with URL of the container registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please enter the name of the image to use in the deployment (default: drafttesting): acrxms72d.azurecr.io/drafttesting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we provide the tag to deploy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please enter the tag of the image to use in the deployment (default: latest): v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we have our manifests to deploy our application into the Kubernetes cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Draft] --&amp;gt; Creating manifests Kubernetes resources...

[Draft] Draft has successfully created deployment resources for your project
[Draft] Use 'draft setup-gh' to set up Github OIDC.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Draft created a &lt;code&gt;manifests&lt;/code&gt; folder in my project directory with a &lt;code&gt;deployment.yaml&lt;/code&gt; and a &lt;code&gt;service.yaml&lt;/code&gt;. I'm not going to reproduce them here, but if you want a bit more on these files, check out &lt;a href="https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-1"&gt;Kubernetes Fundamentals - Pods and Deployments&lt;/a&gt; and &lt;a href="https://azure.github.io/Cloud-Native/cnny-2023/fundamentals-day-2"&gt;Kubernetes Fundamentals - Services and Ingress&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  So, what templates are used by &lt;code&gt;draft create&lt;/code&gt;?
&lt;/h4&gt;

&lt;p&gt;The templates are embedded in the tool and can be found in &lt;a href="https://github.com/Azure/draft"&gt;the source on GitHub&lt;/a&gt;. The Dockerfile templates are &lt;a href="https://github.com/Azure/draft/tree/main/template/dockerfiles"&gt;here&lt;/a&gt; and the deployment templates are &lt;a href="https://github.com/Azure/draft/tree/main/template/deployments"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Securing Access to Azure for my GitHub Workflow with &lt;code&gt;draft setup-gh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Draft suggested we move onto setting up GitHub OpenID Connect (OIDC), so let's get that done.&lt;/p&gt;

&lt;h4&gt;
  
  
  OpenID Connect with Microsoft Entra ID/Azure Active Directory
&lt;/h4&gt;

&lt;p&gt;First, let's define what's going to happen. Draft is first going to create an Microsoft Entra App Registration.&lt;/p&gt;

&lt;p&gt;Then, it will configure a federated credential for that app registration. The federated credential will define what GitHub resources can request a token to access my subscription via the application.&lt;/p&gt;

&lt;p&gt;Finally, it creates a service principal for that application and assigns it permissions to the resource group specified. The application will use that service principal to access the resources for GitHub Actions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Running &lt;code&gt;draft setup-gh&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;First, Draft prompts me to enter an app registration name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter app registration name: drafttesting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I can pick the subscriptions I have access to based on my current &lt;code&gt;az&lt;/code&gt; CLI login session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter app registration name: drafttesting
Use the arrow keys to navigate: ↓ ↑ → ←
v my-azure-subscription-guid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I need to provide a resource group to provide access to. My AKS cluster and ACR instance are in the &lt;code&gt;drafttesting&lt;/code&gt; resource group I created earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter resource group name: drafttesting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to enter our GitHub organization (or user) and repository. As you can see, my naming skills are on point and very clever.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enter github organization and repo (organization/repoName): smurawski/drafttesting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, Draft setups up OIDC between Microsoft Entra ID and GitHub. This can take a minute or two.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Draft] Draft has successfully set up Github OIDC for your project 😃
[Draft] Use 'draft generate-workflow' to generate a Github workflow to build and deploy an application on AKS.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Troubleshooting the Federated Credential
&lt;/h4&gt;

&lt;p&gt;To see what federated credential was created and the values supplied by Draft, you can use the Azure CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;applicationName=drafttesting
objectId=$(az ad app list --display-name $applicationName --query '[].id' -o tsv)
az ad app federated-credential list --id $objectId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will output a configuration for your federated credential. Take note of the subject property. That's where the configuration for what can access the credential lives. You'll see values like &lt;code&gt;repo:smurawski/drafttesting:ref:refs/heads/main&lt;/code&gt; which means that for requests originating from changes to my &lt;code&gt;main&lt;/code&gt; branch, Microsoft Entra ID can provide access to my subscription.&lt;/p&gt;

&lt;p&gt;The most likely mismatch will be attempting to deploy from a branch that was not specified when using &lt;code&gt;draft setup-gh&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating Build Automation with &lt;code&gt;draft generate-workflow&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;With our GitHub OIDC set up and configured to allow access to my Azure subscription, we can create our workflow.&lt;/p&gt;

&lt;p&gt;Draft first asks what type of deployment manifests we are using. We selected manifests earlier, so we'll keep that choice now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DraftTesting on  main ❯  draft generate-workflow
[Draft] --&amp;gt; Generating Github workflow
v manifests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we are prompted for our Azure Container Registry name, our container image name, the resource group for our AKS cluster, and the cluster name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please enter the Azure container registry name: acrxms72d
Please enter the container image name: drafttesting
Please enter the Azure resource group of your AKS cluster: drafttesting
Please enter the AKS cluster name: aks1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we are prompted to enter which branch to deploy from and where in the project repository should be our Docker build context (I specified the default which is the project folder root).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please enter the Github branch to automatically deploy from: main
Please enter the path to the Docker build context (default: .):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Draft then generated our workflow file and ensured that our deployment manifest had the correct repository and deployment, but it removed the tag. This is not a problem as the build created will replace the image at deployment time with one tagged and built in that GitHub Actions run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Draft] Draft has successfully generated a Github workflow for your project 😃
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  So, what templates are used by &lt;code&gt;draft generate-workflow&lt;/code&gt;?
&lt;/h4&gt;

&lt;p&gt;The templates used for the workflows can be found &lt;a href="https://github.com/Azure/draft/tree/main/template/workflows"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Allowing Traffic to my Application with &lt;code&gt;draft update&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;draft update&lt;/code&gt; subcommand supports enabling extensions to AKS. Currently, only &lt;a href="https://learn.microsoft.com/azure/aks/app-routing?tabs=without-osm"&gt;&lt;code&gt;webapp_routing&lt;/code&gt;&lt;/a&gt; is supported.&lt;/p&gt;

&lt;p&gt;You'll need a TLS certificate stored in Azure Key Vault to use this feature.&lt;/p&gt;

&lt;p&gt;You can find the templates for this feature &lt;a href="https://github.com/Azure/draft/tree/main/template/addons/azure/webapp_routing"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other &lt;code&gt;draft&lt;/code&gt; subcommands
&lt;/h3&gt;

&lt;p&gt;There are a few other subcommands available.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;draft completion&lt;/code&gt; will provide autocompletion helpers for your particular shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DraftTesting on  main ❯  draft completion --help
Generate the autocompletion script for draft for the specified shell.
See each sub-command's help for details on how to use the generated script.


Usage:

  draft completion [command]


Available Commands:

  bash         Generate the autocompletion script for bash
  fish         Generate the autocompletion script for fish
  powershell   Generate the autocompletion script for powershell
  zsh          Generate the autocompletion script for zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;draft info&lt;/code&gt; will dump out all the support answers for the languages/runtimes and deployment options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DraftTesting on  main ❯  draft info
{
  "supportedLanguages": [
    {
      "name": "javascript",
      "displayName": "JavaScript",
      "variableExampleValues": {
        "VERSION": [
          "10.16.3",
          "12.16.3",
          "14.15.4"
        ]
      }
    },
    {
      "name": "erlang",
      "displayName": "Erlang",
      "variableExampleValues": {
        "BUILDERVERSION": [
          "24.2-alpine"
        ],
        "VERSION": [
          "3.15"
        ]
      }
    },
    {
      "name": "go",
      "displayName": "Go",
      "variableExampleValues": {
        "VERSION": [
          "1.16",
          "1.17",
          "1.18",
          "1.19"
        ]
      }
    },
    {
      "name": "php",
      "displayName": "PHP",
      "variableExampleValues": {
        "BUILDERVERSION": [
          "1"
        ],
        "VERSION": [
          "7.1-apache"
        ]
      }
    },
    {
      "name": "gradlew",
      "displayName": "Gradle",
      "variableExampleValues": {
        "BUILDERVERSION": [
          "jdk8",
          "jdk11",
          "jdk17",
          "jdk19"
        ],
        "VERSION": [
          "8-jre",
          "11-jre",
          "17-jre",
          "19-jre"
        ]
      }
    },
    {
      "name": "java",
      "displayName": "Java",
      "variableExampleValues": {
        "BUILDERVERSION": [
          "3-jdk-11"
        ],
        "VERSION": [
          "8-jre",
          "11-jre",
          "17-jre",
          "19-jre"
        ]
      }
    },
    {
      "name": "python",
      "displayName": "Python",
      "variableExampleValues": {
        "ENTRYPOINT": [
          "app.py",
          "main.py"
        ],
        "VERSION": [
          "3.9",
          "3.8",
          "3.7",
          "3.6"
        ]
      }
    },
    {
      "name": "ruby",
      "displayName": "Ruby",
      "variableExampleValues": {
        "VERSION": [
          "3.1.2",
          "2.6",
          "2.5",
          "2.4"
        ]
      }
    },
    {
      "name": "rust",
      "displayName": "Rust",
      "variableExampleValues": {
        "VERSION": [
          "1.70.0",
          "1.65.0",
          "1.60",
          "1.54",
          "1.53"
        ]
      }
    },
    {
      "name": "clojure",
      "displayName": "Clojure",
      "variableExampleValues": {
        "VERSION": [
          "8-jdk-alpine",
          "11-jdk-alpine",
          "17-jdk-alpine",
          "19-jdk-alpine"
        ]
      }
    },
    {
      "name": "gradle",
      "displayName": "Gradle",
      "variableExampleValues": {
        "BUILDERVERSION": [
          "jdk8",
          "jdk11",
          "jdk17",
          "jdk19"
        ],
        "VERSION": [
          "8-jre",
          "11-jre",
          "17-jre",
          "19-jre"
        ]
      }
    },
    {
      "name": "swift",
      "displayName": "Swift",
      "variableExampleValues": {
        "VERSION": [
          "5.2",
          "5.5"
        ]
      }
    },
    {
      "name": "csharp",
      "displayName": "C#",
      "variableExampleValues": {
        "VERSION": [
          "3.1",
          "4.0",
          "5.0",
          "6.0"
        ]
      }
    },
    {
      "name": "gomodule",
      "displayName": "Go Module",
      "variableExampleValues": {
        "VERSION": [
          "1.16",
          "1.17",
          "1.18",
          "1.19"
        ]
      }
    }
  ],
  "supportedDeploymentTypes": [
    "manifests",
    "helm",
    "kustomize"
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, of course there's &lt;code&gt;draft version&lt;/code&gt; which will dump out the version information for the build you are using.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DraftTesting on  main ❯  draft version
version:  0.0.33
runtime SHA:  2142dbc745ac8284999f3384358680c7bc6f8570
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Try it out!
&lt;/h2&gt;

&lt;p&gt;Head over to &lt;a href="https://github.com/Azure/draft/releases"&gt;the project releases&lt;/a&gt; and grab the version for your system. Try it out and give us some feedback via Issues or drop me a comment here!&lt;/p&gt;

</description>
      <category>cloudnative</category>
      <category>beginners</category>
      <category>azure</category>
    </item>
    <item>
      <title>Getting Started with Hippo - a WebAssembly PaaS (Part 3)</title>
      <dc:creator>Steven Murawski</dc:creator>
      <pubDate>Wed, 17 Nov 2021 22:43:16 +0000</pubDate>
      <link>https://forem.com/smurawski/getting-started-with-hippo-a-webassembly-paas-part-3-1h76</link>
      <guid>https://forem.com/smurawski/getting-started-with-hippo-a-webassembly-paas-part-3-1h76</guid>
      <description>&lt;h1&gt;
  
  
  Converting an Existing Application
&lt;/h1&gt;

&lt;p&gt;With the understanding we’ve built of the runtime environment, I feel ready to start porting a simple CLI I’ve built in Rust to run in WebAssembly as a service hosted in Hippo. [The project we’ll start with is J2Y(&lt;a href="https://github.com/smurawski/j2y/tree/1-getting-started"&gt;https://github.com/smurawski/j2y/tree/1-getting-started&lt;/a&gt;) – which is a little Rust application that converts JSON to YAML or YAML to JSON. We’ll adapt this to, depending on the target, either be a CLI or a WebAssembly binary to run in WAGI. The heavy lifting of the conversion is done by the &lt;a href="https://github.com/serde-rs/json"&gt;serde-json&lt;/a&gt; and the &lt;a href="https://github.com/dtolnay/serde-yaml"&gt;serde-yaml&lt;/a&gt; crates.&lt;/p&gt;

&lt;p&gt;We’ll start out by taking a look at the existing application. We are still using VS Code with the Remote-SSH extension to connect to our dev server. The main function in main.rs ( &lt;a href="https://github.com/smurawski/j2y/blob/1-getting-started/src/main.rs"&gt;https://github.com/smurawski/j2y/blob/1-getting-started/src/main.rs&lt;/a&gt; ) module is the primary entry point for the application. The function sets up the CLI experience and resolves the incoming parameters and arguments. The application then reads the source content. Based on the direction of the conversion, it passes off the text it read to the desired conversion function. Finally, the application writes the output file in the desired serialization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Q0wuaiu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iw68ac3ebhd25khy7l0r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Q0wuaiu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iw68ac3ebhd25khy7l0r.png" alt="Visual studio code showing the main function in main.rs" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Goals
&lt;/h2&gt;

&lt;p&gt;This isn’t a treatise on how to properly structure a Rust application, but mostly to show how we can adapt an existing application to a WebAssembly module to run in Hippo. There will be plenty of opportunities to clean up the code base and refactor to make it more maintainable over time.&lt;/p&gt;

&lt;p&gt;For our web service, we’ll take a posted body of either JSON or YAML and return the opposite. We’ll use the Content-Type header to help us determine the direction of the conversion. Then, then we’ll return a response with the appropriate file content as the body.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evolving the App
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Compile to WASM
&lt;/h3&gt;

&lt;p&gt;We’ll start by adjusting our main function to behave conditionally based on whether or not it’s built as a WASM module. The &lt;code&gt;cfg!&lt;/code&gt; macro helps us here, as we can create conditions in our code based on the target family (or operating system, or target architecture) we compile for. On anything that isn’t WASM, we want to build our CLI as normal. I’ve stubbed out the return of the parameters for the function and added &lt;a href="https://github.com/smurawski/j2y/blob/2-target-family/src/main.rs#L19"&gt;a conditional based on our &lt;code&gt;target_family&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nd"&gt;cfg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_family&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"wasm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;get_wasm_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;get_cli_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nntV4dpX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ae0928trn6oyh1nam98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nntV4dpX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ae0928trn6oyh1nam98.png" alt="Editor with a conditional based on the target_family compiled for" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, ideally we’d refactor to a common branch point or push the decision logic further down into the functions like &lt;code&gt;read_content&lt;/code&gt;, but for this example, we’ll keep all the branching up in main to keep it visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working with Input from WAGI
&lt;/h3&gt;

&lt;p&gt;Let’s create a module to contain our WASM and WAGI specific behaviors. We’ll start with a function to create the tuple of arguments similar to what the &lt;code&gt;get_cli_args&lt;/code&gt; function does. Then we’ll adjust the stubbed out response in main.rs to call our new function. You can see &lt;a href="https://github.com/smurawski/j2y/commit/82730cf2272ca156e1fbdd5e835ad934293ec6ab"&gt;the full changeset here&lt;/a&gt; and the &lt;a href="https://github.com/smurawski/j2y/tree/3-wasm-args"&gt;current state of the full project&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in wasm.rs&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_wasm_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SourceFormat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// we'll default to verbose being false as it writes stdout,&lt;/span&gt;
    &lt;span class="c1"&gt;// and stdout is what becomes our response back&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// the input and output files are not used.&lt;/span&gt;
    &lt;span class="c1"&gt;// this is a good point for some future refactoring&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;output_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;input_content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HTTP_CONTENT_TYPE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The Content-Type header was not specified. Unable to convert the source content."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;source_format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_source_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input_content_type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cmpN3-E8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mgo615nvjjclngwondc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cmpN3-E8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mgo615nvjjclngwondc0.png" alt="editor with wasm.rs and the get_wasm_args function" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we learned in our experimentation in the last post, we can inspect our environment to get the incoming headers. Next, &lt;a href="https://github.com/smurawski/j2y/tree/4-read-from-wagi"&gt;we can add a function to read from standard in, which is where the body of the incoming request will be available and update &lt;code&gt;main.rs&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in wasm.rs&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;read_wagi_content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;input_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;input_content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read from standard in."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;input_content&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in main.rs&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nd"&gt;cfg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_family&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"wasm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;read_wagi_content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;read_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fUTenEfd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ym5c3njntyl0m8idahg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fUTenEfd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ym5c3njntyl0m8idahg.png" alt="editor with wasm.rs and the read_wagi_content function" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last tweak we have to make is our output. Rather than writing to a file, we’ll write to standard out. Since we are following the CGI convention, we’ll write our our headers, then a blank line, then our body. &lt;a href="https://github.com/smurawski/j2y/tree/5-write-to-wagi"&gt;We’ll update main.rs to call &lt;code&gt;write_wagi_output&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in wasm.rs&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;write_wagi_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;SourceFormat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;get_output_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_format&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in main.rs&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nd"&gt;cfg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_family&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"wasm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;write_wagi_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;output_content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;source_format&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;write_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to write the output file"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connecting with Hippo and Bindle
&lt;/h3&gt;

&lt;p&gt;Now, how do we get this app into Hippo? We can use &lt;code&gt;yo wasm&lt;/code&gt; again! We can use that to create our project in Hippo, create the &lt;code&gt;HIPPOFACTS&lt;/code&gt; file, and prep us to deploy to Bindle. There are a few files that we’ll get a conflict on, and we can just ask &lt;code&gt;yo wasm&lt;/code&gt; to leave our existing files alone.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(HINT: if you need your environment variables again before running &lt;code&gt;yo wasm&lt;/code&gt;, check &lt;code&gt;~/output.txt&lt;/code&gt;.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After &lt;code&gt;yo wasm&lt;/code&gt; is run, we'll see our app configured in Hippo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GGYNp1IR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/geuo14afdovm5be1yccu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GGYNp1IR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/geuo14afdovm5be1yccu.png" alt="new app j2y available in the hippo dashboard" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As before, we’ll build our app for the wasm32-wasi target. We can use the Hippo CLI to push the artifact to Bindle and then into Hippo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-wasi
hippo push &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that, we’ve deployed the application, we can see in the Hippo dashboard that it’s running at &lt;a href="https://admin.j2y.localhost:5003"&gt;https://admin.j2y.localhost:5003&lt;/a&gt;. Let’s make sure that 5003 is being forwarded by our VS Code Remote session.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ab6VH9MC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2wxhsdo0hibff8zd3os.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ab6VH9MC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2wxhsdo0hibff8zd3os.png" alt="Visual studio code showing port forwarding configuration" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing our App
&lt;/h3&gt;

&lt;p&gt;Now that I have that port forwarded, I can start to test that my service behaves as expected. (&lt;a href="https://github.com/smurawski/j2y/tree/6-HIPPOFACTS-and-test-scripts"&gt;If you are playing along, we are here&lt;/a&gt;) Let’s start out with some curl commands. I've created a few sample data files in a &lt;code&gt;tests&lt;/code&gt; directory (&lt;a href="https://github.com/smurawski/j2y/blob/6-HIPPOFACTS-and-test-scripts/tests/test.json"&gt;a json file&lt;/a&gt; and &lt;a href="https://github.com/smurawski/j2y/blob/6-HIPPOFACTS-and-test-scripts/tests/test.yml"&gt;a yaml file&lt;/a&gt;) that we'll use to feed in as the &lt;code&gt;POST&lt;/code&gt; body.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;tests/
&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://admin.j2y.localhost:5003/"&lt;/span&gt;
&lt;span class="c"&gt;# I expect this to fail because there is no application header&lt;/span&gt;
curl &lt;span class="nt"&gt;-vv&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; @test.json &lt;span class="nv"&gt;$url&lt;/span&gt;
&lt;span class="c"&gt;# This should succeed and return some YAML&lt;/span&gt;
curl &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; @test.json &lt;span class="nv"&gt;$url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fUf635fe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6i9dhhxf9tebdq4tozxo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fUf635fe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6i9dhhxf9tebdq4tozxo.png" alt="terminal in editor running successful curl command and getting back YAML" width="800" height="510"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This should succeed and return some JSON
curl --header "Content-Type: application/yaml" --request POST -k --data @test.yml $url

# Uh oh... Let’s look closer
curl -vv --header "Content-Type: application/yaml" --request POST -k --data @test.yml $url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JTPh_vFi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2fsxbuv0n0jm1ccaljnc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JTPh_vFi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2fsxbuv0n0jm1ccaljnc.png" alt="terminal in editor running curl command that fails with a 500 error" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dealing with Errors
&lt;/h3&gt;

&lt;p&gt;We can see that something failed in the conversion from YAML to JSON. Let’s try our CLI and make sure we didn’t break anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo run &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; YAML test.yml output.json
&lt;span class="nb"&gt;cat &lt;/span&gt;output.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EbV-WDYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7opqin0n2e3jmts9xoqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EbV-WDYK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7opqin0n2e3jmts9xoqe.png" alt="terminal in editor running cargo run to execute locally" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, that seemed to work ok. That doesn't help. I was hoping that something was wrong with my YAML file.&lt;/p&gt;

&lt;p&gt;So, how can we figure out what’s going wrong? We could introduce some custom output to help us understand where the failure was. Currently, if anything fails in the conversion between serialization formats the application panics causing a failure and a 500 response, but no helpful information. Let’s change the behavior and rather than unwrapping the conversion results in our main function, let’s pass that into our functions that actually return the results. Because each of the deserializations returns a different error (&lt;code&gt;serde_yaml::Error&lt;/code&gt; or &lt;code&gt;serde_json::Error&lt;/code&gt;, let’s add a custom error of our own to wrap those in &lt;code&gt;converter.rs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in converter.rs&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;source_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Display&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;write!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;write!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"{}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Return Status Code: {}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Source Content: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.detail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.source_content&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tWEy9SEK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nv8i8nbccd6a96pbcva9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tWEy9SEK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nv8i8nbccd6a96pbcva9.png" alt="editor showing new struct for an Error in converter.rs" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This custom error will allow us to capture different points of the failure and give us information to troubleshoot the problem. We can then wire it up into the conversion functions. You’ll see that we won’t need to change our main function just yet. (&lt;a href="https://github.com/smurawski/j2y/tree/7-add-custom-error"&gt;Find the full code for this checkpoint here&lt;/a&gt;.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in converter.rs&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;convert_json_to_yaml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Parse the string of json data into serde_yaml::Value.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;serde_yaml&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;wrapped_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Failed to read the content as JSON."&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;406&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;source_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json_str&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped_error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;yaml_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;serde_yaml&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;wrapped_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Failed to convert the JSON content into YAML."&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;source_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json_str&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped_error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After YAML conversion: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;yaml_string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yaml_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in converter.rs&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;convert_yaml_to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yaml_str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Parse the string of json data into serde_yaml::Value.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;serde_yaml&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yaml_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;wrapped_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Failed to read the content as YAML."&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;406&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;source_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;yaml_str&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped_error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;json_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;wrapped_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Failed to convert the YAML content into JSON."&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;source_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;yaml_str&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped_error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After YAML conversion: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;json_string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we add that custom error, we can wire up our output methods to use it more effectively and provide us some useful output from our Hippo service. You can see &lt;a href="https://github.com/smurawski/j2y/tree/8-custom-error-message"&gt;the full changes here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in wasm.rs&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;write_wagi_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;SourceFormat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_output_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_format&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;output_result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"text/plain"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="py"&gt;.status_code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Status: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in cli.rs&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;write_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Writing: {} &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;output_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output_result&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;File&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to create the output file."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_content&lt;/span&gt;&lt;span class="nf"&gt;.into_bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in main.rs&lt;/span&gt;

      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;output_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;source_format&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;SourceFormat&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;YAML&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;convert_yaml_to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;SourceFormat&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;convert_json_to_yaml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can build and publish our app, and then re-rerun our test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-wasi
hippo push &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;tests/
curl &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/yaml"&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; @test.yml &lt;span class="nv"&gt;$url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6-qeuMUt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fb77wwkiudlo54twofxj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6-qeuMUt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fb77wwkiudlo54twofxj.png" alt="terminal in editor with failed curl command with our custom error data." width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can see why it failed! When we passed in the YAML file, it appears that our line breaks have been lost! Fortunately, we can fix that easily by using a different switch ( &lt;code&gt;--data-binary&lt;/code&gt; rather than &lt;code&gt;--data&lt;/code&gt; ). With that change, we can see our conversion from YAML to JSON works as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--muuVpNK_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xql0wmzmfpnvgfb2suaf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--muuVpNK_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xql0wmzmfpnvgfb2suaf.png" alt="terminal in editor with a successful curl command and JSON response" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;We now have our application running as a WASM module as a service hosted on our Hippo server. From here we can continue to improve our application, explore the different hosting options inside of Hippo – testing the different deployment based on the tags assigned for example, or dive deeper into the WAGI runtime.&lt;/p&gt;

&lt;p&gt;If you’d like to try to migrate the app along the same path I did, you can start from &lt;a href="https://github.com/smurawski/j2y/tree/1-getting-started"&gt;https://github.com/smurawski/j2y/tree/1-getting-started&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have fun with WASM and Hippo!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Getting Started with Hippo - a WebAssembly PaaS (Part 2)</title>
      <dc:creator>Steven Murawski</dc:creator>
      <pubDate>Wed, 17 Nov 2021 22:41:49 +0000</pubDate>
      <link>https://forem.com/smurawski/getting-started-with-hippo-a-webassembly-paas-part-2-2ilo</link>
      <guid>https://forem.com/smurawski/getting-started-with-hippo-a-webassembly-paas-part-2-2ilo</guid>
      <description>&lt;h1&gt;
  
  
  Creating a WebAssembly App and Interacting with WAGI
&lt;/h1&gt;

&lt;p&gt;In Part 1, we defined some of the moving parts of the WASM ecosystem and set up a development server with Hippo, Bindle, and the development tooling that we’ll use to dig around with how we can interact with the WAGI runtime.&lt;/p&gt;

&lt;p&gt;We’ll start out by connecting to our development server. We could just SSH into the server (and that’s how we’ll start), but for our longer term work, we’ll move to &lt;a href="https://code.visualstudio.com/?%20WT.mc_id=containers-44762-stmuraws"&gt;Visual Studio Code&lt;/a&gt; and use &lt;a href="https://code.visualstudio.com/docs/remote/ssh?%20WT.mc_id=containers-44762-stmuraws"&gt;Remote-SSH extension&lt;/a&gt; to more effectively work on our project. Let’s get started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lhTG9Iz3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rfexphz5zll2g8s3v54b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lhTG9Iz3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rfexphz5zll2g8s3v54b.png" alt="Terminal showing the SSH connection to the development server from the local workstation" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cloud-init script left us an output log in output.txt which we can read with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;output.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ls-b0Zrj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sz5z3ie537bv9l3ardds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ls-b0Zrj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sz5z3ie537bv9l3ardds.png" alt="output from the cloud init script with environment variables to export for the next steps" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In that log, we can see that NodeJS, .NET Core, and Rust were installed, along with wasmtime, the hippo CLI, and Yeoman. The last one is what’ll get us started. I like to be organized about my projects, so I’ll create a projects directory and then a directory for helloworld (our exploratory project). There’s also some helpful exports to make the Yeoman generator a bit easier to get started with. The exports are using the same username and password what we configured in the previous post.&lt;/p&gt;

&lt;p&gt;After making the environment variables available, it’s time to create our helloworld project. We’ll use the &lt;a href="https://github.com/deislabs/yo-wasm"&gt;yo-wasm&lt;/a&gt; generator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HIPPO_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HIPPO_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Passw0rd!'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HIPPO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://localhost:5001
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BINDLE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:8080/v1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GLOBAL_AGENT_FORCE_GLOBAL_AGENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false

mkdir &lt;/span&gt;projects/helloworld
&lt;span class="nb"&gt;cd &lt;/span&gt;projects/helloworld
yo wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeoman will ask you a few questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the name of the WASM module? (the default is the current directory name – which we’ll take)&lt;/li&gt;
&lt;li&gt;What type of application is the module? (For a project with Hippo, we want “Web service or application using WAGI”)&lt;/li&gt;
&lt;li&gt;What is the name of the author? (I filled in my name)&lt;/li&gt;
&lt;li&gt;What programming language will you write the module in? (I chose Rust, because Rust is just fun)&lt;/li&gt;
&lt;li&gt;Where do you plan to publish the module? (We are going to publish it to Hippo)&lt;/li&gt;
&lt;li&gt;What is the URL of your Hippo service? (This is defaults to the value in HIPPO_URL variable that we exported earlier)&lt;/li&gt;
&lt;li&gt;What is the URL of your Hippo’s Bindle server? (This defaults to the value in the BINDLE_URL variable that we exported earlier)&lt;/li&gt;
&lt;li&gt;Would you like to create a new Hippo application for your Hippo app? (“Yes” will connect to the Hippo API with our credentials from HIPPO_USERNAME and HIPPO_PASSWORD, which are asked for in a couple of question, and create the application and several deployment environments – which we’ll look soon)&lt;/li&gt;
&lt;li&gt;What storage ID (bindle name) would you like for your Hippo app? (Since this is a dev box, we’ll accept the default of application name.localhost)&lt;/li&gt;
&lt;li&gt;Enter your Hippo user name (default is pulled from the HIPPO_USERNAME)&lt;/li&gt;
&lt;li&gt;Enter your Hippo password (default is pulled from HIPPO_PASSWORD)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iJTFkDAK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0mur3m6brlqon3dtcrld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iJTFkDAK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0mur3m6brlqon3dtcrld.png" alt="yo wasm output scaffolding our hello world project" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After asking those questions, most of which we take the defaults, Yeoman will scaffold out the project and create the application in Hippo. The project will include a basic “Hello World” application and the necessary files to publish it to Bindle and Hippo. We also get GitHub Actions workflows to make it easier to wire up CI/CD down the road.&lt;/p&gt;

&lt;p&gt;At this point, we’ll switch to VS Code and use the VS Code Remote-SSH plugin to connect to our development server. This will give us a great editor experience, simplify port forwarding for local testing, and otherwise simplify our experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Application
&lt;/h2&gt;

&lt;p&gt;We now have the bare minimum required to publish an application to Hippo. The Rust application that was scaffolded down will create a WASM file that can be run in Hippo. Let’s get that started. Since I have a new terminal, I also re-exported the environment variables from the output.txt file to have them handy. Let’s build our app targeting the wasm32-wasi runtime. Then we’ll use the hippo CLI to push the artifact and metadata to our Bindle server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-wasi
hippo push &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GQpAbKdW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5mk0xo7d0fwzpwd2ktx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GQpAbKdW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5mk0xo7d0fwzpwd2ktx.png" alt="In VS Code, building and pushing the hello world application" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our test application deployed, let’s see where that ended up.&lt;/p&gt;

&lt;p&gt;I’ve used the VS Code Remote-SSH plugin to forward a couple of ports – 5001 and 5003.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ab6VH9MC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2wxhsdo0hibff8zd3os.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ab6VH9MC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2wxhsdo0hibff8zd3os.png" alt="Visual studio code showing port forwarding configuration" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Hippo dashboard is running on 5001 and my app will be published on 5003. I’ve logged on to the dashboard at &lt;a href="https://localhost:5001"&gt;https://localhost:5001&lt;/a&gt; and entered my username and password. Now, I see my &lt;code&gt;helloworld&lt;/code&gt; app with “two channels unhealthy”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jW7PmXSc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rvc383pzozsctf1wk9jx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jW7PmXSc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rvc383pzozsctf1wk9jx.png" alt="Hippo dashboard with our hello world application configured" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s dig in further and click on &lt;code&gt;helloworld&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IpdsV6wk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1da3jjc7qouownmc5qbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IpdsV6wk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1da3jjc7qouownmc5qbo.png" alt="Hippo dashboard, application view showing a successful deployment to the development environment" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The yo-wasm generator configured three environments for our application. We can see that the app we pushed with the Hippo CLI has been published to the development environment. Let’s see how that’s configured. From the three dots to the right of the environment, we can see an option to edit.&lt;/p&gt;

&lt;p&gt;In the environment configuration, we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change the domain configured,&lt;/li&gt;
&lt;li&gt;add or edit environment variables and&lt;/li&gt;
&lt;li&gt;specify how we want our app to deploy into this environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Autodeploy is configured, with a version spec. The artifacts being looked at are stored in our local Bindle server. As new versions of the application are published, this environment will deploy any newer versions that match the version range.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gvfrh9Kc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qpy5lcsexh52mt592llu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gvfrh9Kc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qpy5lcsexh52mt592llu.png" alt="Hippo dashboard, application environment configuration" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s see what our application looks like and what content it returns. Back on the application details page, each channel has a dedicated link to that environment. Clicking on the link for the development environment and we should see “Hello, world!”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bO0r_mX7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09qxhp66ir35beulyodd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bO0r_mX7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09qxhp66ir35beulyodd.png" alt="Web browser showing our Hello, world! page." width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s go back to our editor and start exploring the runtime environment. Let’s start with seeing what environment variables are available to our WASM application. We can edit &lt;code&gt;src/main.rs&lt;/code&gt; to print out the environment variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type: text/plain&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Environment vars:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s"&gt;{}: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking closer at the output that we expect, we can see that we start by writing to standard out the Content-Type header. Then, after a blank line (the &lt;code&gt;\n&lt;/code&gt;), we can write out our content.&lt;/p&gt;

&lt;p&gt;Once we build and publish that change, we’ll use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client&amp;amp;WT.mc_id=containers-44762-stmuraws"&gt;REST Client extension&lt;/a&gt; to make it easier to validate our changes. I’ll create a test.http file and put a GET request to my development environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET https://admin.helloworld.localhost:5003/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running that should return a response with the current environment variables as seen by my application. Our response returns a list of variables and their values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Environment vars:

  PATH_INFO:
  HTTP_TRACEPARENT: 00-74fe18ddc171754089fa3bc394cfbea8-ce7be6b2abfb6847-00
  HTTP_USER_AGENT: vscode-restclient
  REMOTE_HOST: 127.0.0.1
  REMOTE_USER:
  SERVER_PROTOCOL: HTTP/1.1
  SERVER_SOFTWARE: WAGI/1
  HTTP_X_FORWARDED_FOR: 127.0.0.1
  SCRIPT_NAME: /
  X_MATCHED_ROUTE: /
  X_FULL_URL: http://127.0.0.1:32770/
  CONTENT_TYPE:
  GATEWAY_INTERFACE: CGI/1.1
  CONTENT_LENGTH: 0
  PATH_TRANSLATED:
  SERVER_NAME: 127.0.0.1
  SERVER_PORT: 32770
  HTTP_HOST: 127.0.0.1:32770
  HTTP_X_FORWARDED_HOST: admin.helloworld.localhost:5003
  REMOTE_ADDR: 127.0.0.1
  HTTP_X_FORWARDED_PROTO: https
  HTTP_ACCEPT_ENCODING: gzip, deflate
  X_RAW_PATH_INFO:
  QUERY_STRING:
  AUTH_TYPE:
  REQUEST_METHOD: GET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The environment for the process provides a lot of context around the user request. We can see where we’d find our query string parameters (in &lt;code&gt;QUERY_STRING&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://admin.helloworld.localhost:5003/?my=query&amp;amp;string=here
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Would set the QUERY_STRING environment variable to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;QUERY_STRING: my=query&amp;amp;string=here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And what about the request body? Where would we see that? The CGI standard is that would be treated as standard input. Let’s update our sample app to show how input would come in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type: text/plain&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Environment vars:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s"&gt;{}: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;input_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;input_content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read from standard in."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The request body is:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After rebuilding and pushing that to Bindle and Hippo,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-wasi
hippo push &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can make a POST request to the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://admin.helloworld.localhost:5003
Content-Type: text/plain

Hello from the request body!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dCXZIojV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oc4he1sijg7wqqoe5aoe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dCXZIojV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oc4he1sijg7wqqoe5aoe.png" alt="Post from Visual Studio Code REST extension to our service hosted in Hippo" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we'll see a response of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Environment vars:
    SCRIPT_NAME: /
    REMOTE_HOST: 127.0.0.1
    PATH_INFO:
    X_FULL_URL: http://127.0.0.1:32770/
    GATEWAY_INTERFACE: CGI/1.1
    HTTP_X_FORWARDED_HOST: admin.helloworld.localhost:5003
    SERVER_PROTOCOL: HTTP/1.1
    CONTENT_LENGTH: 28
    HTTP_X_FORWARDED_FOR: 127.0.0.1
    QUERY_STRING:
    AUTH_TYPE:
    REMOTE_USER:
    PATH_TRANSLATED:
    SERVER_PORT: 32770
    HTTP_ACCEPT_ENCODING: gzip, deflate
    HTTP_USER_AGENT: vscode-restclient
    REQUEST_METHOD: POST
    SERVER_SOFTWARE: WAGI/1
    HTTP_X_FORWARDED_PROTO: https
    HTTP_TRACEPARENT: 00-ac52510305f6fc4c94dab9ec51e4bf1d-609c9e1830b79343-00
    X_RAW_PATH_INFO:
    REMOTE_ADDR: 127.0.0.1
    SERVER_NAME: 127.0.0.1
    HTTP_CONTENT_TYPE: text/plain
    CONTENT_TYPE:
    X_MATCHED_ROUTE: /
    HTTP_CONTENT_LENGTH: 28
    HTTP_HOST: 127.0.0.1:32770

The request body is:
Hello from the request body!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve now got a way to run our application, get parameters and return ouput. There are a few other things to mention before we move to part three of this post. We can return non-200 status messages by writing the status to standard out, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Status: 404"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can set other return headers the same way. If we want to send a redirect, we can send a “Location” header and the status will be automatically set to 302 for us.&lt;/p&gt;

&lt;p&gt;Now that we have a handle on the runtime environment, let’s take a small CLI I’ve built and convert it to a WASM application.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Getting Started with Hippo - a WebAssembly PaaS (Part 1)</title>
      <dc:creator>Steven Murawski</dc:creator>
      <pubDate>Wed, 17 Nov 2021 22:39:56 +0000</pubDate>
      <link>https://forem.com/smurawski/getting-started-with-hippo-a-webassembly-paas-part-1-5470</link>
      <guid>https://forem.com/smurawski/getting-started-with-hippo-a-webassembly-paas-part-1-5470</guid>
      <description>&lt;h1&gt;
  
  
  Setting up a Development Environment
&lt;/h1&gt;

&lt;p&gt;Hey y’all, I’ve recently been dipping into the cloud native space and wanted to share one of the projects I’m looking into. In this series, we’ll set up a development environment, explore how we can interact with that environment, and then port a little CLI tool I built to run as an application in a WebAssembly PaaS called Hippo.&lt;/p&gt;

&lt;p&gt;One of the interesting developments has been the focus on WebAssembly (or WASM) as a secure and portable engine. You may have heard about WebAssembly as a way to improve the performance of web applications that run in the browser, which it can do and does well. WASM is also being looked at to run workloads in other contexts. Right out of the box, WASM brings a lightweight engine and a very strong sandbox model. There’s a standard being defined for how that sandbox can interact with the system around it, the WebAssembly System Interface (WASI). This standard paved the way for an implementation, called Wasmtime, which is now being used in a number of different projects, one of which we’ll dig into here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ...?
&lt;/h2&gt;

&lt;p&gt;Before we get too far into the weeds, there are lot of acronyms and projects, we’ve seen two already in the first paragraph and more are coming. Let’s define a few things here, so if you’re like me and take a time or two to get them straight, there’s a place to go for reference.&lt;/p&gt;

&lt;p&gt;WASM or WebAssembly, a compact, low-level language with a binary format, initially designed to run in the browser. There are a multitude of ways to get a WebAssembly file (a .wasm file for a binary output or .wat file with a textual representation). There are, according to &lt;a href="https://webassembly.org/getting-started/developers-guide/" rel="noopener noreferrer"&gt;webassmbly.org&lt;/a&gt;, twelve or thirteen programming languages that support creating WebAssembly outputs. We’ll look at using Rust in this exploration.&lt;/p&gt;

&lt;p&gt;WASI or WebAssembly System Interface, which is a standard for how the WebAssembly engine can be exposed to the underlying system resources. It offers a controlled path to system resources and requires explicit delegation of resources to the WebAssembly engine. It covers capabilities like file system access, networking, and other core operating system features (like random number generation).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/bytecodealliance/wasmtime" rel="noopener noreferrer"&gt;Wasmtime&lt;/a&gt; is an implementation of a WASM runtime (hence wasmtime) that implements the WASI standard. The project provides the C API defined by the WASI standard, as well as providing a place to experiment with new features which may end up in the standard. Wasmtime can be used in projects in a variety of languages, offering bindings for Rust, Python, Go, and .NET as part of the project and Java, Perl, Zig, and Ruby as external projects. There are two toolchains for targeting Wasmtime, one in C/C++ and one in Rust.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/deislabs/wagi" rel="noopener noreferrer"&gt;WAGI&lt;/a&gt; or the WebAssembly Gateway Interface. Since the WASI standard doesn’t have network support defined yet, WAGI takes the &lt;a href="https://datatracker.ietf.org/doc/html/rfc3875" rel="noopener noreferrer"&gt;Common Gateway Interface standard&lt;/a&gt; and provides the infrastructure to route network requests to WASM modules configured as HTTP handlers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/deislabs/bindle" rel="noopener noreferrer"&gt;Bindle&lt;/a&gt; is an aggregate object store – allowing you to create versioned artifacts and store them with any and all the files needed. Bindle provides the object store for Hippo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/deislabs/hippo" rel="noopener noreferrer"&gt;Hippo&lt;/a&gt; is a Platform-as-a-Service (PaaS) designed to provide a hosting environment that makes following cloud-native best practices easier. Hippo uses WAGI to provide a sandboxed, secure, and highly performant runtime environment with a great developer experience.&lt;/p&gt;

&lt;p&gt;Enough with the definitions and background, let’s get started building an application for WASM and hosting it on Hippo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;

&lt;p&gt;Fortunately, my colleague &lt;a href="https://github.com/scotty-c" rel="noopener noreferrer"&gt;Scott Coulton&lt;/a&gt; has made this easier for us. We can go to his &lt;a href="https://github.com/scotty-c/hippo-dev" rel="noopener noreferrer"&gt;Hippo-Dev&lt;/a&gt; repository where we have two ways to get started. First, for a local development experience, we could use &lt;a href="https://multipass.run/" rel="noopener noreferrer"&gt;Multipass&lt;/a&gt; and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; cloud-init.yaml &lt;span class="s2"&gt;"https://raw.githubusercontent.com/scotty-c/hippo-dev/blob/main/cloud-init.yaml"&lt;/span&gt;

multipass launch &lt;span class="nt"&gt;--name&lt;/span&gt; hippo-server &lt;span class="nt"&gt;--cpus&lt;/span&gt; 2 &lt;span class="nt"&gt;--mem&lt;/span&gt; 4G &lt;span class="nt"&gt;--disk&lt;/span&gt; 20G &lt;span class="nt"&gt;--cloud-init&lt;/span&gt; cloud-init.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our second option, and the route I’ll take, is to spin up a VM in Azure. We’ll need &lt;a href="https://azure.microsoft.com/free?WT.mc_id=containers-44762-stmuraws" rel="noopener noreferrer"&gt;an Azure subscription&lt;/a&gt; and the &lt;a href="https://docs.microsoft.com/cli/azure?WT.mc_id=containers-44762-stmuraws" rel="noopener noreferrer"&gt;Az CLI&lt;/a&gt;. Then,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; deploy-azure.sh &lt;span class="s2"&gt;"https://raw.githubusercontent.com/scotty-c/hippo-dev/main/deploy-azure.sh"&lt;/span&gt;

bash deploy-azure.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had to create a new resource group to start out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhrmslhinn4i1t012yk8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhrmslhinn4i1t012yk8m.png" alt="Terminal with the Az CLI command to create a new resource group."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then the script will prompt you for your subscription ID, the resource group you want to use, and the name of the VM you want to deploy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fve3x009gct8bf0x733ji.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fve3x009gct8bf0x733ji.png" alt="Terminal showing the deployment script with prompts for needed information"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In either case, the cloud-init script will handle setting up a development environment on that server, along with Hippo and Bindle servers running. After the deploy happens (and we wait a few moments for the build of Hippo to complete), we should be able to access the Hippo web portal on port 5001.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvmlr8tbxepg9iglzxur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvmlr8tbxepg9iglzxur.png" alt="Terminal with completed deployment results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s create a new user from the registration link on the login page.&lt;/p&gt;

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

&lt;p&gt;I’ll use the same login credentials as the Hippo tutorial (admin as the user and Passw0rd! for the password), as we’ll start there as we get our first deployment up and running, but that’s our next post!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0mdvgju57rlm9j499x8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0mdvgju57rlm9j499x8.png" alt="Hippo dashboard new user registration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now have a server with Hippo running and a full suite of development tools to explore this new runtime environment.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
