Repository: kubernetes-csi/lib-volume-populator
Pull request #208: Add example for provider specific functions
Author: login: dannawang0221
Labels: name: approved
description: Indicates a PR has been approved by an approver from all required OWNERS files.
name: release-note-none
description: Denotes a PR that doesn't merit a release note.
name: lgtm
description: "Looks good to me", indicates that a PR is ready to be merged.
name: cncf-cla: yes
description: Indicates the PR's author has signed the CNCF CLA.
name: size/L
description: Denotes a PR that changes 100-499 lines, ignoring generated files.
Created: 2025-03-31T08:13:57Z | Merged: 2025-04-22T02:55:40Z

<!--  Thanks for sending a pull request!  Here are some tips for you:

1. If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. Follow the instructions for writing a release note: https://git.k8s.io/community/contributors/guide/release-notes.md
3. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

**What type of PR is this?**
> Uncomment only one ` /kind <>` line, hit enter to put that in a new line, and remove leading whitespaces from that line:
>
> /kind api-change
> /kind bug
> /kind cleanup
> /kind design

documentation

> /kind failing-test
> /kind feature
> /kind flake

**What this PR does / why we need it**:
Add example for provider specific functions

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->
```release-note
NONE
```



Diff:
diff --git a/Makefile b/Makefile
index c0aa7ede..ffa3aefa 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 CMDS_DIR=example
-CMDS=hello-populator
+CMDS ?= hello-populator
 all: build
 
 include release-tools/build.make
diff --git a/example/provider-populator/Dockerfile b/example/provider-populator/Dockerfile
new file mode 100644
index 00000000..107beacc
--- /dev/null
+++ b/example/provider-populator/Dockerfile
@@ -0,0 +1,7 @@
+FROM gcr.io/distroless/static:latest
+LABEL maintainers="Kubernetes Authors"
+LABEL description="provider populator"
+ARG binary=./provider-populator
+
+COPY ${binary} provider-populator
+ENTRYPOINT ["/provider-populator"]
\ No newline at end of file
diff --git a/example/provider-populator/README.md b/example/provider-populator/README.md
new file mode 100644
index 00000000..2072e139
--- /dev/null
+++ b/example/provider-populator/README.md
@@ -0,0 +1,85 @@
+
+# Provider Populator Example
+
+This example demonstrates an extremely simple provider specific populator implementation.
+
+## How To
+
+Install kubernetes 1.33 or later
+
+Install the CRD:
+
+`kubectl apply -f crd.yaml`
+
+Install the controller:
+
+`kubectl apply -f deploy.yaml`
+
+Create a CR:
+
+```
+kubectl create -f - << EOF
+apiVersion: provider.example.com/v1alpha1
+kind: Provider
+metadata:
+  name: provider1
+spec:
+  dataSourceName: example-source
+EOF
+```
+
+Create a PVC:
+
+```
+kubectl create -f - << EOF
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  name: pvc1
+spec:
+  accessModes:
+  - ReadWriteOnce
+  resources:
+    requests:
+      storage: 10Mi
+  dataSourceRef:
+    apiGroup: provider.example.com
+    kind: Provider
+    name: provider1
+EOF
+```
+
+Get the logs from the pod to verify that controller starts:
+
+`kubectl get pod -n provider`
+
+```
+NAME                                  READY   STATUS    RESTARTS   AGE
+provider-populator-7f6c786fb5-q2vsq   1/1     Running   0          11s
+```
+
+`kubectl logs provider-populator-7f6c786fb5-q2vsq -n provider`
+
+```
+I0331 08:06:03.602113       1 controller.go:236] Starting populator controller for Provider.provider.example.com
+W0331 08:06:03.604038       1 client_config.go:667] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
+I0331 08:06:03.606450       1 metrics.go:109] Metrics path successfully registered at /metrics
+I0331 08:06:03.606920       1 metrics.go:121] Metrics http server successfully started on :8080, /metrics
+```
+
+### To build the image from code:
+
+`make all CMDS=provider-populator`
+
+Make sure you have a repo you can push to, and set the variable
+
+`YOUR_REPO=...`
+
+Push the image to your repo:
+
+```
+docker tag provider-populator:latest ${YOUR_REPO}/provider-populator:latest
+docker push ${YOUR_REPO}/provider-populator:latest
+```
+
+To use the image, update deploy.yaml before installing the controller.
diff --git a/example/provider-populator/crd.yaml b/example/provider-populator/crd.yaml
new file mode 100644
index 00000000..d0b7800d
--- /dev/null
+++ b/example/provider-populator/crd.yaml
@@ -0,0 +1,36 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: providers.provider.example.com
+spec:
+  group: provider.example.com
+  names:
+    kind: Provider
+    listKind: ProviderList
+    plural: providers
+    singular: provider
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: Provider is a specification for a Provider resource
+        properties:
+          spec:
+            description: ProviderSpec is the spec for a Provider resource
+            properties:
+              dataSourceName:
+                type: string
+            # Add provider specific data source properties
+            type: object
+        required:
+        - spec
+        type: object
+    served: true
+    storage: true
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/example/provider-populator/deploy.yaml b/example/provider-populator/deploy.yaml
new file mode 100644
index 00000000..9c38d92a
--- /dev/null
+++ b/example/provider-populator/deploy.yaml
@@ -0,0 +1,90 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: provider
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: provider-account
+  namespace: provider
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: provider-role
+rules:
+  - apiGroups: [""]
+    resources: [persistentvolumes]
+    verbs: [get, list, watch, patch]
+  - apiGroups: [""]
+    resources: [persistentvolumeclaims]
+    verbs: [get, list, watch, patch, create, delete]
+  - apiGroups: [""]
+    resources: [pods]
+    verbs: [get, list, watch, create, delete]
+  - apiGroups: [""]
+    resources: [events]
+    verbs: [create]
+  - apiGroups: [storage.k8s.io]
+    resources: [storageclasses]
+    verbs: [get, list, watch]
+
+  - apiGroups: [provider.example.com]
+    resources: [providers]
+    verbs: [get, list, watch]
+  # (Alpha) Access to referencegrants is only needed when the CSI driver
+  # has the CrossNamespaceVolumeDataSource controller capability.
+  # In that case, lib-volume-populator requires "get", "list", "watch"
+  # permissions  for "referencegrants" on "gateway.networking.k8s.io".
+  #- apiGroups: ["gateway.networking.k8s.io"]
+  #  resources: ["referencegrants"]
+  #  verbs: ["get", "list", "watch"]
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: provider-binding
+subjects:
+  - kind: ServiceAccount
+    name: provider-account
+    namespace: provider
+roleRef:
+  kind: ClusterRole
+  name: provider-role
+  apiGroup: rbac.authorization.k8s.io
+---
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+  name: provider-populator
+  namespace: provider
+spec:
+  selector:
+    matchLabels:
+      app: provider
+  template:
+    metadata:
+      labels:
+        app: provider
+    spec:
+      serviceAccount: provider-account
+      containers:
+        - name: provider
+          image: <YOUR_REPO>/provider-populator:v1.0.1
+          imagePullPolicy: IfNotPresent
+          args:
+            - --mode=controller
+            - --http-endpoint=:8080
+          ports:
+            - containerPort: 8080
+              name: http-endpoint
+              protocol: TCP
+---
+kind: VolumePopulator
+apiVersion: populator.storage.k8s.io/v1beta1
+metadata:
+  name: provider-populator
+sourceKind:
+  group: provider.example.com
+  kind: provider
diff --git a/example/provider-populator/main.go b/example/provider-populator/main.go
new file mode 100644
index 00000000..24b2868d
--- /dev/null
+++ b/example/provider-populator/main.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+	"context"
+	"flag"
+
+	populatorMachinery "github.com/kubernetes-csi/lib-volume-populator/populator-machinery"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+
+	"k8s.io/klog/v2"
+)
+
+const (
+	groupName  = "provider.example.com"
+	kind       = "Provider"
+	apiVersion = "v1alpha1"
+	resource   = "providers"
+	prefix     = "provider.example.com"
+)
+
+var (
+	namespace      = flag.String("namespace", "provider", "Namespace to deploy controller")
+	mode           = flag.String("mode", "", "Mode to run the application in, supported values is controller")
+	kubeconfigPath = flag.String("kubeconfig-path", "", "kubeconfig path")
+	httpEndpoint   = flag.String("http-endpoint", "", "The TCP network address where the HTTP server for diagnostics, including metrics and leader election health check, will listen (example: `:8080`). The default is empty string.")
+	metricsPath    = flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.")
+
+	groupKind = schema.GroupKind{
+		Group: groupName,
+		Kind:  kind,
+	}
+	versionResource = schema.GroupVersionResource{
+		Group:    groupKind.Group,
+		Version:  apiVersion,
+		Resource: resource,
+	}
+)
+
+func main() {
+	klog.InitFlags(nil)
+	flag.Parse()
+
+	switch *mode {
+	case "controller":
+		klog.Infof("Run provider-populator controller")
+		pfcfg := &populatorMachinery.ProviderFunctionConfig{
+			PopulateFn:         populateFn,
+			PopulateCompleteFn: populateCompleteFn,
+			PopulateCleanupFn:  populateCleanupFn,
+		}
+
+		vpcfg := &populatorMachinery.VolumePopulatorConfig{
+			MasterURL:              "",
+			Kubeconfig:             *kubeconfigPath,
+			HttpEndpoint:           *httpEndpoint,
+			MetricsPath:            *metricsPath,
+			Namespace:              *namespace,
+			Prefix:                 prefix,
+			Gk:                     groupKind,
+			Gvr:                    versionResource,
+			ProviderFunctionConfig: pfcfg,
+			CrossNamespace:         false,
+		}
+
+		populatorMachinery.RunControllerWithConfig(*vpcfg)
+	default:
+		klog.Infof("Mode %s is not supported", *mode)
+	}
+}
+
+func populateFn(ctx context.Context, params populatorMachinery.PopulatorParams) error {
+	// Implement the provider-specific logic to initiate volume population.
+	// This may involve calling cloud-native APIs or creating temporary Kubernetes resources
+	// such as Pods or Jobs for data transfer.
+
+	// Example steps:
+	// 1. Retrieve data source details from the defined CRD.
+	// 2. Initiate a data transfer job to params.PvcPrime using params.KubeClient.
+	// 3. Report the volume population status to the original PVC's through params.Recorder.
+	// 4. You should check if the transfer job already exists before creating it, otherwise the transfer job might
+	// get created multiple times if everytime you use a unique name.
+
+	klog.Infof("Run populateFn")
+	return nil
+}
+
+func populateCompleteFn(ctx context.Context, params populatorMachinery.PopulatorParams) (bool, error) {
+	// Implement the provider-specific logic to determine the status of volume population.
+	// This may involve calling cloud-native APIs or checking the completion status of
+	// temporary Kubernetes resources like Pods or Jobs.
+
+	// Example steps:
+	// 1. Fetch the transfer job using params.KubeClient.
+	// 2. Verify if the job has finished successfully (returns true) or is still running (returns false).
+	// 3. If the transfer job encountered an error, evaluate the need for cleanup.
+	// 4. Report the volume population status to the original PVC through params.Recorder.
+
+	klog.Infof("Run populateCompleteFn")
+	return true, nil
+}
+
+func populateCleanupFn(ctx context.Context, params populatorMachinery.PopulatorParams) error {
+	// Implement the provider-specific logic to clean up any temporary resources
+	// that were created during the volume population process. This step happens after PV rebind to the original PVC
+	// and before the PVC' gets deleted.
+
+	// Example steps:
+	// 1. Fetch the transfer job using params.KubeClient.
+	// 2. If the transfer job still exists delete the job.
+	// 3. Report the volume population status to the original PVC through params.Recorder.
+
+	klog.Infof("Run populateCleanupFn")
+	return nil
+}


Reviews:
user: login: sunnylovestiramisu
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T16:38:52Z
user: login: dannawang0221
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T17:21:29Z
user: login: xing-yang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T20:37:11Z
user: login: dannawang0221
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T18:17:35Z
user: login: xing-yang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T15:04:45Z
user: login: dannawang0221
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T20:37:25Z

Review comments:
diff_hunk: @@ -13,7 +13,7 @@
 # limitations under the License.
 
 CMDS_DIR=example
-CMDS=hello-populator
+CMDS ?= hello-populator
path: Makefile
user: login: sunnylovestiramisu
body: Why do we need to make this change?
created_at: 2025-03-31T16:38:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,7 +13,7 @@
 # limitations under the License.
 
 CMDS_DIR=example
-CMDS=hello-populator
+CMDS ?= hello-populator
path: Makefile
user: login: dannawang0221
body: I add a new `provider-populator` in the example dir. I change it to variable so the users can choose which image to build
created_at: 2025-03-31T17:21:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,92 @@
+package main
+
+import (
+	"context"
+	"flag"
+
+	populatorMachinery "github.com/kubernetes-csi/lib-volume-populator/populator-machinery"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+
+	"k8s.io/klog/v2"
+)
+
+const (
+	groupName  = "provider.example.com"
+	kind       = "Provider"
+	apiVersion = "v1alpha1"
+	resource   = "providers"
+	prefix     = "provider.example.com"
+)
+
+var (
+	namespace      = flag.String("namespace", "provider", "Namespace to deploy controller")
+	mode           = flag.String("mode", "", "Mode to run the application in, supported values is controller")
+	kubeconfigPath = flag.String("kubeconfig-path", "", "kubeconfig path")
+	httpEndpoint   = flag.String("http-endpoint", "", "The TCP network address where the HTTP server for diagnostics, including metrics and leader election health check, will listen (example: `:8080`). The default is empty string.")
+	metricsPath    = flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.")
+
+	groupKind = schema.GroupKind{
+		Group: groupName,
+		Kind:  kind,
+	}
+	versionResource = schema.GroupVersionResource{
+		Group:    groupKind.Group,
+		Version:  apiVersion,
+		Resource: resource,
+	}
+)
+
+func main() {
+	klog.InitFlags(nil)
+	flag.Parse()
+
+	switch *mode {
+	case "controller":
+		klog.Infof("Run provider-populator controller")
+		pfcfg := &populatorMachinery.ProviderFunctionConfig{
+			PopulateFn:         populateFn,
+			PopulateCompleteFn: populateCompleteFn,
+			PopulateCleanupFn:  populateCleanupFn,
+		}
+
+		vpcfg := &populatorMachinery.VolumePopulatorConfig{
+			MasterURL:              "",
+			Kubeconfig:             *kubeconfigPath,
+			HttpEndpoint:           *httpEndpoint,
+			MetricsPath:            *metricsPath,
+			Namespace:              *namespace,
+			Prefix:                 prefix,
+			Gk:                     groupKind,
+			Gvr:                    versionResource,
+			ProviderFunctionConfig: pfcfg,
+			CrossNamespace:         false,
+		}
+
+		populatorMachinery.RunControllerWithConfig(*vpcfg)
+	default:
+		klog.Infof("Mode %s is not supported", *mode)
+	}
+}
+
+func populateFn(ctx context.Context, params populatorMachinery.PopulatorParams) error {
+	// Implement the provider-specific logic to initiate volume population.
+	// This may involve calling cloud-native APIs or creating temporary Kubernetes resources
+	// such as Pods or Jobs for data transfer.
+	klog.Infof("Run populateFn")
path: example/provider-populator/main.go
user: login: xing-yang
body: This function does not do anything.  It would be great to have some logic like what is in the hello populator.
created_at: 2025-04-07T20:37:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,92 @@
+package main
+
+import (
+	"context"
+	"flag"
+
+	populatorMachinery "github.com/kubernetes-csi/lib-volume-populator/populator-machinery"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+
+	"k8s.io/klog/v2"
+)
+
+const (
+	groupName  = "provider.example.com"
+	kind       = "Provider"
+	apiVersion = "v1alpha1"
+	resource   = "providers"
+	prefix     = "provider.example.com"
+)
+
+var (
+	namespace      = flag.String("namespace", "provider", "Namespace to deploy controller")
+	mode           = flag.String("mode", "", "Mode to run the application in, supported values is controller")
+	kubeconfigPath = flag.String("kubeconfig-path", "", "kubeconfig path")
+	httpEndpoint   = flag.String("http-endpoint", "", "The TCP network address where the HTTP server for diagnostics, including metrics and leader election health check, will listen (example: `:8080`). The default is empty string.")
+	metricsPath    = flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.")
+
+	groupKind = schema.GroupKind{
+		Group: groupName,
+		Kind:  kind,
+	}
+	versionResource = schema.GroupVersionResource{
+		Group:    groupKind.Group,
+		Version:  apiVersion,
+		Resource: resource,
+	}
+)
+
+func main() {
+	klog.InitFlags(nil)
+	flag.Parse()
+
+	switch *mode {
+	case "controller":
+		klog.Infof("Run provider-populator controller")
+		pfcfg := &populatorMachinery.ProviderFunctionConfig{
+			PopulateFn:         populateFn,
+			PopulateCompleteFn: populateCompleteFn,
+			PopulateCleanupFn:  populateCleanupFn,
+		}
+
+		vpcfg := &populatorMachinery.VolumePopulatorConfig{
+			MasterURL:              "",
+			Kubeconfig:             *kubeconfigPath,
+			HttpEndpoint:           *httpEndpoint,
+			MetricsPath:            *metricsPath,
+			Namespace:              *namespace,
+			Prefix:                 prefix,
+			Gk:                     groupKind,
+			Gvr:                    versionResource,
+			ProviderFunctionConfig: pfcfg,
+			CrossNamespace:         false,
+		}
+
+		populatorMachinery.RunControllerWithConfig(*vpcfg)
+	default:
+		klog.Infof("Mode %s is not supported", *mode)
+	}
+}
+
+func populateFn(ctx context.Context, params populatorMachinery.PopulatorParams) error {
+	// Implement the provider-specific logic to initiate volume population.
+	// This may involve calling cloud-native APIs or creating temporary Kubernetes resources
+	// such as Pods or Jobs for data transfer.
+	klog.Infof("Run populateFn")
path: example/provider-populator/main.go
user: login: dannawang0221
body: Hi Xing, what kind of example you are looking for? These functions are for provider specific source and destination storages.  Because of the tight connection to the underlying storage, it's tricky to come up with a really generic example that would be helpful. 
created_at: 2025-04-08T18:17:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,92 @@
+package main
+
+import (
+	"context"
+	"flag"
+
+	populatorMachinery "github.com/kubernetes-csi/lib-volume-populator/populator-machinery"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+
+	"k8s.io/klog/v2"
+)
+
+const (
+	groupName  = "provider.example.com"
+	kind       = "Provider"
+	apiVersion = "v1alpha1"
+	resource   = "providers"
+	prefix     = "provider.example.com"
+)
+
+var (
+	namespace      = flag.String("namespace", "provider", "Namespace to deploy controller")
+	mode           = flag.String("mode", "", "Mode to run the application in, supported values is controller")
+	kubeconfigPath = flag.String("kubeconfig-path", "", "kubeconfig path")
+	httpEndpoint   = flag.String("http-endpoint", "", "The TCP network address where the HTTP server for diagnostics, including metrics and leader election health check, will listen (example: `:8080`). The default is empty string.")
+	metricsPath    = flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.")
+
+	groupKind = schema.GroupKind{
+		Group: groupName,
+		Kind:  kind,
+	}
+	versionResource = schema.GroupVersionResource{
+		Group:    groupKind.Group,
+		Version:  apiVersion,
+		Resource: resource,
+	}
+)
+
+func main() {
+	klog.InitFlags(nil)
+	flag.Parse()
+
+	switch *mode {
+	case "controller":
+		klog.Infof("Run provider-populator controller")
+		pfcfg := &populatorMachinery.ProviderFunctionConfig{
+			PopulateFn:         populateFn,
+			PopulateCompleteFn: populateCompleteFn,
+			PopulateCleanupFn:  populateCleanupFn,
+		}
+
+		vpcfg := &populatorMachinery.VolumePopulatorConfig{
+			MasterURL:              "",
+			Kubeconfig:             *kubeconfigPath,
+			HttpEndpoint:           *httpEndpoint,
+			MetricsPath:            *metricsPath,
+			Namespace:              *namespace,
+			Prefix:                 prefix,
+			Gk:                     groupKind,
+			Gvr:                    versionResource,
+			ProviderFunctionConfig: pfcfg,
+			CrossNamespace:         false,
+		}
+
+		populatorMachinery.RunControllerWithConfig(*vpcfg)
+	default:
+		klog.Infof("Mode %s is not supported", *mode)
+	}
+}
+
+func populateFn(ctx context.Context, params populatorMachinery.PopulatorParams) error {
+	// Implement the provider-specific logic to initiate volume population.
+	// This may involve calling cloud-native APIs or creating temporary Kubernetes resources
+	// such as Pods or Jobs for data transfer.
+	klog.Infof("Run populateFn")
path: example/provider-populator/main.go
user: login: xing-yang
body: Hi Danna, 
That's why I'd like to understand how it works. Can you explain what exactly it needs to create and how that is related to the volume populator?
created_at: 2025-04-10T15:04:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,92 @@
+package main
+
+import (
+	"context"
+	"flag"
+
+	populatorMachinery "github.com/kubernetes-csi/lib-volume-populator/populator-machinery"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+
+	"k8s.io/klog/v2"
+)
+
+const (
+	groupName  = "provider.example.com"
+	kind       = "Provider"
+	apiVersion = "v1alpha1"
+	resource   = "providers"
+	prefix     = "provider.example.com"
+)
+
+var (
+	namespace      = flag.String("namespace", "provider", "Namespace to deploy controller")
+	mode           = flag.String("mode", "", "Mode to run the application in, supported values is controller")
+	kubeconfigPath = flag.String("kubeconfig-path", "", "kubeconfig path")
+	httpEndpoint   = flag.String("http-endpoint", "", "The TCP network address where the HTTP server for diagnostics, including metrics and leader election health check, will listen (example: `:8080`). The default is empty string.")
+	metricsPath    = flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.")
+
+	groupKind = schema.GroupKind{
+		Group: groupName,
+		Kind:  kind,
+	}
+	versionResource = schema.GroupVersionResource{
+		Group:    groupKind.Group,
+		Version:  apiVersion,
+		Resource: resource,
+	}
+)
+
+func main() {
+	klog.InitFlags(nil)
+	flag.Parse()
+
+	switch *mode {
+	case "controller":
+		klog.Infof("Run provider-populator controller")
+		pfcfg := &populatorMachinery.ProviderFunctionConfig{
+			PopulateFn:         populateFn,
+			PopulateCompleteFn: populateCompleteFn,
+			PopulateCleanupFn:  populateCleanupFn,
+		}
+
+		vpcfg := &populatorMachinery.VolumePopulatorConfig{
+			MasterURL:              "",
+			Kubeconfig:             *kubeconfigPath,
+			HttpEndpoint:           *httpEndpoint,
+			MetricsPath:            *metricsPath,
+			Namespace:              *namespace,
+			Prefix:                 prefix,
+			Gk:                     groupKind,
+			Gvr:                    versionResource,
+			ProviderFunctionConfig: pfcfg,
+			CrossNamespace:         false,
+		}
+
+		populatorMachinery.RunControllerWithConfig(*vpcfg)
+	default:
+		klog.Infof("Mode %s is not supported", *mode)
+	}
+}
+
+func populateFn(ctx context.Context, params populatorMachinery.PopulatorParams) error {
+	// Implement the provider-specific logic to initiate volume population.
+	// This may involve calling cloud-native APIs or creating temporary Kubernetes resources
+	// such as Pods or Jobs for data transfer.
+	klog.Infof("Run populateFn")
path: example/provider-populator/main.go
user: login: dannawang0221
body: I add some example steps for each function. Please let me know what do you think.
created_at: 2025-04-14T20:37:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: dannawang0221
created_at: 2025-03-31T16:23:44Z
body: /assign @sunnylovestiramisu 
author_association: CONTRIBUTOR
user: login: sunnylovestiramisu
created_at: 2025-04-21T18:53:12Z
body: /lgtm
author_association: CONTRIBUTOR
user: login: xing-yang
created_at: 2025-04-22T02:53:35Z
body: /lgtm
/approve
author_association: CONTRIBUTOR
user: login: k8s-ci-robot
created_at: 2025-04-22T02:53:41Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-csi/lib-volume-populator/pull/208#" title="Author self-approved">dannawang0221</a>*, *<a href="https://github.com/kubernetes-csi/lib-volume-populator/pull/208#issuecomment-2819931269" title="Approved">xing-yang</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-csi%2Flib-volume-populator).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes-csi/lib-volume-populator/blob/master/OWNERS)~~ [xing-yang]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR