Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions .github/workflows/ci_and_tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Build, Test, Run
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.25.2'
- name: Install dependencies
run: go get .
- name: Build
run: go build -v ./...
- name: Test with the Go CLI
run: go test ./...

lint:
needs: [test]
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.25.2'
- name: Install golangci-lint
run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
- name: Run golangci-lint
run: golangci-lint run ./...

docker-build-and-push:
needs: [test, lint]
runs-on: ubuntu-latest
steps:
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKER_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ vars.DOCKER_USERNAME }}/${{ github.event.repository.name }}:latest

deploy-and-validate:
needs: [test, lint, docker-build-and-push]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Create Kind cluster
uses: helm/kind-action@v1.8.0
with:
cluster_name: ci-cluster-example
wait: 120s

- name: Export kubeconfig
run: |
echo "Verifying Kind cluster setup..."
mkdir -p $HOME/.kube
kind get kubeconfig --name ci-cluster-example > $HOME/.kube/config
echo "KUBECONFIG=$HOME/.kube/config" >> $GITHUB_ENV
kubectl config use-context kind-ci-cluster-example
kubectl cluster-info

- name: Set up Kubectl
uses: azure/setup-kubectl@v3
with:
version: 'latest'

- name: Install Helm
uses: azure/setup-helm@v1
with:
version: 'latest'

- name: Deploy to Kind cluster using Helm
run: |
helm upgrade --install pipeline-example ./pipeline-example --set image.repository=mbts89/pipeline_example --set image.tag=latest
kubectl rollout status deployment/pipeline-example --timeout=90s
- name: Test API endpoint
run: |
echo "Waiting for service to be ready..."
kubectl wait --for=condition=available --timeout=90s deployment/pipeline-example
echo "Starting port-forward..."
kubectl port-forward deployment/pipeline-example 8080:8080 &
sleep 5
echo "Testing API response:"
curl -s http://localhost:8080 | tee result.txt
grep -q "Hello World" result.txt && echo "✅ API returned expected response" || (echo "❌ Unexpected API response" && exit 1)

- name: Start tMate Session
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true
35 changes: 35 additions & 0 deletions .github/workflows/cleanup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Cleanup the cluster

on:
workflow_dispatch:

jobs:
cleanup-kind:
runs-on: ubuntu-latest
env:
CLUSTER_NAME: ci-cluster-example

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Kind
run: |
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

- name: Delete Kind cluster
run: |
kind delete cluster
if kind get clusters | grep -q "$CLUSTER_NAME"; then
echo "🧹 Deleting Kind cluster: $CLUSTER_NAME"
kind delete cluster --name "$CLUSTER_NAME"
else
echo "✅ No existing cluster named $CLUSTER_NAME found — nothing to delete."
fi

- name: Verify cleanup
run: |
echo "🔍 Checking clusters:"
kind get clusters || echo "✅ Cleanup complete — no clusters remain."
60 changes: 60 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

# FROM golang:1.25.2

# # Set destination for COPY
# WORKDIR /app

# # Download Go modules
# COPY go.mod ./
# RUN go mod download

# # Copy the source code. Note the slash at the end, as explained in
# # https://docs.docker.com/reference/dockerfile/#copy
# COPY *.go ./

# # Build
# RUN CGO_ENABLED=0 GOOS=linux go build -o /pipeline-example

# # Optional:
# # To bind to a TCP port, runtime parameters must be supplied to the docker command.
# # But we can document in the Dockerfile what ports
# # the application is going to listen on by default.
# # https://docs.docker.com/reference/dockerfile/#expose
# EXPOSE 8080

# # Run
# CMD ["go run main.go"]

FROM golang:1.25.2 AS builder

WORKDIR /app

# Copy the Go module files
COPY go.mod .
# COPY go.sum .

# Download the Go module dependencies
RUN go mod download

COPY . .

# RUN go build -o myapp main.go
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o myapp main.go

# Run the tests in the container
FROM builder AS run-test-stage
RUN go test -v ./...


# FROM alpine:latest as run
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .

# Copy the application executable from the build image
# COPY *.go ./

WORKDIR /app
EXPOSE 8080
CMD ["./myapp"]

136 changes: 136 additions & 0 deletions README copy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# pipeline-example

example for jamf
docker desktop
run in kind
kind create cluster --name local-test

kubectl get pods

# 🧩 Basic API Pipeline Example

_A lightweight Go web service with automated Kubernetes deployment using Kind, Helm, and GitHub Actions._

---

## 📘 Overview

This repository demonstrates a full CI/CD workflow for a simple Go-based API deployed to a local **Kind (Kubernetes in Docker)** cluster.
The GitHub Actions pipeline handles building, pushing, deploying, and verifying your application end-to-end — making it easy to reproduce locally or debug via **tmate**.

---

## What's it do?

A minimal Go API running on port `8080`:

```go
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
})
http.ListenAndServe(":8080", nil)
}
```

When running, http://localhost:8080

⚙️ Prerequisites

Install these tools locally to mirror the CI environment:
Tool - Version - Install
Go ≥ 1.22 - golang.org/dl
Docker ≥ 24 - docs.docker.com (I use Docker Desktop)
Kubectl ≥ 1.30 - kubernetes.io
Helm ≥ 3.14 - helm.sh
Kind ≥ 0.23 - kind.sigs.k8s.io

# Running the App Locally

## Run locally

Pull the repo down and cd into it then run it locally to verify it works

```
go run main.go
```

## The included workflow automates the full build → deploy → verify cycle.

## Make sure Kubernetes is enabled in Docker

📄 .github/workflows/ci.yml
Pipeline steps:
Build & Test Go App
Build & Push Docker Image — Builds and pushes to Docker Hub.
Create Kind Cluster — Spins up a temporary Kubernetes cluster inside GitHub Actions.
Export kubeconfig — Ensures Helm and kubectl point to the correct cluster.
Deploy Helm Chart
Verify API Response — Uses kubectl port-forward + curl to confirm Hello World.
(Optional) action-tmate — Opens a live debugging session for interactive testing. [link](https://github.com/mxschmitt/action-tmate?tab=readme-ov-file)

🔐 Required GitHub Infp
Name Description
DOCKERHUB_USERNAME - Your Docker Hub username (new variable)
DOCKERHUB_TOKEN - Docker Hub access token for docker push (new secret)
Public SSH Key - Optional, for tMate validation

# Trigger Workflow

1. Make a change to a file and push it up to Github. Create a new PR and the workflow will kick off.

- When the workflow gets to the 'deploy-and-validate' stage, click into the workflow details.
- Inside, wait for 'Start tMate Session' to start

- Click in, and wait for a series of ssh code to show up, should look similar to:
🔹 How to Connect
When the workflow reaches the tmate step, it prints something like:
SSH: ssh abcdefgh@nyc1.tmate.io

SSH URL: Connect from your local terminal:
`ssh abcdefgh@nyc1.tmate.io`

- 🔹 Once Inside tmate
Check your pods:
`kubectl get pods`

Run: `kubectl port-forward deployment/pipeline-example 9091:8080 &`
Wait a couple of seconds, then run: `curl http://localhost:9091`

You should see a 'Hello World' returned to you.
To close the instance, run `exit`. This will also finish your pipeline.
You the pipeline will continue to run until you cancel it you dont use the tMate option.

🧹 Cleanup
To reset your environment:
kind delete cluster --name local-test
docker image rm USERNAME/pipeline_example:latest

🔍 Troubleshooting
Issue - Cause - Fix
`port already in use` - Existing forward active `pkill -f "kubectl port-forward"`

## Structure

```.
├── main.go
├── Dockerfile
├── pipeline-example/
│ └─
│ ├── Chart.yaml
│ ├── templates/
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── values.yaml
└── .github/
└── workflows/
└── ci.yml
```

🏁 Summary

This project is a simple, reproducible template for:
Building and deploying a Go API to Kubernetes.
Managing deployments with Helm.
Automating CI/CD with GitHub Actions.
Debugging live clusters with action-tmate.
18 changes: 18 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: "3.9"

services:
pipeline-example:
build:
context: .
dockerfile: Dockerfile
container_name: pipeline-example
ports:
- "8080:8080"
environment:
HELLO_NAME: "Annie"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/healthz"]
interval: 10s
timeout: 3s
retries: 3
restart: unless-stopped
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module hello-api

go 1.25.2
Binary file added kind
Binary file not shown.
Loading
Loading