@@ -98,7 +98,7 @@ def notifyProvisionRoks(channels: list[str], rc: int, additionalMsg: str | None
9898
9999
100100def notifyPipelineStart (channels : list [str ], instanceId : str | None = None , pipelineName : str | None = None ) -> dict | None :
101- """Send Slack notification about pipeline start and create thread."""
101+ """Send Slack notification about pipeline start and create thread for all channels ."""
102102 namespace = f"mas-{ instanceId } -pipelines"
103103 if instanceId is None or instanceId == "" :
104104 print ("instanceId must be set" )
@@ -110,27 +110,48 @@ def notifyPipelineStart(channels: list[str], instanceId: str | None = None, pipe
110110 print ("Pipeline start notification already sent" )
111111 return threadInfo
112112
113- # Send pipeline started message
113+ # Send pipeline started message to all channels
114114 toolchainLink = _getToolchainLink ()
115115 instanceInfo = f"\n Instance ID: `{ instanceId } `" if instanceId else ""
116116 message = [
117117 SlackUtil .buildHeader (f"🚀 MAS { pipelineName } Pipeline Started" ),
118118 SlackUtil .buildSection (f"Pipeline Run: { instanceInfo } \n { toolchainLink } " )
119119 ]
120- response = SlackUtil .postMessageBlocks (channels [0 ], message )
121- if response .data .get ("ok" , False ):
122- threadId = response ["ts" ]
123- channelId = response ["channel" ]
124- # Store thread information in ConfigMap
125- SlackUtil .createThreadConfigMap (namespace , channelId , threadId , instanceId )
126- return SlackUtil .getThreadConfigMap (namespace , instanceId )
120+ response = SlackUtil .postMessageBlocks (channels , message )
121+
122+ # Store thread information for all channels in ConfigMap
123+ configMapData = {"instanceId" : instanceId }
124+
125+ if isinstance (response , list ):
126+ # Multiple channels - store each channel's thread info
127+ for idx , res in enumerate (response ):
128+ if res .data .get ("ok" , False ):
129+ threadId = res ["ts" ]
130+ channelId = res ["channel" ]
131+ # Store with channel-specific keys
132+ configMapData [f"channel_{ idx } " ] = channelId
133+ configMapData [f"threadId_{ idx } " ] = threadId
134+ configMapData ["channel_count" ] = str (len (response ))
127135 else :
128- print ("Failed to send pipeline start Slack message" )
129- return False
136+ # Single channel
137+ if response .data .get ("ok" , False ):
138+ threadId = response ["ts" ]
139+ channelId = response ["channel" ]
140+ configMapData ["channel_0" ] = channelId
141+ configMapData ["threadId_0" ] = threadId
142+ configMapData ["channel_count" ] = "1"
143+ else :
144+ print ("Failed to send pipeline start Slack message" )
145+ return False
146+
147+ # Create ConfigMap with all channel/thread info
148+ SlackUtil .createThreadConfigMap (namespace , "" , "" , instanceId )
149+ SlackUtil .updateThreadConfigMap (namespace , instanceId , configMapData )
150+ return SlackUtil .getThreadConfigMap (namespace , instanceId )
130151
131152
132153def notifyAnsibleStart (channels : list [str ], taskName : str , instanceId : str | None = None , pipelineName : str | None = None ) -> bool :
133- """Send Slack notification about Ansible task start."""
154+ """Send Slack notification about Ansible task start to all channels ."""
134155 namespace = f"mas-{ instanceId } -pipelines"
135156 if instanceId is None or instanceId == "" :
136157 print ("instanceId must be set" )
@@ -141,29 +162,48 @@ def notifyAnsibleStart(channels: list[str], taskName: str, instanceId: str | Non
141162 if threadInfo is None :
142163 print ("No thread found - creating pipeline start notification" )
143164 threadInfo = notifyPipelineStart (channels , instanceId , pipelineName )
144- threadId = threadInfo .get ("threadId" )
145- channelId = threadInfo .get ("channelId" )
146165
147- # Send task start message as thread reply
166+ # Get channel count
167+ channelCount = int (threadInfo .get ("channel_count" , "0" ))
168+ if channelCount == 0 :
169+ print ("No channels found in thread info" )
170+ return False
171+
172+ # Send task start message as thread reply to all channels
148173 taskMessage = [
149174 SlackUtil .buildSection (f"⏳ **{ taskName } ** - Started" )
150175 ]
151- response = SlackUtil .postMessageBlocks (channelId , taskMessage , threadId )
152176
153- # Save message timestamp in ConfigMap for later editing
154- if response .data .get ("ok" , False ):
155- messageTs = response .data .get ("ts" )
156- if messageTs :
157- # Store with task name as key
158- SlackUtil .updateThreadConfigMap (namespace , instanceId , {f"task_{ taskName } " : messageTs })
177+ allSuccess = True
178+ taskMessageData = {}
159179
160- if isinstance (response , list ):
161- return all ([res .data .get ("ok" , False ) for res in response ])
162- return response .data .get ("ok" , False )
180+ for idx in range (channelCount ):
181+ channelId = threadInfo .get (f"channel_{ idx } " )
182+ threadId = threadInfo .get (f"threadId_{ idx } " )
183+
184+ if channelId and threadId :
185+ response = SlackUtil .postMessageBlocks (channelId , taskMessage , threadId )
186+
187+ # Save message timestamp for this channel
188+ if response .data .get ("ok" , False ):
189+ messageTs = response .data .get ("ts" )
190+ if messageTs :
191+ # Store with task name and channel index as key
192+ taskMessageData [f"task_{ taskName } _{ idx } " ] = messageTs
193+ else :
194+ allSuccess = False
195+ else :
196+ allSuccess = False
197+
198+ # Update ConfigMap with all task message timestamps
199+ if taskMessageData :
200+ SlackUtil .updateThreadConfigMap (namespace , instanceId , taskMessageData )
201+
202+ return allSuccess
163203
164204
165205def notifyAnsibleComplete (channels : list [str ], rc : int , taskName : str , instanceId : str | None = None , pipelineName : str | None = None ) -> bool :
166- """Send Slack notification about Ansible task completion status."""
206+ """Send Slack notification about Ansible task completion status to all channels ."""
167207 namespace = f"mas-{ instanceId } -pipelines"
168208 if instanceId is None or instanceId == "" :
169209 print ("instanceId must be set" )
@@ -175,11 +215,11 @@ def notifyAnsibleComplete(channels: list[str], rc: int, taskName: str, instanceI
175215 print ("No thread found - creating pipeline start notification" )
176216 threadInfo = notifyPipelineStart (channels , instanceId , pipelineName )
177217
178- threadId = threadInfo . get ( "threadId" )
179- channelId = threadInfo .get ("channelId" )
180-
181- # Get the message timestamp for this task
182- taskMessageTs = threadInfo . get ( f"task_ { taskName } " )
218+ # Get channel count
219+ channelCount = int ( threadInfo .get ("channel_count" , "0" ) )
220+ if channelCount == 0 :
221+ print ( "No channels found in thread info" )
222+ return False
183223
184224 # Determine status
185225 if rc == 0 :
@@ -189,62 +229,83 @@ def notifyAnsibleComplete(channels: list[str], rc: int, taskName: str, instanceI
189229 emoji = "❌"
190230 status = "Failed"
191231
192- # Calculate task duration if we have the message timestamp
193- durationText = ""
194- if taskMessageTs :
195- from datetime import datetime
196- try :
197- # Message timestamp is in format "1234567890.123456"
198- startTime = float (taskMessageTs )
199- endTime = datetime .utcnow ().timestamp ()
200- duration = int (endTime - startTime )
201-
202- hours , remainder = divmod (duration , 3600 )
203- minutes , seconds = divmod (remainder , 60 )
204-
205- if hours > 0 :
206- durationText = f" ({ hours } h { minutes } m { seconds } s)"
207- elif minutes > 0 :
208- durationText = f" ({ minutes } m { seconds } s)"
209- else :
210- durationText = f" ({ seconds } s)"
211- except Exception as e :
212- print (f"Failed to calculate duration: { e } " )
232+ allSuccess = True
233+
234+ # Update message in each channel
235+ for idx in range (channelCount ):
236+ channelId = threadInfo .get (f"channel_{ idx } " )
237+ threadId = threadInfo .get (f"threadId_{ idx } " )
238+ taskMessageTs = threadInfo .get (f"task_{ taskName } _{ idx } " )
239+
240+ if not channelId or not threadId :
241+ allSuccess = False
242+ continue
243+
244+ # Calculate task duration if we have the message timestamp
245+ durationText = ""
246+ if taskMessageTs :
247+ from datetime import datetime
248+ try :
249+ # Message timestamp is in format "1234567890.123456"
250+ startTime = float (taskMessageTs )
251+ endTime = datetime .utcnow ().timestamp ()
252+ duration = int (endTime - startTime )
253+
254+ hours , remainder = divmod (duration , 3600 )
255+ minutes , seconds = divmod (remainder , 60 )
256+
257+ if hours > 0 :
258+ durationText = f" ({ hours } h { minutes } m { seconds } s)"
259+ elif minutes > 0 :
260+ durationText = f" ({ minutes } m { seconds } s)"
261+ else :
262+ durationText = f" ({ seconds } s)"
263+ except Exception as e :
264+ print (f"Failed to calculate duration for channel { idx } : { e } " )
265+
266+ # Build the completion message
267+ taskMessage = [
268+ SlackUtil .buildSection (f"{ emoji } **{ taskName } ** - { status } { durationText } " )
269+ ]
270+ if rc != 0 :
271+ taskMessage .append (SlackUtil .buildSection (f"Return Code: `{ rc } `\n Check logs for details" ))
213272
214- # Build the completion message
215- taskMessage = [
216- SlackUtil .buildSection (f"{ emoji } **{ taskName } ** - { status } { durationText } " )
217- ]
218- if rc != 0 :
219- taskMessage .append (SlackUtil .buildSection (f"Return Code: `{ rc } `\n Check logs for details" ))
273+ # If we have the original message timestamp, update it; otherwise post new message
274+ if taskMessageTs :
275+ response = SlackUtil .updateMessageBlocks (channelId , taskMessageTs , taskMessage )
276+ if not response .data .get ("ok" , False ):
277+ allSuccess = False
278+ else :
279+ # Fallback: post new message if task start message wasn't tracked
280+ print (f"No start message found for task { taskName } in channel { idx } , posting new completion message" )
281+ response = SlackUtil .postMessageBlocks (channelId , taskMessage , threadId )
282+ if not response .data .get ("ok" , False ):
283+ allSuccess = False
220284
221- # If we have the original message timestamp, update it; otherwise post new message
222- if taskMessageTs :
223- response = SlackUtil .updateMessageBlocks (channelId , taskMessageTs , taskMessage )
224- return response .data .get ("ok" , False )
225- else :
226- # Fallback: post new message if task start message wasn't tracked
227- print (f"No start message found for task { taskName } , posting new completion message" )
228- response = SlackUtil .postMessageBlocks (channelId , taskMessage , threadId )
229- if isinstance (response , list ):
230- return all ([res .data .get ("ok" , False ) for res in response ])
231- return response .data .get ("ok" , False )
285+ return allSuccess
232286
233287
234288def notifyPipelineComplete (channels : list [str ], rc : int , instanceId : str | None = None , pipelineName : str | None = None ) -> bool :
235- """Send Slack notification about pipeline completion and cleanup ConfigMap."""
289+ """Send Slack notification about pipeline completion to all channels and cleanup ConfigMap."""
236290 namespace = f"mas-{ instanceId } -pipelines"
237291 if instanceId is None or instanceId == "" :
238292 print ("instanceId must be set" )
239293 sys .exit (1 )
294+
240295 # Get thread information
241296 threadInfo = SlackUtil .getThreadConfigMap (namespace , instanceId )
242297 if threadInfo is None :
243298 print ("No thread information found - pipeline may not have started properly" )
244299 return False
245- threadId = threadInfo .get ("threadId" )
246- channelId = threadInfo .get ("channelId" )
300+
301+ # Get channel count
302+ channelCount = int (threadInfo .get ("channel_count" , "0" ))
303+ if channelCount == 0 :
304+ print ("No channels found in thread info" )
305+ return False
306+
247307 startTime = threadInfo .get ("startTime" )
308+
248309 # Calculate duration if start time is available
249310 durationText = ""
250311 if startTime :
@@ -261,6 +322,7 @@ def notifyPipelineComplete(channels: list[str], rc: int, instanceId: str | None
261322 durationText = f"\n Total Duration: { minutes } m { seconds } s"
262323 except Exception :
263324 pass
325+
264326 instanceInfo = f"\n Instance ID: `{ instanceId } `" if instanceId else ""
265327 if rc == 0 :
266328 emoji = "🎉"
@@ -270,16 +332,30 @@ def notifyPipelineComplete(channels: list[str], rc: int, instanceId: str | None
270332 emoji = "💥"
271333 status = "Failed"
272334 additionalInfo = f"\n Pipeline failed with return code: `{ rc } `"
335+
273336 message = [
274337 SlackUtil .buildHeader (f"{ emoji } MAS { pipelineName } Pipeline { status } " ),
275338 SlackUtil .buildSection (f"Pipeline Run: { instanceInfo } { durationText } { additionalInfo } " )
276339 ]
277- response = SlackUtil .postMessageBlocks (channelId , message , threadId )
340+
341+ allSuccess = True
342+
343+ # Send completion message to all channels
344+ for idx in range (channelCount ):
345+ channelId = threadInfo .get (f"channel_{ idx } " )
346+ threadId = threadInfo .get (f"threadId_{ idx } " )
347+
348+ if channelId and threadId :
349+ response = SlackUtil .postMessageBlocks (channelId , message , threadId )
350+ if not response .data .get ("ok" , False ):
351+ allSuccess = False
352+ else :
353+ allSuccess = False
354+
278355 # Clean up ConfigMap
279356 SlackUtil .deleteThreadConfigMap (namespace , instanceId )
280- if isinstance (response , list ):
281- return all ([res .data .get ("ok" , False ) for res in response ])
282- return response .data .get ("ok" , False )
357+
358+ return allSuccess
283359
284360
285361if __name__ == "__main__" :
0 commit comments