Skip to content

feat(crd): support applying crd by operator#6644

Merged
ti-chi-bot[bot] merged 4 commits intopingcap:mainfrom
liubog2008:liubo02/crd-version
Jan 13, 2026
Merged

feat(crd): support applying crd by operator#6644
ti-chi-bot[bot] merged 4 commits intopingcap:mainfrom
liubog2008:liubo02/crd-version

Conversation

@liubog2008
Copy link
Member

@liubog2008 liubog2008 commented Jan 8, 2026

  • support applying crds directly

@github-actions github-actions bot added the v2 for operator v2 label Jan 8, 2026
@ti-chi-bot ti-chi-bot bot added the size/L label Jan 8, 2026
@liubog2008 liubog2008 force-pushed the liubo02/crd-version branch from a8adc80 to fbba783 Compare January 8, 2026 08:40
@ti-chi-bot ti-chi-bot bot added size/XXL and removed size/L labels Jan 8, 2026
@codecov-commenter
Copy link

codecov-commenter commented Jan 8, 2026

Codecov Report

❌ Patch coverage is 92.75362% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 39.54%. Comparing base (0012c37) to head (69574f8).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6644      +/-   ##
==========================================
+ Coverage   39.33%   39.54%   +0.20%     
==========================================
  Files         351      352       +1     
  Lines       20665    20745      +80     
==========================================
+ Hits         8129     8204      +75     
- Misses      12536    12541       +5     
Flag Coverage Δ
unittest 39.54% <92.75%> (+0.20%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +117 to +118
flag.BoolVar(&allowEmptyOldVersion, "allow-empty-old-version", false,
"allow crd version is empty, if false, cannot update crd which has no version annotation")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a flag to control whether or not apply CRDs automatically

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why? I hope to ensure the CRDs are in right version.


if err := applyCRDs(ctx, kubeconfig, allowEmptyOldVersion); err != nil {
setupLog.Error(err, "failed to apply crds")
os.Exit(1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about just logging the error

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as the above.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for the TiDB Operator to automatically apply Custom Resource Definitions (CRDs) at startup, enabling the operator to manage its own CRD lifecycle with version tracking and upgrade validation.

Key changes:

  • Implements CRD application logic with semantic versioning support and downgrade prevention
  • Adds version tracking annotation to CRDs for safe upgrades
  • Integrates CRD application into operator startup sequence before controller initialization

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
pkg/version/version.go Adds IsDirty() helper method to check if Git tree state is dirty
pkg/scheme/scheme.go Registers apiextensions/v1 scheme for CRD objects
pkg/crd/crd.go Core logic for applying CRDs with version validation and upgrade safety
pkg/crd/crd_test.go Comprehensive test coverage for CRD application, version checking, and error cases
manifests/embed.go Embeds CRD YAML files into the binary using Go's embed package
cmd/tidb-operator/main.go Integrates CRD application into operator startup with new --allow-empty-old-version flag
charts/tidb-operator/templates/rbac.yaml Adds RBAC permissions for CRD management
hack/release.sh Adds --allow-empty-old-version flag to e2e operator deployment

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

pkg/crd/crd.go Outdated
path := filepath.Join(crdDirPath, name)
data, err := fs.ReadFile(cfg.CRDDir, path)
if err != nil {
return fmt.Errorf("canot read file %s: %w", path, err)
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in error message: "canot" should be "cannot".

Suggested change
return fmt.Errorf("canot read file %s: %w", path, err)
return fmt.Errorf("cannot read file %s: %w", path, err)

Copilot uses AI. Check for mistakes.
fileContent: "",
version: "1.0.0",
wantErr: true,
errMsg: "canot read file",
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in test error message: "canot" should be "cannot".

Suggested change
errMsg: "canot read file",
errMsg: "cannot read file",

Copilot uses AI. Check for mistakes.
hack/release.sh Outdated
Comment on lines +70 to +71
--set "operator.extraArgs={--watch-delay-duration=2s}" \
--set "operator.extraArgs={--allow-empty-old-version}" \
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The operator.extraArgs setting is duplicated on consecutive lines. Setting the same key twice will cause the second value to override the first. These arguments should be combined into a single array: --set "operator.extraArgs={--watch-delay-duration=2s,--allow-empty-old-version}"

Suggested change
--set "operator.extraArgs={--watch-delay-duration=2s}" \
--set "operator.extraArgs={--allow-empty-old-version}" \
--set "operator.extraArgs={--watch-delay-duration=2s,--allow-empty-old-version}" \

Copilot uses AI. Check for mistakes.
if !cfg.AllowEmptyOldVersion {
return fmt.Errorf("cannot find old version from crd %s, you can enable --allow-empty-old-version or apply manually", crd.Name)
}
logger.Info("warn: crd has no version", "crd", crd.Name)
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using "warn:" prefix in an Info-level log message is unconventional. Consider using a warning-level log method or removing the "warn:" prefix since Info level already indicates this is informational.

Suggested change
logger.Info("warn: crd has no version", "crd", crd.Name)
logger.Info("crd has no version", "crd", crd.Name)

Copilot uses AI. Check for mistakes.
}

fileName := file.Name()
if !strings.HasSuffix(fileName, ".yaml") {
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code only processes files with .yaml extension but some projects also use .yml extension for YAML files. Consider supporting both extensions by checking for both .yaml and .yml suffixes.

Suggested change
if !strings.HasSuffix(fileName, ".yaml") {
if !strings.HasSuffix(fileName, ".yaml") && !strings.HasSuffix(fileName, ".yml") {

Copilot uses AI. Check for mistakes.
verbs: ["get", "list", "watch", "create", "delete", "update", "patch"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "list", "watch", "create", "delete", "update", "patch"]
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The permissions for customresourcedefinitions include "create" and "delete" which may be overly permissive. Since the operator is applying CRDs (which uses patch/update operations), consider restricting this to just "get", "list", "watch", "update", and "patch" unless the operator actually needs to create or delete CRDs.

Suggested change
verbs: ["get", "list", "watch", "create", "delete", "update", "patch"]
verbs: ["get", "list", "watch", "update", "patch"]

Copilot uses AI. Check for mistakes.
@liubog2008 liubog2008 force-pushed the liubo02/crd-version branch from cb6b3a3 to ad83a90 Compare January 9, 2026 02:39
Signed-off-by: liubo02 <liubo02@pingcap.com>
@liubog2008 liubog2008 force-pushed the liubo02/crd-version branch from ad83a90 to b681215 Compare January 9, 2026 02:40
@liubog2008
Copy link
Member Author

/test pull-e2e

Signed-off-by: liubo02 <liubo02@pingcap.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

{{- if eq .Values.manageCRD true}}
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "create", "update", "patch"]
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RBAC permissions use resourceNames to restrict which CRDs can be managed. However, this restriction only applies to get/update/patch operations on specific CRD resources. The "create" verb typically doesn't work with resourceNames filtering in Kubernetes RBAC, as resourceNames only applies to requests that specify a resource name (which create requests don't have yet). Consider splitting this into two rules: one for create without resourceNames, and another for get/update/patch with resourceNames, or verify that your Kubernetes version supports this pattern.

Suggested change
verbs: ["get", "create", "update", "patch"]
verbs: ["create"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "update", "patch"]

Copilot uses AI. Check for mistakes.
- --backup-manager-image={{ template "image" (tuple .Values.backupManager.image $.Chart.AppVersion) }}
- --default-prestop-checker-image={{ template "image" (tuple .Values.prestopChecker.image $.Chart.AppVersion) }}
- --default-resource-syncer-image={{ template "image" (tuple .Values.resourceSyncer.image $.Chart.AppVersion) }}
{{- if eq .Values.manageCRD false -}}
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template syntax is incorrect. The "-" should be on the closing tag "{{- end }}" to trim whitespace, not on "{{- if eq .Values.manageCRD false -}}". The current syntax "false -}}" is malformed. It should be either "{{- if eq .Values.manageCRD false }}" or "{{ if eq .Values.manageCRD false -}}".

Suggested change
{{- if eq .Values.manageCRD false -}}
{{- if eq .Values.manageCRD false }}

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +48
type Config struct {
Client client.Client

Version string

AllowEmptyOldVersion bool
IsDirty bool

CRDDir fs.FS
}
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Config struct lacks documentation. Public types should have doc comments explaining their purpose and how they should be used. Consider adding a comment like: "// Config holds configuration for CRD application operations."

Copilot uses AI. Check for mistakes.

CRDDir fs.FS
}

Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ApplyCRDs function lacks documentation. Public functions should have doc comments explaining their purpose, parameters, and return values. Consider adding a comment describing what this function does and when it should be used.

Suggested change
// ApplyCRDs reads CRD manifests from the "crd" directory in cfg.CRDDir and
// applies them to the Kubernetes cluster referenced by cfg.Client. It uses
// ctx for request scoping and cancellation, and returns an error if any CRD
// cannot be read, parsed, or applied.

Copilot uses AI. Check for mistakes.
@@ -89,6 +89,10 @@ func (info *Info) KeysAndValues() []any {
}
}

Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IsDirty method lacks documentation. Public methods should have doc comments explaining what they do and what the return value represents.

Suggested change
// IsDirty reports whether the Git tree state is "dirty".
// It returns true if the build was made from a modified working tree.

Copilot uses AI. Check for mistakes.
if !cfg.AllowEmptyOldVersion {
return fmt.Errorf("cannot find old version from crd %s, you can enable --allow-empty-old-version or apply manually", crd.Name)
}
logger.Info("warn: crd has no version", "crd", crd.Name)
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message uses a logger.Info call to log what appears to be a warning. This is inconsistent with proper logging practices. Consider using a proper warning level if available, or restructure the message to not start with "warn:" if using Info level.

Suggested change
logger.Info("warn: crd has no version", "crd", crd.Name)
logger.Info("crd has no version annotation; proceeding because AllowEmptyOldVersion is true", "crd", crd.Name)

Copilot uses AI. Check for mistakes.

values, err := parser.ParseFile(path, parser.ParseComments)
if err != nil {
return fmt.Errorf("cannot parse valuse.yaml: %w", err)
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in error message: "valuse.yaml" should be "values.yaml"

Suggested change
return fmt.Errorf("cannot parse valuse.yaml: %w", err)
return fmt.Errorf("cannot parse values.yaml: %w", err)

Copilot uses AI. Check for mistakes.
Signed-off-by: liubo02 <liubo02@pingcap.com>
Signed-off-by: liubo02 <liubo02@pingcap.com>
@liubog2008
Copy link
Member Author

/test pull-e2e

2 similar comments
@liubog2008
Copy link
Member Author

/test pull-e2e

@liubog2008
Copy link
Member Author

/test pull-e2e

@fgksgf
Copy link
Member

fgksgf commented Jan 13, 2026

/lgtm

@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Jan 13, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: fgksgf

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Jan 13, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-01-13 02:21:31.728645006 +0000 UTC m=+324135.790509907: ☑️ agreed by fgksgf.

@ti-chi-bot ti-chi-bot bot merged commit c195c68 into pingcap:main Jan 13, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants