<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Fern on Medium]]></title>
        <description><![CDATA[Stories by Fern on Medium]]></description>
        <link>https://medium.com/@awkwardferny?source=rss-d817b99092a3------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*JxDgBWjCd0_XOo0-4HFa7A.jpeg</url>
            <title>Stories by Fern on Medium</title>
            <link>https://medium.com/@awkwardferny?source=rss-d817b99092a3------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 15 Apr 2026 06:39:38 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@awkwardferny/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[A Simple CLI to See Who’s Disappeared from Your Slack — Tracking Layoffs]]></title>
            <link>https://awkwardferny.medium.com/a-simple-cli-to-see-whos-disappeared-from-your-slack-tracking-layoffs-db4701cc6d12?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/db4701cc6d12</guid>
            <category><![CDATA[layoffs]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Sat, 04 Apr 2026 20:55:41 GMT</pubDate>
            <atom:updated>2026-04-04T20:55:41.414Z</atom:updated>
            <content:encoded><![CDATA[<p>We have seen mass layoffs in the news lately proposed by companies as “rightsizing” or “strategic realignments”. One of the most brutal experiences in tech is watching people quietly vanish from Slack. One day they’re in your channels, the next day their name is greyed out and tagged as deactivated, with no explanation and no goodbye.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4pPIyEsmEOs_wrky" /><figcaption>Photo by <a href="https://unsplash.com/@benchaccounting?utm_source=medium&amp;utm_medium=referral">Bench Accounting</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>I understand that these things happen and it is out of our control, however, one thing I strive for is transparency. I want to know who was affected so I can assist them or at least provide some of time and provide moral support.</p><p>By default, Slack Workspaces allow <a href="https://slack.com/help/articles/202035138-Add-apps-to-your-Slack-workspace">any member to install apps</a>, so your self-created app can usually be installed in that workspace. However, Workspace Owners/Admins can turn on app approval and other restrictions; in that case, your app may need explicit admin approval before install, or might be blocked entirely. <strong>You will need to be able to install a slack app to proceed.</strong></p><p>In this post I’ll show you a <a href="https://gitlab.com/fern-inc/slack-deactivation-tracker-cli">small Python CLI</a> that talks to the Slack API through Slack’s official WebClient, lists all deactivated accounts, lets you filter by last activity date, and prints everything as a clean table. Use this knowledge and application your own risk.</p><h3>How Slack represents deactivated users</h3><p>Slack doesn’t really “delete” people when they’re laid off. Their account is deactivated, their profile can be scrubbed, and admins can choose what to retain, but from the API’s perspective, they’re still part of the user list.</p><p>When you call users.list via the Slack Web API using the official Python client, you get every user in your workspace, including those with a deleted flag set to true. That flag is your signal that the account is no longer active. With the right scopes, you can also see profile fields like email addresses and, via other methods, the last time Slack saw any activity from that user.</p><p>Put simply: the raw ingredients to understand “who disappeared and roughly when” are already there. You just need a small tool to pull them together.</p><h3>The CLI: slack-deactivated-users</h3><p>Instead of building a web app and wiring up a database, the simplest workflow is a command you can run in your terminal whenever you want a snapshot.</p><p>The CLI does three things:</p><ul><li>Uses Slack underlying WebClient to call Slack’s users.list API and fetch every user in your workspace.</li><li>Filters down to those marked as deleted/deactivated.</li><li>Optionally looks up each user’s last activity timestamp (where available), applies any date filters you provided, and prints a nicely formatted table.</li></ul><p>At a high level, the main command looks like this:</p><ul><li>slack-deactivated-users --from-date 2026-01-01 Show deactivated user the from the date range.</li><li>slack-deactivated-users --from-date 2026-01-01 --to-date 2026-03-31<br>Show deactivated users whose last activity falls in that date range.</li></ul><p>This is done with a direct conversation between your terminal and Slack.</p><h3>Setting it up in your own workspace</h3><p>To get this working, you’ll need a basic understanding of Slack and python. We will start by creating a Slack app and obtaining a token, which we will then use to scrape slack for deactivate users with the CLiI</p><p><strong>Create a Slack app and get a token</strong></p><ol><li>Visit <a href="https://app.slack.com">https://app.slack.com</a> and login</li><li>Visit <a href="https://api.slack.com/apps">https://api.slack.com/apps</a></li><li>Press the <strong>Create New App</strong> button and select <strong>From scratch</strong></li><li>Provide the app with a <strong>Name</strong> and select your <strong>Workspace </strong>and press the<strong> Create App</strong> button, you’ll be transported to the App settings page</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/633/1*-CxhnpH0QIomtcDx7G5yKw.png" /><figcaption>Creating the deactivation detector app</figcaption></figure><p>5. Select <strong>OAuth &amp; Permissions</strong> in the side-tab and scroll down to <strong>Scopes</strong> and add the following to <strong>Bot Token Scopes</strong>:</p><ul><li>users:read</li><li>users:read.email (to see email addresses)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/825/1*RrDvxDP7k6BW_k6XvNlDQQ.png" /><figcaption>Bot Token Scopes</figcaption></figure><p>6. Select in <strong>Install App</strong> the side-tab and continue with the instructions to install the app into your workspace.</p><p>7. You will obtain a <strong>Bot User OAuth Token</strong>, keep it someplace safe, it will be used when running the CLI application.</p><p><strong>Install the CLI application</strong></p><p>You can clone the CLI application and then just run it in a python virtual environment and install it on your system.</p><pre># Clone the repository<br>git clone https://gitlab.com/fern-inc/slack-deactivation-tracker-cli<br>cd slack-deactivated-users<br><br># Install in a virtual environment<br>python3 -m venv .venv<br>source .venv/bin/activate<br>pip install .</pre><p><strong>Run the CLI application</strong></p><p>If an admin were to deactivate a user, as seen in this image:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Athw6YXGJTM8pS-ckI66yQ.png" /><figcaption>Admin user deactivation</figcaption></figure><p>then when running the following command, you’ll see the user was deactivated. <strong>Note: </strong>Information has been omitted for privacy reasons.</p><pre># Export the SLACK_TOKEN as an env variable<br>export SLACK_TOKEN=&lt;YOUR_TOKEN_HERE&gt;<br>slack-deactivated-users --from-date 2026-04-01<br><br> Deactivated Slack Users                                      <br>┏━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓<br>┃ User ID     ┃ Name          ┃ Email                   ┃ Bot?  ┃ Last Activity        ┃ Deleted? ┃<br>┡━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩<br>│ U085XXXXXXX │ XXXXX XXXXXX  │ xxxxxxxxxxxxx@gmail.com │ Human │ 2026-04-04 20:16 UTC │ Yes      │<br>└─────────────┴───────────────┴─────────────────────────┴───────┴──────────────────────┴──────────┘</pre><p>If you get permission errors or empty fields where you expect data, double-check your app’s scopes and the plan-level features around presence and user data.</p><p>Additionally, checkout all the possible commands by reading the <strong>README.md</strong> in <a href="https://gitlab.com/fern-inc/slack-deactivation-tracker-cli">https://gitlab.com/fern-inc/slack-deactivation-tracker-cli</a> for example, you can check who is new to slack from a particular date:</p><pre>$ slack-deactivated-users --from-date 2026-01-01 --active</pre><h3>What you can learn (and what you can’t)</h3><p>This CLI gives you a fast way to answer questions like:</p><ul><li>“How many people were deactivated around the time of that ‘small restructuring’ email?”</li><li>“Were there multiple waves of deactivations over the last quarter?”</li><li>“When was the last time that deactivated coworker actually appeared active in Slack?”</li></ul><p>The answers will never be perfect. Some deactivations are just contractors rolling off, access clean-ups, or testing. Last activity timestamps might be missing or imprecise, and Slack’s presence APIs have their own quirks and limitations depending on your plan. And there’s always a delay between when HR makes a decision and when IT flips the switch in Slack.</p><p>But when you’re living through a chaotic layoff cycle, even an approximate view of who disappeared and when can be a lot better than refreshing your channels and guessing.</p><h3>Ethics, privacy, and self‑protection</h3><p>As with any tool that touches people data, it’s important to think about how you use it.</p><ul><li><strong>You’re not discovering secret data</strong><br>The CLI surfaces information that Slack already provides through its SDK layer and Web API methods, including the fact that some users are deactivated and, in some cases, when they were last active. It’s a convenience layer, not a backdoor.</li><li><strong>Treat the output as sensitive</strong><br>A text file listing deactivated colleagues is essentially a layoff ledger. Store it securely, limit who sees it, and avoid posting it in public Slack channels or external social media.</li><li><strong>Remember what Slack can see</strong><br>Separate from this CLI, Slack and workspace admins have broad visibility into user activity and, on higher-tier plans, can export messages, logs, and other data. If you need to talk about layoffs, toxic leadership, or organizing with coworkers, consider using channels that are not owned and logged by your employer.</li></ul><p>Used thoughtfully, a little CLI like this can turn a vague sense of dread “it feels like a lot of people are disappearing” into concrete information you can use to make decisions about your own path.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=db4701cc6d12" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Go  Application Security and AppSec Automation Made Easy]]></title>
            <link>https://awkwardferny.medium.com/go-application-security-and-appsec-automation-made-easy-36bd2f3d520b?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/36bd2f3d520b</guid>
            <category><![CDATA[security]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[go]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[automation]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Thu, 22 Sep 2022 03:36:49 GMT</pubDate>
            <atom:updated>2022-09-22T03:44:49.523Z</atom:updated>
            <content:encoded><![CDATA[<p>GoLang is one of the most popular languages for cloud applications, it even builds up most of <a href="https://kubernetes.io/">Kubernetes</a> ⎈.</p><p>With that being said, according to the <a href="https://info.aquasec.com/cloud-native-threat-report-2022?keyword=cloud%20attacks&amp;campaignID=13175856474&amp;matchtype=e&amp;adgroupID=139880821361&amp;device=c&amp;utm_source=adwords&amp;utm_campaign=Threats_US&amp;utm_medium=cpc&amp;utm_term=cloud%20attacks&amp;utm_content=139880821361&amp;utm_content=596105541264&amp;hsa_acc=4069508776&amp;hsa_src=g&amp;hsa_ad=596105541264&amp;hsa_kw=cloud%20attacks&amp;hsa_ver=3&amp;hsa_mt=e&amp;hsa_grp=139880821361&amp;hsa_net=adwords&amp;hsa_cam=13175856474&amp;hsa_tgt=kwd-320040970222">Nautilus 2022 Cloud Native Threat report</a>, threat actors broadened their targets to include CI/CD environments and vulnerable Kubernetes deployments and applications.</p><p>Over time the amount and types of attacks targeting Kubernetes environments has continued to <em>increase</em>. Based on attacks that <a href="https://www.aquasec.com/">AquaSec</a> observed, the number of malicious images with the potential to target Kubernetes environments increased by <strong>10%</strong> from 2020 (<strong><em>9%</em></strong>) to 2021 (<strong><em>19%</em></strong>). This is why it’s more important than ever to secure your GoLang application!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FJfqRkEVAlvhXpih" /><figcaption>Photo by <a href="https://unsplash.com/@david_thielen?utm_source=medium&amp;utm_medium=referral">David Thielen</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>In this blog post, I will show different ways to scan your application source code for vulnerabilities, as well as how to integrate security scanners into a CI/CD platform like GitLab. I will provide real-world examples with an <a href="https://gitlab.com/awkwardferny/insecure-microservice"><strong>insecure microservice</strong></a> I have created.</p><h4><strong>Prerequisites</strong></h4><ul><li>Basic understanding of the <a href="https://go.dev/doc/tutorial/getting-started">Go programming language</a></li><li>Basic knowledge of <a href="https://www.atlassian.com/git/tutorials">Git</a></li><li>Basic understanding of <a href="https://www.udemy.com/course/introduction-to-application-security-appsec/">application security</a></li><li><a href="https://gitlab.com/">GitLab account (Free)</a></li><li><a href="https://go.dev/dl/">Go version 1.19+</a> (I used the below)</li></ul><pre>$ go version</pre><pre>go version go1.19.1 darwin/amd64</pre><h3><strong>Security Scanners</strong></h3><p>By running security scanners before pushing code, we can detect and remediate vulnerabilities before we deploy our code to a production-level environment. I will go over how to use a variety of different security scanners for Go such as <a href="https://github.com/securego/gosec"><strong><em>GoSec</em></strong></a>, <a href="https://go.dev/blog/vuln"><strong><em>GoVulnCheck</em></strong></a>, and <a href="https://go.dev/security/fuzz/"><strong><em>Fuzz</em></strong></a>.</p><p>First we can start by setting up a proper GOPATH, adding GOPATH/bin to our PATH, and cloning the <a href="https://gitlab.com/awkwardferny/insecure-microservice"><strong>insecure microservice</strong></a>. Additional information on paths can be found <a href="https://go.dev/doc/tutorial/compile-install">here</a>.</p><pre># Set the appropriate GOPATH<br>$ export GOPATH=/path/to/your/go/projects</pre><pre># Add your GOPATH bin directory to your PATH<br>$ export PATH=$PATH:$GOPATH/bin</pre><pre># Go into your GOPATH<br>$ cd $GOPATH</pre><pre># Create the proper directory structure<br>$ mkdir -p src/gitlab.com/awkwardferny</pre><pre># Clone application which we will be scanning<br>$ git clone <a href="mailto:git@gitlab.com">git@gitlab.com</a>:awkwardferny/insecure-microservice.git src/gitlab.com/awkwardferny/insecure-microservice</pre><pre># Go into the application root<br>$ cd src/gitlab.com/awkwardferny/insecure-microservice</pre><p>Now that we have the paths correctly setup and the application has been cloned, we can start running our security scanners!</p><h3><strong>GoSec (Source Code Analysis)</strong></h3><p>The first security scanner we will cover is <a href="https://github.com/securego/gosec">GoSec</a>. It is a popular Go security scanner which scans your application’s source code and dependencies for vulnerabilities. It works by pattern matching your source code against a set of rules.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*u4KNGs8dpJuHFnAMo_ylwA.png" /><figcaption>GoSec mascot 🚓</figcaption></figure><p>GoSec will also automatically scan your application <em>dependencies</em> for vulnerabilities if the go module is turned on (e.g.GO111MODULE=on) or if you explicitly download the <em>dependencies</em> (go get -d). Now let’s run GoSec on our <a href="https://gitlab.com/awkwardferny/insecure-microservice"><strong>insecure microservice</strong></a>:</p><pre># Install GoSec<br>$ go install github.com/securego/gosec/v2/cmd/gosec@latest</pre><pre># Run GoSec<br>$ gosec ./...</pre><p>After the scanner has run we can take a look at the vulnerabilities we found:</p><pre>G404 (CWE-338): Use of weak random number generator (math/rand instead of crypto/rand) (Confidence: MEDIUM, Severity: HIGH)</pre><pre>G114 (CWE): Use of net/http serve function that has no support for setting timeouts (Confidence: HIGH, Severity: MEDIUM)</pre><pre>G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)</pre><pre>G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)</pre><pre>G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)</pre><pre>G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)</pre><p>These vulnerabilities show that our application has many <em>uncaught exceptions</em>, is <em>not setting timeouts</em>, and <em>uses a weak random number generator</em>. The scan returns the <a href="https://github.com/securego/gosec#available-rules"><strong><em>Rule Triggered</em></strong></a>, <a href="https://cwe.mitre.org/"><strong><em>Common Weakness Enumeration(CWE)</em></strong></a>, <strong><em>Confidence</em></strong>, <strong><em>Severity, </em></strong>and<strong><em> </em></strong>the<strong><em> Affected Line of Code</em></strong> (Not Pictured).</p><p>In a typical developer workflow, after vulnerabilities are found, the developer can examine the <em>CWE</em> for tips on remediation, make code changes to the affected line(s) of code, and then re-run the scanner to check for resolution. Regression tests should be run to make sure our application logic is still sound.</p><h3><strong>Govulncheck</strong> <strong>(Source Code Analysis)</strong></h3><p>Next up is <a href="https://go.dev/blog/vuln">Govulncheck</a>! Govulncheck is a security scanner for source code and application dependencies. It is under active development by the Go security team and is different than GoSec in a few ways:</p><p><strong><em>First</em></strong> it is backed by the <a href="https://vuln.go.dev/">Go vulnerability database</a>.</p><p><strong><em>Second</em></strong> it only displays vulnerabilities which your code is actually calling. This reduces noise and lets you know what vulnerabilities actually affect your application.</p><p>Below is the architecture diagram for Govulncheck, showing its <em>datasources</em>, the <em>vulnerability database</em>, <em>tools</em>, and <em>integrations</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E97sm2pMfZDhw21BHGjsfQ.png" /><figcaption>Govulncheck architecture diagram</figcaption></figure><p>Now let’s give it a spin! ⚙️</p><pre># Install govulncheck<br>$ go install golang.org/x/vuln/cmd/govulncheck@latest</pre><pre># Run govulncheck<br>$ govulncheck ./...</pre><p>After the scanner has run, let’s take a look at its findings:</p><pre>Vulnerability #1: GO-2020-0016</pre><pre>An attacker can construct a series of bytes such that calling Reader. Read on the bytes could cause an infinite loop. If<br>parsing user supplied input, this may be used as a denial of service vector.</pre><pre>Call stacks in your code:</pre><pre>internal/logic/logic.go:63:8: gitlab.com/awkwardferny/insecure-microservice/internal/logic.insecure calls github.com/ulikunitz/xz.Reader.Read</pre><pre>Found in: github.com/ulikunitz/xz@v0.5.7<br>Fixed in: github.com/ulikunitz/xz@v0.5.8<br>More info: <a href="https://pkg.go.dev/vuln/GO-2020-0016">https://pkg.go.dev/vuln/GO-2020-0016</a></pre><p>You can see that the scanners presents us with a <strong><em>Vulnerability Rule Reference</em></strong>, <strong><em>Description</em></strong>, <strong><em>Affected Line of Code</em></strong>, <strong><em>Vulnerable Dependency</em></strong>, <strong><em>Resolution</em></strong>, and a <strong><em>Link to Additional Info</em></strong>.</p><p>Because I’m using <strong><em>github.com/ulikunitz/xz@v0.5.7</em></strong> as a <em>dependency</em> in my application and calling <strong><em>xz.Reader.Read</em></strong>, my application is vulnerable to <a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/">DDoS</a> attacks. This vulnerability was detected by the <a href="https://github.com/golang/vulndb/blob/master/data/reports/GO-2020-0016.yaml">GO-2020–016</a> rule from the Go vulnerability database.</p><p>In a typical workflow, a developer would update the dependency version and then rerun the scanner as well as <strong><em>unit</em></strong> and <strong><em>functional</em></strong> tests in order to ensure the application does not break.</p><h3><strong>Fuzz (Fuzz-Testing)</strong></h3><p>And last we are going to go over fuzz testing. Fuzz testing is the practice of inputing <strong><em>random/malformed</em></strong> data into an application in an attempt to reveal security issues or bugs. Go has a native fuzzing library called <a href="https://go.dev/security/fuzz/">fuzz</a>.</p><p><a href="https://go.dev/security/fuzz/">Fuzz</a> performs <strong><em>coverage-based</em></strong> fuzz tests which are written similar to <em>unit-tests</em> and are performed on application functions. They are good at finding edge-cases/bugs you may miss in your own <em>unit-tests</em>. Let’s look at this fuzz test example below:</p><pre>func FuzzAdd(f *testing.F) {<br>  f.Add(&quot;1&quot;, &quot;2&quot;)<br>  f.Fuzz(func(t *testing.T, a string, b string) {<br>    result, err := add(a, b)<br>    if err != nil {<br>      t.Errorf(fmt.Sprintf(&quot;error: %v&quot;, err))<br>    }</pre><pre>    intA, _ := strconv.Atoi(a)<br>    intB, _ := strconv.Atoi(b)<br>    expected := intA + intB</pre><pre>    if result != expected {<br>      t.Errorf(fmt.Sprintf(&quot;expected %v, got %v&quot;, expected, result))<br>    }<br>  })<br>}</pre><pre>func add(a string, b string) (c int, e error) {<br>  intA, err := strconv.Atoi(a)<br>  if err != nil {<br>    return 0, nil<br>  }</pre><pre>  intB, err := strconv.Atoi(b)<br>  if err != nil {<br>    return 0, nil<br>  }</pre><pre>  return (intA + intB), nil<br>}</pre><p>We can see that <strong><em>FuzzAdd()</em></strong> is written similar to a unit test. We enable fuzz testing by adding <strong>f.Fuzz(func(t *testing.T, a string, b string)</strong>, which calls the <strong><em>add(</em>a string, b string<em>)</em></strong> function, supplying random data for variables <strong><em>a</em></strong> and <strong><em>b</em></strong>. Then it compares the <em>result</em> against the <em>expected</em> value.</p><p>The <strong><em>add()</em></strong> function, simply converts 2 strings into integers and then adds them up and returns the result.</p><p>The<strong><em> FuzzAdd()</em></strong> test runs correctly with the <a href="https://go.dev/security/fuzz/#glos-seed-corpus"><strong><em>seeded data</em></strong></a>f.Add(&quot;1&quot;, “2”),but what happens when there’s malformed or random data? Let’s run our fuzz test and find out:</p><pre># Run the fuzz tests<br>$ go test ./internal/logic -fuzz FuzzAdd</pre><p>We can see that the scanner detected an error:</p><pre>--- FAIL: FuzzAdd (0.10s)<br>    --- FAIL: FuzzAdd (0.00s)<br>        logic_test.go:44: expected 1, got 0<br>    <br>    Failing input written to testdata/fuzz/FuzzAdd/9f4dc959af0a73c061c4b4185e9fdb9e5dbfc854cccce7bf9199f0f5556c42a9<br>    To re-run:<br>    go test -run=FuzzAdd/9f4dc959af0a73c061c4b4185e9fdb9e5dbfc854cccce7bf9199f0f5556c42a9<br>FAIL</pre><p>This error was caused because an <strong><em>actual letter (A)</em></strong> was passed instead of a string that can be converted into an integer. Fuzz also generated a <em>seed corpus</em> under the <strong><em>testdata</em></strong> directory, which can be used to test this particular failure again.</p><p>An idea to resolve this would be to simply return <strong><em>err</em></strong> instead of <strong><em>nil</em></strong> in the <strong><em>add()</em></strong> function and expect the error for non-integer convertible strings in <strong><em>FuzzAdd().</em></strong></p><p>We can also<strong><em> </em></strong>consider just making the integer value a 0 and logging the error as seen below. It Just depends on what we are trying to achieve.</p><pre>func add(a string, b string) (c int, e error) {<br>  intA, err := strconv.Atoi(a)<br>  if err != nil {<br>    // change value to 0 if error is thrown<br>    intA = 0<br>    // TODO: Log the error (err)  <br>  }</pre><pre>intB, err := strconv.Atoi(b)<br>  if err != nil {<br>    // change value to 0 if error is thrown<br>    intB = 0<br>    // TODO: Log the error (err)<br>  }</pre><pre>  return (intA + intB), nil<br>}</pre><p>For more advanced usage of fuzz, checkout the <a href="https://go.dev/doc/tutorial/fuzz">Go fuzz-testing tutorial</a>.</p><h3>Automation of Scanners with GitLab</h3><p>Running the security scanners to search for vulnerabilities in your Go application can be automated so that we can run the scanners on a <em>feature branch</em> each time code is pushed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ttWT6Mva-w6CmhnE" /><figcaption>Photo by <a href="https://unsplash.com/@minkus?utm_source=medium&amp;utm_medium=referral">Minku Kang</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>This allows us to address security issues before we push code into production and saves us time by not having to run the scanners manually each time we make a code change.</p><p>These scanners can be automated by creating a CI/CD pipeline in GitLab 🦊. The pipeline can automatically run these scans on each code push to any branch. We will be looking at the <a href="https://gitlab.com/awkwardferny/insecure-microservice/-/blob/master/.gitlab-ci.yml">GitLab CI yaml</a>, which generates a CI/CD pipeline below.</p><p>First thing we see are the stages which will run within the pipeline in the order provided:</p><pre>stages:<br>  - build<br>  - test</pre><p>The <strong>build</strong> stage makes sure the application even builds before proceeding. If you’ve containerized your application, in this stage you would ideally also test if a container image can be built as well:</p><pre>build:<br>  image: golang:alpine<br>  stage: build<br>  script:<br>    - go mod download<br>    - go build .</pre><p>Then the <strong>test</strong> stage will run <strong><em>unit-tests</em></strong>, <strong><em>fuzz-tests</em></strong>, as well as the <strong><em>security scanners</em></strong> described in this blog. The appropriate dependencies for running these jobs are also installed.</p><p>We can see under <strong>fuzz</strong>, that we have an <strong><em>artifact</em></strong> directive with a <strong><em>path</em></strong><em> </em>that runs whenever the job fails. This is done so that we can <a href="https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html#download-job-artifacts">download</a> the <em>seed corpus </em>to run locally:</p><pre>unit:<br>  image: golang:alpine<br>  stage: test<br>  script:<br>    - apk update<br>    - apk add g++ git<br>    - go test -v ./...<br><br>gosec:<br>  image: golang:alpine<br>  stage: test<br>  script:<br>    - apk update<br>    - apk add g++ git<br>    - go install github.com/securego/gosec/v2/cmd/gosec@latest<br>    - gosec ./...<br><br>go-vuln-check:<br>  image: golang:alpine<br>  stage: test<br>  script:<br>    - apk update<br>    - apk add g++ git<br>    - go install golang.org/x/vuln/cmd/govulncheck@latest<br>    - govulncheck ./...<br><br>fuzz:<br>  image: golang:alpine<br>  stage: test<br>  script:<br>    - apk update<br>    - apk add g++ git<br>    - go test ./internal/logic -fuzz FuzzAdd -fuzztime 50s<br>  artifacts:<br>    paths:<br>      - internal/logic/testdata/*<br>    when: on_failure</pre><p>All the above described in the <a href="https://gitlab.com/awkwardferny/insecure-microservice/-/blob/master/.gitlab-ci.yml">GitLab CI yaml</a>, generates the following pipeline where we can see <strong><em>fuzz</em></strong>, <strong><em>gosec</em></strong>, and <strong><em>govulncheck</em></strong> all fail, showing there are vulnerabilities and bugs detected within our code:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/874/1*2E0sq-gDjk5s1HoxlM740w.png" /><figcaption>Insecure microservice pipeline running in GitLab</figcaption></figure><p>If we click on a test we can see the output of our job. For example when clicking on the <strong><em>govulncheck</em></strong><em> </em>job, we see the following:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5B2fV6vh8sdPPx1rb8wo4g.png" /><figcaption>Govulncheck job ouput</figcaption></figure><p>And that is how you can integrate<em> unit-tests</em>, <em>fuzz-tests</em> and <em>security scanners</em> into your CI/CD pipeline. This makes life way easier and removes the need for running everything manually each time!</p><h3>Code Reviews and Secure Coding Practices</h3><p>Last, but not least, in order to enhance application security, you should always perform <em>code reviews</em>. This is crucial because others can find issues that you may miss. Scanners may find vulnerabilities, but they cannot detect incorrect logic.</p><p><a href="https://github.com/OWASP/Go-SCP">Secure Coding Practices</a> are provided by the <a href="https://owasp.org/">Open Web Application Security Project (OWASP</a>). These practices should be reviewed in order to provide great feedback on enhancing security within a code review.</p><p>Some examples of these Secure Coding Practices include <a href="https://github.com/OWASP/Go-SCP/tree/master/src/database-security">Database Security</a>, <a href="https://github.com/OWASP/Go-SCP/tree/master/src/output-encoding">Output Encoding</a>, <a href="https://github.com/OWASP/Go-SCP/blob/master/src/error-handling-logging/logging.md">Error Handling and Logging</a>, and much more.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*n1qRRMqHgmfzWvyF" /><figcaption>Photo by <a href="https://unsplash.com/@charlesdeluvio?utm_source=medium&amp;utm_medium=referral">charlesdeluvio</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Other Considerations</h3><h3><strong>Separation of duties</strong></h3><p>Another way to reduce insecure code from making it to production is to enforce <a href="https://www.totem.tech/cmmc-separation-of-duties/#:~:text=Continuing%20with%20NIST%20definitions,%20separation,privilege%20to%20perpetrate%20damaging%20fraud.">s<em>eparation of duties</em></a>. Separation of duties is the concept where developers should only have access to the functions which are necessary for their job. Some examples of this would be:</p><ul><li>Don’t allow developers to merge their own commits</li><li>Require security team or team-lead <strong><em>approval</em></strong> if a vulnerability is found</li><li>Don’t allow security scans to be disabled by developers</li><li>Implement <a href="https://docs.gitlab.com/ee/user/project/code_owners.html">CODEOWNERS</a> functionality</li></ul><h3><strong>Other attack vectors</strong></h3><p>There are other aspects of an application which can be susceptible to attack which are not part of the application source code. Some examples of this include:</p><ul><li>Container images</li><li>Application dependencies in other languages</li><li>Restrictive licenses</li><li>Configurations within the running application/server</li></ul><p>These items can be remedied with <em>additional security scanners</em> as well as <em>implementing security policies</em> and<em> providing reviews around configurations</em>. I use GitLab Ultimate security <a href="https://docs.gitlab.com/ee/user/application_security/policies/">policies</a> and <a href="https://docs.gitlab.com/ee/user/application_security/configuration/#security-testing">scanners</a> for my day-to-day.</p><h3><strong>Visibility into security posture</strong></h3><p>Another thing to consider is how great your visibility into your application’s <a href="https://csrc.nist.gov/glossary/term/security_posture#:~:text=Definition(s)%3A,react%20as%20the%20situation%20changes."><em>security posture</em></a> is. You should have insight on which projects have the most concerning vulnerabilities and what is being done about them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*fTetY-Sp3dQU01g2" /><figcaption>Photo by <a href="https://unsplash.com/@elleirva?utm_source=medium&amp;utm_medium=referral">Avrielle Suleiman</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>A dashboard type of view would be ideal, that way you can effectively triage and manage vulnerabilities, guiding you to what you should be address first.</p><p>And there you have it, Go 🐿 application security and AppSec automation made easy! Thanks for reading and I hope you enjoyed this article.</p><p>If you want to see similar articles like this, checkout <a href="https://awkwardferny.medium.com/">my other stories</a> and do share this with others! Also feel free to find me on <a href="https://twitter.com/awkwardferny">twitter</a> 🐦, my posts consist of travel, philosophy, tech, comedy, and some cool things I find.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=36bd2f3d520b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What I learned as an Engineering Director Shadow at GitLab]]></title>
            <link>https://awkwardferny.medium.com/what-i-learned-as-an-engineering-director-shadow-at-gitlab-1a783cb564d0?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/1a783cb564d0</guid>
            <category><![CDATA[management]]></category>
            <category><![CDATA[engineering-mangement]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[engineering]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Mon, 19 Sep 2022 18:41:28 GMT</pubDate>
            <atom:updated>2022-09-19T18:43:01.441Z</atom:updated>
            <content:encoded><![CDATA[<h3>What I learned as a Development Director Shadow at GitLab</h3><p>At GitLab we have a <a href="https://about.gitlab.com/handbook/engineering/development/shadow/director-shadow-program.html">development director shadow program</a> which allows any GitLab employee a deeper look into the day-to-day operations of an engineering director.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*_nWQHhOf8OmYFYZ6" /><figcaption>Photo by <a href="https://unsplash.com/@chuklanov?utm_source=medium&amp;utm_medium=referral">Avel Chuklanov</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Last year I had the opportunity to be part of GitLab’s <a href="https://awkwardferny.medium.com/4-things-ive-learned-as-a-ceo-shadow-gitlab-3dc0604f24cd">CEO shadow program</a>. I got tremendous value out of it, and learned what CEO’s do and how they operate. This year, I went ahead and signed up to shadow <a href="https://www.linkedin.com/in/waynehaber/">Wayne Haber</a>, our <strong><em>engineering director</em></strong> for <em>growth</em>, <em>security</em>, and <em>data science</em>. This experience has provided me more insight on how another part of GitLab operates.</p><p>In this program I was exposed to most of Wayne’s meetings in order to learn what an engineering director does, how they do it, and how they make decisions. Exploring more leadership positions has given me perspective on what my growth path should be.</p><h3>Starting the program</h3><p>I learned about the program by posts on the slack channel for the <a href="https://about.gitlab.com/stages-devops-lifecycle/secure/">Secure</a> stage development team. My experience started with an introduction call the week before, where I prepared for the program by:</p><ul><li>Obtaining access to Wayne’s calendar</li><li>Going over what is expected of the program</li><li>Discuss what Wayne is currently working on</li></ul><h3>Notable Meetings</h3><p>As an engineering manager shadow I attended many of Wayne’s <em>zoom</em> meetings in order to gain perspective of his day-to-day.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*yqErMa9lDTxpda3W" /><figcaption>Photo by <a href="https://unsplash.com/@campaign_creators?utm_source=medium&amp;utm_medium=referral">Campaign Creators</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>The below are some of the notable meetings I attended:</p><p><strong>InfraDev/Engineering Allocation</strong></p><p>In this meeting a variety of different VPs and engineering managers discussed <em>error budgets</em>, <em>reliability</em>, and <em>security</em>.</p><p>The SLA requirements, security issues, and corrective actions were discussed. We went over what issues are currently affecting our error budgets and must be remediated. Root causes were analyzed and then a plan was made for remediation.</p><p>This has shown me the efficiency of having all the information on a single document and then discussing proposals to correct. This makes the meeting flow much easier than not having any data beforehand.</p><p><strong>Anti-Abuse Planning</strong></p><p>In this meeting, I gained insight on limits that should be set in order to alert when we believe abuse has been performed on the system. Then we can examine <em>illegitimate</em> usage from free accounts.</p><p>I was able to learn how abuse is detected, and what we are doing to detect the new innovative ways in which malicious actors try to obtain free <em>compute</em> for crypto mining from our system.</p><p><strong>Product Meeting</strong></p><p>In this meeting, Wayne happened to be a guest speaker introducing himself and his responsibilities, counterparts, and top priorities. It was nice to understand his biggest challenges after almost 3 years at GitLab and how he handled them.</p><h3><strong>Notable Merge Requests (MRs)</strong></h3><p>I assisted with reviewing a number of <em>merge requests</em> and <em>issues</em>. Wayne would send me a few each day to show what he has been working on, so that I can provide feedback.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*lnaMKXWDI2kx-bTH" /><figcaption>Photo by <a href="https://unsplash.com/@disruptxn?utm_source=medium&amp;utm_medium=referral">Desola Lanre-Ologun</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Going through these issues allowed me to prepare for meetings and have a deeper understanding as to what is going on in the organization.</p><p><strong>Engineering Internship Working Group</strong></p><p>This MR contained information on starting a new working group for our engineering internship. I provided my thoughts on the process for establishing a working group, and what information should be provided in the handbook.</p><p><strong>ModelOps has been renamed to DataScience and Anti-Abuse has been moved in</strong></p><p>This MR showed me that some development teams were being renamed for others to better understand their purpose. <em>ModelOps</em> maybe a confusing name, but <em>data-science</em> is very clear.</p><p>Anti-Abuse was moved under DataScience since it analyzes patterns in the data in order to determine abuse.</p><p><strong>Preventing Abuse</strong></p><p>In this MR, I was able to gain some insight as to how we identify and continue to advance <em>abuse prevention</em> of GitLab pipelines.</p><p>This is a problem in many companies, due to users taking advantage of free runner cycles for cryptocurrency mining.</p><p><strong>Issues with Logging In</strong></p><p>This MR was a reported issue on certain browsers and plugins not causing errors in the GitLab login process. Although uncommon, there were still occurrences, so it is important to investigate.</p><p>I suggested in each failed login we record the <em>browser</em>,<em> add-ons</em>, and <em>OS version</em>, so that we can test compatibility and keep metrics on what browsers experience issues.</p><h3>Conclusion</h3><p>On the last day of the shadow program, I met with Wayne to discuss what I have learned and ways we can improve the program. I went ahead and suggested the following:</p><ul><li>Director should continue to report a list of merge requests and issues of interest daily — This provides the shadow with something to work on and look into each day aside from meetings</li></ul><p>I really enjoyed my time as an engineer shadow and will continue to seek guidance from Wayne! I got to see aspects of the company I would have never seen without going through this program.</p><p>Thanks for reading, I hope you enjoyed this article! If you want to see similar articles like this, checkout my other stories.</p><p>Feel free to find me on <a href="https://twitter.com/awkwardferny">twitter</a> 🐦, My posts consist of travel, philosophy, tech, comedy, and some cool things I find.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1a783cb564d0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Adding a Minimalistic ReactJS UI to Your Kubernetes Application]]></title>
            <link>https://medium.com/better-programming/adding-a-minimalistic-reactjs-ui-to-your-kubernetes-application-d4e1859d312b?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/d4e1859d312b</guid>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Tue, 05 Apr 2022 19:41:39 GMT</pubDate>
            <atom:updated>2022-04-06T17:27:29.580Z</atom:updated>
            <content:encoded><![CDATA[<h4>ReactJS meets Go</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-VTOSIA5qH5riL3d" /><figcaption>Photo by <a href="https://unsplash.com/@lautaroandreani?utm_source=medium&amp;utm_medium=referral">Lautaro Andreani</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>There are many cool server applications we can create which interact with Kubernetes using client-go. Sometimes we want to make our applications visually appealing, so that we can easily perform functions without the need to send requests via curl, postman, etc.</p><p>In this blog post, I’ll go over how to add a Web UI to a Kubernetes application. I will be building upon <a href="https://gitlab.com/k2511/secreto-server">Secreto-Server</a> which can be seen in detail in my previous blog post <a href="https://awkwardferny.medium.com/build-test-and-automate-a-kubernetes-interfacing-application-in-go-da71e4d5aaef">Build, Test, and Automate a Kubernetes Interfacing Application in Go</a>. It’s recommended that you read that blog before proceeding.</p><p>We’ll be going over the <a href="https://gitlab.com/k2511/secreto-client">Secreto-Client</a> which allows for the following functions to be provided via a web UI:</p><ul><li>Creating a Kubernetes Secret</li><li>Viewing all Kubernetes Secrets</li><li>Describing a Kubernetes Secret</li><li>Deleting a Kubernetes Secret</li></ul><p>These actions can be performed in the following ways:</p><h4>Creating a Secret</h4><p>By clicking on the ✚ button, we can see a dialog popup with a form used for inputting data in order to create a secret.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*ZTq3Ez5uT6ipksz4cMFRVw.png" /></figure><h4>Viewing all Secrets</h4><p>All secrets are displayed in cards as soon as the application is run.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/545/1*yfNsdB1QsfPvmM_eEmBEBw.png" /></figure><h4>Describing a Secret</h4><p>A secret can be described in more detail by clicking on the button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*LNmL5dASCPeKhaImCf60EA.png" /></figure><h4>Deleting a Secret</h4><p>A secret can be deleted by clicking the ❌ button.</p><p>The <a href="https://gitlab.com/k2511/secreto-client">Secreto-Client</a> generates a UI and then sends requests to the <a href="https://gitlab.com/k2511/secreto-server">Secreto-Server</a> based on the action performed.</p><h3>Prerequisites</h3><p>We will be building both a server and a client. The server code is written in Go and has its own requirements, the client is code written in ReactJS.</p><ul><li><a href="https://reactjs.org/">React-JS</a><strong>:</strong> A JavaScript library for building user interfaces. React-JS makes it easy for us to build a user interface knowing minimal nodeJS.</li><li><a href="https://go.dev/">Go</a>: An open-source programming language supported by Google</li><li><a href="https://minikube.sigs.k8s.io/docs/">MiniKube</a>: a tool that quickly sets up a local Kubernetes cluster on macOS, Linux, and Windows. You will also need a virtualization driver for MiniKube to run such as Docker, HyperV, Podman, etc. More information can be found <a href="https://minikube.sigs.k8s.io/docs/drivers/">here</a>. You can use other types of clusters (Docker Desktop Kubernetes, GKE, AKS, etc.), I’m using Minikube because it’s easily accessible.</li><li>Kubernetes Knowledge: You should know a bit about Kubernetes to understand the purpose of the application.</li></ul><p>Let’s make sure that Minikube is running before we proceed. We can do this by running the following command:</p><pre>$ minikube start</pre><pre>😄  minikube v1.25.2 on Darwin 12.3 (arm64)<br>✨  Using the podman (experimental) driver based on existing profile<br>👍  Starting control plane node minikube in cluster minikube<br>🚜  Pulling base image ...<br>E0321 11:05:07.616563   66007 cache.go:203] Error downloading kic artifacts:  not yet implemented, see issue #8426<br>🔄  Restarting existing podman container for &quot;minikube&quot; ...<br>🐳  Preparing Kubernetes v1.23.3 on Docker 20.10.12 ...E0321 11:05:13.251398   66007 start.go:126] Unable to get host IP: RoutableHostIPFromInside is currently only implemented for linux<br>▪ kubelet.housekeeping-interval=5m<br>🔎  Verifying Kubernetes components...<br>    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5<br>🌟  Enabled addons: storage-provisioner, default-storageclass<br>💡  kubectl not found. If you need it, try: &#39;minikube kubectl -- get pods -A&#39;<br>🏄  Done! kubectl is now configured to use &quot;minikube&quot; cluster and &quot;default&quot; namespace by default</pre><p>Now let’s make sure the minikube node is ready.</p><pre>$ minikube kubectl get nodes</pre><pre>NAME       STATUS   ROLES                  AGE     VERSION<br>minikube   Ready    control-plane,master   3m50s   v1.23.3</pre><h3>Loading the Secreto Server</h3><p>The backend can be loaded the following way:</p><ol><li>Clone the application to your <a href="https://go.dev/doc/gopath_code">GOPATH</a></li></ol><pre>$ git clone <a href="mailto:git@gitlab.com">git@gitlab.com</a>:k2511/secreto-server.git</pre><pre>git clone <a href="mailto:git@gitlab.com">git@gitlab.com</a>:k2511/secreto-server.git<br>Cloning into &#39;secreto-server&#39;...<br>remote: Enumerating objects: 235, done.<br>remote: Counting objects: 100% (232/232), done.<br>remote: Compressing objects: 100% (121/121), done.<br>remote: Total 235 (delta 97), reused 177 (delta 69), pack-reused 3<br>Receiving objects: 100% (235/235), 282.99 KiB | 3.11 MiB/s, done.<br>Resolving deltas: 100% (97/97), done.</pre><pre>$ cd secreto-server</pre><p>2. Build the application executable</p><p>I created a Makefile which makes it easy. Once this command is run, there should be a new executable created named <strong><em>secreto-server</em></strong>.</p><pre>$ make build</pre><pre>go mod download<br>GOOS=darwin GOARCH=arm64 go build -o secreto-server .<br>chmod +x secreto-server</pre><p><strong>Note:</strong> You may need to change $GOOS and $GOARCH variables in the <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/Makefile">Makefile</a> if you aren’t running on an M1 Mac. More details <a href="https://www.digitalocean.com/community/tutorials/building-go-applications-for-different-operating-systems-and-architectures">here</a>.</p><p>3. Run the application locally</p><p>This is done by just passing the -local flag when running the executable. Running it without the -local flag, would require the application to be running in the Kubernetes cluster because it uses a different <a href="https://github.com/kubernetes/client-go/tree/master/examples/in-cluster-client-configuration">auth method</a>.</p><pre>$ ./secreto-server -local</pre><pre>2022/03/20 16:18:30 KubeClient running with local configuration<br>2022/03/20 16:18:30 Starting server on the port 8080</pre><p>You can also change the port by setting the SECRETO_PORT environment variable before executing the program.</p><p>Now the application is running. Let’s go ahead and confirm it’s working. We can do this by opening up another terminal and sending a request to the server to obtain its version.</p><pre>$ curl <a href="http://localhost:8080/api/secreto/version">http://localhost:8080/api/secreto/version</a></pre><pre>{&quot;version&quot;:1}</pre><p>We now have a working server! Now let’s go ahead and deploy the client.</p><h3>Loading the Secreto Client</h3><p>The client can be loaded the following way:</p><ol><li>Clone the Repository</li></ol><pre>$ git clone <a href="mailto:git@gitlab.com">git@gitlab.com</a>:k2511/secreto-client.git</pre><pre>Cloning into &#39;secreto-client&#39;...<br>remote: Enumerating objects: 109, done.<br>remote: Total 109 (delta 0), reused 0 (delta 0), pack-reused 109<br>Receiving objects: 100% (109/109), 6.20 MiB | 5.46 MiB/s, done.<br>Resolving deltas: 100% (58/58), done.</pre><p>2. Download Dependencies</p><pre>$ npm install --silent<br>$ npm install react-scripts -g --silent</pre><p>3. Run the application</p><pre>$ npm start<br><br>Compiled successfully!<br><br>You can now view client in the browser.<br><br>  Local:            http://localhost:3000/<br>  On Your Network:  http://192.168.3.7:3000/<br><br>Note that the development build is not optimized.<br>To create a production build, use npm run build.</pre><p>You should now see the following screen when visiting the application page, <a href="http://localhost:3000/">localhost:3000</a>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*fn4hPZ2KNsS2KHsIl5Al6A.png" /><figcaption>Secreto Client running in my browser</figcaption></figure><p><strong>Note: </strong>The secrets displayed on your screen may very. I have been messing around with my Minikube cluster, so it looks a little different for me.</p><p>If you’d like to run the application on a different port, simply set the environment variable PORT<strong> </strong>to the port you’d like to run on, for example:</p><pre>$ PORT=3001 npm start</pre><pre>Compiled successfully!</pre><pre>You can now view client in the browser.</pre><pre>Local:            <a href="http://localhost:3001/">http://localhost:3001/</a><br>  On Your Network:  <a href="http://192.168.86.24:3001/">http://192.168.86.24:3001/</a></pre><pre>Note that the development build is not optimized.<br>To create a production build, use npm run build.</pre><h3>Creating Visuals with Semantic-UI</h3><p><a href="https://react.semantic-ui.com/">Semantic-UI</a> made it really simple for me to get started creating a minimalistic and visually appealing page. Semantic is a development framework that helps create beautiful, responsive layouts using human-friendly HTML.</p><p>An example would be generating a popup would be as follows which is the form for creating a secret. Notice the following:</p><ul><li>Popup is defined using readable HTML</li><li>Popups can be triggered by clicking on an Icon, this is seen by the trigger being the icon and the popup loading on a click</li><li>Content, which is what is displayed within the popup can point towards a function that can load more layouts. In the case of this popup, we are loading a form to input data for generating a new secret.</li></ul><pre>&lt;Popup        <br>  trigger={<br>    &lt;Icon <br>       circular name=&#39;add&#39;<br>       color=&#39;grey&#39;<br>     /&gt;<br>   }<br>               <br>   content={() =&gt; this.renderSubmitForm()}<br>   size=&#39;large&#39;<br>   on=&#39;click&#39;<br>   position=&#39;bottom center&#39;&gt;<br>&lt;/Popup&gt;</pre><p>Semantic has so many different types of objects with so many different configurations. I suggest giving their <a href="https://react.semantic-ui.com/">documentation</a> a look.</p><h3>Sending Requests with Axios</h3><p><a href="https://axios-http.com/">Axios</a> is another great tool which aided me in building this interface. Axios is a <a href="https://javascript.info/promise-basics"><em>promise-based</em></a> HTTP Client for <a href="https://nodejs.org/">node.js</a> and the browser. I used it in order to send requests to the secreto-server based on the actions taken in the ReactJs Semantic UI.</p><p>An example of using Axios is as follows seem below shows the following:</p><ul><li>Variables from the state are grabbed, which are populated by what is input into the form used for generating secrets</li><li>Axios sends a post to a global endpoint I have defined. It uses data (<em>namespace</em>) grabbed from the state in order to populate the rest of the URI</li><li>A body is passed as the 2nd parameter, containing the <em>name</em> and <em>payload</em> of the secret</li><li>Headers are passed as a 3rd parameter</li><li>We log response and reload the secrets which now contain the new secret we just generated</li></ul><pre>    createSecret = () =&gt; {<br>      const {name, namespace, payload} = this.state<br>      <br>      axios.post(this.endpoint + &quot;/api/secreto/&quot; + namespace,<br>        {<br>          &quot;name&quot;: name, <br>          &quot;payload&quot;: payload<br>        },<br>        {<br>          headers: {<br>            &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;,<br>          },<br>        },<br>        ).then(res =&gt; {<br>          console.log(res);<br>          this.getSecrets();<br>      })<br>    }</pre><p>And there you have it, Minimalist UI for a Kubernetes interfacing application. Feel free to examine the source code for both projects and dive in!</p><p>Thanks for reading, and I hope this blog can get you started with adding a cool minimalist UI to your web-server.</p><pre><strong>Want to Connect?</strong></pre><pre>You can find me on twitter <a href="https://twitter.com/awkwardferny">@awkwardferny</a></pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d4e1859d312b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/adding-a-minimalistic-reactjs-ui-to-your-kubernetes-application-d4e1859d312b">Adding a Minimalistic ReactJS UI to Your Kubernetes Application</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Build, Test, and Automate a Kubernetes Interfacing Application in Go]]></title>
            <link>https://medium.com/better-programming/build-test-and-automate-a-kubernetes-interfacing-application-in-go-da71e4d5aaef?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/da71e4d5aaef</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[kubernetes]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Tue, 05 Apr 2022 17:51:50 GMT</pubDate>
            <atom:updated>2022-04-07T01:49:22.666Z</atom:updated>
            <content:encoded><![CDATA[<h4>Learn how to create, build, test, and automate a Go application that interfaces with Kubernetes</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*jaxyB7Khvd44K8eN" /><figcaption>Photo by <a href="https://unsplash.com/@clo_shooting?utm_source=medium&amp;utm_medium=referral">Clovis Wood Photography</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Kubernetes’ <a href="https://github.com/kubernetes/client-go">Client-Go</a> provides all sorts of packages for interacting with your cluster. These packages include the following:</p><ul><li>The kubernetes package contains the clientset to access Kubernetes API.</li><li>The discovery package is used to discover APIs supported by a Kubernetes API server.</li><li>The dynamic package contains a dynamic client that can perform generic operations on arbitrary Kubernetes API objects.</li><li>The plugin/pkg/client/auth packages contain optional authentication plugins for obtaining credentials from external sources.</li><li>The transport package is used to set up auth and start a connection.</li><li>The tools/cache package is useful for writing controllers.</li></ul><p>Along with all the above packages, Client-Go also contains a fake client, which allows you to mockup the creation, reading, editing, and removal of a particular Kubernetes resource in order to easily increase unit-test coverage.</p><p>Back in KubeCon Europe 2019, I presented on performing unit tests using the Go-Client fake client:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FreDCJYbxtRg%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DreDCJYbxtRg&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FreDCJYbxtRg%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/72ceb3178726caaa0488e5dadc211a3d/href">https://medium.com/media/72ceb3178726caaa0488e5dadc211a3d/href</a></iframe><p>Today, I’d like to go over creating a simple <a href="https://github.com/kubernetes/client-go/tree/master/examples/out-of-cluster-client-configuration">out-of-cluster</a> application used to perform actions on your Kubernetes cluster. Then I’ll show you how to mock out the Kubernetes API calls in your unit tests, and automate running these tests and more with GitLab!</p><h3>Prerequisites</h3><p>In order to get started, there are a few programs you must have installed:</p><ul><li><a href="https://go.dev/">Go</a>— an open source programming language.</li><li><a href="https://minikube.sigs.k8s.io/docs/">MiniKube</a>— a tool that quickly sets up a local Kubernetes cluster on macOS, Linux, and Windows. You will also need a virtualization driver for MiniKube to run such as Docker, HyperV, Podman, etc. More information can be found <a href="https://minikube.sigs.k8s.io/docs/drivers/">here</a>.</li></ul><p><strong>Note</strong>: You can use another cluster other than minikube, and it’ll work just fine based on the kubectl context which is set. This guide is written using minikube, because it’s accessible to everyone.</p><p>You must also have knowledge of the following:</p><ul><li><a href="https://go.dev/doc/">Go Basics</a> — The code I have written and will go over is in Go. Make sure you have a basic understanding.</li><li><a href="https://kubernetes.io/docs/concepts/">Kubernetes Basics </a>— Understand how to work with Kubernetes clusters, and using kubectl.</li><li><a href="https://kubernetes.io/docs/concepts/configuration/secret/">Kubernetes Secrets</a> — Understand what secrets are in Kubernetes.</li></ul><p>It is good to know the following:</p><ul><li><a href="https://www.freecodecamp.org/news/learn-the-basics-of-git-in-under-10-minutes-da548267cc91/">Git Basics</a> — Good to know Git to create forks and make modifications to the code yourself.</li><li><a href="https://docs.gitlab.com/ee/ci/">GitLab CI</a>— My code is housed in GitLab, and I am using the GitLab CI in order to run my unit tests and security scans.</li></ul><p>I will be using my <a href="https://gitlab.com/k2511/secreto-server">secreto-server</a> project which performs the following functions:</p><ul><li>Generates generic Kubernetes secrets</li><li>Lists the secrets per a given namespace</li><li>Obtains secret data which includes the payload</li><li>Deletes secrets</li></ul><h3>Running the Application (Locally)</h3><p>To start off, we’ll create a cluster using MiniKube and make sure we can interact with Kubernetes secrets. Then we will launch the application locally and verify all the application’s functions.</p><h4>Creating Kubernetes Cluster</h4><ol><li>Install MiniKube.You’ll be able to download a version of Minikube based on your OS and Architecture on the <a href="https://minikube.sigs.k8s.io/docs/start/">Getting Started Page</a>.</li></ol><p>2. Run Minikube. It may take a few minutes to download the required packages, but running Minikube is straightforward as long as you have the required virtualization drivers. Docker Desktop works for most, but I’m using podman to try something different. See the <a href="https://minikube.sigs.k8s.io/docs/drivers/">Minikube documentation</a> for more information on drivers.</p><pre>$ minikube start</pre><pre>minikube v1.25.2 on Darwin 12.3 (arm64)<br>Using the podman (experimental) driver based on existing profile<br>Starting control plane node minikube in cluster minikube<br>Pulling base image ...<br>E0321 11:05:07.616563   66007 cache.go:203] Error downloading kic artifacts:  not yet implemented, see issue #8426<br>Restarting existing podman container for &quot;minikube&quot; ...<br>Preparing Kubernetes v1.23.3 on Docker 20.10.12 ...E0321 11:05:13.251398   66007 start.go:126] Unable to get host IP: RoutableHostIPFromInside is currently only implemented for linux<br>▪ kubelet.housekeeping-interval=5m<br>Verifying Kubernetes components...<br>    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5<br>Enabled addons: storage-provisioner, default-storageclass<br>kubectl not found. If you need it, try: &#39;minikube kubectl -- get pods -A&#39;<br>Done! kubectl is now configured to use &quot;minikube&quot; cluster and &quot;default&quot; namespace by default</pre><p>3. Verify that Minikube is running correctly. We can do this by checking if the Minikube node has a STATUS of ready.</p><pre>$ minikube kubectl get nodes</pre><pre>NAME       STATUS   ROLES                  AGE     VERSION<br>minikube   Ready    control-plane,master   3m50s   v1.23.3</pre><p>4. Create a secret. We will do this through kubectl just to verify that we have access to the secrets API. I’m creating a generic secret with the literal[<em>shhh</em>]=<em>supersecret .</em></p><pre>$ minikube kubectl create secret generic my-secret -- --from-literal=shhh=supersecret</pre><pre>secret/my-secret created</pre><p>5. Now, let’s verify that the secret was created successfully</p><p>We can grab it in YAML mode and see the base64 encoded supersecret (<em>c3VwZXJzZWNyZXQ=</em>).</p><pre>$ minikube kubectl get secrets my-secret -- -o yaml</pre><pre>minikube kubectl get secrets my-secret -- -o yaml<br>apiVersion: v1<br>data:<br>  shhh: c3VwZXJzZWNyZXQ=<br>kind: Secret<br>metadata:<br>  creationTimestamp: &quot;2022-03-20T21:16:48Z&quot;<br>  name: my-secret<br>  namespace: default<br>  resourceVersion: &quot;728&quot;<br>  uid: 9fcb7814-77f1-44dc-b476-066db11598bd<br>type: Opaque</pre><h3>Building the Application</h3><ol><li>Clone the application to your <a href="https://go.dev/doc/gopath_code">GOPATH</a></li></ol><pre>$ git clone <a href="mailto:git@gitlab.com">git@gitlab.com</a>:k2511/secreto-server.git</pre><pre>git clone <a href="mailto:git@gitlab.com">git@gitlab.com</a>:k2511/secreto-server.git<br>Cloning into &#39;secreto-server&#39;...<br>remote: Enumerating objects: 235, done.<br>remote: Counting objects: 100% (232/232), done.<br>remote: Compressing objects: 100% (121/121), done.<br>remote: Total 235 (delta 97), reused 177 (delta 69), pack-reused 3<br>Receiving objects: 100% (235/235), 282.99 KiB | 3.11 MiB/s, done.<br>Resolving deltas: 100% (97/97), done.</pre><pre>$ cd secreto-server</pre><p>2. Build the application executable. I created a Makefile which makes it easy. Once this command is run, there should be a new executable created named secreto-server.</p><pre>$ make build</pre><pre>go mod download<br>GOOS=darwin GOARCH=arm64 go build -o secreto-server .<br>chmod +x secreto-server</pre><p><strong>Note:</strong> You may need to change $GOOS and $GOARCH variables in the <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/Makefile">Makefile</a> if you aren’t running on an M1 Mac. More details <a href="https://www.digitalocean.com/community/tutorials/building-go-applications-for-different-operating-systems-and-architectures">here</a>.</p><p>3. Run the application locally. This is done by just passing the -local flag when running the executable. Running it without the -local flag, would require the application to be running in the Kubernetes cluster because it uses a different <a href="https://github.com/kubernetes/client-go/tree/master/examples/in-cluster-client-configuration">auth method</a>.</p><pre>$ ./secreto-server -local</pre><pre>2022/03/20 16:18:30 KubeClient running with local configuration<br>2022/03/20 16:18:30 Starting server on the port 8080</pre><p>You can also change the port by setting the SECRETO_PORT environment variable before executing the program.</p><p>Now the application is running. Let’s go ahead and confirm it’s working. We can do this by opening up another terminal and sending a request to the server to obtain its version.</p><pre>$ curl http://localhost:8080/api/secreto/version</pre><pre>{&quot;version&quot;:1}</pre><p>We now have a working application! Let’s verify several of the application’s functions.</p><h4>Adding a secret</h4><p>We can add a secret by passing a name and payload to the secreto API path. Make sure you also add the namespace to the end of the path as seen below.</p><pre>$ curl -X POST http://localhost:8080/api/secreto/default -d &#39;{&quot;name&quot;: &quot;my-secret2&quot;, &quot;payload&quot;: &quot;my-secret-yoo&quot;}&#39;</pre><pre>{&quot;Secret Created Successfully&quot;:{&quot;name&quot;:&quot;my-secret2&quot;,&quot;namespace&quot;:&quot;default&quot;,&quot;date&quot;:&quot;2022-03-20 17:39:41 -0500 CDT&quot;,&quot;payload&quot;:&quot;my-secret-yoo&quot;}}</pre><h4>Viewing secrets</h4><p>We can list all of our secrets, sort by namespace, and even look up the payload. This is done by performing a GET on different paths:</p><ul><li>/api/secreto: lists all secrets</li><li>/api/secreto/{namespace}: lists all secrets in {namespace}</li><li>/api/secreto/{namespace}/{name}: lists data for secret {name} in {namespace}</li></ul><pre>$ curl -X GET <a href="http://localhost/api/secrets">http://localhost:8080/api/secret</a>o</pre><pre>[{&quot;name&quot;:&quot;default-token-m9wsq&quot;,&quot;namespace&quot;:&quot;default&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:27 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;my-secret&quot;,&quot;namespace&quot;:&quot;default&quot;,&quot;date&quot;:&quot;2022-03-20 16:16:48 -0500 CDT&quot;,&quot;payload&quot;:&quot;supersecret&quot;},{&quot;name&quot;:&quot;yeet&quot;,&quot;namespace&quot;:&quot;default&quot;,&quot;date&quot;:&quot;2022-03-20 16:56:24 -0500 CDT&quot;,&quot;payload&quot;:&quot;my-secret-yoo&quot;},{&quot;name&quot;:&quot;default-token-dq5wr&quot;,&quot;namespace&quot;:&quot;kube-node-lease&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:26 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;default-token-nwbxx&quot;,&quot;namespace&quot;:&quot;kube-public&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:26 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;attachdetach-controller-token-cdfl4&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:14 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;bootstrap-signer-token-ljx9n&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:14 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;bootstrap-token-81dbvo&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;certificate-controller-token-9nqdf&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:15 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;clusterrole-aggregation-controller-token-wb95r&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;coredns-token-7sldt&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:14 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;cronjob-controller-token-l5msx&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:16 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;daemon-set-controller-token-ppr8p&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:16 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;default-token-mxzhs&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:26 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;deployment-controller-token-ctsrt&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;disruption-controller-token-kf9qs&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:14 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;endpoint-controller-token-kkp5b&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;endpointslice-controller-token-b4bwk&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;endpointslicemirroring-controller-token-g7bqq&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:15 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;ephemeral-volume-controller-token-t7s6h&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:14 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;expand-controller-token-wvhn8&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:26 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;generic-garbage-collector-token-q62cw&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:26 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;horizontal-pod-autoscaler-token-wmkcc&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;job-controller-token-9492p&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:26 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;kube-proxy-token-6z9ht&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:14 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;namespace-controller-token-lrwx8&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:15 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;node-controller-token-x9vwn&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:16 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;persistent-volume-binder-token-vdw68&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:26 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;pod-garbage-collector-token-jl9z2&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:16 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;pv-protection-controller-token-jv9d8&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;pvc-protection-controller-token-d4ccm&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;replicaset-controller-token-hbdj6&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:15 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;replication-controller-token-74kl8&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;resourcequota-controller-token-767r2&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:13 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;root-ca-cert-publisher-token-7zbhn&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:14 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;service-account-controller-token-vdxgt&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:26 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;service-controller-token-nvt8n&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:15 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;statefulset-controller-token-97d8r&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:26 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;storage-provisioner-token-nsblb&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:16 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;token-cleaner-token-wdbdn&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:15 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;ttl-after-finished-controller-token-rgjt4&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:16 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;ttl-controller-token-tzjfc&quot;,&quot;namespace&quot;:&quot;kube-system&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:14 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;}]</pre><pre>$ curl -X GET <a href="http://localhost/api/secrets/default">http://localhost:8080/api/secreto/default</a></pre><pre>[{&quot;name&quot;:&quot;default-token-m9wsq&quot;,&quot;namespace&quot;:&quot;default&quot;,&quot;date&quot;:&quot;2022-03-20 16:10:27 -0500 CDT&quot;,&quot;payload&quot;:&quot;&quot;},{&quot;name&quot;:&quot;my-secret&quot;,&quot;namespace&quot;:&quot;default&quot;,&quot;date&quot;:&quot;2022-03-20 16:16:48 -0500 CDT&quot;,&quot;payload&quot;:&quot;supersecret&quot;},{&quot;name&quot;:&quot;my-secret2&quot;,&quot;namespace&quot;:&quot;default&quot;,&quot;date&quot;:&quot;2022-03-20 16:56:24 -0500 CDT&quot;,&quot;payload&quot;:&quot;my-secret-yoo&quot;}]</pre><pre>$ curl -X GET <a href="http://localhost/api/secrets/default/yeet">http://localhost:8080/api/secreto/default/</a>my-secret</pre><pre>{&quot;name&quot;:&quot;my-secret&quot;,&quot;namespace&quot;:&quot;default&quot;,&quot;date&quot;:&quot;2022-03-20 16:16:48 -0500 CDT&quot;,&quot;payload&quot;:&quot;supersecret&quot;}</pre><h4>Deleting secrets</h4><p>Now, let’s go ahead and delete a secret we created earlier. This is done by performing a DELETE on the full path of a secret.</p><pre>$ curl -X DELETE <a href="http://localhost:8080/api/secreto/default/my-secret2">http://localhost:8080/api/secreto/default/my-secret</a></pre><pre>{&quot;Secret Deleted Successfully&quot;:&quot;my-secret&quot;}</pre><h3>Analyzing the Code</h3><p>Now let’s dive into the <a href="https://gitlab.com/k2511/secreto-server">application code</a>. I’m primarily going to go over the parts which interact with the Kubernetes API. The application is split up in the following way:</p><ul><li>middleware: Contains all the application logic for processing a request and generating a response. This includes communicating with the Kubernetes API in order to perform different functions on secrets.</li><li>router: Routes calls from particular URIs to the application logic in the middleware.</li><li>model: Contains structs that are used in the application to display and create secrets.</li><li>main.go<strong>: </strong>Starts the application, loading the web-server.</li></ul><p>Now let’s take a dive into how the source code works.</p><h4>Authentication and setup</h4><p>Authenticating with a Kubernetes client can be seen in <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/internal/server/middleware/middleware.go">middleware.go</a>. In this article, we will be going over out-of-cluster authentication.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/937e0b4f0a6ad324fa94f3cc242e0a97/href">https://medium.com/media/937e0b4f0a6ad324fa94f3cc242e0a97/href</a></iframe><p>This code was taken from the <a href="https://github.com/kubernetes/client-go/tree/master/examples/out-of-cluster-client-configuration">out-of-cluster config example</a> of Client-Go. The main parts to notice are:</p><ul><li>The Kubernetes config is set by looking at what’s active in the ~/.kube/config folder</li><li>If no home directory exists, then we must pass -kubeconfig<strong> </strong>along with the path of our Kubernetes configuration in order for it to load properly</li><li>There is a global variable ClientSet=client<strong><em> </em></strong>set so that we don’t need to keep loading the kubeconfig and can just run commands with the ClientSet</li></ul><h4>Adding a secret</h4><p>In order to add a secret, the code below accepts a request, calls the private createSecret function, and then returns the response to the user. The main parts to notice are:</p><ul><li>Sets headers for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS</a>, for example, Access-Control-Allow-Methods which will allow browsers to use different types of methods</li><li>Grabs parameters for the namespace from the URI route {namespace} defined in <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/internal/server/router/router.go">router.go</a></li><li>Generates a Kubernetes API call based on the items in the request body</li><li>Encodes either the actual secret and returns it, or returns an error</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cd505ea0baa38788b8f77ad6852d0ce4/href">https://medium.com/media/cd505ea0baa38788b8f77ad6852d0ce4/href</a></iframe><p>The code below is a private function that uses Client-Go in order to create a secret. The main parts to notice are:</p><ul><li>metav1.ObjectMeta is setup along with the secret payload which is encapsulated in the maps secretData and secretDataBytes</li><li>A Kubernetes v1.Secret based on the data sent in the function is passed to the Kubernetes API for the creation of the secret</li><li>If the generation of the secret is successful then we generate a <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/internal/server/models/models.go">Secreto object</a> and fill it with data from the secret before returning it</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8088081aa9d264b646f3bde281aa2678/href">https://medium.com/media/8088081aa9d264b646f3bde281aa2678/href</a></iframe><h4>Viewing a secret</h4><p>We have several functions used to view secrets. They grab Kubernetes&#39; secrets and their info using Client-Go. I’m just going to go over obtaining the secret details since most functions are similar. The main parts to notice are:</p><ul><li>Sets headers for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS</a>, for example, Access-Control-Allow-Methods which will allow browsers to use different types of methods</li><li>Grabs parameters for the namespace and name from the URI route {namespace}/{name} defined in <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/internal/server/router/router.go">router.go</a>, and generates a call to the Kubernetes API</li><li>Encodes either the actual secret and returns it, or returns an error</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8df97c5805c1da1b20584bb469feb284/href">https://medium.com/media/8df97c5805c1da1b20584bb469feb284/href</a></iframe><p>The code below is a private function that uses Client-Go in order to describe a secret obtaining its details with Client-Go.</p><pre>func getSecretDetails(namespace string, name string) (*v1.Secret, error) {<br>	secret, err := ClientSet.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{})<br><br>	if err != nil {<br>		return nil, err<br>	}<br><br>	return secret, nil<br>}</pre><h4>Deleting a secret</h4><p>Deleting a secret is pretty straightforward. The main parts to notice are:</p><ul><li>Sets headers for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS</a>, for example, Access-Control-Allow-Methods which will allow browsers to use different types of methods</li><li>Grabs parameters for the namespace and name from the URI route {namespace}/{name} defined in <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/internal/server/router/router.go">router.go</a>, and generates a call to the Kubernetes API</li><li>Returns a message saying that the secret was successfully deleted, or returns an error</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eb039d1395bccbc66ed2087f2f3b2555/href">https://medium.com/media/eb039d1395bccbc66ed2087f2f3b2555/href</a></iframe><p>The code below is a private function that uses Client-Go in order to delete a secret:</p><pre>func deleteSecret(name string, namespace string) error {<br>	err := ClientSet.CoreV1().Secrets(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})<br>	if err != nil {<br>		return err<br>	}<br><br>	return nil<br>}</pre><h3>Writing Unit Tests</h3><p>Now that we have seen all the different functions within the application, we are gonna go ahead and write some unit tests. Unit tests are individual tests on different parts of our application, which are important for verifying our logic and making sure our application is doing what it should.</p><p>All the unit tests I have written are located in <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/internal/server/middleware/middleware_test.go">middleware_test.go</a>.</p><h4>General setup</h4><p>I created a function that just sets up test values for the different unit tests. You can see the setupSecrets() function which will generate different secrets as well as expected values to look for.</p><h4>Mocking Client-Go</h4><p>ClientSet is a global variable defined in <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/internal/server/middleware/middleware.go"><em>middleware.go</em></a>. Within the tests, we overwrite it, allowing all the requests to return fake values without communicating to our Kubernetes cluster. The fake client makes requests returning mock objects and values.</p><pre>ClientSet = fake.NewSimpleClientset()</pre><h4>Mocking requests</h4><p>Requests can be mocked using <a href="https://pkg.go.dev/net/http/httptest">httptest</a>, which provides utilities for HTTP testing and allows us to “record” requests. A few things to notice are:</p><ul><li>A request is created and test variables are passed in the requestBody</li><li>The CreateSecret function is called with the fake w http.ResponseWriter, r *http.Request that we generated</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cdb01be09a0b15eaf506fb08dc7ea30d/href">https://medium.com/media/cdb01be09a0b15eaf506fb08dc7ea30d/href</a></iframe><h4>Running tests</h4><p>Now that we’ve gone over the tests, we can go ahead and run them. This can be done by running the following command:</p><pre>$ make test</pre><pre>go test -v ./...<br>?    gitlab.com/k2511/kube-secreto [no test files]<br>=== RUN   TestProcessSecrets<br>--- PASS: TestProcessSecrets (0.00s)<br>=== RUN   TestGetSecretsClient<br>--- PASS: TestGetSecretsClient (0.00s)<br>=== RUN   TestGetSecretDetailsClient<br>--- PASS: TestGetSecretDetailsClient (0.00s)<br>=== RUN   TestCreateSecretClient<br>--- PASS: TestCreateSecretClient (0.00s)<br>=== RUN   TestDeleteSecretClient<br>--- PASS: TestDeleteSecretClient (0.00s)<br>=== RUN   TestGetSecretsByNamespace<br>--- PASS: TestGetSecretsByNamespace (0.00s)<br>=== RUN   TestGetSecretDetails<br>--- PASS: TestGetSecretDetails (0.00s)<br>=== RUN   TestCreateSecret<br>--- PASS: TestCreateSecret (0.00s)<br>=== RUN   TestDeleteSecret<br>--- PASS: TestDeleteSecret (0.00s)<br>=== RUN   TestGetVersion<br>--- PASS: TestGetVersion (0.00s)<br>PASS<br>ok   gitlab.com/k2511/kube-secreto/internal/server/middleware 1.406s<br>?    gitlab.com/k2511/kube-secreto/internal/server/models [no test files]<br>?    gitlab.com/k2511/kube-secreto/internal/server/router [no test files]</pre><h3>Automating Testing With Coverage and Sast</h3><p>I am using GitLab for CI to automate building, testing, and pushing the application container to my registry. This makes it so that I don’t have to manually perform these functions each time I push code.</p><p>I can configure GitLab so that my application is automatically tested so I can verify my application logic. My application is also built, containerized, and pushed to my container registry. I can also check if my source code is secure using <a href="https://docs.gitlab.com/ee/user/application_security/sast/index.html">GitLab SAST</a>.</p><p>My <a href="https://gitlab.com/k2511/secreto-server/-/blob/main/.gitlab-ci.yml">GitLab Yaml</a> looks as follows:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/077e6299d4767b358a02d26a97eb1efb/href">https://medium.com/media/077e6299d4767b358a02d26a97eb1efb/href</a></iframe><p>Now, if we look at the newest running GitLab Pipeline, we can see the following:</p><ul><li>Build stage: runs build which builds the application and docker-build which builds the application container and pushes it to my container registry</li><li>Test stage: unit<em> </em>runs unit tests and generates a coverage report, gosec-sast and semgrep-sast<em> </em>uses gosec and semgrep respectively <em>to </em>scan the application source code for vulnerabilities</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/980/1*jQAfjyuka4VjFu2U2L_mKg.png" /></figure><p>When clicking on the Security tab, we can see a few vulnerabilities we should resolve, sorted by severity.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wFx9GRuj4zFKqSDJRCdcEQ.png" /></figure><p>Clicking on one provides a description, location, CVE, and solution. You can also dismiss the vulnerability or create a confidential issue to work on remediation with others without alerting those without permission.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fSLs1lwc2GbKTl1zMsNNPg.png" /></figure><p>There’s also more <a href="https://docs.gitlab.com/ee/user/application_security/">security scanners</a> you can look into as well as other cool CICD tools at GitLab.</p><p>Thanks for reading, and I hope this article can get you started with creating and testing an application that interacts with Kubernetes!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=da71e4d5aaef" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/build-test-and-automate-a-kubernetes-interfacing-application-in-go-da71e4d5aaef">Build, Test, and Automate a Kubernetes Interfacing Application in Go</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[3 Motivational speeches to help break depression and become a better you]]></title>
            <link>https://awkwardferny.medium.com/3-motivational-speeches-to-help-break-depression-and-become-a-better-you-64b03275084b?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/64b03275084b</guid>
            <category><![CDATA[depression]]></category>
            <category><![CDATA[happiness]]></category>
            <category><![CDATA[self-improvement]]></category>
            <category><![CDATA[life]]></category>
            <category><![CDATA[motivation]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Mon, 03 Jan 2022 19:21:15 GMT</pubDate>
            <atom:updated>2022-01-03T19:21:15.547Z</atom:updated>
            <content:encoded><![CDATA[<p>Life isn’t easy, there are lots of events which interfere with our happiness and well-being, however adversity causes us to grow. We not only become stronger, but also better people in general.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FIs_100UbKEPLWlW" /><figcaption>Photo by <a href="https://unsplash.com/@tegan?utm_source=medium&amp;utm_medium=referral">Tegan Mierle</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>There are times in my life when I am feeling down and depressed. I can’t find meaning in life, but I keep on and pushed myself towards a better life.</p><p>I very much believe that we are the creators of our own happiness and if we want, we do what it takes to attain it, but we need goals and meaning. We also need to be able to endure what life throws at us, since there will always be ups and downs.</p><blockquote>“Don’t take my word for it, go ahead and watch these videos”</blockquote><h3><strong>Live life like you are the hero in your own story — Joe Rogan</strong></h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FysTGb27yCcc%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DysTGb27yCcc&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FysTGb27yCcc%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/3dddac1f7954877b3cd4734732b7e82f/href">https://medium.com/media/3dddac1f7954877b3cd4734732b7e82f/href</a></iframe><p><a href="https://en.wikipedia.org/wiki/Joe_Rogan">Joe Rogan</a> is a is an American podcaster, <a href="https://en.wikipedia.org/wiki/Ultimate_Fighting_Championship">UFC</a> <a href="https://en.wikipedia.org/wiki/Color_commentator">color commentator</a>, comedian, actor, and former television presenter. You may have seen him on Fear Factor, or listened to his podcast.</p><p>The main points I take from this video are:</p><ul><li>Pretend you are the protagonist in the start of a movie where your life is a total disaster. Then progress through the movie (your life) making steps to become the hero. Get out of bed, do the dishes, clean up the house, workout, get out there and solve the problems that have been haunting you.</li><li>Write down your goals and what you need to do to attain them. Then make small progressive steps towards those goals. I like to add these steps to a calendar, in order to see my progression.</li><li>Doing difficult things lessens the stress of regular life. Don’t fear the difficult, they will make you stronger. You’ll know the situation when it comes up again, and know you survived and will again.</li><li>If you feel bad about how you have acted in the past, realize that you are not the same person, you are the person that has learned and can handle the situation better the next time. You have changed and are different person, a better person.</li></ul><h3>Have a Vision — Arnold Schwarzenegger</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fu_ktRTWMX3M%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Du_ktRTWMX3M&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fu_ktRTWMX3M%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/e649a0b0d8558051fb64b3253a92d1d7/href">https://medium.com/media/e649a0b0d8558051fb64b3253a92d1d7/href</a></iframe><p><a href="https://en.wikipedia.org/wiki/Arnold_Schwarzenegger">Arnold Schwarzenegger</a> is is an Austrian-American actor, former bodybuilder, film producer, businessman, and former politician who <a href="https://en.wikipedia.org/wiki/Political_career_of_Arnold_Schwarzenegger">served</a> as the <a href="https://en.wikipedia.org/wiki/List_of_governors_of_California">38th governor of California</a> from 2003 to 2011. As of 2021, he is the most recent <a href="https://en.wikipedia.org/wiki/Republican_Party_(United_States)">Republican</a> governor of California.</p><p>The main points I take from this video are:</p><ul><li>You need vision and goals. If you don’t have these you will aimlessly drift around in an unhappy life. You need a purpose in life to really be happy.</li><li>Work your ass off. If you are passionate about something and want to see it come to fruition, you must put in the work to make it reality. Nothing just falls in your lap, you need to do the work.</li><li>Imagine if you put 1 hour into anything. After 365 hours of reading, working out, etc. you will grow tremendously. You will regret the time spent idle.</li><li>Don’t fear failure, we all fail. What you need is to be persistent and keep trying to achieve.</li></ul><h3>Don’t Give Up — Jordan Peterson</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FdsSV3wVN_yE%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdsSV3wVN_yE&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FdsSV3wVN_yE%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/42f63ddb1249360a18baa577c6b1d263/href">https://medium.com/media/42f63ddb1249360a18baa577c6b1d263/href</a></iframe><p><a href="https://en.wikipedia.org/wiki/Jordan_Peterson">Jordan Peterson</a> is is a Canadian professor of <a href="https://en.wikipedia.org/wiki/Psychology">psychology</a>, <a href="https://en.wikipedia.org/wiki/Clinical_psychologist">clinical psychologist</a>, <a href="https://en.wikipedia.org/wiki/YouTube_personality">YouTube personality</a>, and author.</p><p>The main points I take from this video are:</p><ul><li>Life can be meaningful enough to justify it’s suffering. Life isn’t made to be happy all the time, but for you to find meaning, and be on the right path. You need to figure out what gives you meaning.</li><li>You need goals, and when you are depressed you may feel like you don’t have any. Act like you do, and hang on to something. Start with small goals and build up. For example, getting out of bed and eating healthy can be a goal.</li><li>Face small problems you know are there. Don’t let them become bigger problems. An unpaid bill that you fear looking at will only increase if you don’t take care of it. Don’t let depression make your problems bigger and contribute to more depression.</li><li>Believe in yourself. Treat yourself how you treat a person you really love. You need to be kind to yourself and believe in all the potential within you. You are necessary to more than you think, what you do matters, so respect yourself.</li></ul><p>Thanks for Reading!</p><p>I hope these motivation videos inspire you and help you get on a better path in life. I have found lots of these resources through the <a href="https://www.mulliganbrother.com/">Mulligan Brothers</a>. They have an amazing compilation of motivation videos.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=64b03275084b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Debugging your code in Python — pdb vs. rpdb]]></title>
            <link>https://itnext.io/debugging-your-code-in-python-pdb-vs-rpdb-e7bb918a8ac3?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/e7bb918a8ac3</guid>
            <category><![CDATA[debugging]]></category>
            <category><![CDATA[software-testing]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Wed, 08 Sep 2021 19:39:52 GMT</pubDate>
            <atom:updated>2021-09-09T16:57:15.088Z</atom:updated>
            <content:encoded><![CDATA[<h3>Debugging your code in Python — pdb vs. rpdb</h3><p>Debugging is an important part of resolving programming problems. There are many tools out there that are much better than print statements, although this is a <a href="https://merveilles.town/@akkartik/106138280776488247">debated topic</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Q-QjQHEaGZahyMSJ" /><figcaption>Photo by <a href="https://unsplash.com/@yoal_des?utm_source=medium&amp;utm_medium=referral">Yoal Desurmont</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Python comes with a built-in interactive debugger known as <strong>pdb</strong>.</p><p>Pdb allows you to set breakpoints and single step at the source line level, inspect stack frames, list source code, and evaluate arbitrary Python code in the context of any stack frame. Post-mortem debugging can also be performed under program control.</p><p>Then there’s <strong>rpdb</strong>, a remote debugger based on pdb. It re-routes <em>stdin</em> and <em>stdout</em> to a socket handler, so that you can debug server processes.</p><h3>When do I use pdb over rpdb?</h3><p>Pdb should be used in local applications, whereas rpdb runs remotely and can be used to debug web frameworks like Flask. With rpdb installed, you can start the server and run through the server code remotely.</p><p>Below I’ll show you both pdb and rpdb running in some simple applications, one which is running as a script, and one which starts a Flask server. Be sure to clone the <a href="https://github.com/diazjf/learn-pydebug">Debugging in Python Repo</a> to run the examples in this tutorial.</p><pre>$ git clone <a href="https://github.com/diazjf/learn-pydebug">https://github.com/diazjf/learn-pydebug</a></pre><pre>Cloning into &#39;learn-pydebug&#39;...<br>remote: Enumerating objects: 18, done.<br>remote: Counting objects: 100% (18/18), done.<br>remote: Compressing objects: 100% (14/14), done.<br>remote: Total 18 (delta 0), reused 15 (delta 0), pack-reused 0<br>Receiving objects: 100% (18/18), 103.60 KiB | 573.00 KiB/s, done.</pre><pre>$ cd learn-pydebug</pre><p><strong>Note: </strong>The <a href="https://github.com/diazjf/learn-pydebug">Debugging in Python Repo</a> has information on installing and running applications which include and show off pdb and rpdb. The code has comments on where the issue is and how debugging can help.</p><h3>Using pdb</h3><p>We add the following code to where in the application we want to start pdb. The code in <a href="https://github.com/diazjf/learn-pydebug/blob/main/pdb/main.py#L20"><strong><em>pdb/main.py</em></strong></a><strong><em> </em></strong>contains the following line:</p><pre><strong>import</strong> <strong>pdb</strong>; pdb.set_trace()</pre><p>When running the python application, pdb will automatically start when that <strong><em>pdb.set_trace()</em></strong> is hit.</p><pre>$ python pdb/main.py<br>Enter some words: I am a cat</pre><pre>&gt; /Users/fernandodiaz/Desktop/debugging/pdb/main.py(23)&lt;module&gt;()<br>-&gt; num_chars = count(input)<br>(Pdb)</pre><p>you’ll see that pdb is initiated. Pdb contains the following <a href="https://docs.python.org/3/library/pdb.html">basic functions</a>, which can be run at the prompt. Here are some examples of the output from pdb after some commands are run.</p><ul><li><strong>l(ist): Lists the lines surrounding the current line</strong></li></ul><pre>(Pdb) l</pre><pre>18       input = input(&quot;Enter some words: &quot;)<br>19<br>20       import pdb; pdb.set_trace()<br>21<br>22       # count the number if characters<br><strong>23  -&gt;     num_chars = count(input)</strong><br>24       print(&quot;Number of Characters: %s&quot; % num_chars)</pre><ul><li><strong>w(here): Displays the file and line number where we currently are</strong></li></ul><pre>(Pdb) w</pre><pre>&gt; /Users/fernandodiaz/Desktop/learn-pydebug/pdb/main.py(23)&lt;module&gt;()<br><strong>-&gt; num_chars = count(input)</strong></pre><ul><li><strong>s(tep): Step into the function at the current line</strong></li></ul><pre>(Pdb) s</pre><pre>--Call--<br>&gt; /Users/fernandodiaz/Desktop/learn-pydebug/pdb/main.py(3)count()<br><strong>-&gt; def count(message=&quot;&quot;):</strong></pre><ul><li><strong>n(ext): Execute the current line</strong></li></ul><pre>(Pdb) n</pre><pre>&gt; /Users/fernandodiaz/Desktop/learn-pydebug/pdb/main.py(4)count()<strong><br>-&gt; msg = message.split()</strong></pre><ul><li><strong>a(rgs): Print the argument list of the current function</strong></li></ul><pre>(Pdb) a</pre><pre><strong>message = &#39;I am a cat&#39;</strong></pre><ul><li><strong>p(rint) &lt;name&gt;: Print value of &lt;name&gt;</strong></li></ul><pre>(Pdb) n</pre><pre>&gt; /Users/fernandodiaz/Desktop/learn-pydebug/pdb/main.py(9)count()<br><strong>-&gt; if len(msg) &gt; 3:</strong></pre><pre>(Pdb) p msg</pre><pre><strong>[&#39;I&#39;, &#39;am&#39;, &#39;a&#39;, &#39;cat&#39;]</strong></pre><p><strong>Note:</strong> I ran all these commands in order on the pdb testing application.</p><p>By running these commands we can continue to go to the next lines in the <strong><em>count</em></strong> function until we see something wrong. In this example we can see that</p><pre>if len(msg) &gt; 3: <strong><br>  </strong>return “TOO MANY WORDS, TRY AGAIN”</pre><p>This shows is that we either need to add more details to the message to let users know what is acceptable or we can adjust the number of words to be number of chars instead. No matter how we solve it, we can now tell where the issue is.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4HWZbqfIiHNE8mb9" /><figcaption>Photo by <a href="https://unsplash.com/@sigmund?utm_source=medium&amp;utm_medium=referral">Sigmund</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Although it may not seem that impressive here, pdb is really useful in large codebases where there is alot going on.</p><h3>Using rpdb</h3><p>We add the following to where in the application we want to start rpdb:</p><pre><strong>import rpdb;</strong> rpdb.set_trace()</pre><p><strong>Note:</strong> rpdb must be installed for this command to be recognized. It is installed in the<a href="https://github.com/diazjf/learn-pydebug#prerequisites"> prerequisites</a>.</p><p>If you take a look at <a href="https://github.com/diazjf/learn-pydebug/blob/main/rpdb/remote/routes.py#L13"><strong><em>rpdp/remote/routes.py</em></strong></a>, you can see that the debugger was added to the <strong><em>/count</em></strong> route. We can run the Flask web-server by installing the <a href="https://github.com/diazjf/learn-pydebug#prerequisites">prerequisites</a> and then running:</p><pre>$ python rpdb/run.py</pre><pre>* Serving Flask app &#39;remote&#39; (lazy loading)<br> * Environment: production<br>   WARNING: This is a development server. Do not use it in a production deployment.<br>   Use a production WSGI server instead.<br> * Debug mode: on<br> * Running on all addresses.<br>   WARNING: This is a development server. Do not use it in a production deployment.<br> * Running on <a href="http://192.168.1.147:5000/"><strong>http://192.168.1.147:5000/</strong></a> (Press CTRL+C to quit)<br> * Restarting with stat<br> * Debugger is active!<br> * Debugger PIN: 926-264-267</pre><p>In a separate terminal we can send a request to the endpoint the application is running on.</p><pre>$ curl -X POST<strong> </strong><a href="http://192.168.1.147:5000/count"><strong>http://192.168.1.147:5000/count</strong></a> -d &#39;{&quot;message&quot;: &quot;This is a message&quot;}&#39;<br></pre><p>Here we are sending a message that originally caused the server to throw a 400. With the debugger present, this request causes the application to hang and allows us to go through the debugger and see what’s going on. We can do this by running the following in a separate terminal:</p><pre>$ nc 127.0.0.1 4444</pre><pre>&gt; /Users/fernandodiaz/Desktop/debugging/rpdb/remote/routes.py(15)add_note()<br>-&gt; if not msg:<br>(Pdb)</pre><p>Now we can run commands just like we did with pdb in order to determine why we get 400s with our message.</p><p>This tutorial provides the basics. There are other ways of using these debuggers, as well as more commands, which can be seen in the <a href="https://docs.python.org/3/library/pdb.html">pdb </a>and <a href="https://pypi.org/project/rpdb/">rpdb</a> documentation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*To7KRnZFq_BwzNjM" /><figcaption>Photo by <a href="https://unsplash.com/@jstrippa?utm_source=medium&amp;utm_medium=referral">James Harrison</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Thanks for reading, and I hope you enjoyed! For more content, and to keep up with what I write, follow me on <a href="https://twitter.com/awkwardferny">twitter</a>🐦</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e7bb918a8ac3" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/debugging-your-code-in-python-pdb-vs-rpdb-e7bb918a8ac3">Debugging your code in Python — pdb vs. rpdb</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting up Distributed Tracing with OpenTelemetry & Jaeger in Kubernetes Ingress-NGINX]]></title>
            <link>https://awkwardferny.medium.com/setting-up-distributed-tracing-with-opentelemetry-jaeger-in-kubernetes-ingress-nginx-cfdda7d9441d?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/cfdda7d9441d</guid>
            <category><![CDATA[debugging]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[ingress]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Tue, 27 Apr 2021 22:16:35 GMT</pubDate>
            <atom:updated>2021-05-11T17:29:03.419Z</atom:updated>
            <content:encoded><![CDATA[<h3>Setting up Distributed Tracing in Kubernetes with OpenTracing, Jaeger, and Ingress-NGINX</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FmB7t1kUAx1hlIAznrIL9A.jpeg" /><figcaption><a href="https://en.wikipedia.org/wiki/Evidence_board">Evidence Board</a> helping detectives solve mysteries. Bigfoot is missing.</figcaption></figure><p>In an age where companies like Netflix are running over 500 Microservices at once, it is important to <strong><em>quickly</em></strong> find out where exactly a failure or decrease in performance is coming from. It can be like finding a slightly discolored piece of hay in a haystack, unless something like <strong><em>Distributed Tracing</em></strong> is in place.</p><p><strong>Distributed Tracing</strong> is a way of profiling and monitoring applications. It can pinpoint the location of failures and slowdowns, helping you debug and optimize your code.</p><p>This post will provide a tutorial on getting Distributed Tracing working from ingress-nginx to your microservice functions. We will be using <a href="https://github.com/diazjf/meow-micro">Meow-Micro</a> 🐈, as the tutorial application, which was created for this very blog post.</p><h3>Prerequisites</h3><ul><li>Knowledge of <a href="https://golang.org/">GoLang</a></li><li>Knowledge of <a href="https://kubernetes.io/">Kubernetes</a></li><li>Knowledge of <a href="https://github.com/kubernetes/ingress-nginx">Ingress-NGINX</a></li><li><a href="https://www.docker.com/products/docker-desktop">Docker-Desktop</a></li><li><a href="https://helm.sh/">Helm v3</a></li></ul><p>This guide assumes that you can understand code written in Go and know how to use ingress-nginx and understand how basic Kubernetes objects such as services and deployments work.</p><p>If you want a refresher, you can checkout the following resources:</p><ul><li><a href="https://kubernetes.io/docs/tutorials/kubernetes-basics/">Kubernetes Basics</a></li><li><a href="https://awkwardferny.medium.com/getting-started-with-kubernetes-ingress-nginx-on-minikube-d75e58f52b6c">Ingress-Nginx Basics</a></li></ul><h3>Docker Desktop and Installing Ingress-Nginx</h3><p>Now let’s install <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a>, which is used for the building and sharing of containerized applications and microservices as well as running Kubernetes locally. Once you have installed Docker Desktop, go through the following steps to enable Kubernetes.</p><ol><li>Click on the <strong><em>Preferences</em></strong> Icon</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Iy6yajByaM61cA6gGMUuOQ.png" /></figure><p>2. Select the <strong><em>Kubernetes </em></strong>tab, Check the <strong><em>Enable Kubernetes</em></strong> button, and click the <strong><em>Apply &amp; Restart </em></strong>button</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sswbMT-cbskFSOBQvedFnA.png" /></figure><p>3. Press the <strong><em>Install </em></strong>button, and wait for the installation to complete</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_oBh1nq4ijpcWtGLJKuL_Q.png" /></figure><p>4. In the menu bar select <strong><em>Kubernetes</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*MpRz2uMY_DLHihM1BFGi3Q.png" /></figure><p>5. In the context select <strong><em>docker-desktop</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*gfFZAPA5S5pZAb0s_yQN6w.png" /></figure><p>6. Open the terminal and confirm you are using the correct cluster</p><pre>$ kubectl cluster-info</pre><pre>Kubernetes master is running at <a href="https://kubernetes.docker.internal:6443">https://kubernetes.docker.internal:6443</a><br>KubeDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy</pre><h4>Installing Ingress-Nginx</h4><ol><li>Now we can install Ingress-Nginx simply by running the following command as seen in the Ingress-Nginx <a href="https://kubernetes.github.io/ingress-nginx/deploy/#docker-for-mac">Getting Started Guide</a></li></ol><pre>$ kubectl apply -f <a href="https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.45.0/deploy/static/provider/cloud/deploy.yaml">https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.45.0/deploy/static/provider/cloud/deploy.yaml</a></pre><pre>namespace/ingress-nginx created<br>serviceaccount/ingress-nginx created<br>configmap/ingress-nginx-controller created<br>clusterrole.rbac.authorization.k8s.io/ingress-nginx created<br>clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created<br>role.rbac.authorization.k8s.io/ingress-nginx created<br>rolebinding.rbac.authorization.k8s.io/ingress-nginx created<br>service/ingress-nginx-controller-admission created<br>service/ingress-nginx-controller created\<br>deployment.apps/ingress-nginx-controller created<br>validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created<br>serviceaccount/ingress-nginx-admission created]<br>clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created<br>clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created<br>role.rbac.authorization.k8s.io/ingress-nginx-admission created<br>rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created<br>job.batch/ingress-nginx-admission-create created\<br>job.batch/ingress-nginx-admission-patch created</pre><p>2. We can confirm the installation by checking if the ingress-nginx-controller pod is ready and running successfully</p><pre>$ kubectl get pods -n ingress-nginx</pre><pre>NAME                                     READY   STATUS    RESTARTS </pre><pre>ingress-nginx-admission-create-52jsl        0/1     Completed   0          <br>ingress-nginx-admission-patch-78fkc         0/1     Completed   0          <br>ingress-nginx-controller-6f5454cbfb-qsfnn   1/1     Running     0          </pre><p><strong>Note:</strong> If there are memory/cpu issues, you may need to increase your resources</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8DKTmprX9JX2lNbo8XV9BA.png" /></figure><h3>Installing Jaeger and configuring the Ingress Controller</h3><p><a href="https://github.com/jaegertracing/jaeger">Jaeger</a> is a distributed tracing platform, which we will use to monitor our microservices. Now let’s install Jaeger and enable tracing at the ingress-controller level.</p><ol><li>Start by cloning <a href="https://github.com/diazjf/meow-micro">meow-micro</a>, the project which we will be using for this tutorial</li></ol><pre>$ git clone <a href="https://github.com/diazjf/meow-micro.git">https://github.com/diazjf/meow-micro.git</a></pre><pre>Cloning into &#39;meow-micro&#39;...<br>remote: Enumerating objects: 105, done.<br>...</pre><pre>$ cd meow-micro</pre><p>2. We then need to install Jaeger. I created an updated yaml for <a href="https://hub.docker.com/r/jaegertracing/all-in-one">jaeger-all-in-one</a> which can be applied as follows</p><pre>$ kubectl apply -f jaeger/jaeger-all-in-one.yaml</pre><pre>deployment.apps/jaeger created<br>service/jaeger-query created<br>service/jaeger-collector created<br>service/jaeger-agent created<br>service/zipkin created</pre><p>3. Confirm that jaeger is running and ready</p><pre>$ kubectl get pods</pre><pre>NAME                      READY   STATUS    RESTARTS   AGE<br>jaeger-6f6b5d8689-8gccp   1/1     Running   0          17s</pre><p>4. Now to enable communication between ingress-nginx and jaeger, we need to add the <strong><em>enable-opentracing </em></strong>and <strong><em>jaeger-collector-host</em></strong> keys to the <strong><em>ingress-nginx-controller </em></strong>configmap.</p><p>This tells the ingress controller to send traces to jaeger at the given endpoint, which is defined by the <strong><em>jaeger-agent</em></strong> service</p><pre>$ echo &#39;<br>  apiVersion: v1<br>  kind: ConfigMap<br>  data:<br>    enable-opentracing: &quot;true&quot;<br>    jaeger-collector-host: jaeger-agent.default.svc.cluster.local              <br>  metadata:<br>    name: ingress-nginx-controller<br>    namespace: ingress-nginx<br>  &#39; | kubectl replace -f -</pre><pre>configmap/ingress-nginx-controller replaced</pre><p>5. Confirm that the Ingress Controller has <strong><em>opentracing</em></strong> enabled and is correctly setup</p><pre>$ kubectl get pods -n ingress-nginx | grep controller</pre><pre>ingress-nginx-controller-6f5454cbfb-qptxt   1/1     Running     0          8m56s</pre><pre>$ kubectl exec -it ingress-nginx-controller-6f5454cbfb-qptxt -n ingress-nginx -- bash -c &quot;cat nginx.conf | grep ngx_http_opentracing_module.so&quot;</pre><pre>load_module /etc/nginx/modules/ngx_http_opentracing_module.so;</pre><pre>$ kubectl exec -it ingress-nginx-controller-6f5454cbfb-qptxt -n ingress-nginx -- bash -c &quot;cat nginx.conf | grep jaeger&quot;</pre><pre>opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /etc/nginx/opentracing.json;</pre><pre>$ kubectl exec -it ingress-nginx-controller-6f5454cbfb-qptxt -n ingress-nginx -- bash -c &quot;cat /etc/nginx/opentracing.json&quot;</pre><pre>{<br>  &quot;service_name&quot;: &quot;nginx&quot;,<br>  &quot;propagation_format&quot;: &quot;jaeger&quot;,<br>  &quot;sampler&quot;: {<br>    &quot;type&quot;: &quot;const&quot;,<br>    &quot;param&quot;: 1,<br>    &quot;samplingServerURL&quot;: &quot;http://127.0.0.1:5778/sampling&quot;<br>  },<br>  &quot;reporter&quot;: {<br>    &quot;endpoint&quot;: &quot;&quot;,<br>    &quot;localAgentHostPort&quot;: &quot;jaeger-agent.default.svc.cluster.local:6831&quot;<br>  },<br>  &quot;headers&quot;: {<br>    &quot;TraceContextHeaderName&quot;: &quot;&quot;,<br>    &quot;jaegerDebugHeader&quot;: &quot;&quot;,<br>    &quot;jaegerBaggageHeader&quot;: &quot;&quot;,<br>    &quot;traceBaggageHeaderPrefix&quot;: &quot;&quot;<br>  }<br>}</pre><p>Now we have both jaeger and ingress running on our cluster. We can then deploy our microservices!</p><h3>Deploy Microservices with Instrumentation</h3><p>Instrumentation is added to our microservices to extract important information about the microservices. Instrumentation is required for us to be able to perform traces and map out specific functions.</p><p>In this section we will deploy 2 different Microservices from our sample project <a href="https://github.com/diazjf/meow-micro"><strong>meow-micro</strong></a><strong> 🐈</strong> which contain the OpenTracing Instrumentation. The <strong><em>meow-client </em></strong>microservice will accept a REST call and send the information to the <strong><em>meow-server </em></strong>microservice via GRPC.</p><p>For a refresh on REST and GRPC with GoLang see:</p><ul><li><a href="https://tutorialedge.net/golang/creating-restful-api-with-golang/">GoLang HTTP Server</a></li><li><a href="https://tutorialedge.net/golang/go-grpc-beginners-tutorial/">GoLang GRPC Communication</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*bBMoqsCm3mAloUwU" /><figcaption>Photo by <a href="https://unsplash.com/@solomac?utm_source=medium&amp;utm_medium=referral">Adam Solomon</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Now let’s go over some of the files which instrumentation was added to.</p><h4><a href="https://github.com/diazjf/meow-micro/blob/main/tracing/tracing.go">tracing.go</a></h4><p>Contains the information for configuring the Jaeger tracer. It grabs the configuration values from the environment, which are set in the helm templates for <a href="https://github.com/diazjf/meow-micro/blob/main/helm/templates/meow-client.yaml">meow-client</a> and <a href="https://github.com/diazjf/meow-micro/blob/main/helm/templates/meow-server.yaml">meow-server.</a></p><pre>cfg, err := config.FromEnv()<br>if err != nil {<br>panic(fmt.Sprintf(&quot;Could not parse Jaeger env vars: %s&quot;, err.Error())) <br>}  </pre><pre>tracer, closer, err := cfg.NewTracer()<br>if err != nil {  <br> panic(fmt.Sprintf(&quot;Could not initialize jaeger tracer: %s&quot;, err.Error())) <br>}</pre><h4><a href="https://github.com/diazjf/meow-micro/blob/main/client/client.go">client.go</a></h4><p>In the client, we setup the<strong> service name</strong> for the trace(<a href="https://www.jaegertracing.io/docs/1.22/architecture/#trace">a data/execution path through the system, and can be thought of as a directed acyclic graph of spans</a>) to be <strong>meow-client.</strong></p><p>We also setup the <strong>span</strong> (<a href="https://www.jaegertracing.io/docs/1.22/architecture/#span">a logical unit of work in Jaeger that has an operation name, the start time of the operation, and the duration</a>) to start when the <strong><em>main</em></strong> function is called, as well as when the <strong><em>sleep</em></strong> function is called. This will let us know how long <strong><em>sleep</em></strong> took within <strong><em>main, </em></strong>since there is a span for each one<strong><em>.</em></strong></p><pre>// main function span<br>os.Setenv(&quot;JAEGER_SERVICE_NAME&quot;, &quot;meow-client&quot;)<br>tracer, closer := tracing.Init()<br>defer closer.Close()</pre><pre>http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {</pre><pre>spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))<br>span := tracer.StartSpan(&quot;send-meow-communication&quot;, ext.RPCServerOption(spanCtx))<br>defer span.Finish()</pre><pre>...</pre><pre>// sleep function span<br>os.Setenv(&quot;JAEGER_SERVICE_NAME&quot;, &quot;meow-client&quot;)<br>tracer, closer := tracing.Init()<br>defer closer.Close()</pre><pre>span := tracer.StartSpan(&quot;sleep&quot;)<br>defer span.Finish()</pre><h4>Installing the microservices</h4><p>Now let’s go ahead and deploy the microservices into our cluster. Make sure you have <a href="https://helm.sh/docs/intro/install/">helm v3</a> installed, I installed mine using brew.</p><pre>$ brew install helm<br>...<br>==&gt; Downloading <a href="https://ghcr.io/v2/homebrew/core/helm/manifests/3.5.4">https://ghcr.io/v2/homebrew/core/helm/manifests/3.5.4</a><br>######################################################################## 100.0%<br>==&gt; Downloading <a href="https://ghcr.io/v2/homebrew/core/helm/blobs/sha256:5dac5803c1ad2db3a91b0928fc472aaf80a4">https://ghcr.io/v2/homebrew/core/helm/blobs/sha256:5dac5803c1ad2db3a91b0928fc472aaf80a4</a><br>==&gt; Downloading from <a href="https://pkg-containers-az.githubusercontent.com/ghcr1/blobs/sha256:5dac5803c1ad2db">https://pkg-containers-az.githubusercontent.com/ghcr1/blobs/sha256:5dac5803c1ad2db</a><br>######################################################################## 100.0%<br>==&gt; Pouring helm--3.5.4.big_sur.bottle.tar.gz<br>...</pre><pre>$ helm version<br>version.BuildInfo{Version:&quot;v3.3.4&quot;, GitCommit:&quot;a61ce5633af99708171414353ed49547cf05013d&quot;, GitTreeState:&quot;clean&quot;, GoVersion:&quot;go1.14.9&quot;}</pre><p>Now let’s deploy these microservices onto our Kubernetes Cluster using the Makefile.</p><pre># Build client and server from Dockerfile<br>$ make build</pre><pre>docker build -t meow-client:1.0 -f client/Dockerfile .<br>[+] Building 17.1s (10/10) FINISHED<br>...<br>docker build -t meow-server:1.0 -f server/Dockerfile .<br>[+] Building 0.5s (10/10) FINISHED<br>...</pre><pre># Install Microservices into Kubernetes via Helm<br>$ make install</pre><pre>helm install -f helm/Values.yaml meow-micro ./helm<br>NAME: meow-micro<br>LAST DEPLOYED: Mon Apr 26 13:42:38 2021<br>NAMESPACE: default<br>STATUS: deployed<br>REVISION: 1<br>TEST SUITE: None</pre><p>We can verify that everything is working by making sure the pods are running and ready.</p><pre>$ kubectl get pods</pre><pre>NAME                           READY   STATUS    RESTARTS   AGE<br>jaeger-6f6b5d8689-s7cln        1/1     Running   0          26m<br>meow-client-8b974778c-85896    1/1     Running   0          15m<br>meow-server-56f559db44-5mvgp   1/1     Running   0          15m</pre><h3>Viewing the Trace</h3><p>Now for the exciting part! Taking a look at a trace.</p><ol><li>Let’s open up the Jaeger console, by pointing our browser to <a href="http://localhost:8081">http://localhost:8081</a>. This is where we have the jaeger UI running. You should see a cool interface as follows</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oJUxzwN2L06JReXqb0xArA.png" /></figure><p>2. Now let’s send a request to our application, so that the trace can be logged</p><pre>$ curl <a href="http://localhost/meow">http://localhost/meow</a> -X POST -d &#39;{&quot;name&quot;: &quot;Meow-Mixer&quot;}&#39;</pre><pre>200 - Meow sent: Meow-Mixer</pre><p>3. Now refresh your browser and you should see populated tabs. We can select <strong>nginx </strong>as the service</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xRX43FMs4Cm-g9GvakBHAw.png" /></figure><p>4. Then press the <strong>Find Traces</strong> button</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UkECdqWcy1Qz9NVuMI5OyQ.png" /></figure><p>5. Now you should see all the traces for <strong>nginx</strong>, we can expand by clicking anywhere in the trace item</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8MtHv0edah2iQSR_NMEVZw.png" /></figure><p>6. You can see how long each function took, from the call to <strong><em>nginx</em></strong>(ingress) ➡️ <strong><em>main</em></strong>(client) ➡️ <strong><em>sleep</em></strong>(client). This gives us information on how long we were stuck in each span. We see that we were a whole 2 out of 3 seconds in the sleep, telling us it should be something to examine.</p><p>This is a cool little example to get you started, but you see the real power of tracing when you add this to several functions across your microservices.</p><p>You can further expand an operation in the trace to obtain more information</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mJrLTCW7hoFg7u5das5xVw.png" /></figure><p>7. Here we can see more info on the sleep function</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dgF4cTesAY8bfcmxMpbN0Q.png" /></figure><h3>Other Tracers and Configurations</h3><p>Currently Zipkin, Jaeger, and DataDog are supported for Distributed Tracing with Ingress-Nginx.</p><p>These different Distributed Tracing items in Ingress-Nginx that can be further configured for the supported Tracers. These can be found in the <a href="https://kubernetes.github.io/ingress-nginx/user-guide/third-party-addons/opentracing/">Ingress-Nginx documentation</a>.</p><p>Hope you enjoyed this blog post! For more information on Distributed Tracing, be sure to checkout the <a href="https://www.jaegertracing.io/docs/1.22/">Jaeger Documentation</a> which contains lots of cool resources.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Il-Dh7V_8JfMRQSTRq7-CQ@2x.jpeg" /></figure><p>Also be sure to checkout my Medium profile for more content like this and feel free to follow <a href="https://twitter.com/awkwardferny">@awkwardferny</a> on Twitter.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cfdda7d9441d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making Great Technical Marketing Videos for YouTube — Tips and Tricks]]></title>
            <link>https://awkwardferny.medium.com/making-great-technical-marketing-videos-for-youtube-tips-and-tricks-95fb24d278fa?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/95fb24d278fa</guid>
            <category><![CDATA[devsecops]]></category>
            <category><![CDATA[education]]></category>
            <category><![CDATA[marketing]]></category>
            <category><![CDATA[learning]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Fri, 11 Dec 2020 17:30:05 GMT</pubDate>
            <atom:updated>2020-12-11T18:01:00.738Z</atom:updated>
            <content:encoded><![CDATA[<h3>Making Great Technical Marketing Videos for YouTube — Tips and Tricks</h3><p>I’ve been making lots of videos for marketing technical features for my employer (GitLab). In this guide I will show you how to make great videos that will not only draw, but keep users attention.</p><blockquote>Disclaimer, I’m not at influencer level yet! Keyword <strong>“yet”</strong>.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*UV-wd2OGI5wclb68" /><figcaption>Photo by <a href="https://unsplash.com/@christianw?utm_source=medium&amp;utm_medium=referral">Christian Wiediger</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>I’ll go over the following topics:</p><ul><li>Script Planning 📄</li><li>Gear 📹</li><li>Video Editing + Screen Recording 📼</li><li>Thumbnails 💅</li><li>Titles and Descriptions 🛤</li><li>Pattern Interrupts 👽</li><li>Cards and End Screens 📩</li><li>Promoting on Social Media 🐦</li></ul><p>Here is a video I have created, so you can see the type of content I create:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fv0GhEHZWtdw%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dv0GhEHZWtdw&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fv0GhEHZWtdw%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/b1c6d505aef86027ffd5f09c31ffcb79/href">https://medium.com/media/b1c6d505aef86027ffd5f09c31ffcb79/href</a></iframe><h4>Script Planning and Value Highlighting</h4><p>Your videos should be very well thought out before you actually get into the process of recording. I like to use the <a href="https://docs.google.com/document/d/1NOLVsWp9AviIQsBGaU0q73LTSidw26tvStP0idly6VQ/preview">screenplay format</a> for everything I write. It helps me keep my mind organized. It’s good to have a starting point, and later the flow can be improved during the film process.</p><p>Another tip that is helpful for me is to create a character which the audience can relate to. This makes the video more interesting and more likely to keep the audience engaged. I include a mix of video (with you in it), screen-recordings, audio, and stock photos in the production.</p><p>It’s also important for technical videos to note that you should try not to be too <strong><em>markety</em></strong>. If you are just selling a product, it tends to dis-interest a viewer, especially a developer (I know from experience). Make the video as generic as possible to the technical subject, but show the value of your product as well.</p><p>Once everything is thought out, it should be recorded as it was described in the script, making edits here and there before it is put together. For example this <a href="https://docs.google.com/document/d/1gfXSADI6JeI3y17ORsL9UvsSgpz0Y0VBGTC9Y_1skYw/edit?usp=sharing">script </a>was used in order to make the <a href="https://youtu.be/XnYstHObqlA">DevSecOps Overview video</a>.</p><h4>Gear</h4><p>The quality of your video and audio is important to how long a viewer will be engaged.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*k-ZpkMqOI1zpcyjB" /><figcaption>Photo by <a href="https://unsplash.com/@sharegrid?utm_source=medium&amp;utm_medium=referral">ShareGrid</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>For video-recording I would recommend one of the following:</p><ul><li><strong>DSLR Camera capable of 4K</strong></li><li><strong>iPhone XR or better</strong></li><li><strong>Android capable of 4K video</strong></li></ul><p>Nowadays, most smartphones are capable of delivering high-quality video. I would try to aim for 30fps 4K recording or better.</p><p>For audio-recording I would recommend the following:</p><ul><li><a href="https://amzn.to/2P25hqN"><strong>Lapel Microphone</strong></a></li><li><a href="https://amzn.to/3hHy2Fy"><strong>Shotgun Microphone for DSLR</strong></a><strong> or </strong><a href="https://amzn.to/308SdGC"><strong>Shotgun Microphone for phone</strong></a></li><li><a href="https://amzn.to/3hK3jrg"><strong>Yeti microphone</strong></a><strong> (for recording voice-overs)</strong></li></ul><p>Other equipment you will need:</p><ul><li><strong>Laptop capable of video editing (I have a MacBook Pro)</strong></li><li><a href="https://amzn.to/2X7l8ca"><strong>Tripod</strong></a></li><li><a href="https://amzn.to/3kh9t4t"><strong>Carrying bags</strong></a></li><li><a href="https://amzn.to/2EmKKuR"><strong>Lighting Equipment</strong></a></li><li><a href="https://amzn.to/2KdL6Ho"><strong>101 Things I learned in Film School</strong></a><strong> </strong>(Good easy to follow book, I recommend)</li></ul><p>Don’t worry about getting all these things at once, they are just what I use to try and make videos as professional as possible.</p><p>You can start small and just record video using yourself phone, record the technical content on your computer screen using a free screen-recording tool, and then editing with some free video editing software.</p><h4>Video Editing</h4><p>After recording exactly what you will be showcasing, we get to the editing. The recording should be how it was planned on the script with improvisation where needed.</p><p>We must make sure to have recorded multiple takes and some <a href="https://en.wikipedia.org/wiki/B-roll">b-roll</a>. Multiple takes are important, because you don’t want to have to go back to filming when you are in editing mode.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4cjWJCN3zteG3naS" /><figcaption>Photo by <a href="https://unsplash.com/@jmckinven?utm_source=medium&amp;utm_medium=referral">James McKinven</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>When all your footage is complete, we can now begin the editing process. Here are a few things to consider when editing:</p><ul><li><strong>Cropping:</strong> You should crop out any irrelevant content. Many times I see sooooooooo many tabs open on others screen recordings.</li><li><strong>Voice-Over:</strong> You should maintain the same tone throughout the video. Outside noise should also be reduced.</li><li><strong>Cuts: </strong>If you said certain things like <em>“ummmm”</em> in a video and it would take too much time to re-record everything, simply perform a cut. The transition not being smooth is not necessarily a bad thing (see interrupt below)</li><li><strong>Zooming:</strong> Try and zoom in when showing a particular workflow</li></ul><p>For Screen Recording I would recommend any of the following:</p><ul><li><strong>Zoom </strong>(start a meeting, record, and screen-share)</li><li><strong>Quicktime Player</strong></li><li><strong>Camtasia</strong></li><li><strong>OBS: Open Broadcaster Software</strong></li></ul><p>For Video Editing I would recommend any of the following:</p><ul><li><strong>iMovie</strong></li><li><strong>Adobe Premiere Pro</strong></li><li><strong>Final Cut Pro</strong></li><li><strong>Davinci Resolve</strong></li></ul><p>Each has its own pros and cons, and different people like different tools. For the type of videos I make, I find Final Cut Pro to be the best for me.</p><h4>Pattern Interrupts</h4><blockquote>In video marketing and filmmaking, pattern interrupts are tiny clips or on-screen elements who break the projection of the viewer for what is about to happen in the video they’re watching <a href="https://wildfireconcepts.com/how-to-use-pattern-interrupts-in-your-marketing-videos/">-wildfireconcepts</a></blockquote><p>Adding pattern interrupts to videos makes sure that there are enough changes in your video to keep users engaged. There are a few things you can do to create these interrupts:</p><ul><li><strong>Adding Popups and Sounds throughout the video</strong></li><li><strong>Adding Stock Photos within the video</strong></li><li><strong>Zooming into different parts of the video</strong></li><li><strong>Completely changing the location of the video (example: moving from live video to recorded demo)</strong></li></ul><h4>Thumbnails</h4><p>Thumbnails are an important part of grabbing a viewers attention. With so many videos on YouTube, we tend to click on the ones with an image that grasps our interest.</p><p>Be sure to include the following in your thumbnail:</p><ul><li><strong>An image with something related to the content</strong></li><li><strong>Text giving some context about the video</strong></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sqdaEgQ6OkR6OyVSD6aeQg.png" /><figcaption>Thumbnails created using Adobe Spark</figcaption></figure><p>Youtube provides a great <a href="https://creatoracademy.youtube.com/page/lesson/thumbnails">guide</a> to making thumbnails which captivate an audience. Over time I’m getting better and better at making these. One improvement that can be done on the above is making the font larger.</p><p>I use <a href="http://spark.adobe.com/">Adobe Spark</a> which makes it easy to generate not only thumbnails for YouTube, but also promotional material for Social Media, which we will discuss later.</p><h4>Titles and Descriptions</h4><p>Titles are crucial not only to having your content appear on searches, but also towards getting users to actually click on the video. Make sure your title is not only well-written, but that it is true to your content. You want users to come back to your page and not be discouraged(e.g. No Click-bait).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9tvpsEbTjGyBrFn3DpOBYQ.png" /><figcaption>Vsauce video “Which Way is Down?” description</figcaption></figure><p>Descriptions go hand in hand with titles. Make sure your description provides information via links on the items covered in the video. The above snapshot of the Vsauce video “Which Way is Down?” provides a nice title and so much content on how to learn more on what is covered in the video. I would suggest checking it out.</p><p>You can also add tags which are relevant to your video, but <a href="https://ahrefs.com/blog/youtube-tags/">tags don’t really effect the view rate of your video.</a></p><h4>Cards and End Screens</h4><p>Cards are notifications that can appear throughout the video. They are small rectangular boxes appearing within the video which should link to relevant content, like merchandise or related videos.</p><p>End Screens provide clickable links at the end of your YouTube video. They can be used to entice a viewer to:</p><ul><li><strong>View another video or playlist</strong></li><li><strong>Encourage viewer to subscribe</strong></li><li><strong>Cross-promote another youtube channel</strong></li><li><strong>Link to approved websites</strong></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CL7-FobPXYbDqdh2T11Dqw.png" /><figcaption>End-Screen showing related video and playlist</figcaption></figure><h4>Promoting on Social Media</h4><p>Once your video is complete and has been published, we need to get it out in the world. What better way than to share it!</p><p>I usually share through the following mediums:</p><ul><li><strong>Twitter: </strong>Twitter is an amazing platform for connecting with others with similar interests, send out a tweet with relevant text so that it’s searchable.</li><li><strong>LinkedIn:</strong> Lot’s of your coworkers might be on LinkedIn, it would be nice for them to share the articles with all their colleagues.</li><li><strong>Slack:</strong> Be sure to share it within your organization’s slack channels, that way others can begin to promote your content.</li><li><strong>Reddit: </strong>With the videos being technical, and not too markety, they can be shared on different subreddits.</li><li><strong>Medium: </strong>If you are writing a relevant blog, it would be good to include a link to your video.</li></ul><p>I hope this guide inspires you to get out there and create some great content. I have so much fun creating these videos and you should too. 😎</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=95fb24d278fa" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[4 Things I’ve Learned as a CEO Shadow — GitLab]]></title>
            <link>https://awkwardferny.medium.com/4-things-ive-learned-as-a-ceo-shadow-gitlab-3dc0604f24cd?source=rss-d817b99092a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/3dc0604f24cd</guid>
            <category><![CDATA[ceo]]></category>
            <category><![CDATA[personal-development]]></category>
            <category><![CDATA[leadership]]></category>
            <category><![CDATA[gitlab]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Fern]]></dc:creator>
            <pubDate>Fri, 20 Nov 2020 20:03:17 GMT</pubDate>
            <atom:updated>2020-11-20T20:10:18.773Z</atom:updated>
            <content:encoded><![CDATA[<h3>4 Things I’ve Learned as a CEO Shadow — GitLab</h3><p>I was very fortunate to qualify for the GitLab <a href="https://about.gitlab.com/handbook/ceo/shadow/">CEO Shadow program</a> after there was a last minute slot opening. In this program I followed <a href="https://www.linkedin.com/in/sijbrandij">Sid</a> through his daily work routine (and some of his personal ventures).</p><p>I got to see the inner workings of GitLab, as well as attend meetings with a variety of different Silicon Valley CEOs. Just having a program like this shows how transparent GitLab is.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Z-U6qISvH2bxmvrXQHHOPg.png" /><figcaption><a href="https://www.youtube.com/watch?v=bEKVbx5CYRc">Ty Fujimura and Sid speak about creating a sense of ownership while working remote</a></figcaption></figure><p>In this post, I’m going to go over 4 things I’ve learned after completing the program.</p><h4>How to make and communicate key decisions</h4><p>As a CEO shadow I was able to attend board meetings. These board meeting really showed me how key decisions are made using data and how they are communicated out to the employees.</p><p>Having data is one of the most important factors in decision making. You need to know certain criteria the be able to answer some of these questions:</p><ul><li>Is this implementation feasible?</li><li>How long will it take to implement?</li><li>Will we need more people to work on the implementation?</li><li>Will employees be happy and motivated implementing this?</li><li>What benefit is derived from this implementation and when will we see the benefit?</li></ul><p>These questions can be answered by <em>looking at past data, customer information</em> and <em>having input from a diverse team of individuals with different experiences</em>.</p><p>Once a decision is made it needs to be communicated to the employees in a proper manner. Quickly after making a decision was made an AMA was set up with the appropriate parties. This allows employees to provide questions and receive support for their needs by management.</p><h4>The importance of writing things down</h4><p>As CEO Shadow I attended meetings from all parts of the company. I learned about support, marketing, sales, development, and so much more. With so many decisions being made for such diverse aspects of the company, it is so important to write things down when they are said.</p><p>Memory can get foggy, so it is important that you can go back and see what key decisions were made, and the dialogue which backed these decisions. Notes are taken in detail for every meeting and action items come from those notes.</p><p>To add to the importance of writing, at GitLab we keep a public <a href="https://about.gitlab.com/handbook/">handbook</a>, which provides detailed information on how we run the company. Similar to a Legal System, we continue to iterate on and append sections to our handbook. Not only does this enable collaboration between all employees, but it allows us to grow as a company and allow everyone to have a voice <em>via Merge Request</em>.</p><h4>The importance of collaboration</h4><p>GitLab is all about community and teamwork, it’s so nice to have 2 shadows at once, following a learn from one and teach one. It really make the program that more fun.</p><p>I learned from [David Fisher](<a href="https://about.gitlab.com/company/team/#dfishis1">https://about.gitlab.com/company/team/#dfishis1</a>).</p><p>and taught [Dan Parry](<a href="https://about.gitlab.com/company/team/#dparry">https://about.gitlab.com/company/team/#dparry</a>)</p><p>We became buddies and would help each other out on taking notes and updating the handbook, and other tasks provided by the CEO. This really builds a feeling of rapport between co-workers.</p><h4>How to manage time efficiently</h4><p>I was amazed at how efficiently a CEO’s day is planned. I attended GitLab meetings as well as personal meetings for the CEO’s other ventures.</p><p>I noticed that every meeting was started and ended in the proper amount of time. Not only that, but there was no time wasted in meetings and the items that needed to be discussed were discussed. This makes the use of our time very efficient and allows us to make things for other things in life.</p><p>We could all take a page from the [Communication] section from the GitLab Handbook (<a href="https://about.gitlab.com/handbook/communication/">https://about.gitlab.com/handbook/communication/</a>)</p><p>I also noted that important personal events were included also in the CEO calendar. This inspired me, and I went ahead a started adding important things like working out, calling friends, and more to my calendar, so I can not only manage my work time, but also my life time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*0L1xBr58M617IDBu" /><figcaption>Photo by Andre Hunter on Unsplash</figcaption></figure><p>In Conclusion, it was an amazing experience and I feel very inspired. I’m grateful to work for such a transparent company.</p><p>Thanks for reading, and I hope you enjoyed this post!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3dc0604f24cd" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>