Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable controller and cloudflared metrics #162

Merged
merged 5 commits into from
Oct 13, 2024
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
8 changes: 8 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
permissions:
contents: read
checks: write
packages: write
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
Expand All @@ -59,6 +60,13 @@ jobs:
name: conformance-report
path: '*-report.yaml'

- uses: docker/[email protected]
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- run: make docker-push

release-please:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ CONTAINER_TOOL ?= docker
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec

E2E_TIMEOUT ?= 5m

.PHONY: all
all: build

Expand Down Expand Up @@ -68,7 +70,7 @@ test: manifests generate fmt vet envtest ## Run tests.
# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors.
.PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up.
test-e2e:
go test ./test/e2e/ -v -ginkgo.v -timeout 5m
go test ./test/e2e/ -v -ginkgo.v -timeout $(E2E_TIMEOUT)

.PHONY: lint
lint: golangci-lint ## Run golangci-lint linter
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ Manage Kubernetes ingress traffic with Cloudflare Tunnels via the [Gateway API](
1. Install v1 or later of the Gateway API CRDs: `kubectl apply -k github.com/kubernetes-sigs/gateway-api//config/crd?ref=v1.0.0`
2. Install cloudflare-kubernetes-gateway: `kubectl apply -k github.com/pl4nty/cloudflare-kubernetes-gateway//config/default?ref=v0.6.0` <!-- x-release-please-version -->
3. [Find your Cloudflare account ID](https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/)
3. [Create a Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) with the Account Cloudflare Tunnel Edit and Zone DNS Edit permissions
4. Use them to create a Secret: `kubectl create secret -n cloudflare-gateway generic cloudflare --from-literal=ACCOUNT_ID=your-account-id --from-literal=TOKEN=your-token`
5. Create a file containing your GatewayClass, then apply it with `kubectl apply -f file.yaml`:
4. [Create a Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) with the Account Cloudflare Tunnel Edit and Zone DNS Edit permissions
5. Use them to create a Secret: `kubectl create secret -n cloudflare-gateway generic cloudflare --from-literal=ACCOUNT_ID=your-account-id --from-literal=TOKEN=your-token`
6. Create a file containing your GatewayClass, then apply it with `kubectl apply -f file.yaml`:

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
Expand All @@ -23,6 +24,7 @@ spec:
namespace: cloudflare-gateway
name: cloudflare
```

7. [Create Gateways and HTTPRoutes](https://gateway-api.sigs.k8s.io/guides/http-routing/) to start managing traffic! For example:

```yaml
Expand Down Expand Up @@ -55,6 +57,8 @@ spec:
port: 80
```

8. (optional) Install Prometheus ServiceMonitors to collect controller and cloudflared metrics: `kubectl apply -k github.com/pl4nty/cloudflare-kubernetes-gateway//config/prometheus?ref=v0.6.0` <!-- x-release-please-version -->

## Features

The v1 Core spec is not yet supported, as some features (eg header-based routing) aren't available with Tunnels. The following features are supported:
Expand Down
18 changes: 18 additions & 0 deletions config/default/image_metrics_service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: image
app.kubernetes.io/name: cloudflare-kubernetes-gateway
app.kubernetes.io/managed-by: kustomize
name: image-metrics-service
namespace: system
spec:
ports:
- name: http
port: 2000
protocol: TCP
targetPort: 2000
selector:
app.kubernetes.io/name: cloudflare-kubernetes-gateway
app.kubernetes.io/managed-by: GatewayController
11 changes: 6 additions & 5 deletions config/default/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@ resources:
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
#- ../prometheus
# [METRICS] To enable the controller manager metrics service, uncomment the following line.
#- metrics_service.yaml
- metrics_service.yaml
- image_metrics_service.yaml

# Uncomment the patches line if you enable Metrics, and/or are using webhooks and cert-manager
#patches:
patches:
# [METRICS] The following patch will enable the metrics endpoint. Ensure that you also protect this endpoint.
# More info: https://book.kubebuilder.io/reference/metrics
# If you want to expose the metric endpoint of your controller-manager uncomment the following line.
#- path: manager_metrics_patch.yaml
# target:
# kind: Deployment
- path: manager_metrics_patch.yaml
target:
kind: Deployment

# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
Expand Down
18 changes: 18 additions & 0 deletions config/prometheus/image_monitor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Prometheus Monitor Service (Metrics)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
control-plane: image
app.kubernetes.io/name: cloudflare-kubernetes-gateway
app.kubernetes.io/managed-by: kustomize
name: image-metrics-monitor
namespace: system
spec:
endpoints:
- path: /metrics
port: http # Ensure this is the name of the port that exposes HTTP metrics
scheme: http
selector:
matchLabels:
control-plane: image
1 change: 1 addition & 0 deletions config/prometheus/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
resources:
- monitor.yaml
- image_monitor.yaml
1 change: 1 addition & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ rules:
- create
- get
- list
- update
- watch
- apiGroups:
- ""
Expand Down
30 changes: 29 additions & 1 deletion internal/controller/gateway_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type GatewayReconciler struct {
// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gateways,verbs=get;list;update;watch
// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gateways/finalizers,verbs=update
// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gateways/status,verbs=update
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=create;get;list;watch
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=create;get;list;update;watch
// +kubebuilder:rbac:groups=core,resources=events,verbs=create
// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get

Expand Down Expand Up @@ -412,6 +412,34 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
log.Error(err, "Failed to get Deployment")
// Let's return the error for the reconciliation be re-trigged again
return ctrl.Result{}, err
} else {
// Define a new deployment
dep, err := r.deploymentForGateway(gateway, token)
if err != nil {
log.Error(err, "Failed to define new Deployment resource for Gateway")

// The following implementation will update the status
meta.SetStatusCondition(&gateway.Status.Conditions, metav1.Condition{Type: string(gatewayv1.GatewayConditionAccepted),
Status: metav1.ConditionFalse, Reason: "Reconciling", ObservedGeneration: gateway.Generation,
Message: fmt.Sprintf("Failed to update Deployment for the custom resource (%s): (%s)", gateway.Name, err)})

if err := r.Status().Update(ctx, gateway); err != nil {
log.Error(err, "Failed to update Gateway status")
return ctrl.Result{}, err
}

return ctrl.Result{}, err
}

if err := r.Update(ctx, dep); err != nil {
if strings.Contains(err.Error(), "apply your changes to the latest version and try again") {
log.Info("Conflict when updating Deployment, retrying")
return ctrl.Result{Requeue: true}, nil
} else {
log.Error(err, "Failed to update Deployment")
return ctrl.Result{}, err
}
}
}

if err := r.Get(ctx, req.NamespacedName, gateway); err != nil {
Expand Down
15 changes: 11 additions & 4 deletions internal/controller/gatewayclass_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controller

import (
"context"
"fmt"
"time"

apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -56,22 +57,28 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request
}

// validate parameters
var ok bool
msg := ""
_, api, err := InitCloudflareApi(ctx, r.Client, gatewayClass.Name)
if err == nil {
token, err := api.User.Tokens.Verify(ctx)
if err == nil {
ok = token.Status == "active"
if token.Status != "active" {
msg = fmt.Sprintf("Token status is %s, is not active. Please check the Cloudflare dashboard", token.Status)
}
} else {
msg = err.Error() + " Ensure ACCOUNT_ID and TOKEN are valid"
}
} else {
msg = err.Error() + " Ensure ACCOUNT_ID and TOKEN are set"
}

var condition metav1.Condition
if !ok {
if msg != "" {
condition = metav1.Condition{
Type: string(gatewayv1.GatewayClassConditionStatusAccepted),
Status: metav1.ConditionFalse,
Reason: string(gatewayv1.GatewayClassReasonInvalidParameters),
Message: "Unable to initialize Cloudflare API from secret in GatewayClass parameterRef. Ensure ACCOUNT_ID and TOKEN are set",
Message: "Unable to initialize Cloudflare API. " + msg,
ObservedGeneration: gatewayClass.Generation,
}
} else {
Expand Down
Loading