@@ -8,12 +8,13 @@ import (
88 "encoding/json"
99 "errors"
1010 "fmt"
11- "io/ioutil "
11+ "io"
1212 "log"
1313 "math/rand"
1414 "net/http"
1515 "os"
1616 "regexp"
17+ "strconv"
1718 "strings"
1819 "sync"
1920 "text/template"
@@ -36,8 +37,12 @@ const (
3637 acceptedEventsDefault = "repo:refs_changed,pr:declined,pr:merged,pr:deleted"
3738 openShiftAPIHostEnvVar = "OPENSHIFT_API_HOST"
3839 openShiftAPIHostDefault = "openshift.default.svc.cluster.local"
40+ openShiftAppDomainEnvVar = "OPENSHIFT_APP_DOMAIN"
41+ openShiftAppDomainDefault = ".apps.default.ocp.openshift.com"
3942 allowedExternalProjectsEnvVar = "ALLOWED_EXTERNAL_PROJECTS"
4043 allowedExternalProjectsDefault = "opendevstack"
44+ maxDeletionChecksEnvVar = "MAX_DELETION_CHECKS"
45+ maxDeletionChecksDefault = "10"
4146 allowedChangeRefTypesEnvVar = "ALLOWED_CHANGE_REF_TYPES"
4247 allowedChangeRefTypesDefault = "BRANCH"
4348 namespaceSuffix = "-cd"
@@ -77,28 +82,24 @@ type BuildConfigData struct {
7782// buildConfig represents the relevant fields of an OpenShift BuildConfig, see
7883// https://docs.openshift.com/container-platform/3.11/rest_api/apis-build.openshift.io/v1.BuildConfig.html#object-schema.
7984type buildConfig struct {
80- Metadata struct {
81- ResourceVersion string `json:"resourceVersion"`
82- } `json:"metadata"`
83- Spec struct {
84- Source struct {
85- Git struct {
86- Ref string `json:"ref"`
87- } `json:"git"`
88- } `json:"source"`
89- Strategy struct {
90- JenkinsPipelineStrategy struct {
91- JenkinsfilePath string `json:"jenkinsfilePath"`
92- } `json:"jenkinsPipelineStrategy"`
93- } `json:"strategy"`
94- Triggers []struct {
95- Type string `json:"type"`
96- // Generic struct {
97- // Secret string `json:"secret"`
98- // AllowEnv bool `json:"allowEnv"`
99- // } `json:"generic"`
100- } `json:"triggers"`
101- } `json:"spec"`
85+ Metadata struct {
86+ ResourceVersion string `json:"resourceVersion"`
87+ } `json:"metadata"`
88+ Spec struct {
89+ Source struct {
90+ Git struct {
91+ Ref string `json:"ref"`
92+ } `json:"git"`
93+ } `json:"source"`
94+ Strategy struct {
95+ JenkinsPipelineStrategy struct {
96+ JenkinsfilePath string `json:"jenkinsfilePath"`
97+ } `json:"jenkinsPipelineStrategy"`
98+ } `json:"strategy"`
99+ Triggers []struct {
100+ Type string `json:"type"`
101+ } `json:"triggers"`
102+ } `json:"spec"`
102103}
103104
104105// Client makes requests, e.g. to create and delete pipelines, or to forward
@@ -116,6 +117,7 @@ type ocClient struct {
116117 HTTPClient * http.Client
117118 OpenShiftAPIBaseURL string
118119 Token string
120+ OpenShiftAppDomain string
119121}
120122
121123// Server represents this service, and is a global.
@@ -129,10 +131,11 @@ type Server struct {
129131 AllowedExternalProjects []string
130132 AllowedChangeRefTypes []string
131133 RepoBase string
134+ MaxDeletionChecks int
132135}
133136
134137func init () {
135- rand .Seed ( time .Now ().UnixNano ())
138+ rand .New ( rand . NewSource ( time .Now ().UnixNano () ))
136139}
137140
138141func main () {
@@ -193,6 +196,17 @@ func main() {
193196 )
194197 }
195198
199+ openShiftAppDomain := os .Getenv (openShiftAppDomainEnvVar )
200+ if len (openShiftAppDomain ) == 0 {
201+ openShiftAppDomain = openShiftAppDomainDefault
202+ log .Println (
203+ "INFO:" ,
204+ openShiftAppDomainEnvVar ,
205+ "not set, using default value:" ,
206+ openShiftAppDomainDefault ,
207+ )
208+ }
209+
196210 var allowedExternalProjects []string
197211 envAllowedExternalProjects := strings .ToLower (os .Getenv (allowedExternalProjectsEnvVar ))
198212 if len (envAllowedExternalProjects ) == 0 {
@@ -227,7 +241,13 @@ func main() {
227241 }
228242 }
229243
230- client , err := newClient (openShiftAPIHost , triggerSecret )
244+ maxDeletionChecks := maxDeletionChecksDefault
245+ envMaxDeletionChecks := os .Getenv (maxDeletionChecksEnvVar )
246+ if len (envMaxDeletionChecks ) != 0 {
247+ maxDeletionChecks = envMaxDeletionChecks
248+ }
249+
250+ client , err := newClient (openShiftAPIHost , triggerSecret , openShiftAppDomain )
231251 if err != nil {
232252 log .Fatalln (err )
233253 }
@@ -239,6 +259,11 @@ func main() {
239259
240260 project := strings .TrimSuffix (namespace , namespaceSuffix )
241261
262+ maxDeletionChecksInt , err := strconv .Atoi (maxDeletionChecks )
263+ if err != nil {
264+ log .Fatalln ("Invalid max deletion checks value:" , maxDeletionChecks )
265+ }
266+
242267 server := & Server {
243268 Client : client ,
244269 Namespace : namespace ,
@@ -249,6 +274,7 @@ func main() {
249274 AllowedExternalProjects : allowedExternalProjects ,
250275 AllowedChangeRefTypes : allowedChangeRefTypes ,
251276 RepoBase : repoBase ,
277+ MaxDeletionChecks : maxDeletionChecksInt ,
252278 }
253279
254280 log .Println ("Ready to accept requests" )
@@ -579,11 +605,28 @@ func (s *Server) HandleRoot() http.HandlerFunc {
579605 )
580606 return
581607 }
582- err := s .Client .DeletePipeline (event )
583- if err != nil {
584- log .Println (requestID , err )
585- return
608+ for i := 0 ; i < s .MaxDeletionChecks ; i ++ {
609+ err := s .Client .DeletePipeline (event )
610+ if err != nil {
611+ log .Println (requestID , err )
612+ return
613+ }
614+ log .Println (requestID , "Pipeline deleted, checking for remaining instances" )
615+ time .Sleep (5 * time .Second ) // Wait for 5 seconds before checking
616+ exists , _ , err := s .Client .GetPipeline (event )
617+ if err != nil {
618+ log .Println (requestID , "Error checking for remaining instances:" , err )
619+ return
620+ }
621+ if ! exists {
622+ log .Println (requestID , "No remaining instances found" )
623+ return
624+ }
625+ if i == s .MaxDeletionChecks - 1 {
626+ log .Println (requestID , "Reached maximum iterations, stopping checks" )
627+ }
586628 }
629+
587630 } else {
588631 log .Println (requestID , "Unrecognized event" )
589632 }
@@ -636,7 +679,7 @@ func (c *ocClient) Forward(e *Event, triggerSecret string) (int, []byte, error)
636679 }
637680 defer res .Body .Close ()
638681
639- body , err := ioutil .ReadAll (res .Body )
682+ body , err := io .ReadAll (res .Body )
640683 return res .StatusCode , body , err
641684}
642685
@@ -666,7 +709,7 @@ func (c *ocClient) CreateOrUpdatePipeline(exists bool, tmpl *template.Template,
666709 }
667710 defer res .Body .Close ()
668711
669- body , err := ioutil .ReadAll (res .Body )
712+ body , err := io .ReadAll (res .Body )
670713 if err != nil {
671714 return 500 , fmt .Errorf ("could not read OpenShift response body: %s" , err )
672715 }
@@ -683,6 +726,7 @@ func (c *ocClient) CreateOrUpdatePipeline(exists bool, tmpl *template.Template,
683726// DeletePipeline removes the pipeline corresponding to the event from
684727// OpenShift.
685728func (c * ocClient ) DeletePipeline (e * Event ) error {
729+ // Delete OpenShift BuildConfig
686730 url := fmt .Sprintf (
687731 "%s/namespaces/%s/buildconfigs/%s?propagationPolicy=Foreground" ,
688732 c .OpenShiftAPIBaseURL ,
@@ -701,13 +745,43 @@ func (c *ocClient) DeletePipeline(e *Event) error {
701745 }
702746 defer res .Body .Close ()
703747
704- body , _ := ioutil .ReadAll (res .Body )
748+ body , _ := io .ReadAll (res .Body )
705749
706750 if res .StatusCode < 200 || res .StatusCode >= 300 {
707751 return errors .New (string (body ))
708752 }
709753
710- log .Println (e .RequestID , "Deleted pipeline" , e .Pipeline )
754+ log .Println (e .RequestID , "Deleted Openshift pipeline" , e .Pipeline )
755+
756+ // Delete Jenkins pipeline
757+ jenkinsURL := fmt .Sprintf (
758+ "https://jenkins-%s%s/job/%s/job/%s-%s/doDelete" ,
759+ e .Namespace ,
760+ c .OpenShiftAppDomain ,
761+ e .Namespace ,
762+ e .Namespace ,
763+ e .Pipeline ,
764+ )
765+
766+ jenkinsReq , _ := http .NewRequest (
767+ "POST" ,
768+ jenkinsURL ,
769+ nil ,
770+ )
771+ jenkinsReq .Header .Set ("Authorization" , "Bearer " + c .Token )
772+ jenkinsRes , err := c .do (jenkinsReq )
773+ if err != nil {
774+ return fmt .Errorf ("could not make Jenkins request: %s" , err )
775+ }
776+ defer jenkinsRes .Body .Close ()
777+
778+ jenkinsBody , _ := io .ReadAll (jenkinsRes .Body )
779+
780+ if jenkinsRes .StatusCode < 200 || jenkinsRes .StatusCode >= 300 {
781+ return errors .New (string (jenkinsBody ))
782+ }
783+
784+ log .Println (e .RequestID , "Deleted Jenkins pipeline" , e .Pipeline )
711785
712786 return nil
713787}
@@ -786,7 +860,7 @@ func (c *ocClient) GetPipeline(e *Event) (bool, []byte, error) {
786860 return false , nil , nil
787861 }
788862
789- body , err := ioutil .ReadAll (res .Body )
863+ body , err := io .ReadAll (res .Body )
790864 if err != nil {
791865 return false , nil , fmt .Errorf ("could not read OpenShift response: %s" , err )
792866 }
@@ -826,7 +900,7 @@ func (e *Event) String() string {
826900 )
827901}
828902
829- func newClient (openShiftAPIHost string , triggerSecret string ) (* ocClient , error ) {
903+ func newClient (openShiftAPIHost string , triggerSecret string , openShiftAppDomain string ) (* ocClient , error ) {
830904 token , err := getFileContent (tokenFile )
831905 if err != nil {
832906 return nil , fmt .Errorf ("Could not get token: %s" , err )
@@ -847,6 +921,7 @@ func newClient(openShiftAPIHost string, triggerSecret string) (*ocClient, error)
847921 HTTPClient : secureClient ,
848922 OpenShiftAPIBaseURL : baseURL ,
849923 Token : token ,
924+ OpenShiftAppDomain : openShiftAppDomain ,
850925 }, nil
851926}
852927
@@ -861,7 +936,7 @@ func getBuildConfig(tmpl *template.Template, data BuildConfigData) (*bytes.Buffe
861936
862937func getSecureClient () (* http.Client , error ) {
863938 // Load CA cert
864- caCert , err := ioutil .ReadFile (caCert )
939+ caCert , err := os .ReadFile (caCert )
865940 if err != nil {
866941 return nil , err
867942 }
@@ -883,7 +958,7 @@ func getSecureClient() (*http.Client, error) {
883958}
884959
885960func getFileContent (filename string ) (string , error ) {
886- content , err := ioutil .ReadFile (filename )
961+ content , err := os .ReadFile (filename )
887962 if err != nil {
888963 return "" , err
889964 }
0 commit comments