Skip to content

feat(syncer): add resource syncer#6595

Merged
ti-chi-bot[bot] merged 4 commits intopingcap:mainfrom
liubog2008:liubo02/secret-watcher
Dec 12, 2025
Merged

feat(syncer): add resource syncer#6595
ti-chi-bot[bot] merged 4 commits intopingcap:mainfrom
liubog2008:liubo02/secret-watcher

Conversation

@liubog2008
Copy link
Member

@liubog2008 liubog2008 commented Dec 9, 2025

Resource syncer is designed to sync secrets or configmaps into the pod.

Usage Example

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: test
  name: test
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      containers:
      - command:
        - /bin/sh
        - -c
        - ls -la /data/secrets/; sleep 1000
        image: busybox
        imagePullPolicy: Always
        name: busybox
        volumeMounts:
        - mountPath: /data
          name: secret
          readOnly: true
          recursiveReadOnly: IfPossible
      initContainers:
      - command:
        - /resource-syncer
        - -d=/data
        - -n=default
        - -l=pingcap.com/managed-by=tidb-operator,test=test
        image: pingcap/tidb-operator-resource-syncer:latest
        imagePullPolicy: IfNotPresent
        name: syncer
        resources: {}
        restartPolicy: Always
        startupProbe:
          failureThreshold: 10
          httpGet:
            path: /healthz
            port: 8081
            scheme: HTTP
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        volumeMounts:
        - mountPath: /data
          name: secret
      restartPolicy: Always
      volumes:
      - emptyDir: {}
        name: secret

Signed-off-by: liubo02 <liubo02@pingcap.com>
@ti-chi-bot ti-chi-bot bot requested a review from shonge December 9, 2025 09:30
@github-actions github-actions bot added the v2 for operator v2 label Dec 9, 2025
@ti-chi-bot ti-chi-bot bot added the size/XXL label Dec 9, 2025
Signed-off-by: liubo02 <liubo02@pingcap.com>
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

if err := run(&cfg); err != nil {
setupLog.Error(err, "unable to new manager")
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.

Suggested change

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 introduces a new resource syncer component designed to sync Kubernetes secrets into pod filesystems. The syncer runs as an init container that watches secrets with specific labels and synchronizes their data to a mounted volume.

Key Changes:

  • Adds a new resource-syncer binary with a controller that watches and syncs secrets to the filesystem
  • Implements health and readiness checks that report ready once initial sync completes
  • Updates build system (Makefile, Docker, scripts) to include the new resource-syncer component

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
pkg/controllers/resourcesyncer/secret/controller.go Core reconciler that watches secrets and syncs their data to filesystem directories
pkg/controllers/resourcesyncer/secret/controller_test.go Unit tests for the secret syncer controller
cmd/resource-syncer/main.go Main entry point with CLI flags and manager setup
cmd/resource-syncer/README.md Documentation for the resource syncer component
image/Dockerfile Adds Docker image build stage for resource-syncer
hack/lib/build.sh Adds resource-syncer to build targets
hack/lib/image.sh Configures image prefix for resource-syncer
Makefile Adds resource-syncer to the list of built commands
go.mod Adds afero filesystem abstraction dependency
go.sum Checksums for afero dependency

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

Comment on lines +316 to +325
if fileData.isDir {
err := f.MkdirAll(path, 0o755)
assert.NoError(t, err)
}

dir := filepath.Dir(path)
err := f.MkdirAll(dir, 0o755)
assert.NoError(t, err)
err = afero.WriteFile(f, path, fileData.data, 0o644)
assert.NoError(t, err)
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The setupFs function has a logic error. For directory entries (when fileData.isDir is true), it creates the directory but then continues to write it as a file on line 324. This will fail because you cannot write file data to a directory path. Either skip the file write for directories with a continue statement after line 318, or restructure the logic to handle directories separately.

Copilot uses AI. Check for mistakes.
c := &cases[i]
t.Run(c.desc, func(tt *testing.T) {
fc := client.NewFakeClient(c.objs...)
afero.NewMemMapFs()
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

Unused variable: afero.NewMemMapFs() is called but the result is not used. This line should be removed as setupFs creates and returns the filesystem.

Suggested change
afero.NewMemMapFs()

Copilot uses AI. Check for mistakes.
r := Reconciler{
Client: fc,
Labels: c.labels,
BaseDirFS: dirFs,
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The Reconciler is missing initialization of the cache field. In the test setup, cache is not initialized, which will cause a nil map panic when the reconciler tries to access r.cache[s.Name] on line 180 of the controller. Add cache: map[string]string{} to the Reconciler initialization.

Suggested change
BaseDirFS: dirFs,
BaseDirFS: dirFs,
cache: map[string]string{},

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,3 @@
# Resource Syncer

This binary is designed for syncing resources(secrets, configmap, etc...) of kubernetes into pods
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The README description mentions "configmap" but the implementation only handles secrets. Either update the description to be accurate ("This binary is designed for syncing secrets into pods") or add a note that configmap support is planned for the future.

Suggested change
This binary is designed for syncing resources(secrets, configmap, etc...) of kubernetes into pods
This binary is designed for syncing secrets of Kubernetes into pods.
> Note: Support for syncing configmaps is planned for a future release.

Copilot uses AI. Check for mistakes.
client.InNamespace(r.Namespace),
client.MatchingLabels(r.Labels),
); err != nil {
logger.Error(err, "cannot list secrets", "namepsace", r.Namespace, "labels", r.Labels)
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

Typo in log message: "namepsace" should be "namespace".

Suggested change
logger.Error(err, "cannot list secrets", "namepsace", r.Namespace, "labels", r.Labels)
logger.Error(err, "cannot list secrets", "namespace", r.Namespace, "labels", r.Labels)

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +116
failed := true
for range maxRetryTimes {
// has synced, no need to list secrets again
if r.hasSynced.Load() {
return
}

nctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()

if err := r.Client.List(
nctx,
&sl,
client.InNamespace(r.Namespace),
client.MatchingLabels(r.Labels),
); err != nil {
logger.Error(err, "cannot list secrets", "namepsace", r.Namespace, "labels", r.Labels)
} else {
failed = false
break
}
}

if !failed && len(sl.Items) == 0 {
if r.hasSynced.CompareAndSwap(false, true) {
logger.Info("no secrets, hasSynced set to true")
}

return
}
}
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The variable failed is set but the retry logic doesn't use it to handle the failure case. If all retries fail, the goroutine continues to check len(sl.Items) == 0 with potentially stale data from the last failed attempt. Consider logging an error or handling the case when all retries are exhausted.

Copilot uses AI. Check for mistakes.
errs = append(errs, err)
}
files[name] = struct{}{}
}
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The resourceVersionFile constant should be added to the keep map to prevent it from being cleaned up as an unknown file. Currently, when cleaning up the secret directory, the resourceVersion file is not in the files map (only actual secret data keys are), so it could be incorrectly removed.

Suggested change
}
}
// Ensure resourceVersionFile is not deleted during cleanup
files[resourceVersionFile] = struct{}{}

Copilot uses AI. Check for mistakes.
setupLog.Info("current config", "config", &cfg)

if err := run(&cfg); err != nil {
setupLog.Error(err, "unable to new manager")
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The error message is misleading. It says "unable to new manager" but the run function can fail for multiple reasons (manager creation, directory setup, controller setup, health checks, or starting the manager). Use a more generic message like "unable to run resource syncer" or pass through the actual error from run().

Suggested change
setupLog.Error(err, "unable to new manager")
setupLog.Error(err, "unable to run resource syncer")

Copilot uses AI. Check for mistakes.
Labels map[string]string
BaseDirFS afero.Fs

// secret name to it's resourceVersion
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

Typo in comment: "it's" should be "its" (possessive form doesn't use an apostrophe).

Suggested change
// secret name to it's resourceVersion
// secret name to its resourceVersion

Copilot uses AI. Check for mistakes.
fake.ResourceVersion[corev1.Secret]("test"),
fake.Label[corev1.Secret]("test", "test"),
// change data to test whether update is actually skipped
// not really happended in real world
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

Typo in comment: "happended" should be "happened".

Suggested change
// not really happended in real world
// not really happened in real world

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

codecov-commenter commented Dec 9, 2025

Codecov Report

❌ Patch coverage is 52.53165% with 75 lines in your changes missing coverage. Please review.
✅ Project coverage is 41.47%. Comparing base (37be4e9) to head (b3367e4).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6595      +/-   ##
==========================================
+ Coverage   41.38%   41.47%   +0.08%     
==========================================
  Files         348      349       +1     
  Lines       19885    20043     +158     
==========================================
+ Hits         8230     8313      +83     
- Misses      11655    11730      +75     
Flag Coverage Δ
unittest 41.47% <52.53%> (+0.08%) ⬆️

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.

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

/test pull-e2e

1 similar comment
@liubog2008
Copy link
Member Author

/test pull-e2e

@ti-chi-bot ti-chi-bot bot added the lgtm label Dec 12, 2025
@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Dec 12, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: csuzhangxc

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 Dec 12, 2025

[LGTM Timeline notifier]

Timeline:

  • 2025-12-12 02:12:29.002580447 +0000 UTC m=+1179893.816358020: ☑️ agreed by csuzhangxc.

@ti-chi-bot ti-chi-bot bot added the approved label Dec 12, 2025
@ti-chi-bot ti-chi-bot bot merged commit 2dc570f into pingcap:main Dec 12, 2025
9 checks passed
@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Dec 12, 2025

@liubog2008: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
pull-e2e b3367e4 link unknown /test pull-e2e

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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.

5 participants