Skip to content

Commit c195c68

Browse files
authored
feat(crd): support applying crd by operator (#6644)
1 parent ad1f650 commit c195c68

File tree

13 files changed

+973
-11
lines changed

13 files changed

+973
-11
lines changed

charts/tidb-operator/templates/deployment.yaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,14 @@ spec:
4444
- command:
4545
- /tidb-operator
4646
args:
47-
- --backup-manager-image={{ template "image" (tuple .Values.backupManager.image $.Chart.AppVersion) }}
48-
- --default-prestop-checker-image={{ template "image" (tuple .Values.prestopChecker.image $.Chart.AppVersion) }}
49-
- --default-resource-syncer-image={{ template "image" (tuple .Values.resourceSyncer.image $.Chart.AppVersion) }}
47+
- --backup-manager-image={{ template "image" (tuple .Values.backupManager.image $.Chart.AppVersion) }}
48+
- --default-prestop-checker-image={{ template "image" (tuple .Values.prestopChecker.image $.Chart.AppVersion) }}
49+
- --default-resource-syncer-image={{ template "image" (tuple .Values.resourceSyncer.image $.Chart.AppVersion) }}
50+
{{- if eq .Values.skipCRDsManagement true }}
51+
- --skip-crds-management
52+
{{- end }}
5053
{{- if .Values.operator.extraArgs -}}
51-
{{ toYaml .Values.operator.extraArgs | nindent 10 }}
54+
{{ toYaml .Values.operator.extraArgs | nindent 8 }}
5255
{{- end }}
5356
image: "{{ template "image" (tuple .Values.operator.image $.Chart.AppVersion) }}"
5457
imagePullPolicy: {{ .Values.operator.pullPolicy | default "IfNotPresent" }}

charts/tidb-operator/templates/rbac.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ rules:
5454
- apiGroups: ["br.pingcap.com"]
5555
resources: ["*"]
5656
verbs: ["*"]
57+
{{- if eq .Values.skipCRDsManagement false }}
58+
- apiGroups: ["apiextensions.k8s.io"]
59+
resources: ["customresourcedefinitions"]
60+
verbs: ["get", "create", "update", "patch"]
61+
resourceNames: {{ toYaml .Values.crds | nindent 4 }}
62+
{{- end }}
5763
---
5864
apiVersion: rbac.authorization.k8s.io/v1
5965
kind: ClusterRoleBinding

charts/tidb-operator/values.yaml

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ operator:
1616
# digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
1717

1818
pullPolicy: IfNotPresent
19-
# # REF: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
20-
# priorityClassName: system-cluster-critical
19+
# # REF: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
20+
# priorityClassName: system-cluster-critical
2121
resources:
2222
requests:
2323
cpu: 80m
@@ -90,3 +90,37 @@ rbac:
9090
# Webhook configuration
9191
webhook:
9292
create: true
93+
94+
# If false, manage crds by operator directly.
95+
# If true, operator will not check and apply crds.
96+
skipCRDsManagement: false
97+
98+
# DON'T EDIT IT
99+
crds:
100+
- backups.br.pingcap.com
101+
- backupschedules.br.pingcap.com
102+
- compactbackups.br.pingcap.com
103+
- restores.br.pingcap.com
104+
- tibrgcs.br.pingcap.com
105+
- tibrs.br.pingcap.com
106+
- clusters.core.pingcap.com
107+
- pdgroups.core.pingcap.com
108+
- pds.core.pingcap.com
109+
- schedulergroups.core.pingcap.com
110+
- schedulers.core.pingcap.com
111+
- schedulinggroups.core.pingcap.com
112+
- schedulings.core.pingcap.com
113+
- ticdcgroups.core.pingcap.com
114+
- ticdcs.core.pingcap.com
115+
- tidbgroups.core.pingcap.com
116+
- tidbs.core.pingcap.com
117+
- tiflashes.core.pingcap.com
118+
- tiflashgroups.core.pingcap.com
119+
- tikvgroups.core.pingcap.com
120+
- tikvs.core.pingcap.com
121+
- tikvworkergroups.core.pingcap.com
122+
- tikvworkers.core.pingcap.com
123+
- tiproxies.core.pingcap.com
124+
- tiproxygroups.core.pingcap.com
125+
- tsogroups.core.pingcap.com
126+
- tsos.core.pingcap.com

cmd/crd-modifier/main.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,32 @@ package main
1818
import (
1919
"flag"
2020
"fmt"
21+
"io"
2122
"os"
2223
"path/filepath"
2324
"strings"
2425

26+
"github.com/goccy/go-yaml"
27+
"github.com/goccy/go-yaml/parser"
2528
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2629
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
2730
kyaml "sigs.k8s.io/yaml"
2831
)
2932

3033
func main() {
3134
var dir string
35+
var chart string
3236
flag.StringVar(&dir, "dir", "manifests/crd", "dir of crds")
37+
flag.StringVar(&chart, "chart", "charts/tidb-operator", "dir of operator chart")
3338
flag.Parse()
3439

3540
files, err := os.ReadDir(dir)
3641
if err != nil {
3742
panic(fmt.Errorf("failed to read dir %s: %w", dir, err))
3843
}
3944

45+
var crdNames []string
46+
4047
for _, file := range files {
4148
if file.IsDir() {
4249
continue
@@ -55,6 +62,8 @@ func main() {
5562
panic(err)
5663
}
5764

65+
crdNames = append(crdNames, crd.Name)
66+
5867
if len(crd.Spec.Versions) == 0 {
5968
fmt.Printf(" Skipping %s: no versions defined\n", fileName)
6069
continue
@@ -83,6 +92,10 @@ func main() {
8392
panic(err)
8493
}
8594
}
95+
96+
if err := modifyValues(chart, crdNames); err != nil {
97+
panic(err)
98+
}
8699
}
87100

88101
// checkPathExists checks if a path of keys exists in the schema's properties.
@@ -274,3 +287,45 @@ func intersection(a, b []string) []string {
274287

275288
return ret
276289
}
290+
291+
const (
292+
defaultPerm = 0o666
293+
)
294+
295+
func modifyValues(chartPath string, crds []string) error {
296+
path := filepath.Join(chartPath, "values.yaml")
297+
298+
root, err := yaml.PathString("$")
299+
if err != nil {
300+
return fmt.Errorf("cannot get root: %w", err)
301+
}
302+
303+
values, err := parser.ParseFile(path, parser.ParseComments)
304+
if err != nil {
305+
return fmt.Errorf("cannot parse values.yaml: %w", err)
306+
}
307+
308+
node, err := yaml.ValueToNode(map[string][]string{
309+
"crds": crds,
310+
})
311+
if err != nil {
312+
return fmt.Errorf("cannot parse crds to ast.Node: %w", err)
313+
}
314+
315+
if err := root.MergeFromNode(values, node); err != nil {
316+
return fmt.Errorf("cannot replace node: %w", err)
317+
}
318+
319+
data, err := io.ReadAll(values)
320+
if err != nil {
321+
return fmt.Errorf("cannot marshal data: %w", err)
322+
}
323+
324+
data = append(data, '\n')
325+
326+
if err := os.WriteFile(path, data, defaultPerm); err != nil {
327+
return fmt.Errorf("cannot write file: %w", err)
328+
}
329+
330+
return nil
331+
}

cmd/tidb-operator/main.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"os"
2222
"time"
2323

24+
"github.com/go-logr/logr"
2425
"go.uber.org/zap/zapcore"
2526
appsv1 "k8s.io/api/apps/v1"
2627
batchv1 "k8s.io/api/batch/v1"
@@ -42,6 +43,7 @@ import (
4243

4344
brv1alpha1 "github.com/pingcap/tidb-operator/api/v2/br/v1alpha1"
4445
"github.com/pingcap/tidb-operator/api/v2/core/v1alpha1"
46+
"github.com/pingcap/tidb-operator/v2/manifests"
4547
"github.com/pingcap/tidb-operator/v2/pkg/adoption"
4648
"github.com/pingcap/tidb-operator/v2/pkg/client"
4749
"github.com/pingcap/tidb-operator/v2/pkg/controllers/br/backup"
@@ -71,6 +73,7 @@ import (
7173
"github.com/pingcap/tidb-operator/v2/pkg/controllers/tiproxygroup"
7274
"github.com/pingcap/tidb-operator/v2/pkg/controllers/tso"
7375
"github.com/pingcap/tidb-operator/v2/pkg/controllers/tsogroup"
76+
"github.com/pingcap/tidb-operator/v2/pkg/crd"
7477
"github.com/pingcap/tidb-operator/v2/pkg/image"
7578
"github.com/pingcap/tidb-operator/v2/pkg/metrics"
7679
"github.com/pingcap/tidb-operator/v2/pkg/scheme"
@@ -84,7 +87,7 @@ import (
8487
"github.com/pingcap/tidb-operator/v2/pkg/volumes"
8588
)
8689

87-
var setupLog = ctrl.Log.WithName("setup").WithValues(version.Get().KeysAndValues()...)
90+
var setupLog = ctrl.Log.WithName("setup")
8891

8992
type brConfig struct {
9093
backupManagerImage string
@@ -98,6 +101,9 @@ func main() {
98101
var probeAddr string
99102
var maxConcurrentReconciles int
100103
var watchDelayDuration time.Duration
104+
var allowEmptyOldVersion bool
105+
var skipCRDsManagement bool
106+
101107
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
102108
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
103109
flag.StringVar(&brConf.backupManagerImage, "backup-manager-image", "", "The image of backup-manager.")
@@ -111,6 +117,9 @@ func main() {
111117
"kube clients's read-after-write inconsistency(always read from cache).")
112118
flag.Var(&image.PrestopChecker, "default-prestop-checker-image", "Default image of prestop checker.")
113119
flag.Var(&image.ResourceSyncer, "default-resource-syncer-image", "Default image of resource syncer.")
120+
flag.BoolVar(&allowEmptyOldVersion, "allow-empty-old-version", false,
121+
"allow crd version is empty, if false, cannot update crd which has no version annotation")
122+
flag.BoolVar(&skipCRDsManagement, "skip-crds-management", false, "if true, skip checking and applying crds")
114123

115124
opts := zap.Options{
116125
Development: false,
@@ -136,6 +145,10 @@ func main() {
136145

137146
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
138147

148+
ctx := logr.NewContext(context.Background(), setupLog)
149+
150+
setupLog.Info("start tidb operator", version.Get().KeysAndValues()...)
151+
139152
utilruntime.PanicHandlers = append(utilruntime.PanicHandlers, func(_ context.Context, _ any) {
140153
metrics.ControllerPanic.WithLabelValues().Inc()
141154
})
@@ -146,6 +159,13 @@ func main() {
146159
kubeconfig.UserAgent = client.DefaultFieldManager
147160
kubefeat.MustInitFeatureGates(kubeconfig)
148161

162+
if !skipCRDsManagement {
163+
if err := applyCRDs(ctx, kubeconfig, allowEmptyOldVersion); err != nil {
164+
setupLog.Error(err, "failed to apply crds")
165+
os.Exit(1)
166+
}
167+
}
168+
149169
cacheOpt := cache.Options{
150170
// Disable label selector for our own CRs
151171
// These CRs don't need to be filtered
@@ -178,12 +198,36 @@ func main() {
178198
os.Exit(1)
179199
}
180200

181-
if err := setup(context.Background(), mgr); err != nil {
201+
if err := setup(ctx, mgr); err != nil {
182202
setupLog.Error(err, "failed to setup")
183203
os.Exit(1)
184204
}
185205
}
186206

207+
func applyCRDs(ctx context.Context, kubeconfig *rest.Config, allowEmptyOldVersion bool) error {
208+
c, err := client.New(kubeconfig, ctrlcli.Options{
209+
Scheme: scheme.Scheme,
210+
})
211+
if err != nil {
212+
return fmt.Errorf("cannot new client for applying crd: %w", err)
213+
}
214+
215+
info := version.Get()
216+
cfg := crd.Config{
217+
Client: c,
218+
Version: info.GitVersion,
219+
AllowEmptyOldVersion: allowEmptyOldVersion,
220+
IsDirty: info.IsDirty(),
221+
CRDDir: manifests.CRD,
222+
}
223+
224+
if err := crd.ApplyCRDs(ctx, &cfg); err != nil {
225+
return err
226+
}
227+
228+
return nil
229+
}
230+
187231
func setup(ctx context.Context, mgr ctrl.Manager) error {
188232
// client of manager should be newed by options.NewClient
189233
// which actually returns tidb-operator/v2/pkg/client.Client

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ require (
4545
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535
4646
github.com/go-logr/logr v1.4.3
4747
github.com/go-sql-driver/mysql v1.8.1
48+
github.com/goccy/go-yaml v1.19.2
4849
github.com/gogo/protobuf v1.3.2
4950
github.com/google/gnostic-models v0.6.9
5051
github.com/google/go-cmp v0.7.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
243243
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
244244
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
245245
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
246-
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
247-
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
246+
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
247+
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
248248
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
249249
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
250250
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=

hack/release.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ EOF
6767

6868
${HELM} template tidb-operator ${OUTPUT}/tidb-operator-${RELEASE_VERSION}.tgz \
6969
--kube-version ${V_KUBE_VERSION} \
70-
--set "operator.extraArgs={--watch-delay-duration=2s}" \
70+
--set "operator.extraArgs={--watch-delay-duration=2s,--allow-empty-old-version}" \
7171
-n ${V_DEPLOY_NAMESPACE} \
7272
>> $E2E_OPERATOR
7373

manifests/embed.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package manifests
2+
3+
import "embed"
4+
5+
//go:embed crd/*
6+
var CRD embed.FS

0 commit comments

Comments
 (0)