@@ -13,21 +13,21 @@ import (
1313 "log/slog"
1414 "net/http"
1515 "net/url"
16+ "os"
1617 "time"
1718
19+ "github.com/openshift-kni/oran-o2ims/internal/constants"
20+ ctlrutils "github.com/openshift-kni/oran-o2ims/internal/controllers/utils"
1821 "github.com/openshift-kni/oran-o2ims/internal/service/alarms/internal/db/repo"
1922 "github.com/openshift-kni/oran-o2ims/internal/service/alarms/internal/infrastructure"
20- corev1 "k8s.io/api/core/v1"
21- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
22- "k8s.io/apimachinery/pkg/runtime/schema"
2323 "sigs.k8s.io/controller-runtime/pkg/client"
2424)
2525
2626const (
27- // ACMObsAMRouteName route to collect ACM's AM API host
28- ACMObsAMRouteName = "alertmanager"
29- // ACMObsAMAuthSecretName secret to collect ACM's AM API BEARER and ca.Crt
30- ACMObsAMAuthSecretName = "observability-alertmanager-accessor-token" // nolint: gosec
27+ // ACMObsAMServiceName is the alertmanager service name in ACM observability
28+ ACMObsAMServiceName = "alertmanager"
29+ // ACMObsAMServicePort is the HTTPS port for alertmanager service
30+ ACMObsAMServicePort = 9095
3131)
3232
3333// APIAlert represents the alert structure returned by the Alertmanager API.
@@ -110,16 +110,21 @@ func (c *AMClient) SyncAlerts(ctx context.Context) error {
110110
111111// getAlerts retrieves all alerts from Alertmanager API
112112func (c * AMClient ) getAlerts (ctx context.Context ) ([]APIAlert , error ) {
113- // Get the alertmanager route
114- alertmanagerHost , err := c .GetAlertmanagerRoute ( ctx )
113+ // Initialize a new client each time to pick up the latest token
114+ httpClient , token , err := c .createAlertmanagerClient ( )
115115 if err != nil {
116- return nil , fmt .Errorf ("failed to get alertmanager route : %w" , err )
116+ return nil , fmt .Errorf ("failed to create alertmanager client : %w" , err )
117117 }
118118
119- // Initialize a new client each time to pick up the latest secrets
120- httpClient , token , err := c .createAlertmanagerClient (ctx )
121- if err != nil {
122- return nil , fmt .Errorf ("failed to create alertmanager client: %w" , err )
119+ // Build service URL for alertmanager
120+ // Format: alertmanager.open-cluster-management-observability.svc:9095
121+ // Allow override for testing
122+ alertmanagerHost := os .Getenv ("ALARMS_SERVER_AM_HOST" )
123+ if alertmanagerHost == "" {
124+ alertmanagerHost = fmt .Sprintf ("%s.%s.svc:%d" ,
125+ ACMObsAMServiceName ,
126+ ctlrutils .OpenClusterManagementObservabilityNamespace ,
127+ ACMObsAMServicePort )
123128 }
124129
125130 // Create request
@@ -138,7 +143,7 @@ func (c *AMClient) getAlerts(ctx context.Context) ([]APIAlert, error) {
138143 q .Set ("silenced" , "true" )
139144
140145 u .RawQuery = q .Encode ()
141- req , err := http .NewRequest ( http .MethodGet , u .String (), nil )
146+ req , err := http .NewRequestWithContext ( ctx , http .MethodGet , u .String (), nil )
142147 if err != nil {
143148 return nil , fmt .Errorf ("error creating request: %w" , err )
144149 }
@@ -179,78 +184,32 @@ func (c *AMClient) getAlerts(ctx context.Context) ([]APIAlert, error) {
179184 return alerts , nil
180185}
181186
182- // GetAlertmanagerRoute gets the hostname for the alertmanager route using unstructured type
183- func (c * AMClient ) GetAlertmanagerRoute (ctx context.Context ) (string , error ) {
184- // Define the route GVK (Group, Version, Kind)
185- routeGVK := schema.GroupVersionKind {
186- Group : "route.openshift.io" ,
187- Version : "v1" ,
188- Kind : "Route" ,
189- }
190-
191- // Create an unstructured object
192- route := & unstructured.Unstructured {}
193- route .SetGroupVersionKind (routeGVK )
194-
195- // Get the route
196- if err := c .k8sClient .Get (ctx , client.ObjectKey {
197- Namespace : ACMObsAMNamespace ,
198- Name : ACMObsAMRouteName ,
199- }, route ); err != nil {
200- return "" , fmt .Errorf ("error getting '%s' route: %w" , ACMObsAMRouteName , err )
201- }
202-
203- // Try to get host from spec
204- specHost , found , err := unstructured .NestedString (route .Object , "spec" , "host" )
205- if err != nil {
206- return "" , fmt .Errorf ("error accessing spec.host: %w" , err )
207- }
208- if found && specHost != "" {
209- return specHost , nil
187+ // createAlertmanagerClient creates a new HTTP client with the service account token and service CA certificate.
188+ // The token comes from the pod's service account and CA cert from the service CA file.
189+ func (c * AMClient ) createAlertmanagerClient () (* http.Client , string , error ) {
190+ // Determine token file path (allow override for testing/local development)
191+ tokenPath := constants .DefaultBackendTokenFile
192+ if envPath := os .Getenv ("ALARMS_SERVER_TOKEN_FILE" ); envPath != "" {
193+ tokenPath = envPath
210194 }
211195
212- // Fallback to status if spec host is empty
213- statusIngress , found , err := unstructured . NestedSlice ( route . Object , "status" , "ingress" )
196+ // Read token from service account token file
197+ token , err := os . ReadFile ( tokenPath )
214198 if err != nil {
215- return "" , fmt .Errorf ("error accessing status.ingress : %w" , err )
199+ return nil , "" , fmt .Errorf ("error reading service account token : %w" , err )
216200 }
217201
218- if found && len (statusIngress ) > 0 {
219- ingressMap , ok := statusIngress [0 ].(map [string ]interface {})
220- if ! ok {
221- return "" , fmt .Errorf ("invalid ingress format" )
222- }
223-
224- host , found := ingressMap ["host" ]
225- if found && host != "" {
226- return host .(string ), nil
227- }
228- }
229-
230- return "" , fmt .Errorf ("no host found in alertmanager route" )
231- }
232-
233- // createAlertmanagerClient creates a new HTTP client with the latest token
234- // TODO: we can be more robust by reading our `/var/run/secrets/kubernetes.io/serviceaccount/token` for token instead of relying on ACMObsAMAuthSecretName data.
235- func (c * AMClient ) createAlertmanagerClient (ctx context.Context ) (* http.Client , string , error ) {
236- var secret corev1.Secret
237- if err := c .k8sClient .Get (ctx , client.ObjectKey {
238- Namespace : ACMObsAMNamespace ,
239- Name : ACMObsAMAuthSecretName ,
240- }, & secret ); err != nil {
241- return nil , "" , fmt .Errorf ("error getting token secret from '%s': %w" , ACMObsAMAuthSecretName , err )
202+ // Determine service CA file path (allow override for testing/local development)
203+ caPath := constants .DefaultServiceCAFile
204+ if envPath := os .Getenv ("ALARMS_SERVER_CA_FILE" ); envPath != "" {
205+ caPath = envPath
242206 }
243207
244- // Extract token
245- token , ok := secret .Data ["token" ]
246- if ! ok {
247- return nil , "" , fmt .Errorf ("token not found in secret" )
248- }
249-
250- // Extract CA certificate
251- caCrt , ok := secret .Data ["ca.crt" ]
252- if ! ok {
253- return nil , "" , fmt .Errorf ("ca.crt not found in secret" )
208+ // Read service CA certificate
209+ // This is automatically mounted by Kubernetes for service-to-service TLS
210+ caCrt , err := os .ReadFile (caPath )
211+ if err != nil {
212+ return nil , "" , fmt .Errorf ("error reading service CA certificate: %w" , err )
254213 }
255214
256215 // Create certificate pool with the CA cert
0 commit comments