From ca44c4c184d867c68f3595f99fe3ff8bcd6bca0e Mon Sep 17 00:00:00 2001 From: kvs vishnu kumar Date: Mon, 20 Apr 2026 23:42:39 +0530 Subject: [PATCH 1/6] Update coroot operator with new TLS Support configs --- api/v1/coroot_types.go | 22 ++++++++ controller/cluster_agent.go | 59 +++++++++++++++----- controller/coroot.go | 60 ++++++++++++++++---- controller/node_agent.go | 108 +++++++++++++++++++++++------------- 4 files changed, 186 insertions(+), 63 deletions(-) diff --git a/api/v1/coroot_types.go b/api/v1/coroot_types.go index 0538055..20f0c5f 100644 --- a/api/v1/coroot_types.go +++ b/api/v1/coroot_types.go @@ -34,6 +34,15 @@ type AgentsOnlySpec struct { CorootURL string `json:"corootURL,omitempty"` // Whether to skip verification of the Coroot server's TLS certificate. TLSSkipVerify bool `json:"tlsSkipVerify,omitempty"` + // Secret containing the CA certificate to verify the Coroot server's certificate. + CASecret *corev1.SecretKeySelector `json:"caSecret,omitempty"` +} + +type AgentTLSSpec struct { + // Secret containing the CA certificate to verify the Coroot server's certificate. + CASecret *corev1.SecretKeySelector `json:"caSecret,omitempty"` + // Whether to skip verification of the Coroot server's TLS certificate. + TLSSkipVerify bool `json:"tlsSkipVerify,omitempty"` } type NodeAgentSpec struct { @@ -50,6 +59,8 @@ type NodeAgentSpec struct { // Environment variables for the node-agent. Env []corev1.EnvVar `json:"env,omitempty"` Image ImageSpec `json:"image,omitempty"` + // TLS settings for connecting to Coroot. + TLS *AgentTLSSpec `json:"tls,omitempty"` LogCollector LogCollectorSpec `json:"logCollector,omitempty"` EbpfTracer EbpfTracerSpec `json:"ebpfTracer,omitempty"` @@ -90,6 +101,8 @@ type ClusterAgentSpec struct { // Environment variables for the cluster-agent. Env []corev1.EnvVar `json:"env,omitempty"` Image ImageSpec `json:"image,omitempty"` + // TLS settings for connecting to Coroot. + TLS *AgentTLSSpec `json:"tls,omitempty"` KubeStateMetrics KubeStateMetricsSpec `json:"kubeStateMetrics,omitempty"` } @@ -254,6 +267,10 @@ type ServiceSpec struct { GRPCPort int32 `json:"grpcPort,omitempty"` // gRPC nodePort (if type is NodePort). GRPCNodePort int32 `json:"grpcNodePort,omitempty"` + // HTTPS port (optional). + HTTPSPort int32 `json:"httpsPort,omitempty"` + // HTTPS nodePort (if type is NodePort). + HTTPSNodePort int32 `json:"httpsNodePort,omitempty"` // Annotations for the service. Annotations map[string]string `json:"annotations,omitempty"` } @@ -273,6 +290,7 @@ type TLSSpec struct { } type CorootSpec struct { + // Specifies the metric resolution interval. // +kubebuilder:validation:Pattern="^[0-9]+[sm]$" MetricsRefreshInterval string `json:"metricsRefreshInterval,omitempty"` @@ -292,6 +310,10 @@ type CorootSpec struct { AuthAnonymousRole string `json:"authAnonymousRole,omitempty"` // Initial admin password for bootstrapping. AuthBootstrapAdminPassword string `json:"authBootstrapAdminPassword,omitempty"` + // Disable plain HTTP. + HTTPDisabled bool `json:"httpDisabled,omitempty"` + // HTTPS listen address (default: :8443). + HTTPSListen string `json:"httpsListen,omitempty"` // Secret containing the initial admin password. AuthBootstrapAdminPasswordSecret *corev1.SecretKeySelector `json:"authBootstrapAdminPasswordSecret,omitempty"` // gRPC settings. diff --git a/controller/cluster_agent.go b/controller/cluster_agent.go index 98a14fa..5b9ccd2 100644 --- a/controller/cluster_agent.go +++ b/controller/cluster_agent.go @@ -92,11 +92,23 @@ func (r *CorootReconciler) clusterAgentDeployment(cr *corootv1.Coroot) *appsv1.D }, } - corootURL := fmt.Sprintf("http://%s-coroot.%s:%d", cr.Name, cr.Namespace, cr.Spec.Service.Port) + scheme := "http" + port := cr.Spec.Service.Port + if cr.Spec.TLS != nil || cr.Spec.HTTPDisabled { + scheme = "https" + port = cr.Spec.Service.HTTPSPort + } + corootURL := fmt.Sprintf("%s://%s-coroot.%s:%d", scheme, cr.Name, cr.Namespace, port) var tlsSkipVerify bool + var caSecret *corev1.SecretKeySelector if cr.Spec.AgentsOnly != nil && cr.Spec.AgentsOnly.CorootURL != "" { corootURL = cr.Spec.AgentsOnly.CorootURL tlsSkipVerify = cr.Spec.AgentsOnly.TLSSkipVerify + caSecret = cr.Spec.AgentsOnly.CASecret + } + if cr.Spec.ClusterAgent.TLS != nil { + tlsSkipVerify = cr.Spec.ClusterAgent.TLS.TLSSkipVerify + caSecret = cr.Spec.ClusterAgent.TLS.CASecret } scrapeInterval := cmp.Or(cr.Spec.MetricsRefreshInterval, corootv1.DefaultMetricRefreshInterval) env := []corev1.EnvVar{ @@ -110,11 +122,39 @@ func (r *CorootReconciler) clusterAgentDeployment(cr *corootv1.Coroot) *appsv1.D if tlsSkipVerify { env = append(env, corev1.EnvVar{Name: "INSECURE_SKIP_VERIFY", Value: "true"}) } + if caSecret != nil { + env = append(env, corev1.EnvVar{Name: "CA_FILE", Value: "/etc/coroot-ca/ca.crt"}) + } for _, e := range cr.Spec.ClusterAgent.Env { env = append(env, e) } image := r.getAppImage(cr, AppClusterAgent) ksmImage := r.getAppImage(cr, AppKubeStateMetrics) + + volumeMounts := []corev1.VolumeMount{ + {Name: "tmp", MountPath: "/tmp"}, + } + volumes := []corev1.Volume{ + { + Name: "tmp", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + } + if caSecret != nil { + volumeMounts = append(volumeMounts, corev1.VolumeMount{Name: "ca", MountPath: "/etc/coroot-ca", ReadOnly: true}) + volumes = append(volumes, corev1.Volume{ + Name: "ca", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: caSecret.Name, + Items: []corev1.KeyToPath{{Key: caSecret.Key, Path: "ca.crt"}}, + }, + }, + }) + } + d.Spec = appsv1.DeploymentSpec{ Selector: &metav1.LabelSelector{ MatchLabels: ls, @@ -140,11 +180,9 @@ func (r *CorootReconciler) clusterAgentDeployment(cr *corootv1.Coroot) *appsv1.D "--listen=127.0.0.1:10301", "--metrics-wal-dir=/tmp", }, - Resources: cr.Spec.ClusterAgent.Resources, - VolumeMounts: []corev1.VolumeMount{ - {Name: "tmp", MountPath: "/tmp"}, - }, - Env: env, + Resources: cr.Spec.ClusterAgent.Resources, + VolumeMounts: volumeMounts, + Env: env, }, { Image: ksmImage.Name, @@ -163,14 +201,7 @@ func (r *CorootReconciler) clusterAgentDeployment(cr *corootv1.Coroot) *appsv1.D }, }, }, - Volumes: []corev1.Volume{ - { - Name: "tmp", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - }, + Volumes: volumes, }, }, } diff --git a/controller/coroot.go b/controller/coroot.go index fe4ed1e..2da025a 100644 --- a/controller/coroot.go +++ b/controller/coroot.go @@ -42,6 +42,9 @@ func (r *CorootReconciler) validateCoroot(ctx context.Context, cr *corootv1.Coro if !cr.Spec.GRPC.Disabled && cr.Spec.Service.GRPCPort == 0 { cr.Spec.Service.GRPCPort = 4317 } + if cr.Spec.TLS != nil && cr.Spec.Service.HTTPSPort == 0 { + cr.Spec.Service.HTTPSPort = 8443 + } if s3 := cr.Spec.Clickhouse.S3; s3 != nil { storageSize := cr.Spec.Clickhouse.Storage.Size @@ -350,6 +353,15 @@ func (r *CorootReconciler) corootService(cr *corootv1.Coroot) *corev1.Service { NodePort: cr.Spec.Service.GRPCNodePort, }) } + if cr.Spec.Service.HTTPSPort != 0 { + ports = append(ports, corev1.ServicePort{ + Name: "https", + Protocol: corev1.ProtocolTCP, + Port: cr.Spec.Service.HTTPSPort, + TargetPort: intstr.FromString("https"), + NodePort: cr.Spec.Service.HTTPSNodePort, + }) + } s.Spec = corev1.ServiceSpec{ Selector: ls, @@ -536,10 +548,21 @@ func (r *CorootReconciler) corootStatefulSet(cr *corootv1.Coroot, configEnvs Con {Name: "data", MountPath: "/data"}, } env := []corev1.EnvVar{ - {Name: "LISTEN", Value: ":8080"}, {Name: "GLOBAL_REFRESH_INTERVAL", Value: refreshInterval}, {Name: "INSTALLATION_TYPE", Value: "k8s-operator"}, } + if cr.Spec.HTTPDisabled { + env = append(env, corev1.EnvVar{Name: "HTTP_DISABLED", Value: "true"}) + } else { + env = append(env, corev1.EnvVar{Name: "LISTEN", Value: ":8080"}) + } + httpsListen := cmp.Or(cr.Spec.HTTPSListen, ":8443") + if cr.Spec.Service.HTTPSPort != 0 { + env = append(env, corev1.EnvVar{Name: "HTTPS_LISTEN", Value: httpsListen}) + _, port, _ := strings.Cut(httpsListen, ":") + p, _ := resource.ParseQuantity(port) + ports = append(ports, corev1.ContainerPort{Name: "https", ContainerPort: int32(p.Value()), Protocol: corev1.ProtocolTCP}) + } if cr.Spec.GRPC.Disabled { env = append(env, corev1.EnvVar{Name: "GRPC_DISABLED", Value: "true"}) } else { @@ -682,6 +705,16 @@ func (r *CorootReconciler) corootStatefulSet(cr *corootv1.Coroot, configEnvs Con replicas = 1 } + probe := &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{Path: "/health", Port: intstr.FromString("http")}, + }, + } + if cr.Spec.HTTPDisabled && cr.Spec.Service.HTTPSPort != 0 { + probe.HTTPGet.Port = intstr.FromString("https") + probe.HTTPGet.Scheme = corev1.URISchemeHTTPS + } + ss.Spec = appsv1.StatefulSetSpec{ Selector: &metav1.LabelSelector{ MatchLabels: ls, @@ -715,15 +748,11 @@ func (r *CorootReconciler) corootStatefulSet(cr *corootv1.Coroot, configEnvs Con "--config=/config/config.yaml", "--data-dir=/data", }, - Env: env, - Ports: ports, - VolumeMounts: volumeMounts, - Resources: cr.Spec.Resources, - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{Path: "/health", Port: intstr.FromString("http")}, - }, - }, + Env: env, + Ports: ports, + VolumeMounts: volumeMounts, + Resources: cr.Spec.Resources, + ReadinessProbe: probe, }, }, Volumes: volumes, @@ -751,6 +780,9 @@ func (r *CorootReconciler) corootConfigMap(ctx context.Context, cr *corootv1.Cor } type Config struct { + ListenAddress string `json:"listen_address,omitempty"` + HTTPSListenAddress string `json:"https_listen_address,omitempty"` + HTTPDisabled bool `json:"http_disabled,omitempty"` DisableBuiltinAlerts bool `json:"disableBuiltinAlerts,omitempty"` Projects []corootv1.ProjectSpec `json:"projects,omitempty"` SSO *corootv1.SSOSpec `json:"sso,omitempty"` @@ -766,6 +798,14 @@ func (r *CorootReconciler) corootConfigMap(ctx context.Context, cr *corootv1.Cor AI: cr.Spec.AI, CorootCloud: cr.Spec.CorootCloud, } + if cr.Spec.HTTPDisabled { + cfg.HTTPDisabled = true + } else { + cfg.ListenAddress = ":8080" + } + if cr.Spec.Service.HTTPSPort != 0 { + cfg.HTTPSListenAddress = cmp.Or(cr.Spec.HTTPSListen, ":8443") + } if cr.Spec.TLS != nil { cfg.TLS = &TLS{ CertFile: "/config/tls/tls.crt", diff --git a/controller/node_agent.go b/controller/node_agent.go index 259018c..312e486 100644 --- a/controller/node_agent.go +++ b/controller/node_agent.go @@ -23,11 +23,23 @@ func (r *CorootReconciler) nodeAgentDaemonSet(cr *corootv1.Coroot) *appsv1.Daemo }, } - corootURL := fmt.Sprintf("http://%s-coroot.%s:%d", cr.Name, cr.Namespace, cr.Spec.Service.Port) + scheme := "http" + port := cr.Spec.Service.Port + if cr.Spec.TLS != nil || cr.Spec.HTTPDisabled { + scheme = "https" + port = cr.Spec.Service.HTTPSPort + } + corootURL := fmt.Sprintf("%s://%s-coroot.%s:%d", scheme, cr.Name, cr.Namespace, port) var tlsSkipVerify bool + var caSecret *corev1.SecretKeySelector if cr.Spec.AgentsOnly != nil && cr.Spec.AgentsOnly.CorootURL != "" { corootURL = strings.TrimRight(cr.Spec.AgentsOnly.CorootURL, "/") tlsSkipVerify = cr.Spec.AgentsOnly.TLSSkipVerify + caSecret = cr.Spec.AgentsOnly.CASecret + } + if cr.Spec.NodeAgent.TLS != nil { + tlsSkipVerify = cr.Spec.NodeAgent.TLS.TLSSkipVerify + caSecret = cr.Spec.NodeAgent.TLS.CASecret } scrapeInterval := cmp.Or(cr.Spec.MetricsRefreshInterval, corootv1.DefaultMetricRefreshInterval) env := []corev1.EnvVar{ @@ -39,6 +51,9 @@ func (r *CorootReconciler) nodeAgentDaemonSet(cr *corootv1.Coroot) *appsv1.Daemo if tlsSkipVerify { env = append(env, corev1.EnvVar{Name: "INSECURE_SKIP_VERIFY", Value: "true"}) } + if caSecret != nil { + env = append(env, corev1.EnvVar{Name: "CA_FILE", Value: "/etc/coroot-ca/ca.crt"}) + } env = append(env, corev1.EnvVar{Name: "METRICS_ENDPOINT", Value: corootURL + "/v1/metrics"}) if v := cr.Spec.NodeAgent.LogCollector.CollectLogBasedMetrics; v != nil && !*v { env = append(env, corev1.EnvVar{Name: "DISABLE_LOG_PARSING", Value: "true"}) @@ -84,6 +99,57 @@ func (r *CorootReconciler) nodeAgentDaemonSet(cr *corootv1.Coroot) *appsv1.Daemo image := r.getAppImage(cr, AppNodeAgent) + volumeMounts := []corev1.VolumeMount{ + {Name: "cgroupfs", MountPath: "/host/sys/fs/cgroup", ReadOnly: true}, + {Name: "tracefs", MountPath: "/sys/kernel/tracing"}, + {Name: "debugfs", MountPath: "/sys/kernel/debug"}, + {Name: "tmp", MountPath: "/tmp"}, + } + volumes := []corev1.Volume{ + { + Name: "cgroupfs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/sys/fs/cgroup", + }, + }, + }, + { + Name: "tracefs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/sys/kernel/tracing", + }, + }, + }, + { + Name: "debugfs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/sys/kernel/debug", + }, + }, + }, + { + Name: "tmp", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + } + if caSecret != nil { + volumeMounts = append(volumeMounts, corev1.VolumeMount{Name: "ca", MountPath: "/etc/coroot-ca", ReadOnly: true}) + volumes = append(volumes, corev1.Volume{ + Name: "ca", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: caSecret.Name, + Items: []corev1.KeyToPath{{Key: caSecret.Key, Path: "ca.crt"}}, + }, + }, + }) + } + ds.Spec = appsv1.DaemonSetSpec{ Selector: &metav1.LabelSelector{ MatchLabels: ls, @@ -113,46 +179,10 @@ func (r *CorootReconciler) nodeAgentDaemonSet(cr *corootv1.Coroot) *appsv1.Daemo SecurityContext: &corev1.SecurityContext{Privileged: ptr.To(true)}, Env: env, Resources: resources, - VolumeMounts: []corev1.VolumeMount{ - {Name: "cgroupfs", MountPath: "/host/sys/fs/cgroup", ReadOnly: true}, - {Name: "tracefs", MountPath: "/sys/kernel/tracing"}, - {Name: "debugfs", MountPath: "/sys/kernel/debug"}, - {Name: "tmp", MountPath: "/tmp"}, - }, - }, - }, - Volumes: []corev1.Volume{ - { - Name: "cgroupfs", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/sys/fs/cgroup", - }, - }, - }, - { - Name: "tracefs", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/sys/kernel/tracing", - }, - }, - }, - { - Name: "debugfs", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/sys/kernel/debug", - }, - }, - }, - { - Name: "tmp", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, + VolumeMounts: volumeMounts, }, }, + Volumes: volumes, }, }, } From 98fcbadaa0df5500f8e0941c4a41fd33cf456efe Mon Sep 17 00:00:00 2001 From: kvs vishnu kumar Date: Thu, 23 Apr 2026 00:20:56 +0530 Subject: [PATCH 2/6] bump controller-gen and run make generate --- Makefile | 2 +- api/v1/zz_generated.deepcopy.go | 37 +++++++- config/crd/coroot.com_coroots.yaml | 107 +++++++++++++++++++++- config/crd/coroot.com_coroots_legacy.yaml | 95 ++++++++++++++++++- 4 files changed, 237 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 02210fa..8a558dd 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ $(LOCALBIN): CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ## Tool Versions -CONTROLLER_TOOLS_VERSION ?= v0.16.1 +CONTROLLER_TOOLS_VERSION ?= v0.20.1 .PHONY: controller-gen controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index b7c2696..ea2a0fb 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -40,9 +40,34 @@ func (in *AISpec) DeepCopy() *AISpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgentTLSSpec) DeepCopyInto(out *AgentTLSSpec) { + *out = *in + if in.CASecret != nil { + in, out := &in.CASecret, &out.CASecret + *out = new(corev1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentTLSSpec. +func (in *AgentTLSSpec) DeepCopy() *AgentTLSSpec { + if in == nil { + return nil + } + out := new(AgentTLSSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AgentsOnlySpec) DeepCopyInto(out *AgentsOnlySpec) { *out = *in + if in.CASecret != nil { + in, out := &in.CASecret, &out.CASecret + *out = new(corev1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentsOnlySpec. @@ -642,6 +667,11 @@ func (in *ClusterAgentSpec) DeepCopyInto(out *ClusterAgentSpec) { } } in.Image.DeepCopyInto(&out.Image) + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(AgentTLSSpec) + (*in).DeepCopyInto(*out) + } in.KubeStateMetrics.DeepCopyInto(&out.KubeStateMetrics) } @@ -800,7 +830,7 @@ func (in *CorootSpec) DeepCopyInto(out *CorootSpec) { if in.AgentsOnly != nil { in, out := &in.AgentsOnly, &out.AgentsOnly *out = new(AgentsOnlySpec) - **out = **in + (*in).DeepCopyInto(*out) } in.Service.DeepCopyInto(&out.Service) if in.Ingress != nil { @@ -1294,6 +1324,11 @@ func (in *NodeAgentSpec) DeepCopyInto(out *NodeAgentSpec) { } } in.Image.DeepCopyInto(&out.Image) + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(AgentTLSSpec) + (*in).DeepCopyInto(*out) + } in.LogCollector.DeepCopyInto(&out.LogCollector) in.EbpfTracer.DeepCopyInto(&out.EbpfTracer) in.EbpfProfiler.DeepCopyInto(&out.EbpfProfiler) diff --git a/config/crd/coroot.com_coroots.yaml b/config/crd/coroot.com_coroots.yaml index b8aa6ac..816051f 100644 --- a/config/crd/coroot.com_coroots.yaml +++ b/config/crd/coroot.com_coroots.yaml @@ -4,7 +4,7 @@ kind: CustomResourceDefinition metadata: annotations: argocd.argoproj.io/sync-options: Replace=true - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.20.1 name: coroots.coroot.com spec: group: coroot.com @@ -962,6 +962,31 @@ spec: description: Configures the operator to install only the node-agent and cluster-agent. properties: + caSecret: + description: Secret containing the CA certificate to verify the + Coroot server's certificate. + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be + defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic corootURL: description: URL of the Coroot instance to which agents send metrics, logs, traces, and profiles. @@ -4674,6 +4699,39 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + tls: + description: TLS settings for connecting to Coroot. + properties: + caSecret: + description: Secret containing the CA certificate to verify + the Coroot server's certificate. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + tlsSkipVerify: + description: Whether to skip verification of the Coroot server's + TLS certificate. + type: boolean + type: object tolerations: items: description: |- @@ -5096,6 +5154,12 @@ spec: description: Disables gRPC server. type: boolean type: object + httpDisabled: + description: Disable plain HTTP. + type: boolean + httpsListen: + description: 'HTTPS listen address (default: :8443).' + type: string ingress: description: Ingress configuration for Coroot. properties: @@ -6330,6 +6394,39 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + tls: + description: TLS settings for connecting to Coroot. + properties: + caSecret: + description: Secret containing the CA certificate to verify + the Coroot server's certificate. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + tlsSkipVerify: + description: Whether to skip verification of the Coroot server's + TLS certificate. + type: boolean + type: object tolerations: items: description: |- @@ -8397,6 +8494,14 @@ spec: description: gRPC port (default 4317). format: int32 type: integer + httpsNodePort: + description: HTTPS nodePort (if type is NodePort). + format: int32 + type: integer + httpsPort: + description: HTTPS port (optional). + format: int32 + type: integer nodePort: description: Service nodePort (if type is NodePort). format: int32 diff --git a/config/crd/coroot.com_coroots_legacy.yaml b/config/crd/coroot.com_coroots_legacy.yaml index 3135725..60b96f5 100644 --- a/config/crd/coroot.com_coroots_legacy.yaml +++ b/config/crd/coroot.com_coroots_legacy.yaml @@ -3,7 +3,7 @@ kind: CustomResourceDefinition metadata: annotations: argocd.argoproj.io/sync-options: Replace=true - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.20.1 name: coroots.coroot.com spec: group: coroot.com @@ -869,6 +869,27 @@ spec: agentsOnly: description: Configures the operator to install only the node-agent and cluster-agent. properties: + caSecret: + description: Secret containing the CA certificate to verify the Coroot server's certificate. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object corootURL: description: URL of the Coroot instance to which agents send metrics, logs, traces, and profiles. pattern: ^https?://.+$ @@ -4208,6 +4229,34 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + tls: + description: TLS settings for connecting to Coroot. + properties: + caSecret: + description: Secret containing the CA certificate to verify the Coroot server's certificate. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object + tlsSkipVerify: + description: Whether to skip verification of the Coroot server's TLS certificate. + type: boolean + type: object tolerations: items: description: |- @@ -4596,6 +4645,14 @@ spec: type: boolean type: object nullable: true + httpDisabled: + description: Disable plain HTTP. + type: boolean + nullable: true + httpsListen: + description: 'HTTPS listen address (default: :8443).' + type: string + nullable: true ingress: description: Ingress configuration for Coroot. properties: @@ -5707,6 +5764,34 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + tls: + description: TLS settings for connecting to Coroot. + properties: + caSecret: + description: Secret containing the CA certificate to verify the Coroot server's certificate. + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object + tlsSkipVerify: + description: Whether to skip verification of the Coroot server's TLS certificate. + type: boolean + type: object tolerations: items: description: |- @@ -7588,6 +7673,14 @@ spec: description: gRPC port (default 4317). format: int32 type: integer + httpsNodePort: + description: HTTPS nodePort (if type is NodePort). + format: int32 + type: integer + httpsPort: + description: HTTPS port (optional). + format: int32 + type: integer nodePort: description: Service nodePort (if type is NodePort). format: int32 From f4840bbc6d9019910b8e72e0883cbdfe09952a2d Mon Sep 17 00:00:00 2001 From: kvs vishnu kumar Date: Fri, 24 Apr 2026 00:27:15 +0530 Subject: [PATCH 3/6] add coroot_tls.yaml in samples --- config/samples/coroot_tls.yaml | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 config/samples/coroot_tls.yaml diff --git a/config/samples/coroot_tls.yaml b/config/samples/coroot_tls.yaml new file mode 100644 index 0000000..55c7059 --- /dev/null +++ b/config/samples/coroot_tls.yaml @@ -0,0 +1,40 @@ +apiVersion: coroot.com/v1 +kind: Coroot +metadata: + name: coroot + namespace: coroot +spec: + tls: + certSecret: + name: coroot-server-tls + key: tls.crt + keySecret: + name: coroot-server-tls + key: tls.key + + httpDisabled: true + + apiKeySecret: + name: coroot-api-key + key: key + + projects: + - name: "My Cluster" + apiKeys: + - keySecret: + name: coroot-api-key + key: key + + clusterAgent: + tls: + caSecret: + name: coroot-ca-cert + key: ca.crt + tlsSkipVerify: false + + nodeAgent: + tls: + caSecret: + name: coroot-ca-cert + key: ca.crt + tlsSkipVerify: false From b9ad4a0130d18d3249bd978f4992d44ee3c172bc Mon Sep 17 00:00:00 2001 From: kvs vishnu kumar Date: Sat, 25 Apr 2026 10:14:48 +0530 Subject: [PATCH 4/6] Add HTTPDisabled conditions for ports and ingress specs --- controller/coroot.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/controller/coroot.go b/controller/coroot.go index 2da025a..276375a 100644 --- a/controller/coroot.go +++ b/controller/coroot.go @@ -42,7 +42,7 @@ func (r *CorootReconciler) validateCoroot(ctx context.Context, cr *corootv1.Coro if !cr.Spec.GRPC.Disabled && cr.Spec.Service.GRPCPort == 0 { cr.Spec.Service.GRPCPort = 4317 } - if cr.Spec.TLS != nil && cr.Spec.Service.HTTPSPort == 0 { + if (cr.Spec.TLS != nil || cr.Spec.HTTPDisabled) && cr.Spec.Service.HTTPSPort == 0 { cr.Spec.Service.HTTPSPort = 8443 } @@ -335,14 +335,15 @@ func (r *CorootReconciler) corootService(cr *corootv1.Coroot) *corev1.Service { }, } - ports := []corev1.ServicePort{ - { + var ports []corev1.ServicePort + if !cr.Spec.HTTPDisabled { + ports = append(ports, corev1.ServicePort{ Name: "http", Protocol: corev1.ProtocolTCP, Port: cr.Spec.Service.Port, TargetPort: intstr.FromString("http"), NodePort: cr.Spec.Service.NodePort, - }, + }) } if !cr.Spec.GRPC.Disabled { ports = append(ports, corev1.ServicePort{ @@ -425,6 +426,10 @@ func (r *CorootReconciler) corootIngressV1(cr *corootv1.Coroot) *networkingv1.In if !strings.HasPrefix(path, "/") { path = "/" + path } + portName := "http" + if cr.Spec.HTTPDisabled { + portName = "https" + } i.Spec = networkingv1.IngressSpec{ IngressClassName: cr.Spec.Ingress.ClassName, Rules: []networkingv1.IngressRule{{ @@ -438,7 +443,7 @@ func (r *CorootReconciler) corootIngressV1(cr *corootv1.Coroot) *networkingv1.In Service: &networkingv1.IngressServiceBackend{ Name: fmt.Sprintf("%s-coroot", cr.Name), Port: networkingv1.ServiceBackendPort{ - Name: "http", + Name: portName, }, }, }, @@ -478,6 +483,10 @@ func (r *CorootReconciler) corootIngressV1Beta1(cr *corootv1.Coroot) *networking path = "/" + path } pathType := networkingv1beta1.PathTypePrefix + portName := "http" + if cr.Spec.HTTPDisabled { + portName = "https" + } i.Spec = networkingv1beta1.IngressSpec{ Rules: []networkingv1beta1.IngressRule{{ Host: cr.Spec.Ingress.Host, @@ -488,7 +497,7 @@ func (r *CorootReconciler) corootIngressV1Beta1(cr *corootv1.Coroot) *networking PathType: &pathType, Backend: networkingv1beta1.IngressBackend{ ServiceName: fmt.Sprintf("%s-coroot", cr.Name), - ServicePort: intstr.FromString("http"), + ServicePort: intstr.FromString(portName), }, }}, }, @@ -528,8 +537,9 @@ func (r *CorootReconciler) corootStatefulSet(cr *corootv1.Coroot, configEnvs Con refreshInterval := cmp.Or(cr.Spec.MetricsRefreshInterval, corootv1.DefaultMetricRefreshInterval) - ports := []corev1.ContainerPort{ - {Name: "http", ContainerPort: 8080, Protocol: corev1.ProtocolTCP}, + var ports []corev1.ContainerPort + if !cr.Spec.HTTPDisabled { + ports = append(ports, corev1.ContainerPort{Name: "http", ContainerPort: 8080, Protocol: corev1.ProtocolTCP}) } volumes := []corev1.Volume{ { From dd340c8ad7adf07f966794577e57e4c9f184d2e1 Mon Sep 17 00:00:00 2001 From: kvs vishnu kumar Date: Sat, 25 Apr 2026 11:02:00 +0530 Subject: [PATCH 5/6] Use strconv for parsing port --- controller/coroot.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/controller/coroot.go b/controller/coroot.go index 276375a..06107b9 100644 --- a/controller/coroot.go +++ b/controller/coroot.go @@ -6,6 +6,7 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "strconv" "strings" corootv1 "github.io/coroot/operator/api/v1" @@ -569,9 +570,9 @@ func (r *CorootReconciler) corootStatefulSet(cr *corootv1.Coroot, configEnvs Con httpsListen := cmp.Or(cr.Spec.HTTPSListen, ":8443") if cr.Spec.Service.HTTPSPort != 0 { env = append(env, corev1.EnvVar{Name: "HTTPS_LISTEN", Value: httpsListen}) - _, port, _ := strings.Cut(httpsListen, ":") - p, _ := resource.ParseQuantity(port) - ports = append(ports, corev1.ContainerPort{Name: "https", ContainerPort: int32(p.Value()), Protocol: corev1.ProtocolTCP}) + _, portStr, _ := strings.Cut(httpsListen, ":") + port, _ := strconv.Atoi(portStr) + ports = append(ports, corev1.ContainerPort{Name: "https", ContainerPort: int32(port), Protocol: corev1.ProtocolTCP}) } if cr.Spec.GRPC.Disabled { env = append(env, corev1.EnvVar{Name: "GRPC_DISABLED", Value: "true"}) From 7d7589b5b0e959fe337f6cdb69c5c84d2fa2dc6a Mon Sep 17 00:00:00 2001 From: kvs vishnu kumar Date: Sun, 26 Apr 2026 12:21:16 +0530 Subject: [PATCH 6/6] Refactor TLS, CASecret logic --- api/v1/coroot_types.go | 2 -- api/v1/zz_generated.deepcopy.go | 7 +------ config/crd/coroot.com_coroots.yaml | 25 ----------------------- config/crd/coroot.com_coroots_legacy.yaml | 21 ------------------- controller/cluster_agent.go | 7 ++----- controller/node_agent.go | 7 ++----- 6 files changed, 5 insertions(+), 64 deletions(-) diff --git a/api/v1/coroot_types.go b/api/v1/coroot_types.go index 20f0c5f..ea9e2cb 100644 --- a/api/v1/coroot_types.go +++ b/api/v1/coroot_types.go @@ -34,8 +34,6 @@ type AgentsOnlySpec struct { CorootURL string `json:"corootURL,omitempty"` // Whether to skip verification of the Coroot server's TLS certificate. TLSSkipVerify bool `json:"tlsSkipVerify,omitempty"` - // Secret containing the CA certificate to verify the Coroot server's certificate. - CASecret *corev1.SecretKeySelector `json:"caSecret,omitempty"` } type AgentTLSSpec struct { diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index ea2a0fb..1e8a99b 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -63,11 +63,6 @@ func (in *AgentTLSSpec) DeepCopy() *AgentTLSSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AgentsOnlySpec) DeepCopyInto(out *AgentsOnlySpec) { *out = *in - if in.CASecret != nil { - in, out := &in.CASecret, &out.CASecret - *out = new(corev1.SecretKeySelector) - (*in).DeepCopyInto(*out) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentsOnlySpec. @@ -830,7 +825,7 @@ func (in *CorootSpec) DeepCopyInto(out *CorootSpec) { if in.AgentsOnly != nil { in, out := &in.AgentsOnly, &out.AgentsOnly *out = new(AgentsOnlySpec) - (*in).DeepCopyInto(*out) + **out = **in } in.Service.DeepCopyInto(&out.Service) if in.Ingress != nil { diff --git a/config/crd/coroot.com_coroots.yaml b/config/crd/coroot.com_coroots.yaml index 816051f..f167ec7 100644 --- a/config/crd/coroot.com_coroots.yaml +++ b/config/crd/coroot.com_coroots.yaml @@ -962,31 +962,6 @@ spec: description: Configures the operator to install only the node-agent and cluster-agent. properties: - caSecret: - description: Secret containing the CA certificate to verify the - Coroot server's certificate. - properties: - key: - description: The key of the secret to select from. Must be - a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must be - defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic corootURL: description: URL of the Coroot instance to which agents send metrics, logs, traces, and profiles. diff --git a/config/crd/coroot.com_coroots_legacy.yaml b/config/crd/coroot.com_coroots_legacy.yaml index 60b96f5..d4e04b3 100644 --- a/config/crd/coroot.com_coroots_legacy.yaml +++ b/config/crd/coroot.com_coroots_legacy.yaml @@ -869,27 +869,6 @@ spec: agentsOnly: description: Configures the operator to install only the node-agent and cluster-agent. properties: - caSecret: - description: Secret containing the CA certificate to verify the Coroot server's certificate. - properties: - key: - description: The key of the secret to select from. Must be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean - required: - - key - type: object corootURL: description: URL of the Coroot instance to which agents send metrics, logs, traces, and profiles. pattern: ^https?://.+$ diff --git a/controller/cluster_agent.go b/controller/cluster_agent.go index 5b9ccd2..5ffd4d7 100644 --- a/controller/cluster_agent.go +++ b/controller/cluster_agent.go @@ -99,15 +99,12 @@ func (r *CorootReconciler) clusterAgentDeployment(cr *corootv1.Coroot) *appsv1.D port = cr.Spec.Service.HTTPSPort } corootURL := fmt.Sprintf("%s://%s-coroot.%s:%d", scheme, cr.Name, cr.Namespace, port) - var tlsSkipVerify bool - var caSecret *corev1.SecretKeySelector if cr.Spec.AgentsOnly != nil && cr.Spec.AgentsOnly.CorootURL != "" { corootURL = cr.Spec.AgentsOnly.CorootURL - tlsSkipVerify = cr.Spec.AgentsOnly.TLSSkipVerify - caSecret = cr.Spec.AgentsOnly.CASecret } + tlsSkipVerify := (cr.Spec.AgentsOnly != nil && cr.Spec.AgentsOnly.TLSSkipVerify) || (cr.Spec.ClusterAgent.TLS != nil && cr.Spec.ClusterAgent.TLS.TLSSkipVerify) + var caSecret *corev1.SecretKeySelector if cr.Spec.ClusterAgent.TLS != nil { - tlsSkipVerify = cr.Spec.ClusterAgent.TLS.TLSSkipVerify caSecret = cr.Spec.ClusterAgent.TLS.CASecret } scrapeInterval := cmp.Or(cr.Spec.MetricsRefreshInterval, corootv1.DefaultMetricRefreshInterval) diff --git a/controller/node_agent.go b/controller/node_agent.go index 312e486..85cac56 100644 --- a/controller/node_agent.go +++ b/controller/node_agent.go @@ -30,15 +30,12 @@ func (r *CorootReconciler) nodeAgentDaemonSet(cr *corootv1.Coroot) *appsv1.Daemo port = cr.Spec.Service.HTTPSPort } corootURL := fmt.Sprintf("%s://%s-coroot.%s:%d", scheme, cr.Name, cr.Namespace, port) - var tlsSkipVerify bool - var caSecret *corev1.SecretKeySelector if cr.Spec.AgentsOnly != nil && cr.Spec.AgentsOnly.CorootURL != "" { corootURL = strings.TrimRight(cr.Spec.AgentsOnly.CorootURL, "/") - tlsSkipVerify = cr.Spec.AgentsOnly.TLSSkipVerify - caSecret = cr.Spec.AgentsOnly.CASecret } + tlsSkipVerify := (cr.Spec.AgentsOnly != nil && cr.Spec.AgentsOnly.TLSSkipVerify) || (cr.Spec.NodeAgent.TLS != nil && cr.Spec.NodeAgent.TLS.TLSSkipVerify) + var caSecret *corev1.SecretKeySelector if cr.Spec.NodeAgent.TLS != nil { - tlsSkipVerify = cr.Spec.NodeAgent.TLS.TLSSkipVerify caSecret = cr.Spec.NodeAgent.TLS.CASecret } scrapeInterval := cmp.Or(cr.Spec.MetricsRefreshInterval, corootv1.DefaultMetricRefreshInterval)