@@ -97,6 +97,133 @@ def notifyProvisionRoks(channels: list[str], rc: int, additionalMsg: str | None
9797 return response .data .get ("ok" , False )
9898
9999
100+ def notifyAnsibleComplete (channels : list [str ], rc : int , taskName : str , pipelineName : str , instanceId : str | None = None ) -> bool :
101+ """Send Slack notification about Ansible task completion status."""
102+ namespace = os .getenv ("PIPELINE_NAMESPACE" , "" )
103+ pipelineRunName = os .getenv ("PIPELINERUN_NAME" , "" )
104+
105+ if namespace == "" or pipelineRunName == "" :
106+ print ("PIPELINE_NAMESPACE and PIPELINERUN_NAME env vars must be set" )
107+ sys .exit (1 )
108+
109+ # Check if this is the first task (no ConfigMap exists yet)
110+ threadInfo = SlackUtil .getThreadConfigMap (namespace , pipelineRunName )
111+
112+ if threadInfo is None :
113+ # This is the first task - send pipeline started message
114+ toolchainLink = _getToolchainLink ()
115+ instanceInfo = f"\n Instance ID: `{ instanceId } `" if instanceId else ""
116+
117+ message = [
118+ SlackUtil .buildHeader (f"🚀 MAS { pipelineName .title ()} Pipeline Started" ),
119+ SlackUtil .buildSection (f"Pipeline Run: `{ pipelineRunName } `{ instanceInfo } \n { toolchainLink } " )
120+ ]
121+
122+ response = SlackUtil .postMessageBlocks (channels [0 ], message )
123+
124+ if response .data .get ("ok" , False ):
125+ threadId = response ["ts" ]
126+ channelId = response ["channel" ]
127+
128+ # Store thread information in ConfigMap
129+ SlackUtil .createThreadConfigMap (namespace , pipelineRunName , channelId , threadId , pipelineName )
130+ threadInfo = {
131+ "threadId" : threadId ,
132+ "channelId" : channelId ,
133+ "pipelineName" : pipelineName
134+ }
135+ else :
136+ print ("Failed to send initial Slack message" )
137+ return False
138+
139+ # Send task completion message as thread reply
140+ threadId = threadInfo .get ("threadId" )
141+ channelId = threadInfo .get ("channelId" )
142+
143+ if rc == 0 :
144+ emoji = "✅"
145+ status = "Success"
146+ else :
147+ emoji = "❌"
148+ status = "Failed"
149+
150+ taskMessage = [
151+ SlackUtil .buildSection (f"{ emoji } **{ taskName } ** - { status } " )
152+ ]
153+
154+ if rc != 0 :
155+ taskMessage .append (SlackUtil .buildSection (f"Return Code: `{ rc } `\n Check logs for details" ))
156+
157+ response = SlackUtil .postMessageBlocks (channelId , taskMessage , threadId )
158+
159+ if isinstance (response , list ):
160+ return all ([res .data .get ("ok" , False ) for res in response ])
161+ return response .data .get ("ok" , False )
162+
163+
164+ def notifyPipelineComplete (channels : list [str ], rc : int , pipelineName : str , instanceId : str | None = None ) -> bool :
165+ """Send Slack notification about pipeline completion and cleanup ConfigMap."""
166+ namespace = os .getenv ("PIPELINE_NAMESPACE" , "" )
167+ pipelineRunName = os .getenv ("PIPELINERUN_NAME" , "" )
168+
169+ if namespace == "" or pipelineRunName == "" :
170+ print ("PIPELINE_NAMESPACE and PIPELINERUN_NAME env vars must be set" )
171+ sys .exit (1 )
172+
173+ # Get thread information
174+ threadInfo = SlackUtil .getThreadConfigMap (namespace , pipelineRunName )
175+
176+ if threadInfo is None :
177+ print ("No thread information found - pipeline may not have started properly" )
178+ return False
179+
180+ threadId = threadInfo .get ("threadId" )
181+ channelId = threadInfo .get ("channelId" )
182+ startTime = threadInfo .get ("startTime" )
183+
184+ # Calculate duration if start time is available
185+ durationText = ""
186+ if startTime :
187+ from datetime import datetime
188+ try :
189+ start = datetime .fromisoformat (startTime .replace ("Z" , "+00:00" ))
190+ end = datetime .utcnow ()
191+ duration = end - start
192+ hours , remainder = divmod (int (duration .total_seconds ()), 3600 )
193+ minutes , seconds = divmod (remainder , 60 )
194+ if hours > 0 :
195+ durationText = f"\n Total Duration: { hours } h { minutes } m { seconds } s"
196+ else :
197+ durationText = f"\n Total Duration: { minutes } m { seconds } s"
198+ except :
199+ pass
200+
201+ instanceInfo = f"\n Instance ID: `{ instanceId } `" if instanceId else ""
202+
203+ if rc == 0 :
204+ emoji = "🎉"
205+ status = "Completed Successfully"
206+ additionalInfo = "\n All tasks completed successfully"
207+ else :
208+ emoji = "💥"
209+ status = "Failed"
210+ additionalInfo = f"\n Pipeline failed with return code: `{ rc } `"
211+
212+ message = [
213+ SlackUtil .buildHeader (f"{ emoji } MAS { pipelineName .title ()} Pipeline { status } " ),
214+ SlackUtil .buildSection (f"Pipeline Run: `{ pipelineRunName } `{ instanceInfo } { durationText } { additionalInfo } " )
215+ ]
216+
217+ response = SlackUtil .postMessageBlocks (channelId , message , threadId )
218+
219+ # Clean up ConfigMap
220+ SlackUtil .deleteThreadConfigMap (namespace , pipelineRunName )
221+
222+ if isinstance (response , list ):
223+ return all ([res .data .get ("ok" , False ) for res in response ])
224+ return response .data .get ("ok" , False )
225+
226+
100227if __name__ == "__main__" :
101228 # If SLACK_TOKEN or SLACK_CHANNEL env vars are not set then silently exit taking no action
102229 SLACK_TOKEN = os .getenv ("SLACK_TOKEN" , "" )
@@ -114,10 +241,17 @@ if __name__ == "__main__":
114241 parser .add_argument ("--action" , required = True )
115242 parser .add_argument ("--rc" , required = True , type = int )
116243 parser .add_argument ("--msg" , required = False , default = None )
244+ parser .add_argument ("--task-name" , required = False , default = "" )
245+ parser .add_argument ("--pipeline-name" , required = False , default = "" )
246+ parser .add_argument ("--instance-id" , required = False , default = None )
117247
118248 args , unknown = parser .parse_known_args ()
119249
120250 if args .action == "ocp-provision-fyre" :
121251 notifyProvisionFyre (channelList , args .rc , args .msg )
122252 elif args .action == "ocp-provision-roks" :
123253 notifyProvisionRoks (channelList , args .rc , args .msg )
254+ elif args .action == "ansible-complete" :
255+ notifyAnsibleComplete (channelList , args .rc , args .task_name , args .pipeline_name , args .instance_id )
256+ elif args .action == "pipeline-complete" :
257+ notifyPipelineComplete (channelList , args .rc , args .pipeline_name , args .instance_id )
0 commit comments