Skip to content

Commit f54a55f

Browse files
authored
Merge pull request #1 from cabotage/tailscale-operator-manager
Tailscale operator manager
2 parents f13ba60 + 2624923 commit f54a55f

7 files changed

Lines changed: 463 additions & 3 deletions

File tree

.github/workflows/build-and-push.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,20 @@ jobs:
1818
outputs:
1919
matrix: ${{ steps.set-matrix.outputs.matrix }}
2020
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v6
23+
2124
- name: Determine which containers to build
2225
id: set-matrix
2326
run: |
2427
TAG="${GITHUB_REF#refs/tags/}"
25-
ALL_CONTAINERS='["base","cabotage-ca-admission","enrollment-operator","registry","sidecar"]'
2628
27-
# Check if tag is "{container-name}-{version}" or just "{version}"
29+
# Build list of containers from directory names
30+
ALL_CONTAINERS=$(ls -d containers/*/Dockerfile | xargs -n1 dirname | xargs -n1 basename | jq -R -s -c 'split("\n") | map(select(. != ""))')
31+
32+
# Check if tag matches "{container-name}-{version}"
2833
MATCHED=""
29-
for name in base cabotage-ca-admission enrollment-operator registry sidecar; do
34+
for name in $(echo "$ALL_CONTAINERS" | jq -r '.[]'); do
3035
if [[ "$TAG" == "$name-"* ]]; then
3136
MATCHED="$name"
3237
break
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM cabotage/base:4
2+
3+
USER root
4+
COPY requirements.txt /opt/cabotage/requirements.txt
5+
6+
RUN pip --no-cache-dir --disable-pip-version-check install -r /opt/cabotage/requirements.txt
7+
USER nobody
8+
9+
COPY operator.py /opt/cabotage/operator.py
10+
ENTRYPOINT ["kopf", "run", "/opt/cabotage/operator.py", "--all-namespaces"]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
apiVersion: apiextensions.k8s.io/v1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
name: cabotagetailscaleoperatorconfigs.cabotage.io
5+
spec:
6+
scope: Namespaced
7+
group: cabotage.io
8+
names:
9+
kind: CabotageTailscaleOperatorConfig
10+
plural: cabotagetailscaleoperatorconfigs
11+
singular: cabotagetailscaleoperatorconfig
12+
shortNames:
13+
- tsconfig
14+
- tsconfigs
15+
versions:
16+
- name: v1
17+
served: true
18+
storage: true
19+
additionalPrinterColumns:
20+
- name: State
21+
type: string
22+
jsonPath: .status.reconcile_operator.state
23+
- name: Version
24+
type: string
25+
jsonPath: .status.reconcile_operator.operatorVersion
26+
- name: Age
27+
type: date
28+
jsonPath: .metadata.creationTimestamp
29+
subresources:
30+
status: {}
31+
schema:
32+
openAPIV3Schema:
33+
type: object
34+
properties:
35+
spec:
36+
type: object
37+
required:
38+
- clientId
39+
- operatorImage
40+
- organizationSlug
41+
properties:
42+
clientId:
43+
type: string
44+
description: >
45+
Tailscale OIDC federated identity client ID.
46+
Cabotage mints JWTs signed by its OIDC issuer and
47+
writes them to a K8s Secret for the operator to use.
48+
operatorImage:
49+
type: string
50+
description: >
51+
Full image reference for the Tailscale operator
52+
(e.g. ghcr.io/tailscale/k8s-operator:v1.94.2).
53+
defaultTags:
54+
type: string
55+
description: >
56+
Default ACL tags for operator-created nodes
57+
(e.g. tag:k8s).
58+
organizationSlug:
59+
type: string
60+
description: >
61+
Cabotage organization slug for labeling.
62+
status:
63+
type: object
64+
x-kubernetes-preserve-unknown-fields: true
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
namespace: cabotage
5+
name: tailscale-operator-manager
6+
labels:
7+
app: tailscale-operator-manager
8+
spec:
9+
replicas: 1
10+
strategy:
11+
type: Recreate
12+
selector:
13+
matchLabels:
14+
app: tailscale-operator-manager
15+
template:
16+
metadata:
17+
labels:
18+
app: tailscale-operator-manager
19+
ca-admission.cabotage.io: "true"
20+
spec:
21+
serviceAccountName: cabotage-tailscale-operator-manager-account
22+
containers:
23+
- name: tailscale-operator-manager
24+
image: ghcr.io/cabotage/containers/tailscale-operator-manager:latest
25+
imagePullPolicy: Always
26+
args:
27+
- "--verbose"
28+
- "--liveness"
29+
- "http://0.0.0.0:8080/healthz"
30+
livenessProbe:
31+
httpGet:
32+
path: /healthz
33+
port: 8080
34+
resources:
35+
limits:
36+
memory: "256Mi"
37+
cpu: "100m"
38+
requests:
39+
memory: "100Mi"
40+
cpu: "50m"
41+
securityContext:
42+
readOnlyRootFilesystem: true
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
apiVersion: v1
3+
kind: ServiceAccount
4+
metadata:
5+
namespace: cabotage
6+
name: cabotage-tailscale-operator-manager-account
7+
---
8+
apiVersion: rbac.authorization.k8s.io/v1
9+
kind: ClusterRole
10+
metadata:
11+
name: cabotage-tailscale-operator-manager-role-cluster
12+
rules:
13+
14+
# Framework: knowing which other operators are running (i.e. peering).
15+
- apiGroups: [kopf.dev]
16+
resources: [clusterkopfpeerings]
17+
verbs: [list, watch, patch, get]
18+
19+
# Framework: runtime observation of namespaces & CRDs (addition/deletion).
20+
- apiGroups: [apiextensions.k8s.io]
21+
resources: [customresourcedefinitions]
22+
verbs: [get, list, watch]
23+
- apiGroups: [""]
24+
resources: [namespaces]
25+
verbs: [list, watch]
26+
27+
# Framework: posting the events about the handlers progress/errors.
28+
- apiGroups: [""]
29+
resources: [events]
30+
verbs: [create]
31+
32+
# Application: watch the CabotageTailscaleOperatorConfig CRD cluster-wide.
33+
- apiGroups: [cabotage.io]
34+
resources: [cabotagetailscaleoperatorconfigs, cabotagetailscaleoperatorconfigs/status]
35+
verbs: [list, watch, patch, get]
36+
37+
# Manage Tailnet CRDs and ProxyGroups (cluster-scoped).
38+
- apiGroups: [tailscale.com]
39+
resources: [tailnets, proxygroups]
40+
verbs: [get, create, delete]
41+
---
42+
apiVersion: rbac.authorization.k8s.io/v1
43+
kind: Role
44+
metadata:
45+
namespace: cabotage
46+
name: cabotage-tailscale-operator-manager-role-namespaced
47+
rules:
48+
49+
# Framework: knowing which other operators are running (i.e. peering).
50+
- apiGroups: [kopf.dev]
51+
resources: [kopfpeerings]
52+
verbs: [list, watch, patch, get]
53+
54+
# Framework: posting the events about the handlers progress/errors.
55+
- apiGroups: [""]
56+
resources: [events]
57+
verbs: [create]
58+
59+
# Application: watching & handling for the custom resource we declare.
60+
- apiGroups: [cabotage.io]
61+
resources: [cabotagetailscaleoperatorconfigs]
62+
verbs: [list, watch, patch]
63+
---
64+
apiVersion: rbac.authorization.k8s.io/v1
65+
kind: ClusterRoleBinding
66+
metadata:
67+
name: cabotage-tailscale-operator-manager-rolebinding-cluster
68+
roleRef:
69+
apiGroup: rbac.authorization.k8s.io
70+
kind: ClusterRole
71+
name: cabotage-tailscale-operator-manager-role-cluster
72+
subjects:
73+
- kind: ServiceAccount
74+
name: cabotage-tailscale-operator-manager-account
75+
namespace: cabotage
76+
---
77+
apiVersion: rbac.authorization.k8s.io/v1
78+
kind: RoleBinding
79+
metadata:
80+
namespace: cabotage
81+
name: cabotage-tailscale-operator-manager-rolebinding-namespaced
82+
roleRef:
83+
apiGroup: rbac.authorization.k8s.io
84+
kind: Role
85+
name: cabotage-tailscale-operator-manager-role-namespaced
86+
subjects:
87+
- kind: ServiceAccount
88+
name: cabotage-tailscale-operator-manager-account

0 commit comments

Comments
 (0)