diff --git a/.gitignore b/.gitignore index 9f0f3a1..c025f3e 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ go.work # Kubeconfig might contain secrets *.kubeconfig + +# Go binary downloads +*.tar.gz diff --git a/README.md b/README.md index 81c1963..cccbd0c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -> Right now, only Pods, Deployments and ReplicaSets are being watched, slowly but surely, other resources will be watched. +> Right now, Pods, Deployments, ReplicaSets and Nodes are being watched, slowly but surely, other resources will be watched. > Not only logging, metrics will also be exposed for all resources. > New functionalities are otw. # Logger Controller -A Kubernetes **observer-style controller** that watches **Pods**, **Deployments** and **ReplicaSets** and logs their state based on a declarative Custom Resource (`Logger`). +A Kubernetes **observer-style controller** that watches **Pods**, **Deployments**, **ReplicaSets** and **Nodes** and logs their state based on a declarative Custom Resource (`Logger`). Built using **Kubebuilder / controller-runtime**, this project focuses on reconciliation, watches, and logging patterns rather than resource mutation. @@ -13,14 +13,14 @@ Built using **Kubebuilder / controller-runtime**, this project focuses on reconc ## What this controller does - Defines a `Logger` Custom Resource -- Watches **Pod**, **Deployment** and **ReplicaSet** events (create / update / delete) -- On every Pod/Deployment/ReplicaSet event: +- Watches **Pod**, **Deployment**, **ReplicaSet** and **Node** events (create / update / delete) +- On every Pod/Deployment/ReplicaSet/Node event: - Reconciles matching `Logger` resources - - Logs the current state of Pods/Deployments + - Logs the current state of Pods/Deployments/ReplicaSets/Nodes - Supports: - Namespace-scoped or cluster-scoped logging - Exclusion of Kubernetes system namespaces -- Does **not** modify Pods/Deployments/Replicasets or any cluster resources +- Does **not** modify Pods/Deployments/ReplicaSets/Nodes or any cluster resources This is an **observer controller**, not a CRUD controller. @@ -67,7 +67,7 @@ Make sure that the context of kubectl is set to your target cluster, if you have Next, we want to install our CRD into our cluster. For that, an example CRD file has been provided, ```example/crd.yaml```. Make the changes you want and run the ```make install``` command from repo root. -Go ahead and make some resources (Pods/Deployments/Replicasets) on your cluster, which you want observed. +Go ahead and make some resources (Pods/Deployments/ReplicaSets/Nodes) on your cluster, which you want observed. Then, make CR for you CRD, using the command ```kubectl create -f example/crd.yaml``` from repo root. @@ -100,7 +100,8 @@ spec: resources: - pods - deployments - -replicasets + - replicasets + - nodes trigger: 30s diff --git a/config/samples/logger_v1_logger.yaml b/config/samples/logger_v1_logger.yaml index 91e33e2..b9980d8 100644 --- a/config/samples/logger_v1_logger.yaml +++ b/config/samples/logger_v1_logger.yaml @@ -6,4 +6,12 @@ metadata: app.kubernetes.io/managed-by: kustomize name: logger-sample spec: - # TODO(user): Add fields here + scope: + type: namespace + namespace: default + resources: + - pods + - deployments + - replicasets + - nodes + trigger: 30s diff --git a/example/crd.yaml b/example/crd.yaml index 7d8d666..cc6ad86 100644 --- a/example/crd.yaml +++ b/example/crd.yaml @@ -10,4 +10,5 @@ spec: - pods - deployments - replicasets + - nodes trigger: 30s diff --git a/go.mod b/go.mod index a47f830..b02272a 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.25.3 require ( github.com/onsi/ginkgo/v2 v2.27.2 github.com/onsi/gomega v1.38.2 + go.uber.org/zap v1.27.0 + k8s.io/api v0.35.0 k8s.io/apimachinery v0.35.0 k8s.io/client-go v0.35.0 sigs.k8s.io/controller-runtime v0.23.1 @@ -64,7 +66,6 @@ require ( go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect @@ -85,7 +86,6 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.35.0 // indirect k8s.io/apiextensions-apiserver v0.35.0 // indirect k8s.io/apiserver v0.35.0 // indirect k8s.io/component-base v0.35.0 // indirect diff --git a/go1.23.4.linux-amd64.tar.gz b/go1.23.4.linux-amd64.tar.gz deleted file mode 100644 index 854f582..0000000 Binary files a/go1.23.4.linux-amd64.tar.gz and /dev/null differ diff --git a/internal/controller/logger_controller.go b/internal/controller/logger_controller.go index 09fe05c..8657814 100644 --- a/internal/controller/logger_controller.go +++ b/internal/controller/logger_controller.go @@ -87,7 +87,6 @@ func rsLine(rs appsv1.ReplicaSet) string { func nodeLine(node corev1.Node) string { return "node/" + node.Name + " " + - node.Namespace + " " + fmt.Sprintf("%v", node.Status.Capacity) } @@ -370,6 +369,9 @@ func (r *LoggerReconciler) SetupWithManager(mgr ctrl.Manager) error { ).Watches( &appsv1.Deployment{}, handler.EnqueueRequestsFromMapFunc(r.enqueueAllLoggers), + ).Watches( + &appsv1.ReplicaSet{}, + handler.EnqueueRequestsFromMapFunc(r.enqueueAllLoggers), ).Watches( &corev1.Node{}, handler.EnqueueRequestsFromMapFunc(r.enqueueAllLoggers), diff --git a/internal/controller/logger_controller_test.go b/internal/controller/logger_controller_test.go index 7e2083b..8631877 100644 --- a/internal/controller/logger_controller_test.go +++ b/internal/controller/logger_controller_test.go @@ -100,6 +100,14 @@ func testReplicaSet(name, namespace string) *appsv1.ReplicaSet { } } +func testNode(name string) *corev1.Node { + return &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } +} + func observedCount(ctx context.Context, key types.NamespacedName) int32 { logger := &loggerv1.Logger{} if err := k8sClient.Get(ctx, key, logger); err != nil { @@ -224,4 +232,39 @@ var _ = Describe("Logger Controller", func() { }, 5*time.Second, 200*time.Millisecond). Should(Equal(int32(12))) }) + + It("logs nodes when nodes resource is requested", func() { + logger := &loggerv1.Logger{ + ObjectMeta: metav1.ObjectMeta{ + Name: loggerName, + Namespace: "default", + }, + Spec: loggerv1.LoggerSpec{ + Scope: loggerv1.ScopeSpec{ + Type: "Cluster", + }, + Resources: []string{"nodes"}, + }, + } + + Expect(k8sClient.Create(ctx, testNode("node-a"))).To(Succeed()) + Expect(k8sClient.Create(ctx, testNode("node-b"))).To(Succeed()) + + Expect(k8sClient.Create(ctx, logger)).To(Succeed()) + + reconciler := &LoggerReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := reconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: loggerKey, + }) + Expect(err).NotTo(HaveOccurred()) + + Eventually(func() int32 { + return observedCount(ctx, loggerKey) + }, 5*time.Second, 200*time.Millisecond). + Should(Equal(int32(2))) + }) })