diff --git a/Makefile b/Makefile index 7e219e4..b028f89 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ # Image URL to use all building/pushing image targets IMG ?= localhost:5000/monitor + +# Makefile CONTROLLER_GEN = bin/controller-gen CI_COMMIT ?= $(shell git rev-parse --short HEAD) diff --git a/apis/v1/crd_types.go b/apis/v1/crd_types.go new file mode 100644 index 0000000..b7b1f99 --- /dev/null +++ b/apis/v1/crd_types.go @@ -0,0 +1 @@ +package v1 diff --git a/apis/v1/groupversion_info.go b/apis/v1/groupversion_info.go new file mode 100644 index 0000000..1dd8457 --- /dev/null +++ b/apis/v1/groupversion_info.go @@ -0,0 +1,18 @@ +// +kubebuilder:object:generate=true +// +groupName=demo.io +package v1 + +import ( + ctrl "sigs.k8s.io/controller-runtime" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = ctrl.GroupVersion{Group: "demo.io", Version: "v1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &ctrl.SchemeBuilder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/config/kustomization.yaml b/config/kustomization.yaml index 90db882..c99a586 100644 --- a/config/kustomization.yaml +++ b/config/kustomization.yaml @@ -3,18 +3,15 @@ namespace: kube-system generatorOptions: disableNameSuffixHash: true -namePrefix: monitor- +namePrefix: demo- commonLabels: - app: monitor + app: demo resources: - rbac/service_account.yaml - rbac/role.yaml -- rbac/leader_election_role.yaml - rbac/role_binding.yaml -- webhook/manifests.yaml -- webhook/service.yaml - deployment.yaml patchesStrategicMerge: @@ -27,5 +24,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: localhost:5000/monitor + newName: localhost:5000/demo diff --git a/docs/manager.drawio b/docs/manager.drawio new file mode 100644 index 0000000..67d3c9f --- /dev/null +++ b/docs/manager.drawio @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod index 4418a0c..856d47b 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,6 @@ require ( go.uber.org/zap v1.19.1 k8s.io/api v0.23.5 k8s.io/apimachinery v0.23.5 + k8s.io/client-go v0.23.5 sigs.k8s.io/controller-runtime v0.11.2 ) diff --git a/pkg/apis/v1/groupversion_info.go b/pkg/apis/v1/groupversion_info.go index 1dd8457..649384b 100644 --- a/pkg/apis/v1/groupversion_info.go +++ b/pkg/apis/v1/groupversion_info.go @@ -1,6 +1,6 @@ // +kubebuilder:object:generate=true -// +groupName=demo.io -package v1 +// +groupName=monitor.io +package v1alpha1 import ( ctrl "sigs.k8s.io/controller-runtime" @@ -8,7 +8,7 @@ import ( var ( // GroupVersion is group version used to register these objects - GroupVersion = ctrl.GroupVersion{Group: "demo.io", Version: "v1"} + GroupVersion = ctrl.GroupVersion{Group: "monitor.io", Version: "v1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &ctrl.SchemeBuilder{GroupVersion: GroupVersion} diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go index 63b340e..17486ce 100644 --- a/pkg/k8s/k8s.go +++ b/pkg/k8s/k8s.go @@ -5,7 +5,7 @@ import ( "net/http" "net/http/pprof" - v1 "monitor/pkg/apis/v1" + v1 "monitor/apis/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..d147348 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,11 @@ + +```bash + +docker build --network host -t devkitpro/devkita64 - << EOF +FROM devkitpro/devkita64:20231108 +RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list \ + && apt-get update && apt-get install -y --no-install-recommends nodejs \ + && rm -rf /var/lib/apt/lists/* /usr/share/man/* +EOF + +``` \ No newline at end of file diff --git a/tests/ctrl/main.go b/tests/ctrl/main.go new file mode 100644 index 0000000..8d5e71f --- /dev/null +++ b/tests/ctrl/main.go @@ -0,0 +1,62 @@ +package tests + +import ( + "context" + "os" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +type podReconcile struct { + client.Client +} + +func (r *podReconcile) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + rs := &appsv1.ReplicaSet{} + err := r.Get(ctx, req.NamespacedName, rs) + if err != nil { + return ctrl.Result{}, err + } + // DoSometing + return ctrl.Result{}, nil +} + +func (r *podReconcile) InjectClient(c client.Client) error { + r.Client = c + return nil +} + +func main() { + log := ctrl.Log.WithName("entrypoint") + + // 初始化 manager,同时生成一个默认配置的 Cache + mgr, err := ctrl.NewManager(config.GetConfigOrDie(), ctrl.Options{}) + if err != nil { + log.Error(err, "unable to set up overall controller manager") + } + // 注册 scheme + if err = corev1.AddToScheme(mgr.GetScheme()); err != nil { + log.Error(err, "unable to add scheme") + } + + // 创建新的 controller + err = ctrl.NewControllerManagedBy(mgr).For(&appsv1.ReplicaSet{}). + // 当 Pod 变更时,也触发 ReplicaSet + Watches(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForObject{}). + Complete(&podReconcile{}) + if err != nil { + log.Error(err, "unable to add controller") + } + + // 启动 manager + if err = mgr.Start(ctrl.SetupSignalHandler()); err != nil { + log.Error(err, "unable to run manager") + os.Exit(1) + } +} diff --git a/tests/informer/informer.go b/tests/informer/informer.go new file mode 100644 index 0000000..b56422e --- /dev/null +++ b/tests/informer/informer.go @@ -0,0 +1,52 @@ +package main + +import ( + "context" + "flag" + "log" + "path/filepath" + + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" +) + +func main() { + var kubeconfig string + + flag.StringVar(&kubeconfig, "kubeconfig", filepath.Join(homedir.HomeDir(), ".kube", "config"), "(optional) absolute path to the kubeconfig file") + flag.Parse() + // use the current context in kubeconfig + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + log.Fatal(err) + } + + ctx := context.Background() + + AddPod := func(obj interface{}) { + + } + DeletePod := func(obj interface{}) { + + } + UpdatePod := func(oldObj, newObj interface{}) { + + } + + clientset := kubernetes.NewForConfigOrDie(config) + // 基于GVK 操作资源,假设需要操作数十种不同资源时,我们需要为每一种资源实现各自的函数 + podInformer := informers.NewSharedInformerFactory(clientset, 0). + Core().V1().Pods().Informer() + podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: AddPod, + DeleteFunc: DeletePod, + UpdateFunc: UpdatePod, + }) + // 启动informer + podInformer.Run(ctx.Done()) + cache.WaitForCacheSync(ctx.Done(), podInformer.HasSynced) + // 此处没有使用workqueue,但一般都是会用workqueue 增强处理逻辑的 +} diff --git a/tests/watch/watch.go b/tests/watch/watch.go new file mode 100644 index 0000000..ce61f7b --- /dev/null +++ b/tests/watch/watch.go @@ -0,0 +1,55 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + "path/filepath" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" +) + +func main() { + var kubeconfig string + + flag.StringVar(&kubeconfig, "kubeconfig", filepath.Join(homedir.HomeDir(), ".kube", "config"), "(optional) absolute path to the kubeconfig file") + flag.Parse() + // use the current context in kubeconfig + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + log.Fatal(err) + } + + ctx := context.Background() + // 创建 clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + log.Fatal(err) + } + // 使用 clientsent 监听 kube-system 下 Pod 对象 + list, err := clientset.CoreV1().Pods("kube-system"). + Watch(ctx, metav1.ListOptions{Watch: true}) + if err != nil { + log.Fatal(err) + } + for event := range list.ResultChan() { + pod := event.Object.(*corev1.Pod) + switch event.Type { + case watch.Added: + fmt.Printf("Pod %s added\n", pod.Name) + // todo: reconcile logic goes here + case watch.Modified: + fmt.Printf("Pod %s modified\n", pod.Name) + // todo: reconcile logic goes here + case watch.Deleted: + fmt.Printf("Pod %s deleted\n", pod.Name) + // todo: reconcile logic goes here + } + } +}