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
40 changes: 40 additions & 0 deletions .github/workflows/goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: goreleaser
on:
push:
tags:
- v*
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.22"
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run GoReleaser for tag
if: "startsWith(github.ref, 'refs/tags/')"
uses: goreleaser/goreleaser-action@v4
with:
version: latest
args: release --clean
env:
GORELEASER_CURRENT_TAG: ${{ github.ref_name }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Clear
if: always()
run: rm -f ${HOME}/.docker/config.json
17 changes: 17 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
on: [push, pull_request]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.24.x]
runs-on: ubuntu-latest
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Test
run: go test ./...
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.vscode
vendor
build
api-gateway-controller
48 changes: 48 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
before:
hooks:
- go mod tidy
builds:
- id: api-gateway-controller
dir: ./cmd
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- amd64
- arm64
- arm
binary: api-gateway-controller
ldflags:
- "-s -w -X main.version={{ if not .IsSnapshot }}v{{ end }}{{ .Version }} -X main.gitCommit={{ .ShortCommit }}"

dockers:
- build_flag_templates: [--platform=linux/amd64]
dockerfile: Dockerfile
goarch: amd64
image_templates: ["ghcr.io/serverscom/api-gateway-controller:{{ if not .IsSnapshot }}v{{ end }}{{ .Version }}-amd64"]
use: buildx
- build_flag_templates: [--platform=linux/arm64]
dockerfile: Dockerfile
goarch: arm64
image_templates: ["ghcr.io/serverscom/api-gateway-controller:{{ if not .IsSnapshot }}v{{ end }}{{ .Version }}-arm64v8"]
use: buildx
- build_flag_templates: [--platform=linux/arm/v6]
dockerfile: Dockerfile
goarch: arm
goarm: 6
image_templates: ["ghcr.io/serverscom/api-gateway-controller:{{ if not .IsSnapshot }}v{{ end }}{{ .Version }}-armv6"]

docker_manifests:
- name_template: ghcr.io/serverscom/api-gateway-controller:{{ if not .IsSnapshot }}v{{ end }}{{ .Version }}
image_templates:
- ghcr.io/serverscom/api-gateway-controller:{{ if not .IsSnapshot }}v{{ end }}{{ .Version }}-amd64
- ghcr.io/serverscom/api-gateway-controller:{{ if not .IsSnapshot }}v{{ end }}{{ .Version }}-arm64v8
- ghcr.io/serverscom/api-gateway-controller:{{ if not .IsSnapshot }}v{{ end }}{{ .Version }}-armv6

release:
ids: [""]
draft: true
extra_files:
- glob: "./api-gateway-controller-*.tgz"
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM alpine:3.22

RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
COPY api-gateway-controller /bin/api-gateway-controller

ENTRYPOINT ["/bin/api-gateway-controller"]
117 changes: 117 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"flag"
"fmt"
"log"
"os"

"github.com/serverscom/api-gateway-controller/internal/config"
"github.com/serverscom/api-gateway-controller/internal/flags"
"github.com/serverscom/api-gateway-controller/internal/gateway/controller"
lbsrv "github.com/serverscom/api-gateway-controller/internal/service/lb"
tlssrv "github.com/serverscom/api-gateway-controller/internal/service/tls"

clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/healthz"
ctrlZap "sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/server"

"k8s.io/apimachinery/pkg/runtime"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
)

var (
version string
gitCommit string
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)

func init() {
_ = clientgoscheme.AddToScheme(scheme)
_ = gatewayv1.Install(scheme)
}

func main() {
var opts ctrlZap.Options
opts.BindFlags(flag.CommandLine)
ctrlConf, err := flags.ParseFlags()
if err != nil {
log.Fatalf("Error parsing flags: %v\n", err)
}

if ctrlConf.ShowVersion {
fmt.Printf("Version=%v GitCommit=%v\n", version, gitCommit)
os.Exit(0)
}

ctrl.SetLogger(ctrlZap.New(ctrlZap.UseFlagOptions(&opts)))

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Cache: cache.Options{
DefaultNamespaces: map[string]cache.Config{
ctrlConf.Namespace: {},
},
},
Metrics: server.Options{
BindAddress: ctrlConf.MetricsAddr,
},
HealthProbeBindAddress: ctrlConf.ProbeAddr,
LeaderElection: ctrlConf.EnableLeaderElection,
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}

// setup sc client
scCli, err := config.NewServerscomClient()
if err != nil {
setupLog.Error(err, "unable to create servers.com client")
os.Exit(1)
}
scCli.SetupUserAgent(fmt.Sprintf("%s/%s %s", ctrlConf.ControllerName, version, gitCommit))

// setup gw class reconciler
if err = (&controller.GatewayClassReconciler{
Client: mgr.GetClient(),
ControllerName: ctrlConf.ControllerName,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "GatewayClass")
os.Exit(1)
}

// setup gw reconciler
if err = (&controller.GatewayReconciler{
Client: mgr.GetClient(),
Recorder: mgr.GetEventRecorderFor("gateway-controller"),
ControllerName: ctrlConf.ControllerName,
GatewayClassName: ctrlConf.GatewayClassName,
LBMgr: lbsrv.NewManager(scCli),
TLSMgr: tlssrv.NewManager(scCli),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Gateway")
os.Exit(1)
}

// Health checks
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
}

setupLog.Info("starting manager", "version", version, "gitCommit", gitCommit)
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}

}
62 changes: 62 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Gateway API Controller — Example Installation for local machine

This directory contains sample manifests and step-by-step instructions for deploying a Gateway API controller on Kubernetes local setup.

## Prerequisites

- A running Kubernetes cluster (tested with Docker Desktop)
- [kubectl](https://kubernetes.io/docs/tasks/tools/)
- Your controller image built locally as `gw-controller:local`
The image can be built by running the following command from the root of this repo:
```bash
CGO_ENABLED=0 GOOS=linux go build -o api-gateway-controller cmd/main.go && docker build -t gw-controller:local .
```

## Installation Steps

### 1. Install Gateway API CRDs

Apply the latest CRDs directly from the official repository:

```bash
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml
```


### 2. Create the Namespace

```bash
kubectl apply -f namespace.yaml
```


### 3. Create the serverscom API Secret

Update the values to your actual credentials before applying:

```bash
kubectl apply -f secret.yaml
```


### 4. Create ServiceAccount, RBAC, and Bindings

```bash
kubectl apply -f rbac.yaml
```


### 5. Deploy the Gateway Controller

```bash
kubectl apply -f deployment.yaml
```

## Files Overview

- `namespace.yaml` — Namespace for the controller
- `secret.yaml` — API credentials for serverscom (update these for your environment)
- `rbac.yaml` — ServiceAccount and required RBAC permissions
- `deployment.yaml` — Deployment manifest for the controller

> **Note:** Adjust the image ( image with gw controller should exist ), environment variables, and secrets as needed for your cluster.
43 changes: 43 additions & 0 deletions examples/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gw-controller
namespace: gateway-serverscom
spec:
replicas: 1
selector:
matchLabels:
app: gw-controller
template:
metadata:
labels:
app: gw-controller
spec:
serviceAccountName: gw-controller
containers:
- name: gw-controller
image: gw-controller:local
imagePullPolicy: IfNotPresent
args:
# - "--zap-log-level=debug"
env:
- name: SC_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: serverscom
key: access-token
- name: SC_API_URL
valueFrom:
secretKeyRef:
name: serverscom
key: api-url
- name: SC_CLUSTER_ID
valueFrom:
secretKeyRef:
name: serverscom
key: cluster-id
- name: SC_LOCATION_ID
valueFrom:
secretKeyRef:
name: serverscom
key: location-id
4 changes: 4 additions & 0 deletions examples/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: gateway-serverscom
36 changes: 36 additions & 0 deletions examples/rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: gw-controller
namespace: gateway-serverscom
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:controller:gw-controller
rules:
- apiGroups: [""]
resources: ["secrets", "endpoints", "services", "pods", "nodes", "namespaces", "configmaps", "events"]
verbs: ["get", "list", "watch", "update", "create", "patch"]
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["gatewayclasses", "gateways", "httproutes", "grpcroutes", "referencegrants"]
verbs: ["get", "list", "watch", "update", "create", "patch"]
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["gatewayclasses/status", "gateways/status"]
verbs: ["update", "patch"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["get", "watch", "list", "create", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:controller:gw-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:controller:gw-controller
subjects:
- kind: ServiceAccount
name: gw-controller
namespace: gateway-serverscom
10 changes: 10 additions & 0 deletions examples/secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
name: serverscom
namespace: gateway-serverscom
stringData:
access-token: '12345'
api-url: 'https://api.servers.com/v1'
cluster-id: '123'
location-id: '1'
Loading