<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Alex Pliutau - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Alex Pliutau - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 16:29:39 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/pltvs/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Implement the Outbox Pattern in Go and PostgreSQL ]]>
                </title>
                <description>
                    <![CDATA[ In event-driven systems, two things need to happen when you process a request: you need to save data to your database, and you need to publish an event to a message broker so other services know somet ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-the-outbox-pattern-in-go-and-postgresql/</link>
                <guid isPermaLink="false">69bc31b3b238fd45a31f1291</guid>
                
                    <category>
                        <![CDATA[ PostgreSQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Thu, 19 Mar 2026 17:26:11 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/7a24b5a7-6619-4997-b24c-c4a743f37c33.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In event-driven systems, two things need to happen when you process a request: you need to save data to your database, and you need to publish an event to a message broker so other services know something changed.</p>
<p>These two operations look simple, but they hide a dangerous reliability problem. What if the database write succeeds but the message broker is temporarily unreachable? Or your service crashes between the two steps? You end up in an inconsistent state: your database has the new data, but the rest of the system never heard about it.</p>
<p>The <strong>Outbox Pattern</strong> is a well-established solution to this problem. In this tutorial, you'll learn what the pattern is, why it works, and how to implement it in Go with PostgreSQL and Google Cloud Pub/Sub.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before reading this tutorial, you should be familiar with:</p>
<ul>
<li><p>The basics of the Go programming language</p>
</li>
<li><p>SQL and PostgreSQL</p>
</li>
<li><p>The concept of database transactions</p>
</li>
<li><p>Basic familiarity with event-driven or distributed systems (helpful but not required)</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a href="#heading-the-problem-two-operations-no-atomicity">The Problem: Two Operations, No Atomicity</a></p>
</li>
<li><p><a href="#heading-how-the-outbox-pattern-works">How the Outbox Pattern Works</a></p>
</li>
<li><p><a href="#heading-the-outbox-table-schema">The Outbox Table Schema</a></p>
</li>
<li><p><a href="#heading-the-message-relay">The Message Relay</a></p>
</li>
<li><p><a href="#heading-go-and-postgresql-implementation">Go and PostgreSQL Implementation</a></p>
<ul>
<li><p><a href="#heading-the-orders-service">The Orders Service</a></p>
</li>
<li><p><a href="#heading-the-relay-service">The Relay Service</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-why-messages-can-be-delivered-more-than-once">Why Messages Can Be Delivered More Than Once</a></p>
</li>
<li><p><a href="#heading-alternative-postgresql-logical-replication">Alternative: PostgreSQL Logical Replication</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-the-problem-two-operations-no-atomicity">The Problem: Two Operations, No Atomicity</h2>
<p>To understand why the Outbox Pattern exists, you need to understand a core challenge in distributed systems: <strong>atomicity across different systems</strong>.</p>
<p>In a relational database, a <strong>transaction</strong> lets you group multiple operations so they either all succeed or all fail together. If you insert a row and update another row in the same transaction, you're guaranteed that both happen – or neither does.</p>
<p>The problem arises when you try to extend this guarantee <em>across two different systems:</em> for example, your database and your message broker (like Kafka, RabbitMQ, or Pub/Sub). These systems don't share a transaction boundary.</p>
<p>Here's a typical event-driven flow that breaks without the Outbox Pattern:</p>
<ol>
<li><p>A user places an order.</p>
</li>
<li><p>Your service saves the order to the database ✅</p>
</li>
<li><p>Your service publishes an <code>order.created</code> event to the message broker ❌ (broker is down)</p>
</li>
<li><p>The order exists in the database, but downstream services never learned about it.</p>
</li>
</ol>
<p>Or the reverse failure:</p>
<ol>
<li><p>Your service publishes the event first ✅</p>
</li>
<li><p>Your service tries to save the order to the database ❌ (database times out)</p>
</li>
<li><p>Downstream services received a notification for an order that doesn't exist.</p>
</li>
</ol>
<p>Either scenario leaves your system in an inconsistent state. This is the core problem the Outbox Pattern solves.</p>
<p>Here's what the process looks like when not using the Outbox Pattern:</p>
<img src="https://cdn.hashnode.com/uploads/covers/5ea89c91fdc930d846b413ab/9f9abcaa-adc8-48ab-b8cb-c47cb731724e.png" alt="diagram without outbox" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-how-the-outbox-pattern-works">How the Outbox Pattern Works</h2>
<p>The Outbox Pattern solves the atomicity problem by keeping both operations <em>inside</em> the database:</p>
<ol>
<li><p>Saves your business data (for example, a new order) to your database.</p>
</li>
<li><p>Writes the event message to a special table called the outbox table in the same database transaction.</p>
</li>
<li><p>A separate background process called the Message Relay polls the outbox table and publishes pending messages to the broker.</p>
</li>
<li><p>Once the broker confirms receipt, the relay marks the message as processed.</p>
</li>
</ol>
<p>Because steps 1 and 2 happen in the same database transaction, they are <strong>atomic</strong>. Either both succeed or neither does. You can never end up with saved data but no corresponding event queued – or an event queued for data that was never saved.</p>
<p>The message is never published directly to the broker in your main application code. Instead, the database acts as a reliable staging area.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5ea89c91fdc930d846b413ab/ef5413b0-6c8e-42b8-949f-5eefe3844231.png" alt="diagram with outbox" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-the-outbox-table-schema">The Outbox Table Schema</h2>
<p>The outbox table stores pending messages until the relay picks them up. Here's a typical PostgreSQL schema:</p>
<pre><code class="language-sql">CREATE TABLE outbox (
    id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    topic       varchar(255)  NOT NULL,
    message     jsonb         NOT NULL,
    state       varchar(50)   NOT NULL DEFAULT 'pending',
    created_at  timestamptz   NOT NULL DEFAULT now(),
    processed_at timestamptz
);
</code></pre>
<p>Let's walk through each column:</p>
<ul>
<li><p><code>id</code>: A unique identifier for each message. Using UUIDs makes it easy to reference specific messages.</p>
</li>
<li><p><code>topic</code>: The destination topic or queue name in your message broker (for example, <code>orders.created</code>).</p>
</li>
<li><p><code>message</code>: The event payload, stored as JSON. This is the data your consumers will receive.</p>
</li>
<li><p><code>state</code>: Tracks whether the message has been sent. The two main values are <code>pending</code> (waiting to be published) and <code>processed</code> (successfully published).</p>
</li>
<li><p><code>created_at</code>: When the message was inserted. The relay uses this to process messages in order.</p>
</li>
<li><p><code>processed_at</code>: When the relay successfully published the message.</p>
</li>
</ul>
<p>You may want to add additional columns depending on your needs: for example, a <code>retry_count</code> column to track how many times the relay has attempted to send a message, or an <code>error</code> column to log failure reasons.</p>
<h2 id="heading-the-message-relay">The Message Relay</h2>
<p>The Message Relay is a background process (often a goroutine, a sidecar, or a separate service) that bridges the outbox table and the message broker.</p>
<p>Its responsibilities are:</p>
<ol>
<li><p>Periodically query the outbox table for messages with <code>state = 'pending'</code>.</p>
</li>
<li><p>Publish each message to the appropriate topic in the broker.</p>
</li>
<li><p>Once the broker confirms delivery, update the row's <code>state</code> to <code>'processed'</code>.</p>
</li>
<li><p>Handle failures gracefully: if publishing fails, leave the message as <code>'pending'</code> so it will be retried.</p>
</li>
</ol>
<p>This design gives you <strong>at-least-once delivery</strong>: a message will always be sent, even if the relay crashes and restarts. The trade-off is that a message might occasionally be sent more than once (more on this below), so your consumers should handle duplicates.</p>
<h2 id="heading-go-and-postgresql-implementation">Go and PostgreSQL Implementation</h2>
<p>Let's build a concrete example. Imagine you have an orders service. When a new order is created, you want to:</p>
<ol>
<li><p>Save the order to a PostgreSQL <code>orders</code> table.</p>
</li>
<li><p>Publish an <code>order.created</code> event to Google Cloud Pub/Sub.</p>
</li>
</ol>
<p>You'll use <a href="https://github.com/jackc/pgx">pgx</a> for the PostgreSQL driver.</p>
<h3 id="heading-the-orders-service">The Orders Service</h3>
<p>The key insight is that the order insert and the outbox insert happen <strong>inside the same transaction</strong>. If anything goes wrong, both are rolled back.</p>
<pre><code class="language-go">// orders/main.go

package main

import (
	"context"
	"encoding/json"
	"log"
	"os"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
)

// Order represents a customer order in our system.
type Order struct {
	ID       uuid.UUID `json:"id"`
	Product  string    `json:"product"`
	Quantity int       `json:"quantity"`
}

// OrderCreatedEvent is the payload published to the message broker.
// It contains only the fields that downstream services need to know about.
type OrderCreatedEvent struct {
	OrderID uuid.UUID `json:"order_id"`
	Product string    `json:"product"`
}

// createOrderInTx saves a new order and its outbox event atomically.
// Both operations share the same transaction (tx), so either both succeed
// or both are rolled back — ensuring consistency.
func createOrderInTx(ctx context.Context, tx pgx.Tx, order Order) error {
	// Step 1: Insert the business data (the actual order).
	_, err := tx.Exec(ctx,
		"INSERT INTO orders (id, product, quantity) VALUES (\(1, \)2, $3)",
		order.ID, order.Product, order.Quantity,
	)
	if err != nil {
		return err
	}
	log.Printf("Inserted order %s into database", order.ID)

	// Step 2: Serialize the event payload that consumers will receive.
	event := OrderCreatedEvent{
		OrderID: order.ID,
		Product: order.Product,
	}
	msg, err := json.Marshal(event)
	if err != nil {
		return err
	}

	// Step 3: Write the event to the outbox table.
	// This does NOT publish to Pub/Sub — it just queues it for the relay.
	_, err = tx.Exec(ctx,
		"INSERT INTO outbox (topic, message) VALUES (\(1, \)2)",
		"orders.created", msg,
	)
	if err != nil {
		return err
	}
	log.Printf("Inserted outbox event for order %s", order.ID)

	return nil
}

func main() {
	ctx := context.Background()

	pool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatalf("Unable to connect to database: %v", err)
	}
	defer pool.Close()

	// Begin a transaction that will cover both the order insert
	// and the outbox insert.
	tx, err := pool.Begin(ctx)
	if err != nil {
		log.Fatalf("Unable to begin transaction: %v", err)
	}
	// If anything fails, the deferred Rollback is a no-op after a successful Commit.
	defer tx.Rollback(ctx)

	newOrder := Order{
		ID:       uuid.New(),
		Product:  "Super Widget",
		Quantity: 10,
	}

	if err := createOrderInTx(ctx, tx, newOrder); err != nil {
		log.Fatalf("Failed to create order: %v", err)
	}

	// Committing the transaction makes both writes permanent simultaneously.
	if err := tx.Commit(ctx); err != nil {
		log.Fatalf("Failed to commit transaction: %v", err)
	}

	log.Println("Successfully created order and queued outbox event.")
}
</code></pre>
<p>Notice that <code>createOrderInTx</code> receives a <code>pgx.Tx</code> (a transaction) rather than a pool connection. This is intentional: it enforces that the caller is responsible for managing the transaction boundary, making the atomicity guarantee explicit.</p>
<h3 id="heading-the-relay-service">The Relay Service</h3>
<p>The relay runs as a separate background process. It polls the outbox table, publishes messages, and marks them as processed.</p>
<p>A critical detail here is the use of <code>FOR UPDATE SKIP LOCKED</code> in the SQL query. This PostgreSQL feature lets you run <strong>multiple relay instances</strong> concurrently without them stepping on each other. When one instance locks a row to process it, other instances skip that row and move on to the next one.</p>
<pre><code class="language-go">// relay/main.go

package main

import (
	"context"
	"log"
	"time"

	"cloud.google.com/go/pubsub"
	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgxpool"
)

// OutboxMessage mirrors the columns we need from the outbox table.
type OutboxMessage struct {
	ID      uuid.UUID
	Topic   string
	Message []byte
}

// processOutboxMessages picks up one pending message, publishes it to Pub/Sub,
// and marks it as processed — all within a single database transaction.
func processOutboxMessages(ctx context.Context, pool *pgxpool.Pool, pubsubClient *pubsub.Client) error {
	tx, err := pool.Begin(ctx)
	if err != nil {
		return err
	}
	defer tx.Rollback(ctx)

	// Query for the next pending message.
	// FOR UPDATE SKIP LOCKED ensures that if multiple relay instances are
	// running, they won't try to process the same message simultaneously.
	rows, err := tx.Query(ctx, `
		SELECT id, topic, message
		FROM outbox
		WHERE state = 'pending'
		ORDER BY created_at
		LIMIT 1
		FOR UPDATE SKIP LOCKED
	`)
	if err != nil {
		return err
	}
	defer rows.Close()

	var msg OutboxMessage
	if rows.Next() {
		if err := rows.Scan(&amp;msg.ID, &amp;msg.Topic, &amp;msg.Message); err != nil {
			return err
		}
	} else {
		// No pending messages — nothing to do.
		return nil
	}

	log.Printf("Publishing message %s to topic %s", msg.ID, msg.Topic)

	// Publish the message to the Pub/Sub topic and wait for confirmation.
	result := pubsubClient.Topic(msg.Topic).Publish(ctx, &amp;pubsub.Message{
		Data: msg.Message,
	})
	if _, err = result.Get(ctx); err != nil {
		// Publishing failed. We return the error here without committing,
		// so the transaction rolls back and the message stays 'pending'.
		// The relay will retry it on the next polling interval.
		return err
	}

	// Mark the message as processed now that the broker has confirmed receipt.
	_, err = tx.Exec(ctx,
		"UPDATE outbox SET state = 'processed', processed_at = now() WHERE id = $1",
		msg.ID,
	)
	if err != nil {
		return err
	}
	log.Printf("Marked message %s as processed", msg.ID)

	// Commit the transaction: the state update becomes permanent.
	return tx.Commit(ctx)
}

func main() {
	// In production, initialize real connections using environment variables
	// or a config file. These are left as placeholders for clarity.
	var (
		pool         *pgxpool.Pool
		pubsubClient *pubsub.Client
	)

	// Poll the outbox table every second.
	// Adjust the interval based on your latency requirements.
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	for range ticker.C {
		if err := processOutboxMessages(context.Background(), pool, pubsubClient); err != nil {
			log.Printf("Error processing outbox: %v", err)
		}
	}
}
</code></pre>
<p>The polling interval (1 second in this example) controls the maximum latency between an event being written to the outbox and it being published to the broker. For most use cases, 1–5 seconds is perfectly acceptable. If you need lower latency, you can reduce the interval, or consider using PostgreSQL's <code>LISTEN/NOTIFY</code> feature to wake up the relay immediately when a new row is inserted.</p>
<h2 id="heading-why-messages-can-be-delivered-more-than-once">Why Messages Can Be Delivered More Than Once</h2>
<p>You might wonder: isn't the Outbox Pattern supposed to guarantee <em>exactly once</em> delivery?</p>
<p>It does not. It guarantees <strong>at-least-once</strong> delivery. Here's the edge case:</p>
<ol>
<li><p>The relay publishes the message to Pub/Sub successfully.</p>
</li>
<li><p>Before it can update the outbox row to <code>'processed'</code>, the relay process crashes.</p>
</li>
<li><p>On restart, the relay sees the message is still <code>'pending'</code> and publishes it again.</p>
</li>
</ol>
<p>This is a rare but possible scenario. The standard way to handle it is to design your message <strong>consumers to be idempotent</strong>. This means that they can safely receive and process the same message multiple times without causing incorrect behavior.</p>
<p>Common strategies for idempotency include:</p>
<ul>
<li><p>Using the message's <code>id</code> as a deduplication key, and checking if you've already processed it before acting.</p>
</li>
<li><p>Making your operations naturally idempotent. For example, using <code>INSERT ... ON CONFLICT DO NOTHING</code> instead of a plain <code>INSERT</code>.</p>
</li>
</ul>
<h2 id="heading-alternative-postgresql-logical-replication">Alternative: PostgreSQL Logical Replication</h2>
<p>The polling approach described above is simple and works well, but it has two drawbacks: it introduces some latency (up to one polling interval), and it issues database queries even when there's nothing to process.</p>
<p>For high-throughput systems where these trade-offs matter, PostgreSQL offers a more advanced alternative: <strong>logical replication</strong> via the <strong>Write-Ahead Log (WAL)</strong>.</p>
<p>Every change made to a PostgreSQL database is first written to the WAL – an append-only log used for crash recovery and replication. With logical replication, you can subscribe to changes in specific tables and receive them as a stream in near real-time.</p>
<p>Instead of your relay asking "Are there any new messages?" on a schedule, PostgreSQL will proactively notify your relay the moment a new row is inserted into the outbox table.</p>
<p>This approach is lower latency and more resource-efficient for high-volume workloads. The trade-off is added implementation complexity: you need to manage a replication slot in PostgreSQL and handle the WAL stream correctly.</p>
<p>In Go, you can use the <a href="https://github.com/jackc/pglogrepl">pglogrepl</a> library to interact with PostgreSQL's logical replication protocol.</p>
<p>For more details on how WAL and change data capture work in PostgreSQL, see the <a href="https://www.postgresql.org/docs/current/wal-intro.html">official Write-Ahead Logging documentation</a>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5ea89c91fdc930d846b413ab/c706cc8f-6bbd-49d1-90c0-8c4934c2718e.png" alt="diagram with WAL" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-conclusion">Conclusion</h2>
<p>The Outbox Pattern solves a fundamental problem in distributed systems: how do you reliably perform a database write and publish a message to a broker in a consistent way?</p>
<p>The key idea is to use your database as the source of truth for <em>both</em> the business data and the pending messages. By writing to the outbox table in the same transaction as your business data, you get atomic guarantees from the database itself: no distributed transaction protocol required.</p>
<p>Here's a quick summary of the key concepts:</p>
<ul>
<li><p><strong>The outbox table</strong> stores pending events as part of your regular database schema.</p>
</li>
<li><p><strong>The transaction</strong> wraps both the business write and the outbox write, making them atomic.</p>
</li>
<li><p><strong>The Message Relay</strong> is a background process that reads from the outbox and publishes to the broker.</p>
</li>
<li><p><strong>At-least-once delivery</strong> means your consumers must be idempotent.</p>
</li>
<li><p><code>FOR UPDATE SKIP LOCKED</code> allows multiple relay instances to run safely in parallel.</p>
</li>
<li><p><strong>Logical replication</strong> is an advanced alternative that avoids polling for high-throughput systems.</p>
</li>
</ul>
<p>The pattern is simple in concept, but there are several ways to implement it depending on your scale and infrastructure. The polling approach shown in this tutorial is a solid starting point for most applications.</p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><p><a href="https://github.com/plutov/packagemain/tree/master/outbox">Source code on GitHub</a></p>
</li>
<li><p><a href="https://www.postgresql.org/docs/current/wal-intro.html">PostgreSQL Write-Ahead Logging (WAL)</a></p>
</li>
<li><p><a href="https://github.com/jackc/pglogrepl">pglogrepl – Go library for PostgreSQL logical replication</a></p>
</li>
<li><p><a href="https://github.com/jackc/pgx">pgx – PostgreSQL driver and toolkit for Go</a></p>
</li>
<li><p><a href="https://packagemain.tech">Explore more Go tutorials on packagemain.tech</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Work with OpenAPI in Go ]]>
                </title>
                <description>
                    <![CDATA[ Well-structured and well-documented APIs are a pleasure to work with. And nowadays the standard is OpenAPI, which comes with a good methodology for defining an API interface first, and only then constructing everything around it. This makes it easier... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-work-with-openapi-in-go/</link>
                <guid isPermaLink="false">67b5da4da9d7e29052110938</guid>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ OpenApi ]]>
                    </category>
                
                    <category>
                        <![CDATA[ swagger ]]>
                    </category>
                
                    <category>
                        <![CDATA[ RESTful APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Wed, 19 Feb 2025 13:19:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740164911110/7954a7d5-39dc-4504-82eb-f5fe583b7b84.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Well-structured and well-documented APIs are a pleasure to work with. And nowadays the standard is <a target="_blank" href="https://www.openapis.org/">OpenAPI</a>, which comes with a good methodology for defining an API interface first, and only then constructing everything around it.</p>
<p>This makes it easier to understand, implement, and consume those APIs. And standards matter, as they allow different teams, regardless of their technology stack, to effectively communicate about and work with the same API.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3191c4bd-690b-45e8-86bb-8b460ae434c6_1295x1490.png" alt="API Lifecycle" width="1295" height="1490" loading="lazy"></p>
<p>In this practical guide, I’ll want to walk you through all the important parts involved in architecting, implementing, and consuming an API using the OpenAPI standard.</p>
<p>Before we dive in, it's helpful to have a basic understanding of the following:</p>
<ul>
<li><p>The Go programming language</p>
</li>
<li><p>RESTful APIs</p>
</li>
<li><p>JSON/YAML</p>
</li>
<li><p>Basic command-line usage</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-the-openapi-specification-oas">What is the OpenAPI Specification (OAS)?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-architecting-the-api">Architecting the API</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-openapiyaml">openapi.yaml</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-paths-and-operations">Paths and Operations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-schemas">Schemas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-extensions">Extensions</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-generate-a-go-server">How to Generate a Go Server</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-visualize-api-docs">How to visualize API docs</a></p>
</li>
<li><p><a target="_blank" href="heading-client-code">Client Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-is-the-openapi-specification-oas">What is the OpenAPI Specification (OAS)?</h2>
<p>The OpenAPI Specification (OAS) provides a consistent means to carry information through each stage of the API lifecycle. It is a specification language for HTTP APIs that defines structure and syntax in a way that is not wedded to the programming language the API is created in.</p>
<p>The <a target="_blank" href="https://spec.openapis.org/">OpenAPI Specification (OAS)</a> was originally based on the Swagger 2.0 Specification from SmartBear Software. Later it was moved to the <a target="_blank" href="https://www.openapis.org/">OpenAPI Initiative (OAI)</a>, a consortium of industry experts under the Linux Foundation.</p>
<p>The main idea of OpenAPI is to be able to describe APIs in agnostic terms, decoupling them from any specific programming language. Consumers of your API specification do not need to understand the guts of your application or try to learn Lisp or Haskell if that’s what you chose to write it in. They can understand exactly what they need from your API specification, written in a simple and expressive language.</p>
<p>This simple and expressive language is called <a target="_blank" href="https://www.jetbrains.com/mps/concepts/domain-specific-languages/">DSL (domain specific language)</a>. It can be written in either JSON or YAML.</p>
<p>The latest version of OAS is <a target="_blank" href="https://spec.openapis.org/oas/latest.html">v3.1.1</a> and the specification itself is huge. There are many features and corner cases, but we will try to go through the most important ones.</p>
<h2 id="heading-architecting-the-api">Architecting the API</h2>
<p>It all starts with defining what the API should provide for its consumers and what it is for. While this stage isn't always purely technical, having a sketch of your API design in OAS when gathering requirements gives you a head start when starting the design.</p>
<p>Once the requirements are ready, it's time to open your <a target="_blank" href="https://editor.swagger.io/">OpenAPI editor</a> and collaborate with your teammates.</p>
<p>And it's important to understand that it's not only about writing the JSON/YAML spec, but actually agreeing on the API design.</p>
<p>I recommend that you follow some API design guide – <a target="_blank" href="https://cloud.google.com/apis/design">Google has</a> <a target="_blank" href="https://cloud.google.com/apis/design">one</a>, for example. This will help you avoid mixed styles (like <strong>/resourceName/{id}</strong> and <strong>/resource_name/{id}</strong>, inconsistent use of HTTP methods, or unclear resource relationships.</p>
<h3 id="heading-openapiyaml">openapi.yaml</h3>
<p>The spec of your API starts in the entrypoint document <code>openapi.yaml</code> (recommended but not required name) or <code>openapi.json</code>. I've seen very big <code>openapi.yaml</code> files (50k lines), but it's possible to split your spec into multiple parts. Just keep in mind that this may not work well for some OpenAPI tools as they expect a single file. <a target="_blank" href="https://github.com/googlemaps/openapi-specification/">Google Maps OAS</a> is a good example on how to split the schema, but also comes with a pre-processor to generate a single file.</p>
<p>There are some open source tools to bundle the OAS: <a target="_blank" href="https://github.com/APIDevTools/swagger-cli">swagger-cli</a> (archived) and <a target="_blank" href="https://github.com/Redocly/redocly-cli">redocly-cli</a> are great options.</p>
<pre><code class="lang-bash">swagger-cli bundle -o _bundle/openapi.yaml openapi.yaml
</code></pre>
<p>As I mentioned earlier, the spec is huge, but let's break it into smaller parts. For this tutorial I created a dummy "Smart Home" API. You can see the full spec and code <a target="_blank" href="https://github.com/plutov/packagemain/tree/master/oapi-example">here</a>.</p>
<p>The root object is called <a target="_blank" href="https://spec.openapis.org/oas/latest.html#openapi-object">OpenAPI Object</a> and has the following structure:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># schema version</span>
<span class="hljs-attr">openapi:</span> <span class="hljs-number">3.1</span><span class="hljs-number">.1</span>

<span class="hljs-comment"># docs</span>
<span class="hljs-attr">info:</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">Smart</span> <span class="hljs-string">Home</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">API</span> <span class="hljs-string">Specification</span> <span class="hljs-string">for</span> <span class="hljs-string">Smart</span> <span class="hljs-string">Home</span> <span class="hljs-string">API</span>
  <span class="hljs-attr">version:</span> <span class="hljs-number">0.0</span><span class="hljs-number">.1</span>

<span class="hljs-comment"># optional servers for public APIs</span>
<span class="hljs-attr">servers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">url:</span> <span class="hljs-string">"https://..."</span>

<span class="hljs-comment"># tags are used to group the endpoints</span>
<span class="hljs-attr">tags:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">device</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">Manage</span> <span class="hljs-string">devices</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">room</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">Manage</span> <span class="hljs-string">rooms</span>

<span class="hljs-comment"># endpoints go here</span>
<span class="hljs-attr">paths:</span>
  <span class="hljs-comment"># ...</span>

<span class="hljs-comment"># reusable objects such as schemas, error types, request bodies</span>
<span class="hljs-attr">components:</span>
  <span class="hljs-comment"># ...</span>

<span class="hljs-comment"># security mechanisms, should correspond to components.securitySchemes</span>
<span class="hljs-attr">security:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">apiKeyAuth:</span> []
</code></pre>
<p>We defined the skeleton of our schema, but the majority of OpenAPI schema lays in the <code>paths</code> and <code>components</code> props.</p>
<h3 id="heading-paths-and-operations">Paths and Operations</h3>
<p>Let's now add a few endpoints to our schema. The operations are grouped by paths, so you can have multiple HTTP methods on a single path – for example <code>GET /devices/{deviceId}</code> and <code>DELETE /devices/{deviceId}</code>.</p>
<p>It's a good practice to define all types (request bodies, responses, errors) in the <code>components</code> section and reference them instead of manually defining them in the <code>paths</code> section. This allows for easier re-use of entities. For example, in our API we have a type <code>Device</code> which can be used in many endpoints.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">paths:</span>

  <span class="hljs-comment"># the path has a parameter in it</span>
  <span class="hljs-string">/devices/{deviceId}:</span>
    <span class="hljs-attr">get:</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">device</span>
      <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">Device</span>
      <span class="hljs-attr">operationId:</span> <span class="hljs-string">getDevice</span>

      <span class="hljs-attr">parameters:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">deviceId</span>
          <span class="hljs-attr">in:</span> <span class="hljs-string">path</span>
          <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">schema:</span>
            <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/ULID"</span>

      <span class="hljs-attr">responses:</span>

        <span class="hljs-attr">"200":</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Success</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/Device"</span>

        <span class="hljs-attr">"404":</span>
          <span class="hljs-attr">description:</span> <span class="hljs-string">Not</span> <span class="hljs-string">Found</span>
          <span class="hljs-attr">content:</span>
            <span class="hljs-attr">application/json:</span>
              <span class="hljs-attr">schema:</span>
                <span class="hljs-comment"># use common type for 404 errors</span>
                <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/ErrorNotFound"</span>
</code></pre>
<p>In the spec above, we defined two endpoints of our API and referenced the types which we still need to define: <code>Device</code>, <code>ErrorNotFound</code> and <code>ULID</code>. Notice that for the <code>deviceId</code> path param we also used a custom type instead of a standard string, which can be helpful in the future in case we want to change the format of our IDs (for example UUID, ULID. integer, and so on).</p>
<p>Notice that each operation has a unique <code>operationId</code>. While it's optional, it's very helpful to set one, so then it can be used on the server and client sides.</p>
<p>This is a basic configuration which you can extend further if you want to. For example, when serving this schema in Swagger, it's good to see the examples of our requests (and their variations). We can define it here in <code>responses</code> section, or directly in our <code>components.schemas</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">responses:</span>
  <span class="hljs-attr">"200":</span>
    <span class="hljs-attr">content:</span>
      <span class="hljs-attr">application/json:</span>
        <span class="hljs-attr">examples:</span>
          <span class="hljs-attr">new_device:</span>
            <span class="hljs-attr">value:</span> <span class="hljs-comment"># any value</span>
</code></pre>
<h3 id="heading-schemas">Schemas</h3>
<p><code>components</code> is an integral part of OAS, and contains the following properties:</p>
<ul>
<li><p>schemas</p>
</li>
<li><p>responses</p>
</li>
<li><p>parameters</p>
</li>
<li><p>requestBodies</p>
</li>
<li><p>headers</p>
</li>
<li><p>securitySchemes</p>
</li>
</ul>
<p>You can <a target="_blank" href="https://spec.openapis.org/oas/latest.html#components-object">see all here</a>.</p>
<p>We could define our <code>Device</code> type like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">Device:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">id:</span>
          <span class="hljs-string">$ref:</span> <span class="hljs-string">'#/components/schemas/ULID'</span>
        <span class="hljs-attr">name:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
      <span class="hljs-attr">required:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">id</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">name</span>
</code></pre>
<p>But later you may have other types that have <code>name</code> or <code>id</code> fields, so it's recommended to define them separately and combine them in the final type using <code>allOf</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">components:</span>
  <span class="hljs-attr">schemas:</span>
    <span class="hljs-attr">WithId:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">required:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">id</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">id:</span>
          <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/ULID"</span>

    <span class="hljs-attr">WithName:</span>
      <span class="hljs-attr">type:</span> <span class="hljs-string">object</span>
      <span class="hljs-attr">required:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">name</span>
      <span class="hljs-attr">properties:</span>
        <span class="hljs-attr">name:</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>

    <span class="hljs-attr">Device:</span>
      <span class="hljs-attr">allOf:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/WithId"</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">$ref:</span> <span class="hljs-string">"#/components/schemas/WithName"</span>
</code></pre>
<p><code>allOf</code>, <code>oneOf</code>, and <code>anyOf</code> are very powerful techniques for modeling your OAS.</p>
<h3 id="heading-extensions">Extensions</h3>
<p>OpenAPI schemas can be extended with internal properties that do not affect the schema itself, but are useful for server or client generators. A good example is our <a target="_blank" href="https://github.com/ulid/spec">ULID</a> type for ids:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">ULID:</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>
  <span class="hljs-attr">minLength:</span> <span class="hljs-number">26</span>
  <span class="hljs-attr">maxLength:</span> <span class="hljs-number">26</span>

  <span class="hljs-comment"># example is useful for Swagger docs</span>
  <span class="hljs-attr">example:</span> <span class="hljs-string">01ARZ3NDEKTSV4RRFFQ69G5FAV</span>

  <span class="hljs-attr">x-go-type:</span> <span class="hljs-string">ulid.ULID</span>
  <span class="hljs-attr">x-go-type-import:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">github.com/oklog/ulid/v2</span>
</code></pre>
<p>The <code>x-</code> props will be used by the Go server generator to use existing Go types for this field instead of generating a new one.</p>
<h2 id="heading-how-to-generate-a-go-server">How to Generate a Go Server</h2>
<p>We didn't go through all possible schema properties here and just covered the main ones – so if you’re not familiar with OAS, you should now have a good understanding of this standard. You can read the whole specification <a target="_blank" href="https://spec.openapis.org/oas/latest.html">here</a>. But now as our schema is ready, we can generate a Go server from it.</p>
<p>You can find the full list of generators on <a target="_blank" href="https://openapi.tools/">opeanapi.tools</a> – there are a lot of them. But the most popular one for Go servers is <a target="_blank" href="https://github.com/oapi-codegen/oapi-codegen">oapi-codegen</a>.</p>
<blockquote>
<p>oapi-codegen currently doesn’t support this OAS 3.1. <a target="_blank" href="https://github.com/oapi-codegen/oapi-codegen/issues/373">issue</a>. <a target="_blank" href="https://github.com/ogen-go/ogen/">ogen</a> does, though.</p>
</blockquote>
<p>You can install it via <code>go install</code>:</p>
<pre><code class="lang-bash">go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
</code></pre>
<p>The configuration for the <code>oapi-codegen</code> generator is straightforward. You can either provide command line arguments or specify the same arguments in a yaml configuration file. You can choose which HTTP router to use for the server, where to put the output file, and more. In our case let's use the <a target="_blank" href="https://github.com/labstack/echo">echo</a> router.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># oapi-codegen.yaml</span>

<span class="hljs-attr">package:</span> <span class="hljs-string">api</span>
<span class="hljs-attr">output:</span> <span class="hljs-string">pkg/api/api.gen.go</span>

<span class="hljs-attr">generate:</span>
  <span class="hljs-attr">strict-server:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">models:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">echo-server:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>We can now generate the server code using the following command:</p>
<pre><code class="lang-bash">oapi-codegen --config=oapi-codegen.yaml openapi.yaml
</code></pre>
<p>Let's now explore the generated <code>api.gen.go</code> file.</p>
<p>Since we enabled <code>strict-server</code>, which will generate code that parses request bodies and encodes responses automatically, the interface that we need to implement is called <code>StrictServerInterface</code>:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> StrictServerInterface <span class="hljs-keyword">interface</span> {

  <span class="hljs-comment">// List Devices</span>
  <span class="hljs-comment">// (GET /devices)</span>
  ListDevices(ctx context.Context, request ListDevicesRequestObject) (ListDevicesResponseObject, error)

  <span class="hljs-comment">// Get Device</span>
  <span class="hljs-comment">// (GET /devices/{deviceId})</span>
  GetDevice(ctx context.Context, request GetDeviceRequestObject) (GetDeviceResponseObject, error)

}
</code></pre>
<p>All our types are also generated:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> ULID = ulid.ULID

<span class="hljs-keyword">type</span> Device <span class="hljs-keyword">struct</span> {
    Id   ULID   <span class="hljs-string">`json:"id"`</span>
    Name <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"name"`</span>
}

<span class="hljs-comment">// ...</span>
</code></pre>
<p>As well as code to parse the requests automatically and the Swagger definition.</p>
<h3 id="heading-implementation">Implementation</h3>
<p>What's left for us to do is to create a server using echo, implement the generated interface, and glue everything together. We can write the following code in <code>pkg/api/impl.go</code>:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> api

<span class="hljs-keyword">import</span> <span class="hljs-string">"context"</span>

<span class="hljs-keyword">type</span> Server <span class="hljs-keyword">struct</span>{}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewServer</span><span class="hljs-params">()</span> <span class="hljs-title">Server</span></span> {
    <span class="hljs-keyword">return</span> Server{}
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(Server)</span> <span class="hljs-title">ListDevices</span><span class="hljs-params">(ctx context.Context, request ListDevicesRequestObject)</span> <span class="hljs-params">(ListDevicesResponseObject, error)</span></span> {
    <span class="hljs-comment">// actual implementation</span>
    <span class="hljs-keyword">return</span> ListDevices200JSONResponse{}, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(Server)</span> <span class="hljs-title">GetDevice</span><span class="hljs-params">(ctx context.Context, request GetDeviceRequestObject)</span> <span class="hljs-params">(GetDeviceResponseObject, error)</span></span> {
    <span class="hljs-comment">// actual implementation</span>
    <span class="hljs-keyword">return</span> GetDevice200JSONResponse{}, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>I skipped the implementation part and just demonstrated how to return the responses. It's quite handy that <code>oapi-codegen</code> generated all possible responses for us.</p>
<p>That leaves us to start the echo server itself. Note that we don't need to write any endpoints manually now, and all request and response parsing is handled for us. Still, we need to validate the requests inside our implementation.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"oapiexample/pkg/api"</span>

    <span class="hljs-string">"github.com/labstack/echo/v4"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    server := api.NewServer()

    e := echo.New()

    api.RegisterHandlers(e, api.NewStrictHandler(
        server,
        <span class="hljs-comment">// add middlewares here if needed</span>
        []api.StrictMiddlewareFunc{},
    ))

    e.Start(<span class="hljs-string">"127.0.0.1:8080"</span>)
}
</code></pre>
<p>Now when we run our server using <code>go run .</code>, we can curl <code>localhost:8080/devices</code> to see the response!</p>
<h3 id="heading-supported-servers">Supported servers</h3>
<p><code>oapi-codegen</code> supports many web frameworks/servers, such as Chi, Fiber, Gin as well as standard <code>net/http</code>.</p>
<h3 id="heading-how-to-visualize-api-docs">How to visualize API docs</h3>
<p>Sometimes it's handy to have Swagger docs shipped together with your API – for testing, for example, or just as public documentation. <code>oapi-codegen</code> doesn't generate the Swagger UI out of the box, but we can have a simple HTML page that has a Swagger JS which loads our OAS.</p>
<p>You can find the HTML code for our <code>pkg/api/index.html</code> <a target="_blank" href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/installation/">here</a>.</p>
<p>And then we can use <code>go:embed</code> to embed the static files and add our Swagger endpoint:</p>
<pre><code class="lang-go"><span class="hljs-comment">//go:embed pkg/api/index.html</span>
<span class="hljs-comment">//go:embed openapi.yaml</span>
<span class="hljs-keyword">var</span> swaggerUI embed.FS

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// ...</span>

    <span class="hljs-comment">// serve swagger docs</span>
    e.GET(<span class="hljs-string">"/swagger/*"</span>, echo.WrapHandler(http.StripPrefix(<span class="hljs-string">"/swagger/"</span>, http.FileServer(http.FS(swaggerUI)))))
}
</code></pre>
<p>Now we can visit <code>localhost:8080/swagger/</code> to see the Swagger UI with our OAS.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d35f8f-e1e7-4e51-9149-e180bb192fd8_1092x922.png" alt="Swagger UI" width="1092" height="922" loading="lazy"></p>
<p>Tools like Postman are very popular for API documentation, and it's also possible to <a target="_blank" href="http://learning.postman.com/docs/integrations/available-integrations/working-with-openAPI/">import</a> your existing OpenAPI 3.0 and 3.1 definitions into Postman. Postman supports both YAML and JSON formats.</p>
<h3 id="heading-generate-oas-from-code">Generate OAS from code</h3>
<p>There is also a practice to generate OpenAPI schemas from code, especially in typed languages. This approach has been popular, with the main selling point being that keeping your OpenAPI schema near the code will hopefully mean that developers keep it up to date as they work on the code.</p>
<p>This is not always the case, which is one of a few reasons this practice is dying out. And I am also not a big fan, as I haven’t seen a big value in this. Anyway, you can have a look at the following projects: <a target="_blank" href="https://github.com/go-swagger/go-swagger">go-swagger</a>, <a target="_blank" href="https://github.com/swaggo/swag">swag</a>, <a target="_blank" href="https://github.com/swaggest/rest/">swaggest/rest</a>.</p>
<h2 id="heading-client-code">Client Code</h2>
<p>As mentioned earlier, OpenAPI is very powerful for collaboration between teams, and all you have to do now is to properly version your schema (see <code>info.version</code> part) and distribute it across the teams.</p>
<p>This part can be automated to some extent by packaging your OpenAPI schema and making it available. I've seen devs use Git submodules for that or GitHub actions to publish the version schemas.</p>
<p>Let's assume our client is a web application written in TypeScript, which is quite common for web APIs. Again, there are may generators available at <a target="_blank" href="https://openapi.tools/">opeanapi.tools</a> online but the most popular one is <a target="_blank" href="https://openapi-ts.dev/">openapi-typescript</a>.</p>
<p>Here's how you can generate the TypeScript code for local or remote schemas:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Local schema</span>
npx openapi-typescript openapi.yaml -o ./client/schema.d.ts

<span class="hljs-comment"># Remote schema</span>
npx openapi-typescript https://.../openapi.yaml -o ./client/schema.d.ts
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>OpenAPI is a de-facto standard for designing, implementing, and consuming REST APIs, so it's crucial to understand how it works.</p>
<p>I hope this article has provided a useful introduction to the OpenAPI Specification, as well as practical tips and examples for how to use OAS to architect, implement, and consume APIs.</p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><p><a target="_blank" href="https://github.com/plutov/packagemain/tree/master/oapi-example">Source code</a></p>
</li>
<li><p><a target="_blank" href="https://www.openapis.org/">OpenAPI Initiative</a></p>
</li>
<li><p><a target="_blank" href="https://openapi.tools/">openapi.tools</a></p>
</li>
<li><p><a target="_blank" href="https://editor.swagger.io/">Swagger Editor</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/oapi-codegen/oapi-codegen">oapi-codegen</a></p>
</li>
<li><p><a target="_blank" href="https://packagemain.tech">Explore more articles on packagemain.tech</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Essential CLI/TUI Tools for Developers ]]>
                </title>
                <description>
                    <![CDATA[ As developers, we spend a lot of time in our terminals. And there are tons of great CLI/TUI tools that can boost our productivity (as well as some that are just fun to use). From managing Git repositories and navigating file systems to monitoring sys... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/essential-cli-tui-tools-for-developers/</link>
                <guid isPermaLink="false">6798fd7b4666e531b7dd7586</guid>
                
                    <category>
                        <![CDATA[ terminal ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ command line ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cli ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Tue, 28 Jan 2025 15:53:31 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738077620615/22e3c744-d609-4469-ae10-ef8ad4b515a1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>As developers, we spend a lot of time in our terminals. And there are tons of great CLI/TUI tools that can boost our productivity (as well as some that are just fun to use). From managing Git repositories and navigating file systems to monitoring system performance and even playing retro games, the command line offers a powerful and versatile environment.</p>
<p>In this article, we’ll go through a collection of CLI / TUI tools that have been widely adopted in the developer community, spanning various categories such as version control, system utilities, text editors, and more. I wanted to give you a diverse selection that caters to different needs and workflows.</p>
<p>For each tool, I’ll include an overview, highlighting its key features and use cases, along with clear and concise installation instructions for various operating systems, ensuring you can quickly get up and running with these valuable command-line companions.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-kubernetes-tools">Kubernetes Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-container-tools">Container Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-file-and-text-tools">File and Text Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-git-tools">Git Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-development-tools">Development Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-networking-tools">Networking Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-workstation-tools">Workstation Tools</a></p>
</li>
</ul>
<h2 id="heading-kubernetes-tools"><strong>Kubernetes Tools</strong></h2>
<h3 id="heading-k9shttpsgithubcomderailedk9s-kubernetes-cli-to-manage-your-clusters-in-style"><a target="_blank" href="https://github.com/derailed/k9s"><strong>k9s</strong></a> — Kubernetes CLI To Manage Your Clusters In Style</h3>
<p>K9s is a must-have tool for anyone working with Kubernetes. Its intuitive terminal-based UI, real-time monitoring capabilities, and powerful command options make it a standout in the world of Kubernetes management tools.</p>
<p>The K9s project is designed to continually watch Kubernetes cluster for changes and offer subsequent commands to interact with observed resources. This makes it easier to manage applications, especially in a complex, multi-cluster environment. The project’s aim is to make Kubernetes management more accessible and less daunting, especially for those who are not Kubernetes experts.</p>
<p>Just launch k9s in your terminal and start exploring the Kubernetes resources with ease.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*tkfwKS01NCnUBE-N.png" alt="K9s interface" width="700" height="367" loading="lazy"></p>
<p>To install K9s:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install derailed/k9s/k9s

<span class="hljs-comment"># via snap for Linux</span>
snap install k9s --devmode

<span class="hljs-comment"># via Chocolatey for Windows</span>
choco install k9s

<span class="hljs-comment"># via go install</span>
go install github.com/derailed/k9s@latest
</code></pre>
<h3 id="heading-kubectxhttpsgithubcomahmetbkubectx-switch-between-contexts-clusters-on-kubectl-faster"><a target="_blank" href="https://github.com/ahmetb/kubectx"><strong>kubectx</strong></a> — switch between contexts (clusters) on kubectl faster.</h3>
<p>Kubectx is the most popular tool for switching Kubernetes contexts, but it has the fewest features! It displays all the contexts in your Kubernetes config as a selectable list and lets you pick one. That’s it!</p>
<p>This project comes with 2 tools:</p>
<ul>
<li><p><strong>kubectx</strong> is a tool that helps you switch between contexts (clusters) on kubectl faster.</p>
</li>
<li><p><strong>kubens</strong> is a tool to switch between Kubernetes namespaces (and configure them for kubectl) easily.</p>
</li>
</ul>
<p>These tools make it very easy to switch between Kubernetes clusters and namespaces if you work with many of them daily. Here you can see it in action:</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*g442WF-cXW-z1dKQ.gif" alt="0*g442WF-cXW-z1dKQ" width="1367" height="472" loading="lazy"></p>
<p>To install kubectx:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install kubectx

<span class="hljs-comment"># via apt for Debian</span>
sudo apt install kubectx

<span class="hljs-comment"># via pacman for Arch Linux</span>
sudo pacman -S kubectx

<span class="hljs-comment"># via Chocolatey for Windows</span>
choco install kubens kubectx
</code></pre>
<h3 id="heading-kubescapehttpsgithubcomkubescapekubescape-kubernetes-security-platform-for-your-ide-cicd-pipelines-and-clusters"><a target="_blank" href="https://github.com/kubescape/kubescape"><strong>kubescape</strong></a> — Kubernetes security platform for your IDE, CI/CD pipelines, and clusters.</h3>
<p>I hope you take the security of your Kubernetes clusters seriously. If so, <strong>kubescape</strong> is really great for testing if your Kubernetes cluster is deployed securely according to multiple frameworks.</p>
<p>Kubescape can scan clusters, YAML files, and Helm charts and detects the misconfigurations according to multiple sources.</p>
<p>I usually use it in my CI/CD to scan for vulnerabilities automatically when changing Kubernetes manifests or Helm templates.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*Ft2r01ij9Rxj2-V0.png" alt="kubescape scan" width="700" height="556" loading="lazy"></p>
<p>To install kubescape:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install kubescape

<span class="hljs-comment"># via apt for Debian</span>
sudo add-apt-repository ppa:kubescape/kubescape
sudo apt update
sudo apt install kubescape

<span class="hljs-comment"># via Chocolatey for Windows</span>
choco install kubescape
</code></pre>
<h2 id="heading-container-tools"><strong>Container Tools</strong></h2>
<h3 id="heading-ctophttpsgithubcombcicenctop-a-top-like-interface-for-container-metrics"><a target="_blank" href="https://github.com/bcicen/ctop"><strong>ctop</strong></a> — A top-like interface for container metrics.</h3>
<p><strong>ctop</strong> is basically a better version of <code>docker stats</code>. It provides a concise and condensed overview of real-time metrics for multiple containers. It comes with built-in support for Docker and runC, and connectors for other container and cluster systems are planned for future releases.</p>
<p>Using ctop is simple. Once you have the tool open, you’ll see all of your currently active containers listed.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*EJ5kdlEs5M5QxDBy.gif" alt="ctop in action" width="1195" height="414" loading="lazy"></p>
<p>To install ctop:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install ctop

<span class="hljs-comment"># via pacman for Arch Linux</span>
sudo pacman -S ctop

<span class="hljs-comment"># via scoop for Windows</span>
scoop install ctop
</code></pre>
<h3 id="heading-lazydockerhttpsgithubcomjesseduffieldlazydocker-a-simple-terminal-ui-for-both-docker-and-docker-compose"><a target="_blank" href="https://github.com/jesseduffield/lazydocker"><strong>lazydocker</strong></a> — A simple terminal UI for both docker and docker-compose.</h3>
<p>While Docker's command-line interface is powerful, sometimes you might want a more visual approach without the overhead of a full GUI. This is especially true when managing Docker containers on a headless Linux server where installing a web-based GUI might be undesirable.</p>
<p>Lazydocker was created by <a target="_blank" href="https://github.com/jesseduffield">Jesse Duffield</a> to help make <a target="_blank" href="https://github.com/jesseduffield"></a>managing docker containers a bit easier. Simply put, Lazydocker is a terminal UI (written in Golang) for the docker and docker-compose commands.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*Cbmx4ShRSO7ccVy2.gif" alt="lazydocker in action" width="1456" height="819" loading="lazy"></p>
<p>To install lazydocker:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install lazydocker

<span class="hljs-comment"># via Chocolatey for Windows</span>
choco install lazydocker

<span class="hljs-comment"># via go install</span>
go install github.com/jesseduffield/lazydocker@latest
</code></pre>
<h3 id="heading-divehttpsgithubcomwagoodmandive-a-tool-for-exploring-each-layer-in-a-docker-image"><a target="_blank" href="https://github.com/wagoodman/dive"><strong>dive</strong></a> — A tool for exploring each layer in a Docker image.</h3>
<p>A Docker image is made up of layers, and with every layer you add on, more space will be taken up by the image. Therefore, the more layers in the image, the more space the image will require.</p>
<p>That’s where <strong>dive</strong> shines, it helps you explore your Docker image and layer contents. It can also help you find ways to shrink the size of your Docker/OCI image.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*swo_hrKJ9EV7hyMs.gif" alt="0*swo_hrKJ9EV7hyMs" width="1456" height="909" loading="lazy"></p>
<p>To install dive:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install dive

<span class="hljs-comment"># via pacman for Arch Linux</span>
pacman -S dive

<span class="hljs-comment"># via go install</span>
go get github.com/wagoodman/dive
</code></pre>
<h2 id="heading-file-and-text-tools"><strong>File and Text Tools</strong></h2>
<h3 id="heading-jqhttpsgithubcomjqlangjq-command-line-json-processor"><a target="_blank" href="https://github.com/jqlang/jq"><strong>jq</strong></a> — Command-line JSON processor.</h3>
<p>You may be aware of this one already as it’s well known in the developer community.</p>
<p>Unfortunately, shells such as Bash can’t interpret and work with JSON directly. That’s where you can use <strong>jq</strong> as a command-line JSON processor that’s similar to sed, awk, grep, and so on for JSON data. It’s written in portable C and doesn’t have any runtime dependencies. This lets you slice, filter, map, and transform structured data with ease.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*uwysqWprpmrLrJQP.png" alt="0*uwysqWprpmrLrJQP" width="700" height="328" loading="lazy"></p>
<p>To install jq, you can download the latest releases from the <a target="_blank" href="https://github.com/jqlang/jq/releases">GitHub release page.</a></p>
<h3 id="heading-bathttpsgithubcomsharkdpbat-a-cat1-clone-with-wings"><a target="_blank" href="https://github.com/sharkdp/bat"><strong>bat</strong></a> — A cat(1) clone with wings.</h3>
<p>This is the most used CLI on my machine currently. A few years ago it was <strong>cat</strong>, which is great but doesn’t provide syntax highlighting, or Git integration</p>
<p>Bat’s syntax highlighting supports many programming and markup languages, helping you make your code more readable directly in the terminal. Git integration lets you see modifications in relation to the index, highlighting the lines you’ve added or changed.</p>
<p>Simply run <code>bat filename</code> and enjoy its output.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:656/0*L02HhsqDcq2_G_z4.png" alt="Bat example" width="656" height="450" loading="lazy"></p>
<p>To install bat:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install bat

<span class="hljs-comment"># via apt for Debian</span>
sudo apt install bat

<span class="hljs-comment"># via pacman for Arch Linux</span>
pacman -S bat

<span class="hljs-comment"># via Chocolatey for Windows</span>
choco install bat
</code></pre>
<h3 id="heading-ripgrephttpsgithubcomburntsushiripgrep-recursively-search-directories-for-a-regex-pattern-while-respecting-your-gitignore"><a target="_blank" href="https://github.com/BurntSushi/ripgrep"><strong>ripgrep</strong></a> — Recursively search directories for a regex pattern while respecting your gitignore.</h3>
<p><strong>ripgrep</strong> is definitely becoming a popular alternative (if not the most popular) to the <strong>grep</strong> command. Even some editors like <a target="_blank" href="https://code.visualstudio.com/updates/v1_11">Visual Studio Code</a> are using ripgrep to power their search offerings.</p>
<p>The major selling point is its default behavior for recursive search and speed.</p>
<p>I now rarely use grep on my personal machine, as ripgrep is much faster.</p>
<p>To install ripgrep:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install ripgrep

<span class="hljs-comment"># via apt for Debian</span>
sudo apt-get install ripgrep

<span class="hljs-comment"># via pacman for Arch Linux</span>
pacman -S ripgrep

<span class="hljs-comment"># via Chocolatey for Windows</span>
choco install ripgrep
</code></pre>
<h2 id="heading-git-tools"><strong>Git Tools</strong></h2>
<h3 id="heading-lazygithttpsgithubcomjesseduffieldlazygit-simple-terminal-ui-for-git-commands"><a target="_blank" href="https://github.com/jesseduffield/lazygit"><strong>lazygit</strong></a> — Simple terminal UI for git commands.</h3>
<p><strong>lazygit</strong> is another great terminal UI for Git commands developed by <a target="_blank" href="https://github.com/jesseduffield"><strong>Jesse Duffield</strong></a> using Go.</p>
<p>I don’t mind using the Git CLI directly for simple things, but it is famously verbose for more advanced use cases. I am just too lazy to memorize longer commands.</p>
<p>And lazigit has made me a more productive Git user than ever.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*ykEtn2HQ9QgU40jx.png" alt="lazygit interface" width="700" height="381" loading="lazy"></p>
<p>To install lazygit:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install jesseduffield/lazygit/lazygit

<span class="hljs-comment"># via pacman for Arch Linux</span>
pacman -S lazygit

<span class="hljs-comment"># via scoop for Windows</span>
scoop install lazygit
</code></pre>
<h2 id="heading-development-tools"><strong>Development Tools</strong></h2>
<h3 id="heading-atachttpsgithubcomjulien-cpsnatac-a-simple-api-client-postman-like-in-your-terminal"><a target="_blank" href="https://github.com/Julien-cpsn/ATAC"><strong>ATAC</strong></a> — A simple API client (Postman-like) in your terminal.</h3>
<p>ATAC stands for Arguably a Terminal API Client. It’s based on popular clients like Postman, Insomnia, and Bruno, but it runs inside your terminal without needing any particular graphical environment.</p>
<p>It works best for developers who need an offline, cross-platform API client right at their fingertips (terminal).</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*NoOMeMxkELNFI9RS.png" alt="ATAC" width="700" height="376" loading="lazy"></p>
<p>To install ATAC:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew tap julien-cpsn/atac
brew install atac

<span class="hljs-comment"># via pacman for Arch Linux</span>
pacman -S atac
</code></pre>
<h3 id="heading-k6httpsgithubcomgrafanak6-a-modern-load-testing-tool-using-go-and-javascript"><a target="_blank" href="https://github.com/grafana/k6"><strong>k6</strong></a> — A modern load testing tool, using Go and JavaScript.</h3>
<p>I’ve used many load-testing tools in my career, such as <a target="_blank" href="https://github.com/tsenart/vegeta">vegeta</a> or even <a target="_blank" href="https://httpd.apache.org/docs/2.4/programs/ab.html">ab</a> in the past. But now I mostly use <strong>k6s</strong> as it has everything I need and has a great GUI and TUI.</p>
<p>Why it works well for me:</p>
<ul>
<li><p>k6 has really good <a target="_blank" href="https://k6.io/docs/">documentation</a></p>
</li>
<li><p>Many integrations available: Swagger, JMeter scripts, and so on.</p>
</li>
<li><p>Results reporting is quite good</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737552000859/df5af273-3706-4d41-9dbe-717d2f2d18b7.webp" alt="K6 interface" class="image--center mx-auto" width="1698" height="1184" loading="lazy"></p>
<p>To install k6:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install k6

<span class="hljs-comment"># via apt for Debian</span>
sudo apt-get install k6

<span class="hljs-comment"># via Chocolatey for Windows</span>
choco install k6
</code></pre>
<h3 id="heading-httpiehttpsgithubcomhttpiecli-modern-user-friendly-command-line-http-client-for-the-api-era"><a target="_blank" href="https://github.com/httpie/cli"><strong>httpie</strong></a> — modern, user-friendly command-line HTTP client for the API era.</h3>
<p>Don’t get me wrong, curl is great, but not very human-friendly.</p>
<p>HTTPie has a simple and expressive syntax, supports JSON and form data, handles authentication and headers, and displays colorized and formatted output.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*Bqi3gBKgIkeEPEI_.gif" alt="0*Bqi3gBKgIkeEPEI_" width="1024" height="512" loading="lazy"></p>
<p>To install httpie:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install httpie

<span class="hljs-comment"># via apt for Debian</span>
sudo apt install httpie

<span class="hljs-comment"># via pacman for Arch Linux</span>
pacman -Syu httpie

<span class="hljs-comment"># via Chocolatey for Windows</span>
choco install httpie
</code></pre>
<h3 id="heading-asciinemahttpsgithubcomasciinemaasciinema-terminal-session-recorder"><a target="_blank" href="https://github.com/asciinema/asciinema"><strong>asciinema</strong></a> — Terminal session recorder.</h3>
<p>I call it a terminal YouTube :)</p>
<p>asciinema is a great tool when you want to share your terminal sessions with someone else, instead of recording heavy videos.</p>
<p>I use it often when I develop some CLI tools and want to share the demo of how they work (on GitHub, for example).</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*Exg2XuZlIPaJJ-iB.png" alt="0*Exg2XuZlIPaJJ-iB" width="700" height="389" loading="lazy"></p>
<p>To install asciinema:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install asciinema

<span class="hljs-comment"># via apt for Debian</span>
sudo apt install asciinema

<span class="hljs-comment"># via pacman for Arch Linux</span>
sudo pacman -S asciinema
</code></pre>
<h2 id="heading-networking"><strong>Networking</strong></h2>
<h3 id="heading-doggohttpsgithubcommr-karandoggo-a-command-line-dns-client"><a target="_blank" href="https://github.com/mr-karan/doggo">doggo</a> — A command-line DNS client.</h3>
<p>It's totally inspired by <strong>dog</strong> which is written in Rust.</p>
<p>In the past I would use <strong>dig</strong> to inspect the DNS, but its output is often verbose and difficult to parse visually.</p>
<p><strong>doggo</strong> addresses these shortcomings by offering two key improvements:</p>
<ul>
<li><p>doggo provides the JSON output support for easy scripting and parsing.</p>
</li>
<li><p>doggo offers a human-readable output format that uses color-coding and a tabular layout to present DNS information clearly and concisely.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737552264803/bb902365-bc0d-4a56-9a87-6b065ee5608a.png" alt="bb902365-bc0d-4a56-9a87-6b065ee5608a" class="image--center mx-auto" width="3680" height="2572" loading="lazy"></p>
<p>To install doggo:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install doggo

<span class="hljs-comment"># via scoop for Windows</span>
scoop install doggo

<span class="hljs-comment"># via go install</span>
go install github.com/mr-karan/doggo/cmd/doggo@latest
</code></pre>
<h3 id="heading-gpinghttpsgithubcomorfgping-ping-but-with-a-graph"><a target="_blank" href="https://github.com/orf/gping"><strong>gping</strong></a> — Ping, but with a graph.</h3>
<p>The well-known <strong>ping</strong> command is not the most interesting to look at, and interpreting its output in a useful way can be difficult.</p>
<p><strong>gping</strong> gives a plot of the ping latency to a host, and the most useful feature is the ability to run concurrent pings to multiple hosts and plot all of them on the same graph.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*IPi1TOpiMnWPN1VU.gif" alt="0*IPi1TOpiMnWPN1VU" width="1411" height="757" loading="lazy"></p>
<p>To install gping:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install gping

<span class="hljs-comment"># via Chocolatey for Windows</span>
choco install gping

<span class="hljs-comment"># via apt for Debian</span>
apt install gping
</code></pre>
<h2 id="heading-workstation"><strong>Workstation</strong></h2>
<h3 id="heading-tmuxhttpsgithubcomtmuxtmuxwiki-a-terminal-multiplexer"><a target="_blank" href="https://github.com/tmux/tmux/wiki"><strong>tmux</strong></a> — A terminal multiplexer.</h3>
<p>Why is tmux such a big deal?</p>
<p>You may have run into situations where you need to view multiple terminal consoles at the same time. For example, you may have a few servers running (for example, web, database, debugger) and you might want to monitor all the output coming from these servers in real-time to validate behavior or run commands.</p>
<p>Before tmux, you might have just opened a few different tabs in the terminal and switched between them to see the output.</p>
<p>Thankfully, there’s an easier way — <strong>tmux</strong>.</p>
<p>In a nutshell, here are some of its most popular features:</p>
<ul>
<li><p>Window/Pane management</p>
</li>
<li><p>Session management with persistence</p>
</li>
<li><p>Sharable sessions with other users</p>
</li>
<li><p>Scriptable configurations</p>
</li>
</ul>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*u8o0WxutrPXxg6FG.png" alt="0*u8o0WxutrPXxg6FG" width="700" height="294" loading="lazy"></p>
<p>To install tmux:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install tmux

<span class="hljs-comment"># via apt for Debian</span>
apt install tmux

<span class="hljs-comment"># via pacman for Arch Linux</span>
pacman -S tmux
</code></pre>
<h3 id="heading-zellijhttpsgithubcomzellij-orgzellij-a-terminal-workspace-with-batteries-included"><a target="_blank" href="https://github.com/zellij-org/zellij"><strong>zellij</strong></a> — A terminal workspace with batteries included.</h3>
<p>Since I listed tmux here, it also makes sense to include a new competitor, <strong>Zellij</strong>, which has been gaining traction in the developer community. Both have their own unique features and purposes.</p>
<p>Compared to traditional terminal multiplexers, zellij offers a more user-friendly interface, modern design elements, built-in layout systems, and a plugin system, making it easier for newcomers to get started.</p>
<p>I still like tmux. It has a special place in my heart because it has served a great purpose for years. But zellij is another good option.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*VwAit4tO1IjxH9dp.gif" alt="0*VwAit4tO1IjxH9dp" width="825" height="435" loading="lazy"></p>
<p>To install zellij:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install zellij

<span class="hljs-comment"># via apt for Debian</span>
apt install zellij

<span class="hljs-comment"># via pacman for Arch Linux</span>
pacman -S zellij
</code></pre>
<h3 id="heading-btophttpsgithubcomaristocratosbtop-a-monitor-of-resources"><a target="_blank" href="https://github.com/aristocratos/btop"><strong>btop</strong></a> — A monitor of resources.</h3>
<p>I can’t live without btop, and it’s installed on all my machines via my personal <a target="_blank" href="https://github.com/plutov/dotfiles">dotfiles</a>. I rarely use now built-in OS GUIs to check the resource utilization on my host machine, because <strong>btop</strong> can do it much better.</p>
<p>I use to to quickly explore what uses the most memory, monitor and kill some processes, and more.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*HbuJrCbT6xVApLoh.png" alt="0*HbuJrCbT6xVApLoh" width="700" height="441" loading="lazy"></p>
<p>To install btop:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># via Homebrew for macOS</span>
brew install btop

<span class="hljs-comment"># via snap for Debian</span>
sudo snap install btop
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>These CLIs/TUIs should work well in any modern terminal. I personally use <a target="_blank" href="https://ghostty.org/">Ghostty</a> currently and it works great, but other popular options like <strong>iTerm2, Kitty</strong>, and the default terminal applications on macOS and Linux should also provide a seamless experience. The key is to ensure your terminal supports features like 256-color palettes and UTF-8 encoding for optimal display of these tools.</p>
<p>There’s a huge amount of CLIs/TUIs out there, and I couldn’t list them all (though I tried to list some of the best). This selection represents a starting point for exploring the rich ecosystem of command-line tools available to developers. I encourage you to explore further, discover new tools that fit your specific needs, and contribute back to the community by sharing your findings.</p>
<p><a target="_blank" href="https://packagemain.tech">Explore more articles on packagemain.tech</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Understanding the Language Server Protocol – Easier Code Editing Across Languages and Tools ]]>
                </title>
                <description>
                    <![CDATA[ In the past, many code editors were built just for one specific language. To provide rich and smart code editing, tight integration between the editor and the language tooling was a must. On the other hand, there were (and still are) more general-pur... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-the-language-server-protocol-easier-code-editing-across-languages/</link>
                <guid isPermaLink="false">677ef3f83b47b21899e957c4</guid>
                
                    <category>
                        <![CDATA[ Language Server Protocol ]]>
                    </category>
                
                    <category>
                        <![CDATA[ programming languages ]]>
                    </category>
                
                    <category>
                        <![CDATA[ lsp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Wed, 08 Jan 2025 21:54:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736371728078/f1da2e66-4095-44e6-bcdf-de44c92e81ad.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In the past, many code editors were built just for one specific language. To provide rich and smart code editing, tight integration between the editor and the language tooling was a must.</p>
<p>On the other hand, there were (and still are) more general-purpose editors, but they lacked in functionality when it came to more advanced language-specific features like code completion, “go to definition”, and so on. As an example, code highlighting was done using regular expressions.</p>
<p>With the growing number of code editors and programming languages available, this became the classic <strong>M*N</strong> complexity problem.</p>
<p>But then Microsoft introduced the <a target="_blank" href="https://microsoft.github.io/language-server-protocol/">Language Server Protocol</a> (LSP) as a solution to the problem above, which elegantly transforms this <strong>M*N</strong> complexity into a more manageable <strong>M+N</strong> situation.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-microsoft-created-the-lsp">Why Microsoft Created the LSP</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-language-server-protocol-lsp">What is the Language Server Protocol (LSP)?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-language-server-features">Language Server Features</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-lsp-work">How Does LSP Work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-language-server-for-go">Language Server for Go</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lsp-adoption">LSP Adoption</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources">Resources</a></p>
</li>
</ul>
<h2 id="heading-why-microsoft-created-the-lsp">Why Microsoft Created the LSP</h2>
<p>The LSP was initially driven by the needs of VS Code. VS Code had to work with hundreds of different Language Servers as part of its extensions. But there were multiple problems:</p>
<ul>
<li><p><strong>Language mismatch</strong>: Language Servers are often written in different languages than the editor (like VS Code's Node.js).</p>
</li>
<li><p><strong>Performance impact</strong>: Language features can be resource-intensive, potentially slowing down the editor.</p>
</li>
<li><p><strong>Integration complexity</strong>: Multiple editors and multiple languages introduces the <strong>M*N</strong> complexity mentioned above.</p>
</li>
</ul>
<p>Exactly because of that, Microsoft introduced LSP to standardize the communication between language tools and editors, allowing language servers to be written in any language, run separately for better performance, and easily integrate with any LSP-compliant editor. This simplifies language support for both tool providers and editor vendors.</p>
<p>You can find more info in this <a target="_blank" href="https://code.visualstudio.com/api/language-extensions/language-server-extension-guide">Language Server Extension Guide</a>.</p>
<h2 id="heading-what-is-the-language-server-protocol-lsp">What is the Language Server Protocol (LSP)?</h2>
<p>The LSP separates language servers from code editors (language clients). By making language servers independent processes dedicated to language understanding, the LSP enables any editor to utilize a standard language server. This means that a single standard language server can be used by all editors.</p>
<p>This interoperability is achieved through a defined set of standard messages and procedures that govern communication between language servers and editors. The LSP defines the format of the messages sent using JSON-RPC between the development tool and the language server.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*Vdycq7316e_hKTCe.png" alt="Communication between the editor and the Language Server" width="700" height="265" loading="lazy"></p>
<h2 id="heading-language-server-features"><strong>Language Server Features</strong></h2>
<p>The list of features may vary for each individual language server, but usually they provide the following functionalities:</p>
<ul>
<li><p>Auto-completion</p>
</li>
<li><p>Go to definition/declaration</p>
</li>
<li><p>Find references</p>
</li>
<li><p>Code formatting</p>
</li>
<li><p>Diagnostics</p>
</li>
<li><p>Documentation</p>
</li>
</ul>
<p>And more.</p>
<p>For example, <a target="_blank" href="https://github.com/golang/tools/blob/master/gopls/doc/features/README.md">here</a> you can see the list of editor features that <strong>gopls</strong> (the Go Language Server) provides.</p>
<p>And <a target="_blank" href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#languageFeatures">here</a> you can see the full LSP specification for available features.</p>
<p>There are hundreds of Language Servers out there. Typically, every mature programming language (or markup language) has at least one Language Server. You can see the full list of language servers that implement LSP <a target="_blank" href="https://microsoft.github.io/language-server-protocol/implementors/servers/">here</a>.</p>
<h2 id="heading-how-does-lsp-work"><strong>How Does LSP Work?</strong></h2>
<p>The Language Server Protocol is built upon <a target="_blank" href="https://www.jsonrpc.org/">JSON-RPC</a>. It specifically uses JSON RPC 2.0. You can think of JSON-RPC as a remote procedure call protocol that uses JSON for data encoding.</p>
<p>In a nutshell, it works like this. First, the editor establishes a connection with the language server. Then as the developer types code, the editor sends incremental changes to the language server. It then sends back insights like code completion suggestions and diagnostics.</p>
<p>Let’s see one real example for auto-completion. The request from the Language Client (editor) for this case will be:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"jsonrpc"</span>: <span class="hljs-string">"2.0"</span>,
  <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
  <span class="hljs-attr">"method"</span>: <span class="hljs-string">"textDocument/completion"</span>,
  <span class="hljs-attr">"params"</span>: {
    <span class="hljs-attr">"textDocument"</span>: {
      <span class="hljs-attr">"uri"</span>: <span class="hljs-string">"file:///home/alex/code/test/main.go"</span>
    },
    <span class="hljs-attr">"position"</span>: {
      <span class="hljs-attr">"line"</span>: <span class="hljs-number">35</span>,
      <span class="hljs-attr">"character"</span>: <span class="hljs-number">21</span>
    }
  }
}
</code></pre>
<p>As you can see, it sends the information about current cursor position and the buffer file. Let’s break it down:</p>
<ul>
<li><p><strong>ID</strong>: The client sets this field to identify the request uniquely. Once the request is processed, it will return a response with the same request ID so that the client can match which response is for what request.</p>
</li>
<li><p><strong>Method</strong>: A string containing the name of the method to be invoked.</p>
</li>
<li><p><strong>Params</strong>: The parameters to be passed to the method. This can be structured as an array or an object.</p>
</li>
</ul>
<p>Language server can access this file, analyze it, and respond with suggestions:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"jsonrpc"</span>: <span class="hljs-string">"2.0"</span>,
  <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
  <span class="hljs-attr">"result"</span>: {
    <span class="hljs-attr">"isIncomplete"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"items"</span>: [
      {
        <span class="hljs-attr">"label"</span>: <span class="hljs-string">"Println"</span>,
        <span class="hljs-attr">"kind"</span>: <span class="hljs-number">3</span>,
        <span class="hljs-attr">"insertText"</span>: <span class="hljs-string">"Println(${1:format}, ${2:a ...interface{}})$0"</span>,
        <span class="hljs-attr">"insertTextFormat"</span>: <span class="hljs-number">2</span>,
        <span class="hljs-attr">"detail"</span>: <span class="hljs-string">"func Println(a ...interface{}) (n int, err error)"</span>,
        <span class="hljs-attr">"documentation"</span>: <span class="hljs-string">"Println formats ..."</span>
      },
      <span class="hljs-comment">// ... other items</span>
    ]
  }
}
</code></pre>
<h2 id="heading-language-server-for-go"><strong>Language Server for Go</strong></h2>
<p>The most popular and commonly used language server for Go is <a target="_blank" href="https://github.com/golang/tools/tree/master/gopls">gopls</a>. It is used by many editors, for example by the <a target="_blank" href="https://github.com/golang/vscode-go">Visual Studio Code Go extension</a>. Previously, there was another popular Language Server for Go by the Sourcegraph team called <a target="_blank" href="https://github.com/sourcegraph/go-langserver">go-langserver</a>, but this is no longer under active maintenance.</p>
<p>Many editors install gopls Language Server automatically if it’s not present on the host machine. But you can install it manually as well:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># check if its' installed and which version</span>
gopls version
<span class="hljs-comment"># golang.org/x/tools/gopls v0.16.2</span>

<span class="hljs-comment"># install or upgrade</span>
go install golang.org/x/tools/gopls@latest
</code></pre>
<p>gopls also provides an experimental CLI interface:</p>
<pre><code class="lang-bash">gopls references ./main.go:35:8
</code></pre>
<h2 id="heading-lsp-adoption"><strong>LSP Adoption</strong></h2>
<p>The trend is definitely towards LSP adoption. Many editors that didn't initially support it are adding LSP capabilities due to its benefits.</p>
<p>But it's important to note that not all editors use LSP. Classic editors like <strong>Vi</strong>, <strong>Vim</strong> (in its basic configuration), and <strong>emacs</strong> (without specific plugins) traditionally rely on simpler methods for language support, such as syntax highlighting based on regular expressions.</p>
<p>Also, when your editor uses a Language Server, it can have a noticeable impact on CPU and memory, especially for large projects or complex languages. The good news is that you can choose a more efficient language server or disable them in your editor.</p>
<p>Here is how I can inspect what language servers my <a target="_blank" href="https://zed.dev/">Zed</a> editor is currently running:</p>
<pre><code class="lang-bash">ps aux | grep language-server

node yaml-language-server --stdio
node tailwindcss-language-server --stdio
...
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Thanks to the Language Server Protocol, advanced coding capabilities are becoming universally accessible across various programming languages and coding environments.</p>
<p>It’s good to know how your code editors work, so it’s beneficial to have an understanding of this widely used technology called LSP. In short, LSP knowledge helps you understand and better utilize modern coding tools.</p>
<p>LSP is a win for both language providers and tooling vendors.</p>
<h3 id="heading-resources"><strong>Resources</strong></h3>
<ul>
<li><p><a target="_blank" href="https://microsoft.github.io/language-server-protocol/">Language Server Protocol</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/golang/tools/tree/master/gopls">gopls</a></p>
</li>
<li><p>Discover more articles on <a target="_blank" href="https://packagemain.tech/">packagemain.tech</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Run Integration Tests with GitHub Service Containers ]]>
                </title>
                <description>
                    <![CDATA[ Recently, I published an article about using Testcontainers to emulate external dependencies like a database and cache for backend integration tests. That article also explained the different ways of running the integration tests, environment scaffol... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-run-integration-tests-with-github-service-containers/</link>
                <guid isPermaLink="false">677d8125f9b13835118c7958</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ github-actions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ containers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CI/CD ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Tue, 07 Jan 2025 19:31:49 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735305764768/8e3d8980-456b-4828-abb7-dff749bbf1fd.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Recently, I published an <a target="_blank" href="https://www.freecodecamp.org/news/integration-tests-using-testcontainers/"><strong>article</strong></a> about using <a target="_blank" href="https://testcontainers.com/"><strong>Testcontainers</strong></a> to emulate external dependencies like a database and cache for backend integration tests. That article also explained the different ways of running the integration tests, environment scaffolding, and their pros and cons.</p>
<p>In this article, I want to show another alternative in case you use GitHub Actions as your CI platform (the most popular CI/CD solution at the moment). This alternative is called <a target="_blank" href="https://docs.github.com/en/actions/use-cases-and-examples/using-containerized-services/about-service-containers"><strong>Service Containers</strong></a>, and I’ve realized that not many developers seem to know about it.</p>
<p>In this hands-on tutorial, I’ll demonstrate how to create a GitHub Actions workflow for integration tests with external dependencies (MongoDB and Redis) using the <a target="_blank" href="https://github.com/plutov/packagemain/tree/master/testcontainers-demo">demo Go application</a> we created in that previous tutorial. We’ll also review the pros and cons of GitHub Service Containers.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>A basic understanding of GitHub Actions workflows.</p>
</li>
<li><p>Familiarity with Docker containers.</p>
</li>
<li><p>Basic knowledge of Go toolchain.</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-are-service-containers">What are Service Containers?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-not-docker-compose">Why not Docker Compose?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-job-runtime">Job Runtime</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-readiness-healthcheck">Readiness Healthcheck</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-private-container-registries">Private Container Registries</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sharing-data-between-services">Sharing Data Between Services</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-golang-integration-tests">Golang Integration Tests</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-personal-experience-amp-limitations">Personal Experience &amp; Limitations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources">Resources</a></p>
</li>
</ul>
<h2 id="heading-what-are-service-containers">What are Service Containers?</h2>
<p>Service Containers are Docker containers that offer a simple and portable way to host dependencies like databases (MongoDB in our example), web services, or caching systems (Redis in our example) that your application needs within a workflow.</p>
<p>This article focuses on integration tests, but there are many other possible applications for service containers. For example, you can also use them to run supporting tools required by your workflow, such as code analysis tools, linters, or security scanners.</p>
<h2 id="heading-why-not-docker-compose">Why Not Docker Compose?</h2>
<p>Sounds similar to <strong>services</strong> in Docker Compose, right? Well, that’s because it is.</p>
<p>But while you could technically <a target="_blank" href="https://github.com/marketplace/actions/docker-compose-action">use Docker Compose</a> within a GitHub Actions workflow by installing Docker Compose and running <strong>docker-compose up</strong>, service containers provide a more integrated and streamlined approach that’s specifically designed for the GitHub Actions environment.</p>
<p>Also, while they are similar, they solve different problems and have different general purposes:</p>
<ul>
<li><p>Docker Compose is good when you need to manage a multi-container application on your local machine or a single server. It’s best suited for long-living environments.</p>
</li>
<li><p>Service Containers are ephemeral and exist only for the duration of a workflow run, and they’re defined directly within your GitHub Actions workflow file.</p>
</li>
</ul>
<p>Just keep in mind that the feature set of service containers (at least as of now) is more limited compared to Docker Compose, so be ready to discover some potential bottlenecks. We will cover some of them at the end of this article.</p>
<h2 id="heading-job-runtime">Job Runtime</h2>
<p>You can run GitHub jobs directly on a runner machine or in a Docker container (by specifying the <strong>container</strong> property). The second option simplifies the access to your services by using labels you define in the <strong>services</strong> section.</p>
<p>To run directly on a runner machine:</p>
<p><strong>.github/workflows/test.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">integration-tests:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-24.04</span>

    <span class="hljs-attr">services:</span>
      <span class="hljs-attr">mongo:</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">mongodb/mongodb-community-server:7.0-ubi8</span>
        <span class="hljs-attr">ports:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-number">27017</span><span class="hljs-string">:27017</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">|</span>
          <span class="hljs-string">echo</span> <span class="hljs-string">"addr 127.0.0.1:27017"</span>
</code></pre>
<p>Or you can run it in a container (<a target="_blank" href="https://images.chainguard.dev/directory/image/go/overview">Chainguard Go Image</a> in our case):</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">integration-tests:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-24.04</span>
    <span class="hljs-attr">container:</span> <span class="hljs-string">cgr.dev/chainguard/go:latest</span>

    <span class="hljs-attr">services:</span>
      <span class="hljs-attr">mongo:</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">mongodb/mongodb-community-server:7.0-ubi8</span>
        <span class="hljs-attr">ports:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-number">27017</span><span class="hljs-string">:27017</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">|</span>
          <span class="hljs-string">echo</span> <span class="hljs-string">"addr mongo:27017"</span>
</code></pre>
<p>You can also omit the host port, so the container port will be randomly assigned to a free port on the host. You can then access the port using the variable.</p>
<p>Benefits of omitting the host port:</p>
<ul>
<li><p>Avoids port conflicts – for example when you run many services on the same host.</p>
</li>
<li><p>Enhances Portability – your configurations become less dependent on the specific host environment.</p>
</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">integration-tests:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-24.04</span>
    <span class="hljs-attr">container:</span> <span class="hljs-string">cgr.dev/chainguard/go:1.23</span>

    <span class="hljs-attr">services:</span>
      <span class="hljs-attr">mongo:</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">mongodb/mongodb-community-server:7.0-ubi8</span>
        <span class="hljs-attr">ports:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-number">27017</span><span class="hljs-string">/tcp</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">|</span>
          <span class="hljs-string">echo</span> <span class="hljs-string">"addr mongo:$<span class="hljs-template-variable">{{ job.services.mongo.ports['27017'] }}</span>"</span>
</code></pre>
<p>Of course, there are pros and cons to each approach.</p>
<p>Running in a container:</p>
<ul>
<li><p><strong>Pros</strong>: Simplified network access (use labels as hostnames), and automatic port exposure within the container network. You also get better isolation/security as the job runs in an isolated environment.</p>
</li>
<li><p><strong>Cons</strong>: Implied overhead of containerization.</p>
</li>
</ul>
<p>Running on the runner machine:</p>
<ul>
<li><p><strong>Pros</strong>: Potentially less overhead than running the job inside a container.</p>
</li>
<li><p><strong>Cons</strong>: Requires manual port mapping for service container access (using localhost:). There’s also less isolation/security, as the job runs directly on the runner machine. This potentially affects other jobs or the runner itself if something goes wrong.</p>
</li>
</ul>
<h2 id="heading-readiness-healthcheck">Readiness Healthcheck</h2>
<p>Prior to running the integration tests that connect to your provisioned containers, you’ll often need to make sure that the services are ready. You can do this by specifying <a target="_blank" href="https://docs.docker.com/reference/cli/docker/container/create/#options">docker create options</a> such as <strong>health-cmd</strong>.</p>
<p>This is very important – otherwise the services may not be ready when you start accessing them.</p>
<p>In the case of MongoDB and Redis, these will be the following:</p>
<pre><code class="lang-yaml">    <span class="hljs-attr">services:</span>
      <span class="hljs-attr">mongo:</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">mongodb/mongodb-community-server:7.0-ubi8</span>
        <span class="hljs-attr">ports:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-number">27017</span><span class="hljs-string">/27017</span>
        <span class="hljs-attr">options:</span> <span class="hljs-string">&gt;-
          --health-cmd "echo 'db.runCommand("ping").ok' | mongosh mongodb://localhost:27017/test --quiet"
          --health-interval 5s
          --health-timeout 10s
          --health-retries 10
</span>
      <span class="hljs-attr">redis:</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">redis:7</span>
        <span class="hljs-attr">ports:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-number">6379</span><span class="hljs-string">:6379</span>
        <span class="hljs-attr">options:</span> <span class="hljs-string">&gt;-
          --health-cmd "redis-cli ping"
          --health-interval 5s
          --health-timeout 10s
          --health-retries 10</span>
</code></pre>
<p>In the Action logs, you can see the readiness status:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736245987630/0b0bf229-b8d3-4e4e-8e0b-e3bbe5f9a6d8.png" alt="GitHub Actions Logs" class="image--center mx-auto" width="512" height="210" loading="lazy"></p>
<h2 id="heading-private-container-registries">Private Container Registries</h2>
<p>In our example, we’re using public images from Dockerhub, but it’s possible to use private images from you private registries as well, such as Amazon Elastic Container Registry (ECR), Google Artifact Registry, and so on.</p>
<p>Make sure to store the credentials in <a target="_blank" href="https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions">Secrets</a> and then reference them in the <strong>credentials</strong> section.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">private_service:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">ghcr.io/org/service_repo</span>
    <span class="hljs-attr">credentials:</span>
      <span class="hljs-attr">username:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.registry_username</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">password:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.registry_token</span> <span class="hljs-string">}}</span>
</code></pre>
<h2 id="heading-sharing-data-between-services">Sharing Data Between Services</h2>
<p>You can use volumes to share data between services or other steps in a job. You can specify named Docker volumes, anonymous Docker volumes, or bind mounts on the host. But it’s not directly possible to mount the source code as a container volume. You can refer to this <a target="_blank" href="https://github.com/orgs/community/discussions/42127">open discussion</a> for more context.</p>
<p>To specify a volume, you specify the source and destination path: <code>&lt;source&gt;:&lt;destinationPath&gt;</code></p>
<p>The <code>&lt;source&gt;</code> is a volume name or an absolute path on the host machine, and <code>&lt;destinationPath&gt;</code> is an absolute path in the container.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">volumes:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">/src/dir:/dst/dir</span>
</code></pre>
<p>Volumes in Docker (and GitHub Actions using Docker) provide persistent data storage and sharing between containers or job steps, decoupling data from container images.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Before diving into the full source code, let's set up our project for running integration tests with GitHub Service Containers.</p>
<ol>
<li><p>Create a new GitHub repository.</p>
</li>
<li><p>Initialize a Go module using <code>go mod init</code></p>
</li>
<li><p>Create a simple Go application.</p>
</li>
<li><p>Add integration tests in <code>integration_test.go</code></p>
</li>
<li><p>Create a <code>.github/workflows</code> directory.</p>
</li>
<li><p>Create a file named <code>integration-tests.yaml</code> inside the <code>.github/workflows</code> directory.</p>
</li>
</ol>
<h2 id="heading-golang-integration-tests">Golang Integration Tests</h2>
<p>Now as we can provision our external dependencies, let’s have a look at how to run our integration tests in Go. We will do it in the <strong>steps</strong> section of our workflow file.</p>
<p>We will run our tests in a container which uses <a target="_blank" href="https://images.chainguard.dev/directory/image/go/overview">Chainguard Go image</a>. This means we don’t have to install/setup Go. If you want to run your tests directly on a runner machine, you need to use the <a target="_blank" href="https://github.com/actions/setup-go">setup-go</a> Action.</p>
<p>You can find the full source code with tests and this workflow <a target="_blank" href="https://github.com/plutov/service-containers">here</a>.</p>
<p><strong>.github/workflows/integration-tests.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">"integration-tests"</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">workflow_dispatch:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">integration-tests:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-24.04</span>
    <span class="hljs-attr">container:</span> <span class="hljs-string">cgr.dev/chainguard/go:latest</span>

    <span class="hljs-attr">env:</span>
      <span class="hljs-attr">MONGO_URI:</span> <span class="hljs-string">mongodb://mongo:27017</span>
      <span class="hljs-attr">REDIS_URI:</span> <span class="hljs-string">redis://redis:6379</span>

    <span class="hljs-attr">services:</span>
      <span class="hljs-attr">mongo:</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">mongodb/mongodb-community-server:7.0-ubi8</span>
        <span class="hljs-attr">ports:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-number">27017</span><span class="hljs-string">:27017</span>
        <span class="hljs-attr">options:</span> <span class="hljs-string">&gt;-
          --health-cmd "echo 'db.runCommand("ping").ok' | mongosh mongodb://localhost:27017/test --quiet"
          --health-interval 5s
          --health-timeout 10s
          --health-retries 10
</span>
      <span class="hljs-attr">redis:</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">redis:7</span>
        <span class="hljs-attr">ports:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-number">6379</span><span class="hljs-string">:6379</span>
        <span class="hljs-attr">options:</span> <span class="hljs-string">&gt;-
          --health-cmd "redis-cli ping"
          --health-interval 5s
          --health-timeout 10s
          --health-retries 10
</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Check</span> <span class="hljs-string">out</span> <span class="hljs-string">repository</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Download</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">go</span> <span class="hljs-string">mod</span> <span class="hljs-string">download</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Integration</span> <span class="hljs-string">Tests</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">go</span> <span class="hljs-string">test</span> <span class="hljs-string">-tags=integration</span> <span class="hljs-string">-timeout=120s</span> <span class="hljs-string">-v</span> <span class="hljs-string">./...</span>
</code></pre>
<p>To summarize what’s going on here:</p>
<ol>
<li><p>We run our job in a container with Go (<strong>container</strong>)</p>
</li>
<li><p>We spin up two services: MongoDB and Redis (<strong>services</strong>)</p>
</li>
<li><p>We configure healthchecks to make sure our services are “Healthy” when we run the tests (<strong>options</strong>)</p>
</li>
<li><p>We perform a standard code checkout</p>
</li>
<li><p>Then we run the Go tests</p>
</li>
</ol>
<p>Once the Action is completed (it took <strong>~1 min</strong> for this example), all the services will be stopped and orphaned so we don’t need to worry about that.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:480/0*QLl4vjotU6o1osy-.png" alt="GitHub Actions Logs: full run" width="480" height="409" loading="lazy"></p>
<h2 id="heading-personal-experience-amp-limitations">Personal Experience &amp; Limitations</h2>
<p>We’ve been using service containers for running backend integration tests at <a target="_blank" href="https://www.binarly.io/">BINARLY</a> for some time, and they work great. But the initial workflow creation took some time and we encountered the following bottlenecks:</p>
<ul>
<li><p>It’s not possible to override or run custom commands in an action service container (as you would do in Docker Compose using the <strong>command</strong> property). <a target="_blank" href="https://github.com/actions/runner/pull/1152">Open pull request</a></p>
<ul>
<li>Workaround: we had to find a solution that doesn’t require that. In our case, we were lucky and could do the same with environment variables.</li>
</ul>
</li>
<li><p>It’s not directly possible to mount the source code as a container volume. <a target="_blank" href="https://github.com/orgs/community/discussions/42127">Open discussion</a></p>
<ul>
<li>While this is indeed a big limitation, you can copy the code from your repository into your mounted directory after the service container has started.</li>
</ul>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>GitHub service containers are a great option to scaffold an ephemeral testing environment by configuring it directly in your GitHub workflow. With configuration being somewhat similar to Docker Compose, it’s easy to run any containerised application and communication with it in your pipeline. This ensures that GitHub runners take care of shutting everything down upon completion.</p>
<p>If you use Github Actions, this approach works extremely well as it is specifically designed for the GitHub Actions environment.</p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><p><a target="_blank" href="https://github.com/plutov/service-containers">Source Code</a></p>
</li>
<li><p><a target="_blank" href="https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idservices">GitHub Documentation</a></p>
</li>
<li><p>Discover more articles on <a target="_blank" href="https://packagemain.tech/">packagemain.tech</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Fuzz Test Golang HTTP Services ]]>
                </title>
                <description>
                    <![CDATA[ As a developer, you can’t always envision all of the possible inputs your programs or functions might receive. Even though you can define the major edge cases, you still can’t predict how your program will behave in the case of some weird unexpected ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-fuzz-test-golang-http-services/</link>
                <guid isPermaLink="false">672941e6066de072ede39685</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ fuzzing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Mon, 04 Nov 2024 21:51:34 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730664559414/b64781c3-341f-4a5d-94fe-38ff78099b42.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>As a developer, you can’t always envision all of the possible inputs your programs or functions might receive.</p>
<p>Even though you can define the major edge cases, you still can’t predict how your program will behave in the case of some weird unexpected input. In other words, you can typically only find bugs you <em>expect</em> to find.</p>
<p>That’s where fuzz testing or fuzzing comes to the rescue. And in this tutorial, you’ll learn how to perform fuzz testing in Go.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-fuzz-testing">What is Fuzz Testing</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-fuzz-testing-in-go">Fuzz Testing in Go</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-fuzzing-http-services">Fuzzing HTTP Services</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources">Resources</a></p>
</li>
</ul>
<h2 id="heading-what-is-fuzz-testing">What is Fuzz Testing?</h2>
<p>Fuzzing is an automated software testing technique that involves inputting a large amount of valid, nearly-valid, or invalid random data into a computer program and observing its behavior and output. So the goal of fuzzing is to reveal bugs, crashes, and security vulnerabilities in source code you might not find through traditional testing methods.</p>
<p>The <a target="_blank" href="https://youtu.be/w8STTZWdG9Y">Fuzz Testing in Go</a> video which I made a few years ago shows a very simple example of the Go code that may work well unless you provide a certain input:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Equal</span><span class="hljs-params">(a []<span class="hljs-keyword">byte</span>, b []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">bool</span></span> {
  <span class="hljs-keyword">for</span> i := <span class="hljs-keyword">range</span> a {
    <span class="hljs-comment">// can panic with runtime error: index out of range.</span>
    <span class="hljs-keyword">if</span> a[i] != b[i] {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
  }

  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
}
</code></pre>
<p>This sample function works perfectly as long as the length of two slices are equal. But it will panic when the first slice is longer than the second (index out of range error). Furthermore, it doesn’t return a correct result when the second slice is the subset of the first one.</p>
<p>The fuzzing technique would easily spot this bug by bombarding this function with various inputs.</p>
<p>It’s a good practice to integrate fuzzing into your team’s software development lifecycle (SDLC) as well. For example, Microsoft uses fuzzing as <a target="_blank" href="https://learn.microsoft.com/en-us/compliance/assurance/assurance-microsoft-security-development-lifecycle">one of the stages in its SDLC</a>, to find potential bugs and vulnerabilities.</p>
<h2 id="heading-fuzz-testing-in-go">Fuzz Testing in Go</h2>
<p>There are many fuzzing tools that have been available for a while – such as <a target="_blank" href="https://github.com/google/oss-fuzz">oss-fuzz</a>, for example – but since Go 1.18, fuzzing was added to Go’s standard library. So it’s now part of the regular testing package since it’s a kind of test. You can also use it together with the other testing primitives which is nice.</p>
<p>The steps to create a fuzz test in Go are the following:</p>
<ol>
<li><p>In a <strong>_test.go</strong> file, create a function that starts with <code>Fuzz</code> which accepts <code>*testing.F</code></p>
</li>
<li><p>Add corpus seeds using <code>f.Add()</code> to allow the fuzzer to generate the data based on it.</p>
</li>
<li><p>Call the fuzz target using <code>f.Fuzz()</code> by passing fuzzing arguments which our target function accepts.</p>
</li>
<li><p>Start the fuzzer using the regular <code>go test</code> command, but with the <code>–fuzz=Fuzz</code> flag</p>
</li>
</ol>
<p>Note that the fuzzing arguments can only be the following types:</p>
<ul>
<li><p>string, byte, []byte</p>
</li>
<li><p>int, int8, int16, int32/rune, int64</p>
</li>
<li><p>uint, uint8, uint16, uint32, uint64</p>
</li>
<li><p>float32, float64</p>
</li>
<li><p>bool</p>
</li>
</ul>
<p>A simple fuzz test for the <strong>Equal</strong> function above may look like this:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Fuzz test</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">FuzzEqual</span><span class="hljs-params">(f *testing.F)</span></span> {
  <span class="hljs-comment">// Seed corpus addition</span>
  f.Add([]<span class="hljs-keyword">byte</span>{<span class="hljs-string">'f'</span>, <span class="hljs-string">'u'</span>, <span class="hljs-string">'z'</span>, <span class="hljs-string">'z'</span>}, []<span class="hljs-keyword">byte</span>{<span class="hljs-string">'t'</span>, <span class="hljs-string">'e'</span>, <span class="hljs-string">'s'</span>, <span class="hljs-string">'t'</span>})

  <span class="hljs-comment">// Fuzz target with fuzzing arguments</span>
  f.Fuzz(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T, a []<span class="hljs-keyword">byte</span>, b []<span class="hljs-keyword">byte</span>)</span></span> {
    <span class="hljs-comment">// Call our target function and pass fuzzing arguments</span>
    Equal(a, b)
  })
}
</code></pre>
<p>By default, fuzz tests run forever, so you either need to specify the time limit or wait for fuzz tests to fail. You can specify which tests to run using the <code>--fuzz</code> argument.</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> --fuzz=Fuzz -fuzztime=10s
</code></pre>
<p>If there are any errors during the execution, the output should look similar to this:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> --fuzz=Fuzz -fuzztime=30s
--- FAIL: FuzzEqual (0.02s)
    --- FAIL: FuzzEqual (0.00s)
        testing.go:1591: panic: runtime error: index out of range
    Failing input written to testdata/fuzz/FuzzEqual/84ed65595ad05a58
    To re-run:
    go <span class="hljs-built_in">test</span> -run=FuzzEqual/84ed65595ad05a58
</code></pre>
<p>Notice that the input for which the <code>fuzz</code> test has failed are written into a file in the <code>testdata</code> folder and can be re-played by using that input identifier.</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> -run=FuzzEqual/84ed65595ad05a58
</code></pre>
<p>The <code>testdata</code> folder can be checked into the repository and be used for regular tests, because fuzz tests can also act as regular tests when executed without the <code>--fuzz</code> flag.</p>
<h2 id="heading-fuzzing-http-services">Fuzzing HTTP Services</h2>
<p>It’s also possible to fuzz test the HTTP services by writing a test for your <code>HandlerFunc</code> and using the <code>httptest</code> package. This can be very useful if you need to test the whole HTTP service, not only the underlying functions.</p>
<p>Let’s now introduce a more real example such as an HTTP Handler that accepts some user input in the request body and then write a fuzz test for it.</p>
<p>Our handler accepts a JSON request with <code>limit</code> and <code>offset</code> fields to paginate some static mocked data. Let's define the types first.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Request <span class="hljs-keyword">struct</span> {
  Limit  <span class="hljs-keyword">int</span> <span class="hljs-string">`json:"limit"`</span>
  Offset <span class="hljs-keyword">int</span> <span class="hljs-string">`json:"offset"`</span>
}

<span class="hljs-keyword">type</span> Response <span class="hljs-keyword">struct</span> {
  Results    []<span class="hljs-keyword">int</span> <span class="hljs-string">`json:"items"`</span>
  PagesCount <span class="hljs-keyword">int</span>   <span class="hljs-string">`json:"pagesCount"`</span>
}
</code></pre>
<p>Our handler function then parses the JSON, paginates the static slice, and returns a new JSON in response.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ProcessRequest</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
 <span class="hljs-keyword">var</span> req Request

  <span class="hljs-comment">// Decode JSON request</span>
  <span class="hljs-keyword">if</span> err := json.NewDecoder(r.Body).Decode(&amp;req); err != <span class="hljs-literal">nil</span> {
    http.Error(w, err.Error(), http.StatusBadRequest)
    <span class="hljs-keyword">return</span>
  }

 <span class="hljs-comment">// Apply offset and limit to some static data</span>
 all := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">int</span>, <span class="hljs-number">1000</span>)
 start := req.Offset
 end := req.Offset + req.Limit
 res := Response{
   Results:    all[start:end],
   PagesCount: <span class="hljs-built_in">len</span>(all) / req.Limit,
 }

 <span class="hljs-comment">// Send JSON response</span>
 <span class="hljs-keyword">if</span> err := json.NewEncoder(w).Encode(res); err != <span class="hljs-literal">nil</span> {
   http.Error(w, err.Error(), http.StatusInternalServerError)
   <span class="hljs-keyword">return</span>
 }

 w.WriteHeader(http.StatusOK)
}
</code></pre>
<p>As you may have already noticed, this function doesn’t handle slice operations very well and can easily <code>panic</code>. Also, it can panic if it tries to divide by 0. It's great if we can spot this during the development or using only unit tests, but sometimes not everything is visible to our eye, and our handler may pass the input to other functions and so forth.</p>
<p>Following our <code>FuzzEqual</code> example above, let’s implement a fuzz test for the <code>ProcessRequest</code> handler. The first thing we need to do is to provide the sample inputs for the fuzzer. This is the data that the fuzzer will use and modify into new inputs that are tried. We can craft some sample JSON request and use <code>f.Add()</code> with the <code>[]byte</code> type.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">FuzzProcessRequest</span><span class="hljs-params">(f *testing.F)</span></span> {
  <span class="hljs-comment">// Create sample inputs for the fuzzer</span>
  testRequests := []Request{
    {Limit: <span class="hljs-number">-10</span>, Offset: <span class="hljs-number">-10</span>},
    {Limit: <span class="hljs-number">0</span>, Offset: <span class="hljs-number">0</span>},
    {Limit: <span class="hljs-number">100</span>, Offset: <span class="hljs-number">100</span>},
    {Limit: <span class="hljs-number">200</span>, Offset: <span class="hljs-number">200</span>},
  }

  <span class="hljs-comment">// Add to the seed corpus</span>
  <span class="hljs-keyword">for</span> _, r := <span class="hljs-keyword">range</span> testRequests {
    <span class="hljs-keyword">if</span> data, err := json.Marshal(r); err == <span class="hljs-literal">nil</span> {
      f.Add(data)
    }
  }

  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>After that we can use the <strong>httptest</strong> package to create a test HTTP server and make requests to it.</p>
<p>Note: Since our fuzzer can generate invalid non-JSON requests, it’s better just to skip them and ignore with <code>t.Skip()</code>. We can also skip <code>BadRequest</code> errors.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">FuzzProcessRequest</span><span class="hljs-params">(f *testing.F)</span></span> {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-comment">// Create a test server</span>
  srv := httptest.NewServer(http.HandlerFunc(ProcessRequest))
  <span class="hljs-keyword">defer</span> srv.Close()

  <span class="hljs-comment">// Fuzz target with a single []byte argument</span>
  f.Fuzz(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T, data []<span class="hljs-keyword">byte</span>)</span></span> {
    <span class="hljs-keyword">var</span> req Request
    <span class="hljs-keyword">if</span> err := json.Unmarshal(data, &amp;req); err != <span class="hljs-literal">nil</span> {
      <span class="hljs-comment">// Skip invalid JSON requests that may be generated during fuzz</span>
      t.Skip(<span class="hljs-string">"invalid json"</span>)
    }

    <span class="hljs-comment">// Pass data to the server</span>
    resp, err := http.DefaultClient.Post(srv.URL, <span class="hljs-string">"application/json"</span>, bytes.NewBuffer(data))
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
      t.Fatalf(<span class="hljs-string">"unable to call server: %v, data: %s"</span>, err, <span class="hljs-keyword">string</span>(data))
    }
    <span class="hljs-keyword">defer</span> resp.Body.Close()

    <span class="hljs-comment">// Skip BadRequest errors</span>
    <span class="hljs-keyword">if</span> resp.StatusCode == http.StatusBadRequest {
      t.Skip(<span class="hljs-string">"invalid json"</span>)
    }

    <span class="hljs-comment">// Check status code</span>
    <span class="hljs-keyword">if</span> resp.StatusCode != http.StatusOK {
      t.Fatalf(<span class="hljs-string">"non-200 status code %d"</span>, resp.StatusCode)
    }
  })
}
</code></pre>
<p>Our fuzz target has a single argument with a type <code>[]byte</code> that contains the full JSON request, but you can change it to have multiple arguments.</p>
<p>Everything is ready now to run our fuzz tests. When fuzzing HTTP servers, you may need to adjust the amount of parallel workers, otherwise the load may overwhelm the test server. You can do that by setting <code>-parallel=1</code> flag.</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> --fuzz=Fuzz -fuzztime=10s -parallel=1
go <span class="hljs-built_in">test</span> --fuzz=Fuzz -fuzztime=30s
--- FAIL: FuzzProcessRequest (0.02s)
    --- FAIL: FuzzProcessRequest (0.00s)
        runtime error: <span class="hljs-built_in">integer</span> divide by zero
        runtime error: slice bounds out of range
</code></pre>
<p>And as expected, we will see the above errors uncovered.</p>
<p>We can also see the fuzz inputs in the <code>testdata</code> folder to see which JSON contributed to this failure. Here is a sample content of the file:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> fuzz v1
[]byte(<span class="hljs-string">"{"</span><span class="hljs-built_in">limit</span><span class="hljs-string">":0,"</span>offset<span class="hljs-string">":0}"</span>)
</code></pre>
<p>To fix that issue, we can introduce input validation and default settings:</p>
<pre><code class="lang-go"><span class="hljs-keyword">if</span> req.Limit &lt;= <span class="hljs-number">0</span> {
  req.Limit = <span class="hljs-number">1</span>
}
<span class="hljs-keyword">if</span> req.Offset &lt; <span class="hljs-number">0</span> {
  req.Offset = <span class="hljs-number">0</span>
}
<span class="hljs-keyword">if</span> req.Offset &gt; <span class="hljs-built_in">len</span>(all) {
  start = <span class="hljs-built_in">len</span>(all) - <span class="hljs-number">1</span>
}
<span class="hljs-keyword">if</span> end &gt; <span class="hljs-built_in">len</span>(all) {
  end = <span class="hljs-built_in">len</span>(all)
}
</code></pre>
<p>With this change, the fuzz tests will run for 10 seconds and exit without an error.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Writing fuzz tests for your HTTP services or any other methods is a great way to detect hard-to-find bugs. Fuzzers can detect hard-to-spot bugs that happen for only some weird unexpected input.</p>
<p>It’s amazing to see that fuzzing is a part of Go’s built-in testing library, making it easy to combine with regular tests. Note: prior to Go 1.18, developers used <a target="_blank" href="https://github.com/dvyukov/go-fuzz">go-fuzz</a>, which is a great tool for fuzzing as well.</p>
<h3 id="heading-resources"><strong>Resources</strong></h3>
<ul>
<li><p><a target="_blank" href="https://github.com/plutov/packagemain/tree/master/fuzz-testing-http-services">Source Code</a></p>
</li>
<li><p><a target="_blank" href="https://youtu.be/w8STTZWdG9Y">Fuzz Testing in Go</a></p>
</li>
<li><p><a target="_blank" href="https://go.dev/doc/security/fuzz/">Go Fuzzing</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Self-host a Container Registry ]]>
                </title>
                <description>
                    <![CDATA[ A container registry is a storage catalog from where you can push and pull container images. There are many public and private registries available to developers such as Docker Hub, Amazon ECR, and Google Cloud Artifact Registry. But sometimes, inste... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-self-host-a-container-registry/</link>
                <guid isPermaLink="false">670ea63e203bba3017cc96ff</guid>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ containers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ nginx ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SSL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Tue, 15 Oct 2024 17:28:30 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728918386211/cf6fd053-453e-4257-abcd-16942c345845.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A container registry is a storage catalog from where you can push and pull container images.</p>
<p>There are many public and private registries available to developers such as <a target="_blank" href="https://hub.docker.com/">Docker Hub</a>, <a target="_blank" href="https://aws.amazon.com/ecr/">Amazon ECR</a>, and <a target="_blank" href="https://cloud.google.com/artifact-registry/docs">Google Cloud Artifact Registry</a>. But sometimes, instead of relying on an external vendor, you might want to host your images yourself. This gives you more control over how the registry is configured and where the container images are hosted.</p>
<p>This article is a hands-on tutorial that’ll teach you how to self-host a Container Registry.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-container-image">What is a Container Image?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-container-registry">What is a Container Registry?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-you-might-want-to-self-host-a-container-registry">Why you might want to self-host a Container Registry</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-self-host-a-container-registry">How to self-host a Container Registry</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-install-docker-and-docker-compose-on-the-server">Step 1: Install Docker and Docker Compose on the server</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-configure-and-run-the-registry-container">Step 2: Configure and run the registry container</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-run-nginx-for-handling-tls">Step 3: Run NGINX for handling TLS</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-ready-to-go">Ready to go!</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-other-options">Other options</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<p>You will get the most out of this article if you’re already familiar with the tools like Docker and NGINX, and have a general understanding of what a container is.</p>
<h2 id="heading-what-is-a-container-image">What is a Container Image?</h2>
<p>Before we talk about container registries, let's first understand what a container image is. In a nutshell, a container image is a package that includes all of the files, libraries, and configurations to run a container. They are composed of <a target="_blank" href="https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/">layers</a> where each layer represents a set of file system changes that add, remove, or modify files.</p>
<p>The most common way to create a container image is to use a <strong>Dockerfile</strong>.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># build an image</span>
docker build -t pliutau/hello-world:v0 .

<span class="hljs-comment"># check the images locally</span>
docker images
<span class="hljs-comment"># REPOSITORY    TAG       IMAGE ID       CREATED          SIZE</span>
<span class="hljs-comment"># hello-world   latest    9facd12bbcdd   22 seconds ago   11MB</span>
</code></pre>
<p>This creates a container image that is stored on your local machine. But what if you want to share this image with others or use it on a different machine? This is where container registries come in.</p>
<h2 id="heading-what-is-a-container-registry">What is a Container Registry?</h2>
<p>A container registry is a storage catalog where you can push and pull container images from. The images are grouped into repositories, which are collections of related images with the same name. For example, on Docker Hub registry, <a target="_blank" href="https://hub.docker.com/_/nginx">nginx</a> is the name of the repository that contains different versions of the NGINX images.</p>
<p>Some registries are public, meaning that the images hosted on them are accessible to anyone on the Internet. Public registries such as <a target="_blank" href="https://hub.docker.com/">Docker Hub</a> are a good option to host open-source projects.</p>
<p>On the other hand, private registries provide a way to incorporate security and privacy into enterprise container image storage, either hosted in cloud or on-premises. These private registries often come with advanced security features and technical support.</p>
<p>There is a growing list of private registries available such as <a target="_blank" href="https://aws.amazon.com/ecr/">Amazon ECR</a>, <a target="_blank" href="https://cloud.google.com/artifact-registry/docs">GCP Artifact Registry</a>, <a target="_blank" href="https://github.com/features/packages">GitHub Container Registry</a>, and Docker Hub also offers a private repository feature.</p>
<p>As a developer, you interact with a container registry when using the <code>docker push</code> and <code>docker pull</code> commands.</p>
<pre><code class="lang-bash">docker push docker.io/pliutau/hello-world:v0

<span class="hljs-comment"># In case of Docker Hub we could also skip the registry part</span>
docker push pliutau/hello-world:v0
</code></pre>
<p>Let's look at the anatomy of a container image URL:</p>
<pre><code class="lang-bash">docker pull docker.io/pliutau/hello-world:v0@sha256:dc11b2...
                |            |            |          |
                ↓            ↓            ↓          ↓
             registry    repository      tag       digest
</code></pre>
<h2 id="heading-why-you-might-want-to-self-host-a-container-registry">Why You Might Want to Self-host a Container Registry</h2>
<p>Sometimes, instead of relying on a provider like AWS or GCP, you might want to host your images yourself. This keeps your infrastructure internal and makes you less reliant on external vendors. In some heavily regulated industries, this is even a requirement.</p>
<p>A self-hosted registry runs on your own servers, giving you more control over how the registry is configured and where the container images are hosted. At the same time it comes with a cost of maintaining and securing the registry.</p>
<h2 id="heading-how-to-self-host-a-container-registry">How to Self-host a Container Registry</h2>
<p>There are several open-source container registry solutions available. The most popular one is officially supported by Docker, called <a target="_blank" href="https://hub.docker.com/_/registry">registry</a>, with its implementation for storing and distributing of container images and artifacts. This means that you can run your own registry inside a container.</p>
<p>Here are the main steps to run a registry on a server:</p>
<ul>
<li><p>Install Docker and Docker Compose on the server.</p>
</li>
<li><p>Configure and run the <strong>registry</strong> container.</p>
</li>
<li><p>Run <strong>NGINX</strong> for handling TLS and forwarding requests to the registry container.</p>
</li>
<li><p>Setup SSL certificates and configure a domain.</p>
</li>
</ul>
<h3 id="heading-step-1-install-docker-and-docker-compose-on-the-server">Step 1: Install Docker and Docker Compose on the server</h3>
<p>You can use any server that supports Docker. For example, you can use a DigitalOcean Droplet with Ubuntu. For this demo I used Google Cloud Compute to create a VM with Ubuntu.</p>
<pre><code class="lang-bash">neofetch

<span class="hljs-comment"># OS: Ubuntu 20.04.6 LTS x86_64</span>
<span class="hljs-comment"># CPU: Intel Xeon (2) @ 2.200GHz</span>
<span class="hljs-comment"># Memory: 3908MiB</span>
</code></pre>
<p>Once we're inside our VM, we should install Docker and Docker Compose. Docker Compose is optional, but it makes it easier to manage multi-container applications.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># install docker engine and docker-compose</span>
sudo snap install docker

<span class="hljs-comment"># verify the installation</span>
docker --version
docker-compose --version
</code></pre>
<h3 id="heading-step-2-configure-and-run-the-registry-container">Step 2: Configure and run the registry container</h3>
<p>Next we need to configure our registry container. The following <strong>compose.yaml</strong> file will create a registry container with a volume for storing the images and a volume for storing the password file.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">registry:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">registry:latest</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">REGISTRY_AUTH:</span> <span class="hljs-string">htpasswd</span>
      <span class="hljs-attr">REGISTRY_AUTH_HTPASSWD_REALM:</span> <span class="hljs-string">Registry</span> <span class="hljs-string">Realm</span>
      <span class="hljs-attr">REGISTRY_AUTH_HTPASSWD_PATH:</span> <span class="hljs-string">/auth/registry.password</span>
      <span class="hljs-attr">REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY:</span> <span class="hljs-string">/data</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-comment"># Mount the password file</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./registry/registry.password:/auth/registry.password</span>
      <span class="hljs-comment"># Mount the data directory</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./registry/data:/data</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-number">5000</span>
</code></pre>
<p>The password file defined in <strong>REGISTRY_AUTH_HTPASSWD_PATH</strong> is used to authenticate users when they push or pull images from the registry. We should create a password file using the <strong>htpasswd</strong> command. We should also create a folder for storing the images.</p>
<pre><code class="lang-yaml"><span class="hljs-string">mkdir</span> <span class="hljs-string">-p</span> <span class="hljs-string">./registry/data</span>

<span class="hljs-comment"># install htpasswd</span>
<span class="hljs-string">sudo</span> <span class="hljs-string">apt</span> <span class="hljs-string">install</span> <span class="hljs-string">apache2-utils</span>

<span class="hljs-comment"># create a password file. username: busy, password: bee</span>
<span class="hljs-string">htpasswd</span> <span class="hljs-string">-Bbn</span> <span class="hljs-string">busy</span> <span class="hljs-string">bee</span> <span class="hljs-string">&gt;</span> <span class="hljs-string">./registry/registry.password</span>
</code></pre>
<p>Now we can start the registry container. If you see this message, than everything is working as it should:</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker-compose</span> <span class="hljs-string">up</span>

<span class="hljs-comment"># successfull run should output something like this:</span>
<span class="hljs-comment"># registry | level=info msg="listening on [::]:5000"</span>
</code></pre>
<h3 id="heading-step-3-run-nginx-for-handling-tls">Step 3: Run NGINX for handling TLS</h3>
<p>As mentioned earlier, we can use NGINX to handle TLS and forward requests to the registry container.</p>
<p>The Docker Registry requires a valid trusted SSL certificate to work. You can use something like <a target="_blank" href="https://letsencrypt.org/">Let's Encrypt</a> or obtain it manually. Make sure you have a domain name pointing to your server (<strong>registry.pliutau.com</strong> in my case). For this demo I already obtained the certificates using <a target="_blank" href="https://certbot.eff.org/">certbot</a> and put it in the <strong>./nginx/certs</strong> directory.</p>
<p>Since we're running our Docker Registry in a container, we can run NGINX in a container as well by adding the following service to the <strong>compose.yaml</strong> file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">registry:</span>
    <span class="hljs-comment"># ...</span>
  <span class="hljs-attr">nginx:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx:latest</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">registry</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-comment"># mount the nginx configuration</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./nginx/nginx.conf:/etc/nginx/nginx.conf</span>
      <span class="hljs-comment"># mount the certificates obtained from Let's Encrypt</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./nginx/certs:/etc/nginx/certs</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"443:443"</span>
</code></pre>
<p>Our <strong>nginx.conf</strong> file could look like this:</p>
<pre><code class="lang-yaml"><span class="hljs-string">worker_processes</span> <span class="hljs-string">auto;</span>

<span class="hljs-string">events</span> {
    <span class="hljs-string">worker_connections</span> <span class="hljs-number">1024</span><span class="hljs-string">;</span>
}

<span class="hljs-string">http</span> {
    <span class="hljs-string">upstream</span> <span class="hljs-string">registry</span> {
        <span class="hljs-string">server</span> <span class="hljs-string">registry:5000;</span>
    }

    <span class="hljs-string">server</span> {
        <span class="hljs-string">server_name</span> <span class="hljs-string">registry.pliutau.com;</span>
        <span class="hljs-string">listen</span> <span class="hljs-number">443</span> <span class="hljs-string">ssl;</span>

        <span class="hljs-string">ssl_certificate</span> <span class="hljs-string">/etc/nginx/certs/fullchain.pem;</span>
        <span class="hljs-string">ssl_certificate_key</span> <span class="hljs-string">/etc/nginx/certs/privkey.pem;</span>

        <span class="hljs-string">location</span> <span class="hljs-string">/</span> {
            <span class="hljs-comment"># important setting for large images</span>
            <span class="hljs-string">client_max_body_size</span>                <span class="hljs-string">1000m;</span>

            <span class="hljs-string">proxy_pass</span>                          <span class="hljs-string">http://registry;</span>
            <span class="hljs-string">proxy_set_header</span>  <span class="hljs-string">Host</span>              <span class="hljs-string">$http_host;</span>
            <span class="hljs-string">proxy_set_header</span>  <span class="hljs-string">X-Real-IP</span>         <span class="hljs-string">$remote_addr;</span>
            <span class="hljs-string">proxy_set_header</span>  <span class="hljs-string">X-Forwarded-For</span>   <span class="hljs-string">$proxy_add_x_forwarded_for;</span>
            <span class="hljs-string">proxy_set_header</span>  <span class="hljs-string">X-Forwarded-Proto</span> <span class="hljs-string">$scheme;</span>
            <span class="hljs-string">proxy_read_timeout</span>                  <span class="hljs-number">900</span><span class="hljs-string">;</span>
        }
    }
}
</code></pre>
<h3 id="heading-ready-to-go">Ready to go!</h3>
<p>After these steps we can run our registry and Nginx containers.</p>
<pre><code class="lang-bash">docker-compose up
</code></pre>
<p>Now, on the client side, you can push and pull the images from your registry. But first we need to login to the registry.</p>
<pre><code class="lang-bash">docker login registry.pliutau.com

<span class="hljs-comment"># Username: busy</span>
<span class="hljs-comment"># Password: bee</span>
<span class="hljs-comment"># Login Succeeded</span>
</code></pre>
<p>Time to build and push our image to our self-hosted registry:</p>
<pre><code class="lang-bash">docker build -t registry.pliutau.com/pliutau/hello-world:v0 .

docker push registry.pliutau.com/pliutau/hello-world:v0
<span class="hljs-comment"># v0: digest: sha256:a56ea4... size: 738</span>
</code></pre>
<p>On your server you can check the uploaded images in the data folder:</p>
<pre><code class="lang-bash">ls -la ./registry/data/docker/registry/v2/repositories/
</code></pre>
<h3 id="heading-other-options">Other options</h3>
<p>Following the example above, you can also run the registry on Kubernetes. Or you could use a managed registry service like <a target="_blank" href="https://goharbor.io/">Harbor</a>, which is an open-source registry that provides advanced security features and is compatible with Docker and Kubernetes.</p>
<p>Also, if you want to have a UI for your self-hosted registry, you could use a project like <a target="_blank" href="https://github.com/Joxit/docker-registry-ui">joxit/docker-registry-ui</a> and run it in a separate container.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Self-hosted Container Registries allow you to have complete control over your registry and the way it's deployed. At the same time it comes with a cost of maintaining and securing the registry.</p>
<p>Whatever your reasons for running a self-hosted registry, you now know how it's done. From here you can compare the different options and choose the one that best fits your needs.</p>
<p>You can find the full source code for this demo on <a target="_blank" href="https://github.com/plutov/packagemain/tree/master/26-self-hosted-container-registry">GitHub</a>. Also, you can watch it as a video on <a target="_blank" href="https://www.youtube.com/watch?v=TGLfQZ9qRaI">our YouTube channel</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Run Database Migrations in Kubernetes – Different Approaches with Examples ]]>
                </title>
                <description>
                    <![CDATA[ In the era of Microservices and Kubernetes, managing database migrations has become more complex than ever. Traditional methods of running migrations during application startup are no longer sufficient. This article explores various approaches to han... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-run-database-migrations-in-kubernetes/</link>
                <guid isPermaLink="false">66fd623ecac98bddbe3cc1ee</guid>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Helm ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Wed, 02 Oct 2024 15:09:50 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727685813983/f4672022-9a38-49c9-a252-96f40181fac1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In the era of Microservices and Kubernetes, managing database migrations has become more complex than ever. Traditional methods of running migrations during application startup are no longer sufficient.</p>
<p>This article explores various approaches to handling database migrations in a Kubernetes environment, with a focus on Go tooling. You'll get the most out of this article if you already have some experience with Go, Kubernetes, and relational databases.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-challenge-of-migrations-in-kubernetes">The challenge of migrations in Kubernetes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-popular-migration-tools-for-golang">Popular migration tools for Golang</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-migrations-inside-the-application">Run migrations inside the application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-migrations-in-initcontainers">Run migrations in initContainers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-migrations-as-a-kubernetes-job">Run migrations as a Kubernetes Job</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-helm-hooks">Helm hooks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-best-practices-for-kubernetes-migrations">Best practices for Kubernetes migrations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources">Resources</a></p>
</li>
</ul>
<h2 id="heading-the-challenge-of-migrations-in-kubernetes">The Challenge of Migrations in Kubernetes</h2>
<p>Kubernetes introduces new challenges for database migrations:</p>
<ul>
<li><p>Multiple replicas starting simultaneously. These can span the same migration twice which may introduce some database locks.</p>
</li>
<li><p>Separation of concerns between application and migration logic. This means it’s good to be able to run or rollback migrations without redeploying your application.</p>
</li>
</ul>
<h2 id="heading-popular-migration-tools-for-golang">Popular Migration Tools for Golang</h2>
<p>As I mentioned in another <a target="_blank" href="https://packagemain.tech/i/149097592/database-migrations">post</a>, there are a few different tools you can use to manage your migrations. They are quite similar, so I personally don’t have a strong preference between once or another. I just wanted to provide a few options so you know what the popular tools are.</p>
<ol>
<li><a target="_blank" href="https://github.com/golang-migrate/migrate"><strong>golang-migrate</strong></a></li>
</ol>
<ul>
<li><p>Widely used and supports numerous databases.</p>
</li>
<li><p>Simple CLI and API.</p>
</li>
<li><p>Supports various migration sources (local files, S3, Google Storage).</p>
</li>
</ul>
<ol start="2">
<li><a target="_blank" href="https://github.com/pressly/goose"><strong>goose</strong></a></li>
</ol>
<ul>
<li><p>Supports main SQL databases.</p>
</li>
<li><p>Allows migrations written in Go for complex scenarios.</p>
</li>
<li><p>Flexible versioning schemas.</p>
</li>
</ul>
<ol start="3">
<li><a target="_blank" href="https://atlasgo.io/"><strong>atlas</strong></a></li>
</ol>
<ul>
<li><p>Powerful database schema management tool.</p>
</li>
<li><p>Supports declarative and versioned migrations.</p>
</li>
<li><p>Offers integrity checks and migration linting.</p>
</li>
<li><p>Provides GitHub Actions and Terraform provider.</p>
</li>
</ul>
<h2 id="heading-run-migrations-inside-the-application">Run Migrations Inside the Application</h2>
<p>A naïve implementation would be to run the code of the migration directly inside your main function before you start your server.</p>
<p><strong>Example using golang-migrate:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"database/sql"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"net/http"</span>

    <span class="hljs-string">"github.com/golang-migrate/migrate/v4"</span>
    <span class="hljs-string">"github.com/golang-migrate/migrate/v4/database/postgres"</span>
    _ <span class="hljs-string">"github.com/golang-migrate/migrate/v4/source/file"</span>
    _ <span class="hljs-string">"github.com/lib/pq"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Database connection parameters</span>
    url := <span class="hljs-string">"postgres://user:pass@localhost:5432/dbname"</span>

    <span class="hljs-comment">// Connect to the database</span>
    db, err := sql.Open(<span class="hljs-string">"postgres"</span>, url)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatalf(<span class="hljs-string">"could not connect to database: %v"</span>, err)
    }
    <span class="hljs-keyword">defer</span> db.Close()

    <span class="hljs-comment">// Run migrations</span>
    <span class="hljs-keyword">if</span> err := runMigrations(db); err != <span class="hljs-literal">nil</span> {
        log.Fatalf(<span class="hljs-string">"could not run migrations: %v"</span>, err)
    }

    <span class="hljs-comment">// Run the application, for example start the server</span>
    <span class="hljs-keyword">if</span> err := http.ListenAndServe(<span class="hljs-string">":8080"</span>, <span class="hljs-literal">nil</span>); err != <span class="hljs-literal">nil</span> {
        log.Fatalf(<span class="hljs-string">"server failed to start: %v"</span>, err)
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">runMigrations</span><span class="hljs-params">(db *sql.DB)</span> <span class="hljs-title">error</span></span> {
    driver, err := postgres.WithInstance(db, &amp;postgres.Config{})
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"could not create database driver: %w"</span>, err)
    }

    m, err := migrate.NewWithDatabaseInstance(
        <span class="hljs-string">"file://migrations"</span>, <span class="hljs-comment">// Path to your migration files</span>
        <span class="hljs-string">"postgres"</span>,          <span class="hljs-comment">// Database type</span>
        driver,
    )
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"could not create migrate instance: %w"</span>, err)
    }

    <span class="hljs-keyword">if</span> err := m.Up(); err != <span class="hljs-literal">nil</span> &amp;&amp; err != migrate.ErrNoChange {
        <span class="hljs-keyword">return</span> fmt.Errorf(<span class="hljs-string">"could not run migrations: %w"</span>, err)
    }

    log.Println(<span class="hljs-string">"migrations completed successfully"</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>However, these could cause different issues like your migrations being slow and Kubernetes considering that the pod didn’t start successfully and therefore killing it. You could run those migrations in a Go routine, but how do you handle failures then?</p>
<p>In cases when multiple pods are created at the same time, you would have a potential concurrency problem.</p>
<p>It also means your migrations need to be inside your Docker image.</p>
<p>Even with its downsides, this approach might work well for quick and stable database changes and small projects.</p>
<h2 id="heading-run-migrations-in-initcontainers"><strong>Run Migrations in initContainers</strong></h2>
<p>By using <a target="_blank" href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/">initContainers</a> in your Kubernetes Deployment, it will run the migration before the main application container starts. This is a good first solution for when scaling is not a problem yet.</p>
<p>If the initContainer fails, the blue/green deployment from Kubernetes won’t go further and your previous pods stay where they are. This prevents having a newer version of the code without the planned migration.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">initContainers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">migrations</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">migrate/migrate:latest</span>
    <span class="hljs-attr">command:</span> [<span class="hljs-string">'/migrate'</span>]
    <span class="hljs-attr">args:</span> [<span class="hljs-string">'-source'</span>, <span class="hljs-string">'file:///migrations'</span>, <span class="hljs-string">'-database'</span>,<span class="hljs-string">'postgres://user:pass@db:5432/dbname'</span>, <span class="hljs-string">'up'</span>]
</code></pre>
<p>This approach might work well for quick and stable database changes for deployments with a single Pod. And it already separates the application and migration layers.</p>
<h2 id="heading-run-migrations-as-a-kubernetes-job"><strong>Run Migrations as a Kubernetes Job</strong></h2>
<p>You could create a <a target="_blank" href="https://kubernetes.io/docs/concepts/workloads/controllers/job/">Kubernetes Job</a> that runs your migrations, and trigger that job during the deployment process before rolling out the application.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">batch/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Job</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">db-migrate</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">migrate</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">your-migration-image:latest</span>
        <span class="hljs-attr">command:</span> [<span class="hljs-string">'/app/migrate'</span>]
</code></pre>
<p>You can also combine it with initContainers, making sure that the pod starts only when the job is successful.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">initContainers:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">migrations-wait</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">ghcr.io/groundnuty/k8s-wait-for:v2.0</span>
    <span class="hljs-attr">args:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"job"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"my-migration-job"</span>
</code></pre>
<p>This approach can solve the problems related to multiple replicas mentioned above.</p>
<h2 id="heading-helm-hooks">Helm Hooks</h2>
<p>If you use Helm, it has <a target="_blank" href="https://helm.sh/docs/topics/charts_hooks/">hooks</a> that you can use for running migrations during chart installation/upgrade. You just define a pre-install or pre-upgrade hook in your Helm chart.</p>
<p><strong>pre-install-hook.yaml:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">batch/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Job</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">include</span> <span class="hljs-string">"mychart.fullname"</span> <span class="hljs-string">.</span> }}<span class="hljs-string">-migrations</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">"helm.sh/hook":</span> <span class="hljs-string">pre-install,pre-upgrade</span>
    <span class="hljs-attr">"helm.sh/hook-weight":</span> <span class="hljs-string">"-5"</span>
    <span class="hljs-attr">"helm.sh/hook-delete-policy":</span> <span class="hljs-string">hook-succeeded</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">migrations</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">your-migrations-image:tag</span>
          <span class="hljs-attr">command:</span> [<span class="hljs-string">"./run-migrations.sh"</span>]
</code></pre>
<p>In this example, the pre-install hook executes after templates are rendered, but before any resources are created in Kubernetes.</p>
<p>This of course works only when you use Helm, meaning you need to find something else if you decide not to use Helm.</p>
<h2 id="heading-best-practices-for-kubernetes-migrations"><strong>Best Practices for Kubernetes Migrations</strong></h2>
<p>Decouple migrations from application code:</p>
<ol>
<li><p>Create a separate Docker image for migrations. This ensures that migration logic is encapsulated and doesn't interfere with the application codebase.</p>
</li>
<li><p>Use tools like Atlas to manage migrations independently. Tools like Atlas provide features for automating migration processes, scheduling, and rollback.</p>
</li>
</ol>
<p>Use version control for migrations:</p>
<ol>
<li><p>Store migration files in your Git repository. This ensures a complete history of migration changes, making it easier to track and revert changes.</p>
</li>
<li><p>Use sequential or timestamp-based versioning. Sequential versioning guarantees the correct order of migrations which is very important for relational databases.</p>
</li>
</ol>
<p>Ensure idempotent migrations:</p>
<ol>
<li>Ensure migrations can be run multiple times without side effects. Idempotent migrations prevent accidental data corruption or inconsistencies if a migration is run multiple times.</li>
</ol>
<p>Have a rollback strategy</p>
<ol>
<li>Implement and test rollback procedures for each migration. Having a rollback strategy ensures that you can revert changes if a migration fails or causes unexpected issues.</li>
</ol>
<p>Perform monitoring and logging</p>
<ol>
<li>Use tools like Atlas Cloud for visibility into migration history. Atlas Cloud provides detailed logs and history of migrations, making it easy to track changes and troubleshoot issues.</li>
</ol>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Managing database migrations in a Kubernetes environment requires careful planning and execution.</p>
<p>By leveraging tools like golang-migrate, goose, or atlas, and following best practices, you can create robust, scalable, and maintainable migration strategies.</p>
<p>Remember to decouple migrations from application code, use version control, and implement proper monitoring to ensure smooth database evolution in your Kubernetes-based architecture.</p>
<h3 id="heading-resources"><strong>Resources</strong></h3>
<ul>
<li><p><a target="_blank" href="https://packagemain.tech/p/database-migrations-in-kubernetes">Discover more articles from packagemain.tech</a></p>
</li>
<li><p><a target="_blank" href="https://helm.sh/docs/topics/charts_hooks/">Helm Hooks</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Work with SQL Databases in Go – Different Approaches and Examples ]]>
                </title>
                <description>
                    <![CDATA[ Different programming languages have their own ways of working with relational databases and SQL. Ruby on Rails has its Active Record, Python has SQLAlchemy, Typescript has Drizzle, and so on. Go is a language with quite a diverse standard library, w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-work-with-sql-databases-in-go/</link>
                <guid isPermaLink="false">66f2e7b67f466b5e1f4dfe66</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Tue, 24 Sep 2024 16:24:22 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727195039014/02f3c2f4-1eb7-4fd6-9701-15fdee2f6849.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Different programming languages have their own ways of working with relational databases and SQL. Ruby on Rails has its <a target="_blank" href="https://guides.rubyonrails.org/active_record_basics.html">Active Record</a>, Python has <a target="_blank" href="https://www.sqlalchemy.org/">SQLAlchemy</a>, Typescript has <a target="_blank" href="https://orm.drizzle.team/">Drizzle</a>, and so on.</p>
<p>Go is a language with quite a diverse standard library, which includes the well-known <a target="_blank" href="https://pkg.go.dev/database/sql">database/sql</a> package. And it has its own libraries and solutions for working with SQL, that suit different needs, preferences, and teams.</p>
<p>In this article, we'll explore and compare the most popularly used Go packages that let you work with SQL. We’ll look at some hands-on examples, as well as the pros and cons. We’ll also briefly touch on the topic of database migrations and how to manage them in Go.</p>
<p>You'll get the most out of this article if you already have some experience with Go, SQL, and relational databases (doesn't matter which one).</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-demo-schema">Demo Schema</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-raw-sql-and-databasesql">Raw SQL and database/sql</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-raw-sql-and-sqlx">Raw SQL and sqlx</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-orms">ORMs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-generated-go-code-from-sql-using-sqlc">Generated Go code from SQL using sqlc</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-database-migrations">Database Migrations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources">Resources</a></p>
</li>
</ul>
<h2 id="heading-demo-schema">Demo Schema</h2>
<p>For this article, we'll use a simple schema with three tables: <strong>users</strong>, <strong>posts</strong>, and <strong>blogs</strong>. For simplicity, we'll be using <strong>SQLite</strong> as our database engine. But if you want to choose another database engine, that shouldn’t be a problem, as all the libraries we'll be exploring support multiple SQL dialects.</p>
<p>Here is our database schema in SQL:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span> (
    <span class="hljs-keyword">id</span> <span class="hljs-built_in">INTEGER</span> PRIMARY <span class="hljs-keyword">KEY</span> AUTOINCREMENT,
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    email <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">UNIQUE</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> blogs (
    <span class="hljs-keyword">id</span> <span class="hljs-built_in">INTEGER</span> PRIMARY <span class="hljs-keyword">KEY</span> AUTOINCREMENT,
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    <span class="hljs-keyword">url</span> <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">UNIQUE</span>
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> posts (
    <span class="hljs-keyword">id</span> <span class="hljs-built_in">INTEGER</span> PRIMARY <span class="hljs-keyword">KEY</span> AUTOINCREMENT,
    title <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    <span class="hljs-keyword">content</span> <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    user_id <span class="hljs-built_in">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    blog_id <span class="hljs-built_in">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    <span class="hljs-keyword">FOREIGN</span> <span class="hljs-keyword">KEY</span> (user_id) <span class="hljs-keyword">REFERENCES</span> <span class="hljs-keyword">users</span> (<span class="hljs-keyword">id</span>) <span class="hljs-keyword">ON</span> <span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">CASCADE</span>,
    <span class="hljs-keyword">FOREIGN</span> <span class="hljs-keyword">KEY</span> (blog_id) <span class="hljs-keyword">REFERENCES</span> blogs (<span class="hljs-keyword">id</span>) <span class="hljs-keyword">ON</span> <span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">CASCADE</span>
);
</code></pre>
<p>And here is its <a target="_blank" href="https://www.visual-paradigm.com/guide/data-modeling/what-is-entity-relationship-diagram/">Entity-Relationship Diagram (ERD)</a>:</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d65ca9-a6c1-4870-9b97-8c69fa1c82fd_2332x1284.png" alt="ERD displaying an illustration of the three tables: users, posts, and blogs" width="1456" height="802" loading="lazy"></p>
<h2 id="heading-raw-sql-and-databasesql">Raw SQL and database/sql</h2>
<p>Let’s imagine your application needs to perform the following action:</p>
<p><em>Find the users who have posted at least two articles, along with the total number of posts they've made.</em></p>
<p>In pure SQL, you could translate that into the following query:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> u.name, <span class="hljs-keyword">COUNT</span>(p.id) <span class="hljs-keyword">AS</span> post_count
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">AS</span> u
<span class="hljs-keyword">JOIN</span> posts <span class="hljs-keyword">AS</span> p <span class="hljs-keyword">ON</span> u.id = p.user_id
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> u.id
<span class="hljs-keyword">HAVING</span> post_count &gt;= <span class="hljs-number">2</span>;
</code></pre>
<p>A brief explanation of this query: we JOIN the users and posts tables, then GROUP by user_id. The HAVING clause filters the results to only include users who have posted at least 2 posts, and COUNT aggregates the amount of posts.</p>
<p>As mentioned above, Go provides a built-in package called <strong>database/sql</strong> with the necessary tools for working with SQL databases. It was developed with simplicity in mind, but supports all the necessary functionality such as transactions, parameterized queries, connection pool management, and so on.</p>
<p>As long as you’re comfortable writing your own queries and handling errors and results, it’s a great option. And some would say that it’s the best option, as there is no hidden logic and you can always copy the query and analyze it with EXPLAIN.</p>
<p>Here is how you can get the results of the query above in Go code using database/sql (some parts like connection are omitted):</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> userStats <span class="hljs-keyword">struct</span> {
  UserName  sql.NullString
  PostCount sql.NullInt64
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getUsersStats</span><span class="hljs-params">(conn *sql.DB, minPosts <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">([]userStats, error)</span></span> {
  query := <span class="hljs-string">`SELECT u.name, COUNT(p.id) AS post_count
FROM users AS u
JOIN posts AS p ON u.id = p.user_id
GROUP BY u.id
HAVING post_count &gt;= ?;`</span>

  rows, err := conn.Query(query, minPosts)
  <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
  }
  <span class="hljs-keyword">defer</span> rows.Close()

  users := []userStats{}
  <span class="hljs-keyword">for</span> rows.Next() {
    <span class="hljs-keyword">var</span> user userStats

    <span class="hljs-keyword">if</span> err := rows.Scan(&amp;user.UserName, &amp;user.PostCount); err != <span class="hljs-literal">nil</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    users = <span class="hljs-built_in">append</span>(users, user)
  }

  <span class="hljs-keyword">if</span> err := rows.Err(); err != <span class="hljs-literal">nil</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
  }

  <span class="hljs-keyword">return</span> users, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>In this code we:</p>
<ul>
<li><p>Use the raw SQL query with an unnamed parameter, and pass the value of this parameter in <code>conn.Query()</code></p>
</li>
<li><p>Iterate over returned rows and manually scan each row into a struct <code>userStats</code> defined above. Note that the struct uses <code>sql.Null*</code> types to handle nullable values properly.</p>
</li>
<li><p>We need to manually check for possible errors and close the rows to release the resources.</p>
</li>
</ul>
<p>Pros:</p>
<ul>
<li><p>No additional abstraction/complexity added. Easy to debug raw SQL queries.</p>
</li>
<li><p>Performance. The database/sql package is quite performant.</p>
</li>
<li><p>Provides a good enough abstraction from different database backends.</p>
</li>
</ul>
<p>Cons:</p>
<ul>
<li><p>The code becomes a bit verbose as there is a need to scan each row, define proper types, and handle errors.</p>
</li>
<li><p>No compile-time type safety.</p>
</li>
</ul>
<p>You can find the full source for this article in <a target="_blank" href="https://github.com/plutov/packagemain/tree/master/sql-gorm-sqlx-sqlc">this Github Repository</a>.</p>
<h2 id="heading-raw-sql-and-sqlx">Raw SQL and sqlx</h2>
<p>Now let’s have a look at some external packages which are popular in the Go community.</p>
<p>If you’re already familiar with database/sql and like its simplicity, you may enjoy working with <a target="_blank" href="https://github.com/jmoiron/sqlx"><strong>sqlx</strong></a>. It’s built on top of the standard library and just extends its features.</p>
<p>It’s very easy to integrate existing codebases using database/sql with sqlx, because it leaves the underlying interfaces such as sql.DB, sql.Tx, and so on untouched.</p>
<p>The core features of sqlx are:</p>
<ul>
<li><p>Named parameters.</p>
</li>
<li><p>Easier row scanning into structs with embedded struct support.</p>
</li>
<li><p>Better separation between single and multiple rows by using the <code>Get()</code> and <code>Select()</code> methods.</p>
</li>
<li><p>Ability to bind a slice of values as a single parameter to an IN query.</p>
</li>
</ul>
<p>Here is how you can get the results of the query above using sqlx:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> userStats <span class="hljs-keyword">struct</span> {
  UserName  <span class="hljs-keyword">string</span> <span class="hljs-string">`db:"name"`</span>
  PostCount <span class="hljs-keyword">string</span> <span class="hljs-string">`db:"post_count"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getUsersStats</span><span class="hljs-params">(conn *sqlx.DB, minPosts <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">([]userStats, error)</span></span> {
  users := []userStats{}

  query := <span class="hljs-string">`SELECT u.name, COUNT(p.id) AS post_count
FROM users AS u
JOIN posts AS p ON u.id = p.user_id
GROUP BY u.id
HAVING post_count &gt;= ?;`</span>

  <span class="hljs-keyword">if</span> err := conn.Select(&amp;users, query, minPosts); err != <span class="hljs-literal">nil</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
  }

  <span class="hljs-keyword">return</span> users, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>In this code, we use the <code>Select()</code> method which handles the scanning of the rows. It also closes the rows automatically so we don’t have to deal with that.</p>
<p>The code is much shorter than the <strong>database/sql</strong> version, but it can hide some implementation details from us. For example, be aware that Select loads the whole set into memory at once.</p>
<p>Pros:</p>
<ul>
<li><p>Not that different from database/sql. Still easy to debug raw SQL queries.</p>
</li>
<li><p>A bunch of great features to reduce code verbosity.</p>
</li>
</ul>
<p>Cons:</p>
<ul>
<li>Same as database/sql</li>
</ul>
<h2 id="heading-orms">ORMs</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Object-relational_mapping">Object-relational mapping</a> (ORM) is a technique (some call it a design pattern) of accessing a relational database by working with objects without having to craft complex SQL statements. It’s very popular in object-oriented languages – Ruby on Rails has its <a target="_blank" href="https://guides.rubyonrails.org/active_record_basics.html">Active Record</a>, Python has <a target="_blank" href="https://www.sqlalchemy.org/">SQLAlchemy</a>, Typescript has <a target="_blank" href="https://orm.drizzle.team/">Drizzle</a>, and so on.</p>
<p>And Go has <a target="_blank" href="https://github.com/go-gorm/gorm"><strong>GORM</strong></a>. In a nutshell, it lets you write queries as Go code by calling various methods on objects, which are then translated into SQL queries. But not only that, it has other features like database migrations, database resolvers, and more.</p>
<p>You may need to spend a bit of time initially setting up your GORM models, but later it can reduce a lot of boilerplate code.</p>
<p>Our simple schema and query example may not be the best to visualize the strengths and weaknesses of GORM, but should be enough to demonstrate how we can run a similar query and scan the results:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {
  gorm.Model
  ID    <span class="hljs-keyword">int</span>
  Name  <span class="hljs-keyword">string</span>
  Posts []Post
}

<span class="hljs-keyword">type</span> Post <span class="hljs-keyword">struct</span> {
  gorm.Model
  ID     <span class="hljs-keyword">int</span>
  UserID <span class="hljs-keyword">int</span>
}

<span class="hljs-keyword">type</span> userStats <span class="hljs-keyword">struct</span> {
  Name  <span class="hljs-keyword">string</span>
  Count <span class="hljs-keyword">int</span> <span class="hljs-string">`gorm:"column:post_count"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getUsersStats</span><span class="hljs-params">(conn *gorm.DB, minPosts <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">([]userStats, error)</span></span> {
  <span class="hljs-keyword">var</span> users []userStats

  err := conn.Model(&amp;User{}).
    Select(<span class="hljs-string">"name"</span>, <span class="hljs-string">"COUNT(p.id) AS post_count"</span>).
    Joins(<span class="hljs-string">"JOIN posts AS p ON users.id = p.user_id"</span>).
    Group(<span class="hljs-string">"users.id"</span>).
    Having(<span class="hljs-string">"post_count &gt;= ?"</span>, minPosts).
    Find(&amp;users).Error

  <span class="hljs-keyword">return</span> users, err
}
</code></pre>
<p>The SQL query generated by <strong>gorm</strong> will be roughly the same as the one we wrote manually in the database/sql variant.</p>
<p>To summarize the code above:</p>
<ul>
<li><p>We declared our User and Post models and extended it with the default <code>gorm.Model</code> struct. Later we can use these two models to build any queries we want by using gorm methods.</p>
</li>
<li><p>We also defined our small result type <code>userStats</code></p>
</li>
<li><p>We used methods such as <code>Select()</code>, <code>Joins()</code>, <code>Group()</code>, and <code>Having()</code> to produce the query we want.</p>
</li>
</ul>
<p>With such an easy example, it’s hard to see the potential issues – everything looks just right. But when your project becomes more complex, you will most definitely encounter some issues with that. Just look at the StackOverflow questions marked with <a target="_blank" href="https://stackoverflow.com/questions/tagged/go-gorm">go-gorm</a>.</p>
<p>It's good to be careful about using ORMs in performance-critical systems or where you need direct control over database interactions. This is because gorm uses a lot of reflection, and can add overhead and sometimes obscure what's happening at the database level. Any project where the functionality is wrapped in another huge layer runs the risk of increasing the overall complexity.</p>
<p>Pros:</p>
<ul>
<li><p>Abstraction from different database backends.</p>
</li>
<li><p>Big feature set: migrations, hooks, database resolvers, and more.</p>
</li>
<li><p>Saves quite a bit of tedious coding.</p>
</li>
</ul>
<p>Cons:</p>
<ul>
<li><p>Another layer of complexity and overhead. Hard to debug raw SQL queries.</p>
</li>
<li><p>Performance drawbacks. May not be as efficient for some critical applications.</p>
</li>
<li><p>Initial setup can require some time to configure all the models.</p>
</li>
</ul>
<h2 id="heading-generated-go-code-from-sql-using-sqlc">Generated Go Code from SQL using sqlc</h2>
<p>This nicely brings us to another unique approach of generating Go code from SQL queries using <a target="_blank" href="https://sqlc.dev/"><strong>sqlc</strong></a>. With sqlc, you write your schema and SQL queries, then use a CLI tool to generate Go code from it and then use the generated code to interact with databases.</p>
<p>This ensures that your queries are syntactically correct and type-safe. It’s ideal for those who prefer writing SQL but are looking for an efficient way to integrate it into a Go application.</p>
<p>sqlc needs to know your database schema and queries in order to generate code, so it requires some initial setup. We can add our schema and query above to the files <strong>schema.sql</strong> and <strong>query.sql</strong>. Then using the following config, we can generate the Go code:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"2"</span>
<span class="hljs-attr">sql:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">engine:</span> <span class="hljs-string">"sqlite"</span>
    <span class="hljs-attr">queries:</span> <span class="hljs-string">"query.sql"</span>
    <span class="hljs-attr">schema:</span> <span class="hljs-string">"schema.sql"</span>
    <span class="hljs-attr">gen:</span>
      <span class="hljs-attr">go:</span>
        <span class="hljs-attr">package:</span> <span class="hljs-string">"main"</span>
        <span class="hljs-attr">out:</span> <span class="hljs-string">"."</span>
</code></pre>
<p>We also need to name our query in query.sql and mark the parameters:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- name: GetUsersStats :many</span>
<span class="hljs-keyword">SELECT</span> u.name, <span class="hljs-keyword">COUNT</span>(p.id) <span class="hljs-keyword">AS</span> post_count
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">AS</span> u
<span class="hljs-keyword">JOIN</span> posts <span class="hljs-keyword">AS</span> p <span class="hljs-keyword">ON</span> u.id = p.user_id
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> u.id
<span class="hljs-keyword">HAVING</span> post_count &gt;= ?;
</code></pre>
<p>After we run <code>sqlc generate</code>, we can use the following generated types and functions which make our code type-safe and quite short.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getUsersStats</span><span class="hljs-params">(conn *sql.DB, minPosts <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">([]GetUsersStatsRow, error)</span></span> {
  queries := New(conn)

  ctx := context.Background()
  <span class="hljs-keyword">return</span> queries.GetUsersStats(ctx, minPosts)
}
</code></pre>
<p>What makes sqlc special is that it understands your database schema, and uses that to validate the SQL you write. So your SQL queries are being validated against the actual database table, and sqlc will give you a compile-time error if something is wrong.</p>
<p>Pros:</p>
<ul>
<li><p>Type safety with generated Go code.</p>
</li>
<li><p>Still easy to debug SQL code.</p>
</li>
<li><p>Saves quite a bit of tedious coding.</p>
</li>
<li><p>Performance.</p>
</li>
</ul>
<p>Cons:</p>
<ul>
<li><p>Initial configuration setup for database schema and queries.</p>
</li>
<li><p>Not perfect static analysis. Sometimes you need to explicitly set the parameter type, and so on.</p>
</li>
</ul>
<p>If you’re good with SQL statements and prefer not to use much code to express your database interactions, this is your package.</p>
<h2 id="heading-database-migrations">Database Migrations</h2>
<p>Since we’re on the topic of SQL databases here, let’s briefly review how database migrations work in Go. The schema of the database almost always evolves over time and no one wants to do these changes manually. So there are tools developed to help with that.</p>
<p>The main goal of database migration tools is to ensure that all environments have the same schema and developers can easily apply the changes or roll them back.</p>
<p>I mentioned above that GORM can do the migrations as well if your project uses it as its ORM. If you use database/sql, sqlx or sqlc you’ll have to use separate projects to manage them.</p>
<p>The most popular projects are:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/golang-migrate/migrate"><strong>golang-migrate</strong></a>: one of the most famous tools for handling database migrations. It supports many database drivers and migration sources, and takes a simple and direct approach for handling database migrations.</p>
</li>
<li><p><a target="_blank" href="https://github.com/pressly/goose"><strong>goose</strong></a>: another solid option when choosing a migration tool. It also has support for the main database drivers. Two of its main features are support for migrations written in Go and more control of the migration application process.</p>
</li>
</ul>
<p>You can then integrate these tools directly into your application or in CI/CD. But running them properly in CI/CD requires some setup (for example in case of deploying to Kubernetes), and I’ll dive deeper into that in my upcoming articles.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>There are many well-written, tested, and supported database packages for Go that can help you with faster development and writing cleaner code. There is also the very powerful database/sql included in the standard library that can do most of your daily work.</p>
<p>But whether you should use it or not depends on your needs as a developer, your preferences, and your project. In this article, I tried to highlight the strengths and weaknesses of each option.</p>
<p>You can find the full source for this article on <a target="_blank" href="https://github.com/plutov/packagemain/tree/master/sql-gorm-sqlx-sqlc">this Github Repository</a>.</p>
<p>I’ll end this article with this famous meme:</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04406f54-00f2-4c88-ad8c-5335499398a4_844x467.png" alt="https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04406f54-00f2-4c88-ad8c-5335499398a4_844x467" width="844" height="467" loading="lazy"></p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><p><a target="_blank" href="https://pkg.go.dev/database/sql">database/sql</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/jmoiron/sqlx">sqlx</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/go-gorm/gorm">GORM</a></p>
</li>
<li><p><a target="_blank" href="https://sqlc.dev/">sqlc</a></p>
</li>
<li><p><a target="_blank" href="https://packagemain.tech/p/different-ways-of-working-with-sql">Discover more articles from packagemain.tech</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Statically and Dynamically Linked Go Binaries Work ]]>
                </title>
                <description>
                    <![CDATA[ One of the biggest strengths of Go is its compiler. It abstracts many things for you and lets you compile your program easily for almost any platform and architecture. And though it seems easy, there are some nuances to it and multiple ways of compil... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/golang-statically-and-dynamically-linked-go-binaries/</link>
                <guid isPermaLink="false">66e0543f384b1317e5e30add</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ compiler ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Tue, 10 Sep 2024 14:14:23 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1725977444176/20f3bebf-e250-45c3-926e-146d50e4db93.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>One of the biggest strengths of Go is its compiler. It abstracts many things for you and lets you compile your program easily for almost any <a target="_blank" href="https://pkg.go.dev/cmd/dist">platform and architecture</a>.</p>
<p>And though it seems easy, there are some nuances to it and multiple ways of compiling the same program which results in different executables.</p>
<p>In this article, we’ll explore statically and dynamically linked executables, internal and external linkers, and examine binaries using tools like <strong>file, ld</strong>, and <strong>ldd</strong>.</p>
<h3 id="heading-heres-what-well-cover">Here's what we'll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-overview">Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-static-and-dynamic-linking">What is Static and Dynamic linking?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-statically-linked-program">Statically Linked Program</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-binary-anyway">What is a binary anyway?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-dynamically-linked-program">Dynamically Linked Program</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-can-we-make-it-statically-linked">Can we make it statically linked?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-internal-vs-external-linker">Internal vs External linker</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cross-compilation">Cross-Compilation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-bonus-point-reduce-binary-size">Bonus Point: Reduce binary size</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-beware-ldpreload-trick">Beware: LD_PRELOAD trick</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-further-reads">Further Reads</a></p>
</li>
</ul>
<h2 id="heading-what-is-static-and-dynamic-linking">What is Static and Dynamic linking?</h2>
<p><strong>Static linking</strong> is the practice of copying all the libraries your program needs directly into the final executable file image.</p>
<p>And Go <em>loves and wants</em> that whenever it’s possible. This is because it's more portable, as it doesn’t require the presence of the library on the host system where it runs. So your binary can run on any system no matter which distro/version, and it won't depend on any system libraries.</p>
<p><strong>Dynamic linking</strong>, on the other hand, is when external or shared libraries are copied into the executable file <em>by name during run time</em>.</p>
<p>And it has its own advantages, too. For example the program can re-use popular <strong>libc</strong> libraries that are available on the host system and not re-implement them. You can also benefit from host updates without re-linking your program. It can also reduce the executable file size in many cases.</p>
<h2 id="heading-statically-linked-program">Statically Linked Program</h2>
<p>Let’s review a program that will <em>always</em> get statically linked. This program doesn’t call C code using <a target="_blank" href="https://pkg.go.dev/cmd/cgo"><strong>cgo</strong></a>, so everything can be packaged in a static binary. Our program only prints a simple message to stdout, which Go can do internally without needing to use something from <strong>libc</strong>.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"hi, user"</span>)
}
</code></pre>
<h2 id="heading-what-is-a-binary-anyway">What is a Binary Anyway?</h2>
<p>We can use a <a target="_blank" href="https://www.man7.org/linux/man-pages/man1/file.1.html"><strong>file</strong></a> program to examine the file type first.</p>
<pre><code class="lang-bash">$ go build main1.go

$ file main1 | tr , <span class="hljs-string">'\n'</span>
main1: ELF 64-bit LSB executable
 ARM aarch64
 version 1 (SYSV)
 statically linked
 Go BuildID=...
 with debug_info
 not stripped
</code></pre>
<p>It tells us that it’s an <a target="_blank" href="https://wiki.osdev.org/ELF"><strong>ELF</strong></a> (Executable and Linkable Format) executable file. It also tells us that it’s “statically linked“.</p>
<p>We won’t dive into what ELF is, but there are other executable file formats. ELF is the default one on Linux, Mach-O is the default one for macOS, PE/PE32+ for Windows, and so on.</p>
<p>Note: in this article we’ll be working with Linux (Ubuntu) and its tooling, but the same is possible on other platforms.</p>
<p>And there is another Linux program called <a target="_blank" href="https://man7.org/linux/man-pages/man1/ldd.1.html"><strong>ldd</strong></a> that can tell us if the binary is statically or dynamically linked.</p>
<pre><code class="lang-bash">$ ldd main1
not a dynamic executable
</code></pre>
<h2 id="heading-dynamically-linked-program">Dynamically Linked Program</h2>
<p>As mentioned above, Go has a mechanism called <strong>cgo</strong> to call C code from Go. Even Go’s stdlib uses it in multiple places – for example in the <a target="_blank" href="https://pkg.go.dev/net"><strong>net</strong></a> package, where it uses the standard C library to work with DNS.</p>
<p>Importing such packages or using cgo in your code by default produces a dynamically-linked binary, linked to those <strong>libc</strong> libraries.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"net"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    ipv4Addr, ipv4Net, err := net.ParseCIDR(<span class="hljs-string">"192.0.2.1/24"</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    fmt.Println(ipv4Addr)
    fmt.Println(ipv4Net)
}
</code></pre>
<p>We can use our <strong>file</strong> and <strong>ldd</strong> programs again to examine the second binary.</p>
<pre><code class="lang-bash">$ go build main2.go

$ file main2 | tr , <span class="hljs-string">'\n'</span>
main2: ELF 64-bit LSB executable
 ARM aarch64
 version 1 (SYSV)
 dynamically linked
 interpreter /lib/ld-linux-aarch64.so.1
 Go BuildID=...
 with debug_info
 not stripped

$ ldd main2
    linux-vdso.so.1 (0x0000ffff87c81000)
    libc.so.6 =&gt; /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffff87a80000)
    /lib/ld-linux-aarch64.so.1 (0x0000ffff87c44000)
</code></pre>
<p>The <strong>file</strong> program now shows us that it is a <strong>dynamically liked</strong> binary and <strong>ldd</strong> shows us the dynamic dependencies of our binary. In this case it relies on <strong>libc.so.6</strong> and <strong>ld-linux</strong> which is a dynamic linker for Linux systems.</p>
<h2 id="heading-can-we-make-it-statically-linked">Can We Make it Statically Linked?</h2>
<p>There are multiple reasons why you might want your binaries to be static, but the main one is to make deployment and distribution easier. But! It’s not always necessary, and by linking <strong>libc</strong> you benefit from host updates. Also, in case of our <strong>net</strong> package, you use those complex DNS lookup functions included in <strong>libc</strong>.</p>
<p>What’s interesting is that Go’s net package also has a pure-Go version, which makes it possible to disable cgo during compile time. You can do it by specifying build tags or by fully disabling cgo using <strong>CGO_ENABLED=0</strong>.</p>
<pre><code class="lang-bash">$ go build -tags netgo main2.go
$ ldd main2
not a dynamic executable

$ CGO_ENABLED=0 go build main2.go
$ ldd main2
not a dynamic executable
</code></pre>
<p>The above proves that we end up with a static binary in both cases.</p>
<h2 id="heading-internal-vs-external-linker">Internal vs External Linker</h2>
<p>Linker is a program that reads the Go archive or object for a package main, along with its dependencies, and combines them into an executable binary.</p>
<p>By default, Go’s toolchain uses its internal linker (<a target="_blank" href="https://pkg.go.dev/cmd/link">go tool link</a>), but you can specify which linker to use during the compilation time. This can give you a combination of benefits of a static binary as well as full-fledged libc capabilities.</p>
<p>On Linux, the default linker is gcc’s <a target="_blank" href="https://man7.org/linux/man-pages/man1/ld.1.html"><strong>ld</strong></a>. And we can tell it to produce a static binary.</p>
<pre><code class="lang-bash">$ go build -ldflags <span class="hljs-string">"-linkmode 'external' -extldflags '-static'"</span> main2.go
<span class="hljs-comment"># command-line-arguments</span>
/usr/bin/ld: /tmp/go-link-629224677/000004.o: <span class="hljs-keyword">in</span> <span class="hljs-keyword">function</span> `_cgo_97ab22c4dc7b_C2func_getaddrinfo<span class="hljs-string">':
/tmp/go-build/cgo_unix_cgo.cgo2.c:60:(.text+0x30):
warning: Using '</span>getaddrinfo<span class="hljs-string">' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
$ ldd main2
not a dynamic executable</span>
</code></pre>
<p>It works, but we have a warning here. In our case <strong>glibc</strong> uses <strong>libnss</strong> to support a number of different providers for address resolution services and you cannot statically link libnss.</p>
<p>Other cgo packages may produce similar warnings and you’ll have to check the documentation to see if they’re critical or not.</p>
<h2 id="heading-cross-compilation">Cross-Compilation</h2>
<p>As mentioned in the introduction, cross-compilation is a very nice feature of Go. It lets you compile your program for almost any platform/architecture. But it can be very tricky if your program uses <strong>cgo</strong>, because it’s generally tricky to cross-compile C code.</p>
<pre><code class="lang-bash">$ CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build main2.go
$ CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build main2.go
<span class="hljs-comment"># runtime/cgo</span>
cgo: C compiler <span class="hljs-string">"clang"</span> not found: <span class="hljs-built_in">exec</span>: <span class="hljs-string">"clang"</span>:
executable file not found <span class="hljs-keyword">in</span> <span class="hljs-variable">$PATH</span>
</code></pre>
<p>You can overcome that by installing the toolchain for the target OS and/or architecture.</p>
<p>If you can, it’s always better to just not use <strong>cgo</strong> for cross-compilation. You’ll get stable binaries which are statically linked.</p>
<h2 id="heading-bonus-point-reduce-binary-size">Bonus Point: Reduce Binary Size</h2>
<p>As you may notice, the output of the <strong>file</strong> command above had the following: “with debug_info not stripped“. This means that our binary has debugging information in it. But we usually don’t need it, and removing it may reduce the binary size.</p>
<pre><code class="lang-bash">$ go build main1.go
$ du -sh main1
1.9M    main1

$ go build -ldflags=<span class="hljs-string">"-w -s"</span> main1.go
$ du -sh main1
1.3M    main1

$ file main1 | tr , <span class="hljs-string">'\n'</span>
main1: ELF 64-bit LSB executable
 ARM aarch64
 version 1 (SYSV)
 statically linked
 Go BuildID=...
 stripped
</code></pre>
<h2 id="heading-beware-ldpreload-trick">Beware: LD_PRELOAD Trick</h2>
<p>The Linux system program ld-linux.so (dynamic linker/loader) uses <strong>LD_PRELOAD</strong> to load specified shared libraries. In particular, before any other library, the dynamic loader will first load shared libraries that are in LD_PRELOAD.</p>
<p>The LD_PRELOAD trick is a powerful technique used in dynamically linked binaries to override or intercept function calls to shared libraries.</p>
<p>By setting the LD_PRELOAD environment variable to point to a custom shared object file, users can inject their own code into a program's execution, effectively replacing or augmenting existing library functions.</p>
<p>This method allows for various applications, such as debugging, testing, and even modifying program behaviour without altering the original source code.</p>
<pre><code class="lang-bash">LD_PRELOAD=/path/to/my/malloc.so /bin/ls
</code></pre>
<p>It also shows that <strong>statically linked binaries</strong> are more secure, as they don’t have this issue since they don’t seek any external libraries. Also, there is a “<strong>secure-execution mode”</strong> – a security feature implemented by the dynamic linker on Linux systems to restrict certain behaviours when running programs that require elevated privileges.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Computers are not magic, you just have to understand them.</p>
<p>And understanding Go compilation and execution processes is crucial for developing robust cross-platform applications.</p>
<p>Hopefully, after reading this article, you now have a better understanding of how Go compilation works.</p>
<h3 id="heading-further-reads">Further Reads</h3>
<ul>
<li><p><a target="_blank" href="https://packagemain.tech/">Explore more articles from packagemain.tech</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/plutov/packagemain/tree/master/static-dynamic-linking">Source Code</a></p>
</li>
<li><p><a target="_blank" href="https://cs.opensource.google/go/go/+/refs/tags/go1.19.3:src/cmd/cgo/doc.go">src/cmd/cgo/doc.go</a></p>
</li>
<li><p><a target="_blank" href="https://pkg.go.dev/cmd/link">cmd/link</a></p>
</li>
<li><p><a target="_blank" href="https://jvns.ca/blog/2021/11/17/debugging-a-weird--file-not-found--error/">Debugging a weird 'file not found' error</a></p>
</li>
<li><p><a target="_blank" href="http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html">How the heck do we get to main()</a></p>
</li>
<li><p><a target="_blank" href="https://embeddedartistry.com/blog/2019/04/08/a-general-overview-of-what-happens-before-main/">A General Overview of What Happens Before main()</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=q8irLfXwaFM">Rust Before Main</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement Server-Sent Events in Go ]]>
                </title>
                <description>
                    <![CDATA[ Server-Sent Events (SSE) is a powerful technology that enables real-time, unidirectional communication from servers to clients. In this article, we'll explore how to implement SSE in Go, discussing its benefits, use cases, and providing practical exa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-server-sent-events-in-go/</link>
                <guid isPermaLink="false">66cf2f37fda106b7e106dff1</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ APIs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websockets ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Wed, 28 Aug 2024 14:07:51 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724762290560/de9c7afd-2a81-4bd6-aa12-da92a759ebdb.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Server-Sent Events (SSE) is a powerful technology that enables real-time, unidirectional communication from servers to clients.</p>
<p>In this article, we'll explore how to implement SSE in Go, discussing its benefits, use cases, and providing practical examples. By the end, you should know the basics of building real-time applications with efficient, unidirectional communication.</p>
<h2 id="heading-what-are-server-sent-events">What are Server-Sent Events?</h2>
<p>SSE is a web technology that allows servers to push data to clients over a single HTTP connection.</p>
<p>Unlike WebSockets, SSE is unidirectional, making it simpler to implement and ideal for scenarios where real-time updates from the server are required, but client-to-server communication is not necessary.</p>
<p>Developing a web application that uses SSE is straightforward. You'll need a bit of code on the server to stream events to the front-end, but the client side code works almost identically to websockets when it comes to handling incoming events. This is a one-way connection, so you can't send events from a client to a server.</p>
<h3 id="heading-benefits-of-sse">Benefits of SSE</h3>
<ol>
<li><p><strong>Simplicity</strong>: SSE is easier to implement compared to WebSockets.</p>
</li>
<li><p><strong>Native browser support</strong>: Most modern browsers support SSE out of the box.</p>
</li>
<li><p><strong>Automatic reconnection</strong>: Clients automatically attempt to reconnect if the connection is lost.</p>
</li>
<li><p><strong>Efficient</strong>: Uses a single HTTP connection, reducing overhead.</p>
</li>
</ol>
<h2 id="heading-how-to-implement-sse-in-go">How to Implement SSE in Go</h2>
<p>For our example here, we'll create a simple SSE server in Go which just sends the data to the client every second with a current timestamp. The client can then connect to our server on port 8080 and receive these messages.</p>
<p>A real example could be something more sophisticated like sending notifications, displaying progress bar updates, and so on.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sseHandler</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    <span class="hljs-comment">// Set http headers required for SSE</span>
    w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"text/event-stream"</span>)
    w.Header().Set(<span class="hljs-string">"Cache-Control"</span>, <span class="hljs-string">"no-cache"</span>)
    w.Header().Set(<span class="hljs-string">"Connection"</span>, <span class="hljs-string">"keep-alive"</span>)

    <span class="hljs-comment">// You may need this locally for CORS requests</span>
    w.Header().Set(<span class="hljs-string">"Access-Control-Allow-Origin"</span>, <span class="hljs-string">"*"</span>)

    <span class="hljs-comment">// Create a channel for client disconnection</span>
    clientGone := r.Context().Done()

    rc := http.NewResponseController(w)
    t := time.NewTicker(time.Second)
    <span class="hljs-keyword">defer</span> t.Stop()
    <span class="hljs-keyword">for</span> {
        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> &lt;-clientGone:
            fmt.Println(<span class="hljs-string">"Client disconnected"</span>)
            <span class="hljs-keyword">return</span>
        <span class="hljs-keyword">case</span> &lt;-t.C:
            <span class="hljs-comment">// Send an event to the client</span>
            <span class="hljs-comment">// Here we send only the "data" field, but there are few others</span>
            _, err := fmt.Fprintf(w, <span class="hljs-string">"data: The time is %s\n\n"</span>, time.Now().Format(time.UnixDate))
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                <span class="hljs-keyword">return</span>
            }
            err = rc.Flush()
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                <span class="hljs-keyword">return</span>
            }
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    http.HandleFunc(<span class="hljs-string">"/events"</span>, sseHandler)
    fmt.Println(<span class="hljs-string">"server is running on :8080"</span>)
    <span class="hljs-keyword">if</span> err := http.ListenAndServe(<span class="hljs-string">":8080"</span>, <span class="hljs-literal">nil</span>); err != <span class="hljs-literal">nil</span> {
        fmt.Println(err.Error())
    }
}
</code></pre>
<h3 id="heading-key-components-of-the-sse-implementation"><strong>Key Components of the SSE Implementation</strong></h3>
<p>The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format">event stream</a> is a simple stream of text data which must be encoded using UTF-8. Messages in the event stream are separated by a pair of newline characters – <strong>\n\n</strong>. A colon as the first character of a line is in essence a comment, and is ignored.</p>
<p>In our server it is done here:</p>
<pre><code class="lang-go">rc := http.NewResponseController(w)
fmt.Fprintf(w, <span class="hljs-string">"data: The time is %s\n\n"</span>, time.Now().Format(time.UnixDate))
<span class="hljs-comment">// To make sure that the data is sent immediately</span>
rc.Flush()
</code></pre>
<p>The server that sends events needs to respond using the MIME type <strong>text/event-stream.</strong> We do it by setting the response header here:</p>
<pre><code class="lang-go">w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"text/event-stream"</span>)
</code></pre>
<p>You may have noticed that we set few other headers as well. One is to keep the HTTP connection open, and another to bypass CORS:</p>
<pre><code class="lang-go">w.Header().Set(<span class="hljs-string">"Cache-Control"</span>, <span class="hljs-string">"no-cache"</span>)
w.Header().Set(<span class="hljs-string">"Connection"</span>, <span class="hljs-string">"keep-alive"</span>)
w.Header().Set(<span class="hljs-string">"Access-Control-Allow-Origin"</span>, <span class="hljs-string">"*"</span>)
</code></pre>
<p>And the last important piece is to detect the disconnect. In Go, we'll receive it as a message in a specified channel:</p>
<pre><code class="lang-go">clientGone := r.Context().Done()

<span class="hljs-keyword">for</span> {
    <span class="hljs-keyword">select</span> {
    <span class="hljs-keyword">case</span> &lt;-clientGone:
        fmt.Println(<span class="hljs-string">"Client disconnected"</span>)
        <span class="hljs-keyword">return</span>
    }
}
</code></pre>
<p>Each message received has some combination of the following fields, one per line. In our server we send only the data field which is enough, as other fields are optional. More details <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events">here</a>.</p>
<ul>
<li><p><strong>event</strong> – a string identifying the type of event described.</p>
</li>
<li><p><strong>data</strong> – the data field for the message.</p>
</li>
<li><p><strong>id</strong> – the event ID to set the EventSource object's last event ID value.</p>
</li>
<li><p><strong>retry</strong> – the reconnection time.</p>
</li>
</ul>
<h3 id="heading-how-to-receive-the-events-on-the-client-side">How to Receive the Events on the Client Side</h3>
<p>On the front end or client side, you will have to use the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#creating_an_eventsource_instance">EventSource</a> interface. It's a browser API encapsulating Server-Sent Events. In the following example, our browser application receives the events from the server and prints them in a list.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!doctype <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"list"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>&gt;</span><span class="javascript">
        <span class="hljs-keyword">const</span> eventSrc = <span class="hljs-keyword">new</span> EventSource(<span class="hljs-string">"http://127.0.0.1:8080/events"</span>);

        <span class="hljs-keyword">const</span> list = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"list"</span>);

        eventSrc.onmessage = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> li = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"li"</span>);
            li.textContent = <span class="hljs-string">`message: <span class="hljs-subst">${event.data}</span>`</span>;

            list.appendChild(li);
        };
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Here is how it may look in your browser:</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2cda643-36a6-4986-8100-76d1d7c3fb33_998x490.png" alt="logs" width="998" height="490" loading="lazy"></p>
<h2 id="heading-best-practices-for-sse-in-golang"><strong>Best Practices for SSE in Golang</strong></h2>
<h3 id="heading-event-formatting"><strong>Event Formatting</strong></h3>
<p>In a real world project, a simple string of data may not be enough. In these cases, using a structured format like JSON can be a good option to send multiple data fields once. Here's an example:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"status"</span>: <span class="hljs-string">"in_progress"</span>,
  <span class="hljs-attr">"completion"</span>: <span class="hljs-number">51.22</span>
}
</code></pre>
<h3 id="heading-reconnection-strategy-and-error-handling"><strong>Reconnection Strategy and Error Handling</strong></h3>
<p>Something could always go wrong on both sides: the server might reject the connection for some reason or a client might abruptly disconnect.</p>
<p>In each case, you'll need to implement a backoff strategy for graceful reconnections. It's better to miss one message than completely break the event loop.</p>
<p>In JavaScript, you can check for errors in EventSource and then act accordingly:</p>
<pre><code class="lang-javascript">eventSrc.onerror = <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"EventSource failed:"</span>, err);
};
</code></pre>
<h3 id="heading-load-balancing"><strong>Load Balancing</strong></h3>
<p>For high-traffic applications, you may consider using a Load Balancer, for example NGINX. If you plan to have many clients connecting to your server, it's good to test it beforehand by simulating the load from the clients.</p>
<h2 id="heading-use-cases-for-sse"><strong>Use Cases for SSE</strong></h2>
<ol>
<li><p>Real-time dashboards</p>
</li>
<li><p>Live sports scores</p>
</li>
<li><p>Social media feeds</p>
</li>
<li><p>Stock market tickers</p>
</li>
<li><p>Progress indicators for long-running tasks</p>
</li>
</ol>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Server-Sent Events provide an efficient and straightforward way to implement real-time, server-to-client communication in Golang applications. By leveraging SSE, developers can create responsive and dynamic web applications with minimal overhead and complexity.</p>
<p>As you build your SSE-powered applications, remember to consider scalability, error handling, and client-side implementation to ensure a robust and efficient real-time communication system.</p>
<p><a target="_blank" href="https://packagemain.tech">Explore more articles from packagemain.tech</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Software Architecture Diagrams Using the C4 Model ]]>
                </title>
                <description>
                    <![CDATA[ As a developer, you'll likely work on a complex project at some point where deciphering the codebase feels like reading a whole novel. Engineers are code wizards, but even the best get lost in sprawling code. The challenge is that architecture diagra... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-software-architecture-diagrams-using-the-c4-model/</link>
                <guid isPermaLink="false">66c636dc83227f02672177db</guid>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C4 Model ]]>
                    </category>
                
                    <category>
                        <![CDATA[ diagrams ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Wed, 21 Aug 2024 18:50:04 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724187048778/7d2821c6-c0c9-4d03-999f-37022388210c.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>As a developer, you'll likely work on a complex project at some point where deciphering the codebase feels like reading a whole novel. Engineers are code wizards, but even the best get lost in sprawling code.</p>
<p>The challenge is that architecture diagrams – if they even exist – are often outdated relics from a bygone era.</p>
<p>This is why creating and maintaining effective and clear diagrams should be effortless. Up-to-date visuals ensure everyone stays on the same page, eliminating confusion and wasted time.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a target="_blank" href="heading-what-is-the-c4-model">What is the C4 Model?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-level-1-context">Level 1: Context</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-level-2-containers">Level 2: Containers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-level-3-components">Level 3: Components</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-level-4-code">Level 4: Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-supplementary-diagrams">Supplementary Diagrams</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-diagrams-as-code">Diagrams as Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-automate-rendering-in-your-ci">Automate Rendering in Your CI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources">Resources</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-c4-model">What is the C4 Model?</h2>
<p>The <a target="_blank" href="https://c4model.com/">C4 model</a> was created as a way to help software development teams describe and communicate software architecture.</p>
<p>C4 stands for “Context, Containers, Components, and Code”. Those are the four levels that should be enough to describe a complex system.</p>
<p>The best way to explain the concept is to think about how we use Google Maps. When we are exploring an area in Google Maps, we will often start zoomed out to help us get context. Once we find the rough area we are interested in we can zoom in to get a little more detail.</p>
<h3 id="heading-level-1-context">Level 1: Context</h3>
<p>This level is the most zoomed out. It's a bird’s eye view of the system in the greater context of the world. The diagram concentrates on actors and systems.</p>
<p>For the examples below, we will use a simple Task Management Software System to demonstrate all these 4 levels.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d096cb9-3058-4bbd-9c69-6a41361e4d3b_1600x2000.png" alt="Diagram showing the context Level" width="1456" height="1820" loading="lazy"></p>
<p>This diagram portrays the Task Management Software System's interactions with external systems and the different user groups that utilize it. We can see that the Task Management software relies on two external systems: Email and Calendar, and two types of actors (users) use it: Customer and Admin User.</p>
<h3 id="heading-level-2-containers">Level 2: Containers</h3>
<p>The containers level is a more detailed view of your system (don’t confuse C4 containers with Docker containers).</p>
<p>It reveals how various functional units like applications and databases work together and distribute responsibilities.</p>
<p>This diagram also highlights the key technologies employed and showcases the communication flow between these containers. It presents a simplified, technology-centric view of the system's core components and their interactions.</p>
<p>If you have Microservice architecture, then each Microservice would be a container.</p>
<p>Examples of containers are:</p>
<ul>
<li><p>Single page application</p>
</li>
<li><p>Web server</p>
</li>
<li><p>Serverless function</p>
</li>
<li><p>Database</p>
</li>
<li><p>API</p>
</li>
<li><p>Message buses</p>
</li>
</ul>
<p>And so on.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb614c4e5-4fbd-4e10-8682-c3e67ec72f2d_3028x2691.png" alt="Diagram showing the Containers Level" width="1456" height="1294" loading="lazy"></p>
<p>This level delves into the internal composition of the Task Management Software System. It showcases that our Task Management software system consists of containers such as User Web UI, Admin Web UI, API and a Database. API is also the container that is connected to external systems, for example to send emails or create events in calendar.</p>
<h3 id="heading-level-3-components">Level 3: Components</h3>
<p>The next level of zoom is components. This shows the major structural building blocks of your application, and is often a conceptual view of the application. The term component is loose here. It could represent a controller or a service containing business logic.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55564aaa-d404-4322-a6f3-9674326410d5_1995x1900.png" alt="Diagram showing the Components Level" width="1456" height="1387" loading="lazy"></p>
<p>This diagram focuses on the internal structure of the API container within the Task Management Software System. It reveals that the API container houses crucial functionalities like CRUD operations (Create, Read, Update, Delete) for data manipulation and user authentication mechanisms. The CRUD components is the one that talks to the database.</p>
<h3 id="heading-level-4-code">Level 4: Code</h3>
<p>The deepest level of zoom is the code diagram. Although this diagram exists, it is often not used as the code paints a very similar picture. However, in highly regulated environments and complex legacy projects this level can help to paint a better picture of inner intricacies of the software.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde6e40f0-d713-46b9-906e-618fb61eb622_602x339.png" alt="https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde6e40f0-d713-46b9-906e-618fb61eb622_602x339" width="602" height="339" loading="lazy"></p>
<h3 id="heading-supplementary-diagrams">Supplementary Diagrams</h3>
<p>Besides the 4 diagrams above, there are a couple more worth mentioning:</p>
<ul>
<li><p>Deployment diagram</p>
</li>
<li><p>Dynamic diagram: to describe the process or a flow</p>
</li>
</ul>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa13b10a-2093-4ab8-8302-dc75b09be96f_1570x1461.png" alt="Login Flow" width="1456" height="1355" loading="lazy"></p>
<p>On this diagram we show a Login Flow, which is not a container or component, but rather a software process that happens in our software system. It shows that Web/Admin UIs use JWT-based authentication for communication with the API and the JWT token is stored in local storage on a client side.</p>
<h2 id="heading-diagrams-as-code">Diagrams as Code</h2>
<p>The power of C4 comes with a diagram-as-code approach. This means treating your diagrams just like your codebase:</p>
<ul>
<li><p><strong>Version control:</strong> Store them in a source control system (like Git) for easy tracking and collaboration.</p>
</li>
<li><p><strong>Collaboration:</strong> Work together on diagrams using pull requests, similar to code reviews.</p>
</li>
<li><p><strong>Automation:</strong> Integrate them into your build pipelines for automatic rendering with your preferred tools.</p>
</li>
</ul>
<h3 id="heading-helpful-tool-structurizr">Helpful Tool: Structurizr</h3>
<p>There are few tools to help with modeling and diagramming, but the most popular nowadays is <a target="_blank" href="https://www.structurizr.com/">Structurizr</a> with their custom DSL (Domain Specific Language).</p>
<p>All you need is to get familiar with the DSL syntax, which is pretty simple. As long as you get used to it you will be able to create or update diagrams in no time.</p>
<p>Below you can see the DSL for our Task Management Software System.</p>
<pre><code class="lang-bash">workspace {
    model {
        <span class="hljs-comment"># Actors</span>
        customer = person <span class="hljs-string">"Customer"</span> <span class="hljs-string">""</span> <span class="hljs-string">"person"</span>
        admin = person <span class="hljs-string">"Admin User"</span> <span class="hljs-string">""</span> <span class="hljs-string">"person"</span>

        <span class="hljs-comment"># External systems</span>
        emailSystem = softwareSystem <span class="hljs-string">"Email System"</span> <span class="hljs-string">"Mailgun"</span> <span class="hljs-string">"external"</span>
        calendarSystem = softwareSystem <span class="hljs-string">"Calendar System"</span> <span class="hljs-string">"Calendly"</span> <span class="hljs-string">"external"</span>

        <span class="hljs-comment"># Task Management System</span>
        taskManagementSystem  = softwareSystem <span class="hljs-string">"Task Management System"</span>{
            webContainer = container <span class="hljs-string">"User Web UI"</span> <span class="hljs-string">""</span> <span class="hljs-string">""</span> <span class="hljs-string">"frontend"</span>
            adminContainer = container <span class="hljs-string">"Admin Web UI"</span> <span class="hljs-string">""</span> <span class="hljs-string">""</span> <span class="hljs-string">"frontend"</span>
            dbContainer = container <span class="hljs-string">"Database"</span> <span class="hljs-string">"PostgreSQL"</span> <span class="hljs-string">""</span> <span class="hljs-string">"database"</span>
            apiContainer = container <span class="hljs-string">"API"</span> <span class="hljs-string">"Go"</span> {
                authComp = component <span class="hljs-string">"Authentication"</span>
                crudComp = component <span class="hljs-string">"CRUD"</span>
            }
        }

        <span class="hljs-comment"># Relationships (Actors &amp; Systems)</span>
        customer -&gt; webContainer <span class="hljs-string">"Manages tasks"</span>
        admin -&gt; adminContainer <span class="hljs-string">"Manages users"</span>
        apiContainer -&gt; emailSystem <span class="hljs-string">"Sends emails"</span>
        apiContainer -&gt; calendarSystem <span class="hljs-string">"Creates tasks in Calendar"</span>

        <span class="hljs-comment"># Relationships (Containers)</span>
        webContainer -&gt; apiContainer <span class="hljs-string">"Uses"</span>
        adminContainer -&gt; apiContainer <span class="hljs-string">"Uses"</span>
        apiContainer -&gt; dbContainer <span class="hljs-string">"Persists data"</span>

        <span class="hljs-comment"># Relationships (Components &amp; Containers)</span>
        crudComp -&gt; dbContainer <span class="hljs-string">"Reads from and writes to"</span>
        webContainer -&gt; authComp <span class="hljs-string">"Authenticates using"</span>
        adminContainer -&gt; authComp <span class="hljs-string">"Authenticates using"</span>
    }
}
</code></pre>
<p>Let's dive into the most important parts:</p>
<pre><code class="lang-yaml"><span class="hljs-string">workspace</span> [<span class="hljs-string">name</span>] [<span class="hljs-string">description</span>] {
    <span class="hljs-string">model</span> {
    }
}
</code></pre>
<p>Here we define our workspace which should have at least one model. A workspace can optionally be given a name and description.</p>
<pre><code class="lang-yaml"><span class="hljs-string">customer</span> <span class="hljs-string">=</span> <span class="hljs-string">person</span> <span class="hljs-string">"Customer"</span> <span class="hljs-string">""</span> <span class="hljs-string">"person"</span>
<span class="hljs-string">admin</span> <span class="hljs-string">=</span> <span class="hljs-string">person</span> <span class="hljs-string">"Admin User"</span> <span class="hljs-string">""</span> <span class="hljs-string">"person"</span>
</code></pre>
<p>In this section we define our persons (for example, a user, actor, role, or persona) in the following format: <code>person &lt;name&gt; [description] [tags]</code>.</p>
<p>You can use a similar format (name, description, tags) to identify the external systems:</p>
<pre><code class="lang-yaml">        <span class="hljs-string">emailSystem</span> <span class="hljs-string">=</span> <span class="hljs-string">softwareSystem</span> <span class="hljs-string">"Email System"</span> <span class="hljs-string">"Mailgun"</span> <span class="hljs-string">"external"</span>
        <span class="hljs-string">calendarSystem</span> <span class="hljs-string">=</span> <span class="hljs-string">softwareSystem</span> <span class="hljs-string">"Calendar System"</span> <span class="hljs-string">"Calendly"</span> <span class="hljs-string">"external"</span>
</code></pre>
<p>To describe the internal software system we need to write a block that also shows its containers and components:</p>
<pre><code class="lang-yaml"><span class="hljs-string">taskManagementSystem</span>  <span class="hljs-string">=</span> <span class="hljs-string">softwareSystem</span> <span class="hljs-string">"Task Management System"</span>{
    <span class="hljs-string">webContainer</span> <span class="hljs-string">=</span> <span class="hljs-string">container</span> <span class="hljs-string">"User Web UI"</span> <span class="hljs-string">""</span> <span class="hljs-string">""</span> <span class="hljs-string">"frontend"</span>
    <span class="hljs-string">adminContainer</span> <span class="hljs-string">=</span> <span class="hljs-string">container</span> <span class="hljs-string">"Admin Web UI"</span> <span class="hljs-string">""</span> <span class="hljs-string">""</span> <span class="hljs-string">"frontend"</span>
    <span class="hljs-string">dbContainer</span> <span class="hljs-string">=</span> <span class="hljs-string">container</span> <span class="hljs-string">"Database"</span> <span class="hljs-string">"PostgreSQL"</span> <span class="hljs-string">""</span> <span class="hljs-string">"database"</span>
    <span class="hljs-string">apiContainer</span> <span class="hljs-string">=</span> <span class="hljs-string">container</span> <span class="hljs-string">"API"</span> <span class="hljs-string">"Go"</span> {
        <span class="hljs-string">authComp</span> <span class="hljs-string">=</span> <span class="hljs-string">component</span> <span class="hljs-string">"Authentication"</span>
        <span class="hljs-string">crudComp</span> <span class="hljs-string">=</span> <span class="hljs-string">component</span> <span class="hljs-string">"CRUD"</span>
    }
}
</code></pre>
<ul>
<li><p>Container format: <code>container &lt;name&gt; [description] [technology] [tags]</code></p>
</li>
<li><p>Component format: <code>component &lt;name&gt; [description] [technology] [tags]</code></p>
</li>
</ul>
<p>The rest of the model is the most interesting part where we define the relationships between all parts (systems, containers, components):</p>
<pre><code class="lang-yaml"><span class="hljs-string">apiContainer</span> <span class="hljs-string">-&gt;</span> <span class="hljs-string">emailSystem</span> <span class="hljs-string">"Sends emails"</span>
</code></pre>
<p>The following format is used: <code>&lt;identifier&gt; -&gt; &lt;identifier&gt; [description] [technology] [tags]</code>.</p>
<p>There are other features available in Structurizr DSL, such as styling, themes, visibility, etc. You can find find them <a target="_blank" href="https://docs.structurizr.com/dsl/language">here</a>.</p>
<h2 id="heading-automate-rendering-in-your-ci">Automate Rendering in Your CI</h2>
<p>Since you can host your models on GitHub, it is very easy to automate the pipeline for rendering the diagrams in the tools of your choice.</p>
<p>In our case, Structurizr has a GitHub Action that allows you to run <strong>structurizr-cli</strong>, a command line utility for Structurizr that lets you create software architecture models based upon the C4 model using a textual domain specific language (DSL).</p>
<p>This sample repository contains a <a target="_blank" href="https://github.com/plutov/c4-diagram-example/blob/main/.github/workflows/pages.yaml">workflow</a> that simply generates a static page and publishes it to GitHub Pages.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">static</span> <span class="hljs-string">content</span> <span class="hljs-string">to</span> <span class="hljs-string">Github</span> <span class="hljs-string">Pages</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">"main"</span>]

<span class="hljs-attr">permissions:</span>
  <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>
  <span class="hljs-attr">pages:</span> <span class="hljs-string">write</span>
  <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span>

<span class="hljs-attr">concurrency:</span>
  <span class="hljs-attr">group:</span> <span class="hljs-string">"pages"</span>
  <span class="hljs-attr">cancel-in-progress:</span> <span class="hljs-literal">false</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">container:</span>
      <span class="hljs-attr">image:</span> <span class="hljs-string">ghcr.io/avisi-cloud/structurizr-site-generatr</span>
      <span class="hljs-attr">options:</span> <span class="hljs-string">--user</span> <span class="hljs-string">root</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Create</span> <span class="hljs-string">site</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          /opt/structurizr-site-generatr/bin/structurizr-site-generatr generate-site -w diagram.dsl
</span>      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-artifact@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">website</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">build/site</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">build</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">github-pages</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.deployment.outputs.page_url</span> <span class="hljs-string">}}</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/download-artifact@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">website</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">build/site</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Pages</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/configure-pages@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">artifact</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-pages-artifact@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">"build/site"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">deployment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/deploy-pages@v2</span>
</code></pre>
<p>This Github Action uses Structurizr CLI action to compile our DSL file as HTML and publish it to Github Pages.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I believe that creating and maintaining effective and clear diagrams should be effortless. Up-to-date visuals ensure everyone stays on the same page, eliminating confusion and wasted time.</p>
<p>The C4 model and a bit of automation with Structurizr DSL can help make this process faster and keep diagrams close to the codebase. The whole process can now be automated as well into your SDLC.</p>
<h3 id="heading-resources">Resources</h3>
<ul>
<li><p><a target="_blank" href="https://github.com/plutov/c4-diagram-example">Github Repository</a></p>
</li>
<li><p><a target="_blank" href="https://c4model.com/">C4 Model</a></p>
</li>
<li><p><a target="_blank" href="https://docs.structurizr.com/dsl/language">DSL Language Reference</a></p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=systemticks.c4-dsl-extension">C4 DSL Visual Studio Code Extension</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/marketplace/actions/structurizr-cli-action">structurizr-cli-action</a></p>
</li>
<li><p><a target="_blank" href="https://packagemain.tech">Discover more articles from packagemain.tech</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Emulate Real Dependencies in Integration Tests using Testcontainers ]]>
                </title>
                <description>
                    <![CDATA[ What is Integration Testing? The purpose of integration tests is to validate that different software components, subsystems, or applications work well together combined as a group. It’s an important step in the testing pyramid that can help you ident... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/integration-tests-using-testcontainers/</link>
                <guid isPermaLink="false">66bd0ba0c0cd4c479bce0052</guid>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ containers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Wed, 14 Aug 2024 19:55:12 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723631770942/c82aabb6-a9b4-4085-8b06-b4ba1b1cdbd3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-what-is-integration-testing">What is Integration Testing?</h2>
<p>The purpose of integration tests is to validate that different software components, subsystems, or applications work well together combined as a group.</p>
<p>It’s an important step in the testing pyramid that can help you identify any issues that arise when components are combined – for example compatibility issues, data inconsistence, or communication issues.</p>
<p>This article is a hands-on guide to integration Tests in Go using Testcontainers. We'll define integrations tests as tests of communication between a backend application and external components such as the database and cache.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-different-ways-to-run-integration-tests">Different Ways to Run Integration Tests</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-our-guinea-pig-service-a-simple-url-shortener">Our Guinea Pig Service: a Simple URL Shortener</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-unit-tests-with-mocked-dependencies">Unit Tests with Mocked Dependencies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-integration-tests-with-real-dependencies">Integration Tests with Real Dependencies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-integration-tests-with-testcontainers">Integration Tests with Testcontainers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-testcontainers-work">How Testcontainers Work</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources">Resources</a></p>
</li>
</ul>
<h2 id="heading-different-ways-to-run-integration-tests">Different Ways to Run Integration Tests</h2>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*AbYCn0k0XzRIk3wR.png" alt="Testing pyramid" width="700" height="500" loading="lazy"></p>
<p>This diagram shows only 3 types of tests – but there are other kinds as well: components tests, system tests, load testing, and so on.</p>
<p>While unit tests are easy to run (you just execute tests as you would execute your code), integration tests usually require some scaffolding (spin up temporary testing environment with databases and other dependencies). In the companies where I've worked, I’ve seen the following approaches to address the integration testing environment problem.</p>
<p><strong>Option 1:</strong> Using throwaway databases and other dependencies, which must be provisioned before the integration tests start and destroyed afterwards.</p>
<p>Depending on your application complexity, the effort involved in this option can be quite high, as you must ensure that the infrastructure is up and running and data is pre-configured in a specific desired state.</p>
<p><strong>Option 2:</strong> Using the existing shared databases and other dependencies. You may create a separate environment for integration tests or even use the existing one (staging for example) that integration tests can use.</p>
<p>But there are many disadvantages here, and I would not recommend it. Because it is a shared environment, multiple tests can run in parallel and modify the data simultaneously. So you may end up with inconsistent data state for multiple reasons.</p>
<p><strong>Option 3:</strong> Using in-memory or embedded variations of the required services for integration testing. While this is a good approach, not all dependencies have in-memory variations, and even if they do, these implementations may not have the same features as your production database.</p>
<p><strong>Option 4:</strong> Using <a target="_blank" href="https://testcontainers.com/">Testcontainers</a> to bootstrap and manage your testing dependencies right inside your testing code. This ensures a full isolation between test runs, reproducibility and better CI experience. We will dive into that in a second.</p>
<h2 id="heading-our-guinea-pig-service-a-simple-url-shortener">Our Guinea Pig Service: a Simple URL Shortener</h2>
<p>To demonstrate the tests, we'll use a super simple URL shortener API written in Go. It uses MongoDB for data storage and Redis as a <a target="_blank" href="https://www.enjoyalgorithms.com/blog/read-through-caching-strategy">read-through cache</a>. It has two endpoints which we’ll be testing in our tests:</p>
<ul>
<li><p><code>/create?url=</code> generates the hash for a given URL and stores it in the database.</p>
</li>
<li><p><code>/get?key=</code> returns the original URL for a given key.</p>
</li>
</ul>
<p>We won’t delve into the details of the endpoints much, but you can find the full code in <a target="_blank" href="https://github.com/plutov/packagemain/blob/master/testcontainers-demo/main.go">this Github repository</a>. Still, let’s see how we define our “server“ struct:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> server <span class="hljs-keyword">struct</span> {
  DB    DB
  Cache Cache
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewServer</span><span class="hljs-params">(db DB, cache Cache)</span> <span class="hljs-params">(*server, error)</span></span> {
  <span class="hljs-keyword">if</span> err := db.Init(); err != <span class="hljs-literal">nil</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
  }
  <span class="hljs-keyword">if</span> err := cache.Init(); err != <span class="hljs-literal">nil</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
  }
  <span class="hljs-keyword">return</span> &amp;server{DB: db, Cache: cache}, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>The <strong>NewServer</strong> function allows us to initialize a server with the database and cache instances that implement DB and Cache interfaces.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> DB <span class="hljs-keyword">interface</span> {
  Init() error
  StoreURL(url <span class="hljs-keyword">string</span>, key <span class="hljs-keyword">string</span>) error
  GetURL(key <span class="hljs-keyword">string</span>) (<span class="hljs-keyword">string</span>, error)
}

<span class="hljs-keyword">type</span> Cache <span class="hljs-keyword">interface</span> {
  Init() error
  Set(key <span class="hljs-keyword">string</span>, val <span class="hljs-keyword">string</span>) error
  Get(key <span class="hljs-keyword">string</span>) (<span class="hljs-keyword">string</span>, <span class="hljs-keyword">bool</span>)
}
</code></pre>
<h2 id="heading-unit-tests-with-mocked-dependencies">Unit Tests with Mocked Dependencies</h2>
<p>Because we had all dependencies defined as interfaces, we can easily generate mocks for them using <a target="_blank" href="https://github.com/vektra/mockery">mockery</a> and use them in our unit tests.</p>
<pre><code class="lang-bash">mockery --all --with-expecter
go <span class="hljs-built_in">test</span> -v ./...
</code></pre>
<p>With the help of unit tests, we can cover quite well the low level components of our application: endpoints, hash key logic, and so on. All we need is to mock the function calls of database and cache dependencies.</p>
<p>unit_test.go:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestServerWithMocks</span><span class="hljs-params">(t *testing.T)</span></span> {
  mockDB := mocks.NewDB(t)
  mockCache := mocks.NewCache(t)

  mockDB.EXPECT().Init().Return(<span class="hljs-literal">nil</span>)
  mockDB.EXPECT().StoreURL(mock.Anything, mock.Anything).Return(<span class="hljs-literal">nil</span>)
  mockDB.EXPECT().GetURL(mock.Anything).Return(<span class="hljs-string">"url"</span>, <span class="hljs-literal">nil</span>)

  mockCache.EXPECT().Init().Return(<span class="hljs-literal">nil</span>)
  mockCache.EXPECT().Get(mock.Anything).Return(<span class="hljs-string">"url"</span>, <span class="hljs-literal">true</span>)
  mockCache.EXPECT().Set(mock.Anything, mock.Anything).Return(<span class="hljs-literal">nil</span>)

  s, err := NewServer(mockDB, mockCache)
  assert.NoError(t, err)

  srv := httptest.NewServer(s)
  <span class="hljs-keyword">defer</span> srv.Close()

  <span class="hljs-comment">// actual tests happen here, see the code in the repository</span>
  testServer(srv, t)
}
</code></pre>
<p><code>mocks.NewDB(t)</code> and <code>mocks.NewCache(t)</code> have been auto-generated by mockery and we use <code>EXPECT()</code> to mock the functions. Notice that we created a separate function <code>testServer(srv, t)</code> that we will use later in other tests as well, but providing a different server struct.</p>
<p>As you may already understand, these unit tests are not testing the communications between our application and our database/cache, and we may easily miss some very critical bugs.</p>
<p>To be more confident with our application, we should write integration tests along with unit tests to ensure that our application is fully functional.</p>
<h2 id="heading-integration-tests-with-real-dependencies">Integration Tests with Real Dependencies</h2>
<p>As Option 1 and 2 mention above, we can provision our dependencies beforehand and run our tests against these instances. One option would be to have a Docker Compose configuration with MongoDB and Redis, which we start before the tests and shutdown after. The seed data could be a part of this configuration, or done separately.</p>
<p>compose.yaml:</p>
<pre><code class="lang-go">services:
  mongodb:
    image: mongodb/mongodb-community-server:<span class="hljs-number">7.0</span>-ubi8
    restart: always
    ports:
      - <span class="hljs-string">"27017:27017"</span>

  redis:
    image: redis:<span class="hljs-number">7.4</span>-alpine
    restart: always
    ports:
      - <span class="hljs-string">"6379:6379"</span>
</code></pre>
<p>realdeps_test.go:</p>
<pre><code class="lang-go"><span class="hljs-comment">//go:build realdeps</span>
<span class="hljs-comment">// +build realdeps</span>

<span class="hljs-keyword">package</span> main

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestServerWithRealDependencies</span><span class="hljs-params">(t *testing.T)</span></span> {
  os.Setenv(<span class="hljs-string">"MONGO_URI"</span>, <span class="hljs-string">"mongodb://localhost:27017"</span>)
  os.Setenv(<span class="hljs-string">"REDIS_URI"</span>, <span class="hljs-string">"redis://localhost:6379"</span>)

  s, err := NewServer(&amp;MongoDB{}, &amp;Redis{})
  assert.NoError(t, err)

  srv := httptest.NewServer(s)
  <span class="hljs-keyword">defer</span> srv.Close()

  testServer(srv, t)
}
</code></pre>
<p>Now these tests don’t use mocks, but simply connect to the already provisioned database and cache. Note: we added a “realdeps“ build tag so these tests should be executed by specifying this tag explicitly.</p>
<pre><code class="lang-bash">docker-compose up -d
go <span class="hljs-built_in">test</span> -tags=realdeps -v ./...
docker-compose down
</code></pre>
<h2 id="heading-integration-tests-with-testcontainers">Integration Tests with Testcontainers</h2>
<p>However, creating reliable service dependencies using Docker Compose requires good knowledge of Docker internals and how to best run specific technologies in a container. For example, creating a dynamic integration testing environment may result in port conflicts, containers not being fully running and available, and so on.</p>
<p>With Testcontainers, we can now do the same – but inside our test suite, using our language API. This means we can control our throwaway dependencies better and make sure they’re isolated per each test run. You can run pretty much anything in Testcontainers, as long as it has a Docker-API compatible container runtime.</p>
<p>integration_test.go:</p>
<pre><code class="lang-go"><span class="hljs-comment">//go:build integration</span>
<span class="hljs-comment">// +build integration</span>

<span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
  <span class="hljs-string">"context"</span>
  <span class="hljs-string">"net/http/httptest"</span>
  <span class="hljs-string">"os"</span>
  <span class="hljs-string">"testing"</span>
  <span class="hljs-string">"github.com/stretchr/testify/assert"</span>
  <span class="hljs-string">"github.com/testcontainers/testcontainers-go/modules/mongodb"</span>
  <span class="hljs-string">"github.com/testcontainers/testcontainers-go/modules/redis"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestServerWithTestcontainers</span><span class="hljs-params">(t *testing.T)</span></span> {
  ctx := context.Background()

  mongodbContainer, err := mongodb.Run(ctx, <span class="hljs-string">"docker.io/mongodb/mongodb-community-server:7.0-ubi8"</span>)
  assert.NoError(t, err)
  <span class="hljs-keyword">defer</span> mongodbContainer.Terminate(ctx)

  redisContainer, err := redis.Run(ctx, <span class="hljs-string">"docker.io/redis:7.4-alpine"</span>)
  assert.NoError(t, err)
  <span class="hljs-keyword">defer</span> redisContainer.Terminate(ctx)

  mongodbEndpoint, _ := mongodbContainer.Endpoint(ctx, <span class="hljs-string">""</span>)
  redisEndpoint, _ := redisContainer.Endpoint(ctx, <span class="hljs-string">""</span>)

  os.Setenv(<span class="hljs-string">"MONGO_URI"</span>, <span class="hljs-string">"mongodb://"</span>+mongodbEndpoint)
  os.Setenv(<span class="hljs-string">"REDIS_URI"</span>, <span class="hljs-string">"redis://"</span>+redisEndpoint)

  s, err := NewServer(&amp;MongoDB{}, &amp;Redis{})
  assert.NoError(t, err)

  srv := httptest.NewServer(s)
  <span class="hljs-keyword">defer</span> srv.Close()

  testServer(srv, t)
}
</code></pre>
<p>This is very similar to the previous test: we just initialized two containers at the top of our test.</p>
<p>The first run may take a while to download the images. But the subsequent runs are almost instant.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:700/0*A3NirSvt1jkADZjq.png" alt="Test run output using Testcontainers" width="700" height="562" loading="lazy"></p>
<h2 id="heading-how-testcontainers-work">How Testcontainers Work</h2>
<p>To run tests with Testcontainers, you need a Docker-API compatible container runtime or to install Docker locally. Try stopping your Docker engine and it won’t work.</p>
<p>But this should not be an issue for most developers, because having a Docker runtime in your CI/CD or locally is a very common practice nowadays. You can easily have this environment in Github Actions, for example.</p>
<p>When it comes to supported languages, Testcontainers support a big list of popular languages and platforms including Java, .NET, Go, NodeJS, Python, Rust, Haskell, and others.</p>
<p>There is also a growing list of preconfigured implementations (called modules) which you can find <a target="_blank" href="https://testcontainers.com/modules/">here</a>. But as I mentioned earlier, you can run any Docker image.</p>
<p>In Go, you could use the following code to provision Redis instead of using a preconfigured module:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Using available module</span>
redisContainer, err := redis.Run(ctx, <span class="hljs-string">"redis:latest"</span>)

<span class="hljs-comment">// Or using GenericContainer</span>
req := testcontainers.ContainerRequest{
  Image:        <span class="hljs-string">"redis:latest"</span>,
  ExposedPorts: []<span class="hljs-keyword">string</span>{<span class="hljs-string">"6379/tcp"</span>},
  WaitingFor:   wait.ForLog(<span class="hljs-string">"Ready to accept connections"</span>),
}

redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
  ContainerRequest: req,
  Started:          <span class="hljs-literal">true</span>,
})
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>While the development and maintenance of integration tests require significant effort, they are crucial part of the SDLC ensuring that components, subsystems, or applications work well together combined as a group.</p>
<p>Using Testcontainers, we can simplify the provisioning and de-provisioning of throwaway dependencies for testing, making the test runs fully isolated and more predicatble.</p>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><p><a target="_blank" href="https://github.com/plutov/packagemain/blob/master/testcontainers-demo">Github repository</a></p>
</li>
<li><p><a target="_blank" href="https://testcontainers.com/">Testcontainers</a></p>
</li>
<li><p><a target="_blank" href="https://packagemain.tech">Discover more articles from packagemain.tech</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Terminate Go Programs Elegantly – A Guide to Graceful Shutdowns ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever pulled the power cord out of your computer in frustration? While this might seem like a quick solution to certain problems, it can lead to data loss and system instability. In the world of software, a similar concept exists: the hard sh... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/graceful-shutdowns-k8s-go/</link>
                <guid isPermaLink="false">66bbc28235694d727821d34d</guid>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Pliutau ]]>
                </dc:creator>
                <pubDate>Tue, 13 Aug 2024 20:30:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723496940277/5fe7a894-9c67-40fd-95c4-64ef32444a4d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever pulled the power cord out of your computer in frustration? While this might seem like a quick solution to certain problems, it can lead to data loss and system instability.</p>
<p>In the world of software, a similar concept exists: the hard shutdown. This abrupt termination can cause problems just like its physical counterpart. Thankfully, there's a better way: the graceful shutdown.</p>
<p>For applications deployed in orchestrated environments (like Kubernetes), graceful handling of termination signals is crucial.</p>
<p>By integrating graceful shutdown, you provide advance notification to the service. This enables it to complete ongoing requests, potentially save state information to disk, and ultimately avoid data corruption during shutdown.</p>
<p>In this guide, we'll dive into the world of graceful shutdowns, specifically focusing on their implementation in Go applications running on Kubernetes.</p>
<h2 id="heading-signals-in-unix-systems">Signals in Unix Systems</h2>
<p>One of the key tools for achieving graceful shutdowns in Unix-based systems is the concept of signals. These are, in basic terms, a simple way to communicate one specific thing to a process, from another process.</p>
<p>By understanding how signals work, you can leverage them to implement controlled termination procedures within your applications, ensuring a smooth and data-safe shutdown process.</p>
<p>There are many signals, and you can find them <a target="_blank" href="https://en.wikipedia.org/wiki/Signal_(IPC)"><strong>here</strong></a>. But our concern in this article is only shutdown signals:</p>
<ul>
<li><p><strong>SIGTERM</strong> – sent to a process to request its termination. Most commonly used, and we’ll be focusing on it later.</p>
</li>
<li><p><strong>SIGKILL</strong> – “quit immediately”, can not be interfered with.</p>
</li>
<li><p><strong>SIGINT</strong> – interrupt signal (such as Ctrl+C)</p>
</li>
<li><p><strong>SIGQUIT</strong> – quit signal (such as Ctrl+D)</p>
</li>
</ul>
<p>These signals can be sent from the user (Ctrl+C / Ctrl+D), from another program/process, or from the system itself (kernel / OS). For example, a <strong>SIGSEGV</strong> aka segmentation fault is sent by the OS.</p>
<h2 id="heading-our-guinea-pig-service">Our Guinea Pig Service</h2>
<p>To explore the world of graceful shutdowns in a practical setting, let's create a simple service we can experiment with. This "guinea pig" service will have a single endpoint that simulates some real-world work (we’ll add a slight delay) by calling Redis's <a target="_blank" href="https://redis.io/docs/latest/commands/incr/">INCR</a> command. We'll also provide a basic Kubernetes configuration to test how the platform handles termination signals.</p>
<p>The ultimate goal: ensure our service gracefully handles shutdowns without losing any requests/data. By comparing the number of requests sent in parallel with the final counter value in Redis, we'll be able to verify if our graceful shutdown implementation is successful.</p>
<p>We won’t go into details of setting up the Kubernetes cluster and Redis, but you can find the <a target="_blank" href="https://github.com/plutov/packagemain/tree/master/graceful-shutdown">full setup in this Github repository</a>.</p>
<p>The verification process is the following:</p>
<ol>
<li><p>Deploy Redis and Go application to Kubernetes.</p>
</li>
<li><p>Use <a target="_blank" href="https://github.com/tsenart/vegeta"><strong>vegeta</strong></a> to send 1000 requests (25/s over 40 seconds).</p>
</li>
<li><p>While vegeta is running, initialize a Kubernetes <a target="_blank" href="https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/"><strong>Rolling Update</strong></a> by updating the image tag.</p>
</li>
<li><p>Connect to Redis to verify the “counter“, it should be 1000.</p>
</li>
</ol>
<p>Let’s start with our base Go HTTP Server.</p>
<p><strong>hard-shutdown/main.go:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
  <span class="hljs-string">"net/http"</span>
  <span class="hljs-string">"os"</span>
  <span class="hljs-string">"time"</span>

  <span class="hljs-string">"github.com/go-redis/redis"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
  redisdb := redis.NewClient(&amp;redis.Options{
    Addr: os.Getenv(<span class="hljs-string">"REDIS_ADDR"</span>),
  })

  server := http.Server{
    Addr: <span class="hljs-string">":8080"</span>,
  }

  http.HandleFunc(<span class="hljs-string">"/incr"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    <span class="hljs-keyword">go</span> processRequest(redisdb)
    w.WriteHeader(http.StatusOK)
  })

  server.ListenAndServe()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processRequest</span><span class="hljs-params">(redisdb *redis.Client)</span></span> {
  <span class="hljs-comment">// simulate some business logic here</span>
  time.Sleep(time.Second * <span class="hljs-number">5</span>)
  redisdb.Incr(<span class="hljs-string">"counter"</span>)
}
</code></pre>
<p>When we run our verification procedure using this code, we’ll see that some requests fail and the <strong>counter is less than 1000</strong> (the number may vary each run).</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96fe0766-1aee-4865-a233-1827d4eb92cc_1172x222.png" alt="https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96fe0766-1aee-4865-a233-1827d4eb92cc_1172x222" width="1172" height="222" loading="lazy"></p>
<p>Which clearly means that we lost some data during the rolling update. 😢</p>
<h2 id="heading-how-to-handle-signals-in-go">How to Handle Signals in Go</h2>
<p>Go provides a <a target="_blank" href="https://pkg.go.dev/os/signal">signal</a> package that allows you to handle Unix Signals. It’s important to note that by default, the SIGINT and SIGTERM signals cause the Go program to exit. And in order for our Go application not to exit so abruptly, we need to handle incoming signals.</p>
<p>There are two options to do so.</p>
<p>The first is using channel:</p>
<pre><code class="lang-go">c := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> os.Signal, <span class="hljs-number">1</span>)
signal.Notify(c, syscall.SIGTERM)
</code></pre>
<p>The second is using context (the preferred approach nowadays):</p>
<pre><code class="lang-go">ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM)
<span class="hljs-keyword">defer</span> stop()
</code></pre>
<p><strong>NotifyContext</strong> returns a copy of the parent context that is marked done (its Done channel is closed) when one of the listed signals arrives, when the returned <strong>stop()</strong> function is called, or when the parent context's Done channel is closed – whichever happens first.</p>
<p>There are few problems with our current implementation of HTTP Server:</p>
<ol>
<li><p>We have a slow <code>processRequest</code> goroutine, and since we don’t handle the termination signal, the program exits automatically. This means that all running goroutines are terminated as well.</p>
</li>
<li><p>The program doesn’t close any connections.</p>
</li>
</ol>
<p>Let’s rewrite it.</p>
<p><strong>graceful-shutdown/main.go:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-comment">// imports</span>

<span class="hljs-keyword">var</span> wg sync.WaitGroup

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
  ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM)
  <span class="hljs-keyword">defer</span> stop()

  <span class="hljs-comment">// redisdb, server</span>

  http.HandleFunc(<span class="hljs-string">"/incr"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    wg.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> processRequest(redisdb)
    w.WriteHeader(http.StatusOK)
  })

  <span class="hljs-comment">// make it a goroutine</span>
  <span class="hljs-keyword">go</span> server.ListenAndServe()

  <span class="hljs-comment">// listen for the interrupt signal</span>
  &lt;-ctx.Done()

  <span class="hljs-comment">// stop the server</span>
  <span class="hljs-keyword">if</span> err := server.Shutdown(context.Background()); err != <span class="hljs-literal">nil</span> {
    log.Fatalf(<span class="hljs-string">"could not shutdown: %v\n"</span>, err)
  }

  <span class="hljs-comment">// wait for all goroutines to finish</span>
  wg.Wait()

  <span class="hljs-comment">// close redis connection</span>
  redisdb.Close()

  os.Exit(<span class="hljs-number">0</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processRequest</span><span class="hljs-params">(redisdb *redis.Client)</span></span> {
  <span class="hljs-keyword">defer</span> wg.Done()

  <span class="hljs-comment">// simulate some business logic here</span>
  time.Sleep(time.Second * <span class="hljs-number">5</span>)
  redisdb.Incr(<span class="hljs-string">"counter"</span>)
}
</code></pre>
<p>Here’s the summary of updates:</p>
<ul>
<li><p>Added <strong>signal.NotifyContext</strong> to listen for the SIGTERM termination signal.</p>
</li>
<li><p>Introduced a <strong>sync.WaitGroup</strong> to track in-flight requests (processRequest goroutines).</p>
</li>
<li><p>Wrapped the server in a goroutine and used <strong>server.Shutdown</strong> with context to gracefully stop accepting new connections.</p>
</li>
<li><p>Used <strong>wg.Wait()</strong> to ensure all in-flight requests (processRequest goroutines) finish before proceeding.</p>
</li>
<li><p>Resource Cleanup: Added <strong>redisdb.Close()</strong> to properly close the Redis connection before exiting.</p>
</li>
<li><p>Clean Exit: Used <strong>os.Exit(0)</strong> to indicate a successful termination.</p>
</li>
</ul>
<p>Now, if we repeat our verification process, we will see that all 1000 requests are processed correctly. 🎉</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0852d7a6-be64-44fb-bb00-c48489365585_1172x222.png" alt="https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0852d7a6-be64-44fb-bb00-c48489365585_1172x222" width="1172" height="222" loading="lazy"></p>
<h3 id="heading-web-frameworks-http-library">Web Frameworks / HTTP Library</h3>
<p>Frameworks like Echo, Gin, Fiber and others will spawn a goroutine for each incoming request. This gives it a context and then calls your function / handler depending on the routing you decided. In our case, it would be the anonymous function given to HandleFunc for the “/incr” path.</p>
<p>When you intercept a <strong>SIGTERM</strong> signal and ask your framework to gracefully shutdown, two important things happen (to oversimplify):</p>
<ul>
<li><p>Your framework stops accepting incoming requests</p>
</li>
<li><p>It waits for any existing incoming requests to finish (implicitly waiting for the goroutines to end).</p>
</li>
</ul>
<p><em>Note: Kubernetes also stops directing incoming traffic from the loadbalancer to your pod once it has labelled it as Terminating.</em></p>
<h3 id="heading-optional-shutdown-timeout">Optional: Shutdown Timeout</h3>
<p>Terminating a process can be complex, especially if there are many steps involved like closing connections. To ensure things run smoothly, you can set a timeout. This timeout acts as a safety net, gracefully exiting the process if it takes longer than expected.</p>
<pre><code class="lang-go">shutdownCtx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">10</span>*time.Second)
<span class="hljs-keyword">defer</span> cancel()

<span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
  <span class="hljs-keyword">if</span> err := server.Shutdown(shutdownCtx); err != <span class="hljs-literal">nil</span> {
    log.Fatalf(<span class="hljs-string">"could not shutdown: %v\n"</span>, err)
  }
}()

<span class="hljs-keyword">select</span> {
<span class="hljs-keyword">case</span> &lt;-shutdownCtx.Done():
  <span class="hljs-keyword">if</span> shutdownCtx.Err() == context.DeadlineExceeded {
    log.Fatalln(<span class="hljs-string">"timeout exceeded, forcing shutdown"</span>)
  }

  os.Exit(<span class="hljs-number">0</span>)
}
</code></pre>
<h2 id="heading-kubernetes-termination-lifecycle">Kubernetes Termination Lifecycle</h2>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a391d61-99c1-4e3b-a4f3-35877570b74f_4251x940.jpeg" alt="https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a391d61-99c1-4e3b-a4f3-35877570b74f_4251x940" width="1456" height="322" loading="lazy"></p>
<p>Since we used Kubernetes to deploy our service, let’s dive deeper into how it terminates the pods. Once Kubernetes decides to terminate the pod, the following events will take place:</p>
<ol>
<li><p>Pod is set to the “Terminating” State and removed from the endpoints list of all Services.</p>
</li>
<li><p><strong>preStop</strong> Hook is executed if defined.</p>
</li>
<li><p><strong>SIGTERM</strong> signal is sent to the pod. But hey, now our application knows what to do!</p>
</li>
<li><p>Kubernetes waits for a grace period (<strong>terminationGracePeriodSeconds</strong>), which is 30s by default.</p>
</li>
<li><p><strong>SIGKILL</strong> signal is sent to pod, and the pod is removed.</p>
</li>
</ol>
<p>As you can see, if you have a long-running termination process, it may be necessary to increase the <strong>terminationGracePeriodSeconds</strong> setting<strong>.</strong> This allows your application enough time to shut down gracefully.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Graceful shutdowns safeguard data integrity, maintain a seamless user experience, and optimize resource management. With its rich standard library and emphasis on concurrency, Go empowers developers to effortlessly integrate graceful shutdown practices – a necessity for applications deployed in containerized or orchestrated environments like Kubernetes.</p>
<p>You can find the Go code and Kubernetes manifests in <a target="_blank" href="https://github.com/plutov/packagemain/tree/master/graceful-shutdown">this Github repository</a>.</p>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><p><a target="_blank" href="https://pkg.go.dev/os/signal">os/signal package</a></p>
</li>
<li><p><a target="_blank" href="https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/">Kubernetes Pod Lifecycle</a></p>
</li>
<li><p><a target="_blank" href="https://packagemain.tech/p/graceful-shutdowns-k8s-go">Explore more articles from packagemain.tech</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
