cmd/k8s-operator: generate static kube manifests from the Helm chart. (#10436)

* cmd/k8s-operator: generate static manifests from Helm charts

This is done to ensure that there is a single source of truth
for the operator kube manifests.
Also adds linux node selector to the static manifests as
this was added as a default to the Helm chart.

Static manifests can now be generated by running
`go generate tailscale.com/cmd/k8s-operator`.

Updates tailscale/tailscale#9222

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
Irbe Krumina 2023-12-04 10:18:07 +00:00 committed by GitHub
parent 263e01c47b
commit 49fd0a62c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 270 additions and 142 deletions

View File

@ -22,3 +22,9 @@ jobs:
eval `./tool/go run ./cmd/mkversion` eval `./tool/go run ./cmd/mkversion`
./tool/helm package --app-version="${VERSION_SHORT}" --version=${VERSION_SHORT} './cmd/k8s-operator/deploy/chart' ./tool/helm package --app-version="${VERSION_SHORT}" --version=${VERSION_SHORT} './cmd/k8s-operator/deploy/chart'
./tool/helm lint "tailscale-operator-${VERSION_SHORT}.tgz" ./tool/helm lint "tailscale-operator-${VERSION_SHORT}.tgz"
- name: Verify that static manifests are up to date
run: |
./tool/go generate tailscale.com/cmd/k8s-operator
echo
echo
git diff --name-only --exit-code || (echo "Static manifests for Tailscale Kubernetes operator are out of date. Please run 'go generate tailscale.com/cmd/k8s-operator' and commit the diff."; exit 1)

View File

@ -429,7 +429,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: check that 'go generate' is clean - name: check that 'go generate' is clean
run: | run: |
pkgs=$(./tool/go list ./... | grep -v dnsfallback) pkgs=$(./tool/go list ./... | grep -Ev 'dnsfallback|k8s-operator')
./tool/go generate $pkgs ./tool/go generate $pkgs
echo echo
echo echo

View File

@ -0,0 +1,12 @@
# Tailscale Kubernetes operator deployment manifests
./cmd/k8s-operator/deploy contain various Tailscale Kubernetes operator deployment manifests.
## Helm chart
`./cmd/k8s-operator/deploy/chart` contains Tailscale operator Helm chart templates.
The chart templates are also used to generate the static manifest, so developers must ensure that any changes applied to the chart have been propagated to the static manifest by running `go generate tailscale.com/cmd/k8s-operator`
## Static manifests
`./cmd/k8s-operator/deploy/manifests/operator.yaml` is a static manifest for the operator generated from the Helm chart templates for the operator.

View File

@ -7,94 +7,6 @@ metadata:
name: tailscale name: tailscale
--- ---
apiVersion: v1 apiVersion: v1
kind: ServiceAccount
metadata:
name: proxies
namespace: tailscale
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: proxies
namespace: tailscale
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: proxies
namespace: tailscale
subjects:
- kind: ServiceAccount
name: proxies
namespace: tailscale
roleRef:
kind: Role
name: proxies
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: operator
namespace: tailscale
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tailscale-operator
rules:
- apiGroups: [""]
resources: ["events", "services", "services/status"]
verbs: ["*"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses", "ingresses/status"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tailscale-operator
subjects:
- kind: ServiceAccount
name: operator
namespace: tailscale
roleRef:
kind: ClusterRole
name: tailscale-operator
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: operator
namespace: tailscale
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["*"]
- apiGroups: ["apps"]
resources: ["statefulsets"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: operator
namespace: tailscale
subjects:
- kind: ServiceAccount
name: operator
namespace: tailscale
roleRef:
kind: Role
name: operator
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
name: operator-oauth name: operator-oauth
@ -103,6 +15,112 @@ stringData:
client_id: # SET CLIENT ID HERE client_id: # SET CLIENT ID HERE
client_secret: # SET CLIENT SECRET HERE client_secret: # SET CLIENT SECRET HERE
--- ---
apiVersion: v1
kind: ServiceAccount
metadata:
name: operator
namespace: tailscale
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: proxies
namespace: tailscale
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tailscale-operator
rules:
- apiGroups:
- ""
resources:
- events
- services
- services/status
verbs:
- '*'
- apiGroups:
- networking.k8s.io
resources:
- ingresses
- ingresses/status
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tailscale-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tailscale-operator
subjects:
- kind: ServiceAccount
name: operator
namespace: tailscale
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: operator
namespace: tailscale
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- '*'
- apiGroups:
- apps
resources:
- statefulsets
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: proxies
namespace: tailscale
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: operator
namespace: tailscale
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: operator
subjects:
- kind: ServiceAccount
name: operator
namespace: tailscale
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: proxies
namespace: tailscale
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: proxies
subjects:
- kind: ServiceAccount
name: proxies
namespace: tailscale
---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@ -110,29 +128,18 @@ metadata:
namespace: tailscale namespace: tailscale
spec: spec:
replicas: 1 replicas: 1
strategy:
type: Recreate
selector: selector:
matchLabels: matchLabels:
app: operator app: operator
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
app: operator app: operator
spec: spec:
serviceAccountName: operator
volumes:
- name: oauth
secret:
secretName: operator-oauth
containers: containers:
- name: operator - env:
image: tailscale/k8s-operator:unstable
resources:
requests:
cpu: 500m
memory: 100Mi
env:
- name: OPERATOR_HOSTNAME - name: OPERATOR_HOSTNAME
value: tailscale-operator value: tailscale-operator
- name: OPERATOR_SECRET - name: OPERATOR_SECRET
@ -155,7 +162,17 @@ spec:
value: "false" value: "false"
- name: PROXY_FIREWALL_MODE - name: PROXY_FIREWALL_MODE
value: auto value: auto
image: tailscale/k8s-operator:unstable
imagePullPolicy: Always
name: operator
volumeMounts: volumeMounts:
- name: oauth - mountPath: /oauth
mountPath: /oauth name: oauth
readOnly: true readOnly: true
nodeSelector:
kubernetes.io/os: linux
serviceAccountName: operator
volumes:
- name: oauth
secret:
secretName: operator-oauth

View File

@ -0,0 +1,3 @@
# Copyright (c) Tailscale Inc & AUTHORS
# SPDX-License-Identifier: BSD-3-Clause

View File

@ -0,0 +1,5 @@
apiVersion: v1
kind: Namespace
metadata:
name: tailscale
---

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: operator-oauth
namespace: tailscale
stringData:
client_id: # SET CLIENT ID HERE
client_secret: # SET CLIENT SECRET HERE
---

View File

@ -0,0 +1,74 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !plan9
package main
import (
"bytes"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"gopkg.in/yaml.v3"
)
func main() {
repoRoot := "../../"
cmd := exec.Command("./tool/helm", "template", "operator", "./cmd/k8s-operator/deploy/chart",
"--namespace=tailscale")
cmd.Dir = repoRoot
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatalf("error templating helm manifests: %v", err)
}
var final bytes.Buffer
templatePath := filepath.Join(repoRoot, "cmd/k8s-operator/deploy/manifests/templates")
fileInfos, err := os.ReadDir(templatePath)
if err != nil {
log.Fatalf("error reading templates: %v", err)
}
for _, fi := range fileInfos {
templateBytes, err := os.ReadFile(filepath.Join(templatePath, fi.Name()))
if err != nil {
log.Fatalf("error reading template: %v", err)
}
final.Write(templateBytes)
}
decoder := yaml.NewDecoder(&out)
for {
var document any
err := decoder.Decode(&document)
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("failed read from input data: %v", err)
}
bytes, err := yaml.Marshal(document)
if err != nil {
log.Fatalf("failed to marshal YAML document: %v", err)
}
if strings.TrimSpace(string(bytes)) == "null" {
continue
}
if _, err = final.Write(bytes); err != nil {
log.Fatalf("error marshaling yaml: %v", err)
}
fmt.Fprint(&final, "---\n")
}
finalString, _ := strings.CutSuffix(final.String(), "---\n")
if err := os.WriteFile(filepath.Join(repoRoot, "cmd/k8s-operator/deploy/manifests/operator.yaml"), []byte(finalString), 0664); err != nil {
log.Fatalf("error writing new file: %v", err)
}
}

View File

@ -42,6 +42,8 @@
"tailscale.com/version" "tailscale.com/version"
) )
//go:generate go run tailscale.com/cmd/k8s-operator/generate
func main() { func main() {
// Required to use our client API. We're fine with the instability since the // Required to use our client API. We're fine with the instability since the
// client lives in the same repo as this code. // client lives in the same repo as this code.

2
go.mod
View File

@ -358,7 +358,7 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.0 // indirect howett.net/plist v1.0.0 // indirect
k8s.io/apiextensions-apiserver v0.28.2 // indirect k8s.io/apiextensions-apiserver v0.28.2 // indirect
k8s.io/component-base v0.28.2 // indirect k8s.io/component-base v0.28.2 // indirect