Merge pull request #65 from jakalkhalili/v0.2.0
Add notification library
This commit is contained in:
		
						commit
						3c2ff79c30
					
				
							
								
								
									
										4
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										4
									
								
								go.mod
								
								
								
								
							| 
						 | 
					@ -9,6 +9,8 @@ require (
 | 
				
			||||||
	github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
 | 
						github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
 | 
				
			||||||
	github.com/docker/distribution v2.7.1+incompatible
 | 
						github.com/docker/distribution v2.7.1+incompatible
 | 
				
			||||||
	github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
 | 
						github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
 | 
				
			||||||
 | 
						github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2 // indirect
 | 
				
			||||||
 | 
						github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 // indirect
 | 
				
			||||||
	github.com/emicklei/go-restful v2.8.1+incompatible // indirect
 | 
						github.com/emicklei/go-restful v2.8.1+incompatible // indirect
 | 
				
			||||||
	github.com/go-logr/logr v0.1.0
 | 
						github.com/go-logr/logr v0.1.0
 | 
				
			||||||
	github.com/go-logr/zapr v0.1.0
 | 
						github.com/go-logr/zapr v0.1.0
 | 
				
			||||||
| 
						 | 
					@ -22,12 +24,14 @@ require (
 | 
				
			||||||
	github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
 | 
						github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
 | 
				
			||||||
	github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect
 | 
						github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect
 | 
				
			||||||
	github.com/imdario/mergo v0.3.6 // indirect
 | 
						github.com/imdario/mergo v0.3.6 // indirect
 | 
				
			||||||
 | 
						github.com/mailgun/mailgun-go/v3 v3.6.0
 | 
				
			||||||
	github.com/mitchellh/go-homedir v1.1.0 // indirect
 | 
						github.com/mitchellh/go-homedir v1.1.0 // indirect
 | 
				
			||||||
	github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
 | 
						github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
 | 
				
			||||||
	github.com/operator-framework/operator-sdk v0.8.2-0.20190522220659-031d71ef8154
 | 
						github.com/operator-framework/operator-sdk v0.8.2-0.20190522220659-031d71ef8154
 | 
				
			||||||
	github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect
 | 
						github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect
 | 
				
			||||||
	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
 | 
						github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.8.1
 | 
						github.com/pkg/errors v0.8.1
 | 
				
			||||||
 | 
						github.com/sergi/go-diff v1.0.0 // indirect
 | 
				
			||||||
	github.com/spf13/pflag v1.0.3
 | 
						github.com/spf13/pflag v1.0.3
 | 
				
			||||||
	github.com/stretchr/testify v1.3.0
 | 
						github.com/stretchr/testify v1.3.0
 | 
				
			||||||
	go.opencensus.io v0.19.2 // indirect
 | 
						go.opencensus.io v0.19.2 // indirect
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										31
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										31
									
								
								go.sum
								
								
								
								
							| 
						 | 
					@ -40,13 +40,26 @@ github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZ
 | 
				
			||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 | 
					github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 | 
				
			||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 | 
					github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 | 
				
			||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 | 
					github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 | 
				
			||||||
 | 
					github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2 h1:aZtFdDNWY/yH86JPR2WX/PN63635VsE/f/nXNPAbYxY=
 | 
				
			||||||
 | 
					github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 | 
				
			||||||
 | 
					github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
 | 
				
			||||||
 | 
					github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
 | 
				
			||||||
github.com/emicklei/go-restful v2.8.1+incompatible h1:AyDqLHbJ1quqbWr/OWDw+PlIP8ZFoTmYrGYaxzrLbNg=
 | 
					github.com/emicklei/go-restful v2.8.1+incompatible h1:AyDqLHbJ1quqbWr/OWDw+PlIP8ZFoTmYrGYaxzrLbNg=
 | 
				
			||||||
github.com/emicklei/go-restful v2.8.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
					github.com/emicklei/go-restful v2.8.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
				
			||||||
github.com/evanphx/json-patch v4.0.0+incompatible h1:xregGRMLBeuRcwiOTHRCsPPuzCQlqhxUPbqdw+zNkLc=
 | 
					github.com/evanphx/json-patch v4.0.0+incompatible h1:xregGRMLBeuRcwiOTHRCsPPuzCQlqhxUPbqdw+zNkLc=
 | 
				
			||||||
github.com/evanphx/json-patch v4.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
					github.com/evanphx/json-patch v4.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
				
			||||||
 | 
					github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
 | 
				
			||||||
 | 
					github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
 | 
				
			||||||
 | 
					github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
 | 
				
			||||||
 | 
					github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
 | 
				
			||||||
 | 
					github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
 | 
				
			||||||
 | 
					github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
 | 
				
			||||||
 | 
					github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 | 
				
			||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
					github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
				
			||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 | 
					github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 | 
				
			||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
					github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
				
			||||||
 | 
					github.com/go-chi/chi v4.0.0+incompatible h1:SiLLEDyAkqNnw+T/uDTf3aFB9T4FTrwMpuYrgaRcnW4=
 | 
				
			||||||
 | 
					github.com/go-chi/chi v4.0.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
 | 
				
			||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
					github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
				
			||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
					github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
				
			||||||
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
 | 
					github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
 | 
				
			||||||
| 
						 | 
					@ -83,6 +96,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 | 
				
			||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
					github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
				
			||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
 | 
					github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
 | 
				
			||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
					github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
 | 
				
			||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
					github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
				
			||||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
 | 
					github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
 | 
				
			||||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 | 
					github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 | 
				
			||||||
| 
						 | 
					@ -101,6 +115,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJ
 | 
				
			||||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
					github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
 | 
					github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
					github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
				
			||||||
 | 
					github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 | 
				
			||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
					github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
				
			||||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
 | 
					github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
 | 
				
			||||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
					github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
				
			||||||
| 
						 | 
					@ -116,10 +131,14 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
 | 
				
			||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 | 
					github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 | 
				
			||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
					github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
				
			||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
					github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
				
			||||||
 | 
					github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 | 
				
			||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
					github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
				
			||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
					github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
				
			||||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
					github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
				
			||||||
 | 
					github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
				
			||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
					github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
				
			||||||
 | 
					github.com/mailgun/mailgun-go/v3 v3.6.0 h1:oQWhyDTFjSiuO6vx1PRlfLZ7Fu+oK0Axn0UTREh3k/g=
 | 
				
			||||||
 | 
					github.com/mailgun/mailgun-go/v3 v3.6.0/go.mod h1:E81I5Agcfi/u1szdehi6p6ttdRX/UD3Rq2SrUzwyFIU=
 | 
				
			||||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
 | 
					github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
 | 
				
			||||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
					github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
				
			||||||
github.com/markbates/inflect v1.0.4 h1:5fh1gzTFhfae06u3hzHYO9xe3l3v3nW5Pwt3naLTP5g=
 | 
					github.com/markbates/inflect v1.0.4 h1:5fh1gzTFhfae06u3hzHYO9xe3l3v3nW5Pwt3naLTP5g=
 | 
				
			||||||
| 
						 | 
					@ -134,7 +153,9 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
 | 
				
			||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
					github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
				
			||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
					github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
				
			||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
					github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
				
			||||||
 | 
					github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
 | 
				
			||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
					github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
				
			||||||
 | 
					github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
 | 
				
			||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
					github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
				
			||||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
 | 
					github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
 | 
				
			||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 | 
					github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 | 
				
			||||||
| 
						 | 
					@ -165,9 +186,12 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ
 | 
				
			||||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
					github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
				
			||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 | 
					github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 | 
				
			||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
					github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
				
			||||||
 | 
					github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
					github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.2.2 h1:J7U/N7eRtzjhs26d6GqMh2HBuXP8/Z64Densiiieafo=
 | 
					github.com/rogpeppe/go-internal v1.2.2 h1:J7U/N7eRtzjhs26d6GqMh2HBuXP8/Z64Densiiieafo=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
					github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
				
			||||||
 | 
					github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 | 
				
			||||||
 | 
					github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
				
			||||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
 | 
					github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
 | 
				
			||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
					github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
				
			||||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 | 
					github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 | 
				
			||||||
| 
						 | 
					@ -203,8 +227,6 @@ golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTk
 | 
				
			||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
					golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
				
			||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f h1:hX65Cu3JDlGH3uEdK7I99Ii+9kjD6mvnnpfLdEAH0x4=
 | 
					golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f h1:hX65Cu3JDlGH3uEdK7I99Ii+9kjD6mvnnpfLdEAH0x4=
 | 
				
			||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
					golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
				
			||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI=
 | 
					 | 
				
			||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
| 
						 | 
					@ -259,7 +281,6 @@ golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGm
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190213015956-f7e1b50d2251/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20190213015956-f7e1b50d2251/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
					golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138 h1:H3uGjxCR/6Ds0Mjgyp7LMK81+LvmbvWWEnJhzk1Pi9E=
 | 
					golang.org/x/tools v0.0.0-20190312170243-e65039ee4138 h1:H3uGjxCR/6Ds0Mjgyp7LMK81+LvmbvWWEnJhzk1Pi9E=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
					golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190708203411-c8855242db9c h1:rRFNgkkT7zOyWlroLBmsrKYtBNhox8WtulQlOr3jIDk=
 | 
					golang.org/x/tools v0.0.0-20190708203411-c8855242db9c h1:rRFNgkkT7zOyWlroLBmsrKYtBNhox8WtulQlOr3jIDk=
 | 
				
			||||||
| 
						 | 
					@ -282,12 +303,15 @@ google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
 | 
				
			||||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
					google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
				
			||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
					gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
					gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
				
			||||||
 | 
					gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
				
			||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
					gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
				
			||||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 | 
					gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 | 
				
			||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 | 
					gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 | 
				
			||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
					gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
				
			||||||
 | 
					gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
				
			||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
					gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
					gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
| 
						 | 
					@ -316,6 +340,7 @@ sigs.k8s.io/controller-runtime v0.1.10 h1:amLOmcekVdnsD1uIpmgRqfTbQWJ2qxvQkcdeFh
 | 
				
			||||||
sigs.k8s.io/controller-runtime v0.1.10/go.mod h1:HFAYoOh6XMV+jKF1UjFwrknPbowfyHEHHRdJMf2jMX8=
 | 
					sigs.k8s.io/controller-runtime v0.1.10/go.mod h1:HFAYoOh6XMV+jKF1UjFwrknPbowfyHEHHRdJMf2jMX8=
 | 
				
			||||||
sigs.k8s.io/controller-tools v0.1.11-0.20190411181648-9d55346c2bde h1:ZkaHf5rNYzIB6CB82keKMQNv7xxkqT0ylOBdfJPfi+k=
 | 
					sigs.k8s.io/controller-tools v0.1.11-0.20190411181648-9d55346c2bde h1:ZkaHf5rNYzIB6CB82keKMQNv7xxkqT0ylOBdfJPfi+k=
 | 
				
			||||||
sigs.k8s.io/controller-tools v0.1.11-0.20190411181648-9d55346c2bde/go.mod h1:ATWLRP3WGxuAN9HcT2LaKHReXIH+EZGzRuMHuxjXfhQ=
 | 
					sigs.k8s.io/controller-tools v0.1.11-0.20190411181648-9d55346c2bde/go.mod h1:ATWLRP3WGxuAN9HcT2LaKHReXIH+EZGzRuMHuxjXfhQ=
 | 
				
			||||||
 | 
					sigs.k8s.io/testing_frameworks v0.1.0 h1:2hBE1sDhKWALoqvhi2i/mnQOFZVfWtQFtsfH0QBTI0U=
 | 
				
			||||||
sigs.k8s.io/testing_frameworks v0.1.0/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U=
 | 
					sigs.k8s.io/testing_frameworks v0.1.0/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U=
 | 
				
			||||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 | 
					sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 | 
				
			||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 | 
					sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,74 @@
 | 
				
			||||||
 | 
					package notifier
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mailgun/mailgun-go/v3"
 | 
				
			||||||
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const content = `
 | 
				
			||||||
 | 
					<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 | 
				
			||||||
 | 
					        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					<head></head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
							<h1 style="background-color: %s; color: white; padding: 3px 10px;">Jenkins Operator Reconciled</h1>
 | 
				
			||||||
 | 
							<h3>Failed to do something</h3>
 | 
				
			||||||
 | 
							<table>
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td><b>CR name:</b></td>
 | 
				
			||||||
 | 
									<td>%s</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td><b>Configuration type:</b></td>
 | 
				
			||||||
 | 
									<td>%s</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td><b>Status:</b></td>
 | 
				
			||||||
 | 
									<td><b style="color: %s;">%s</b></td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
							</table>
 | 
				
			||||||
 | 
							<h6 style="font-size: 11px; color: grey; margin-top: 15px;">Powered by Jenkins Operator <3</h6>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mailgun is service for sending emails
 | 
				
			||||||
 | 
					type Mailgun struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send is function for sending directly to API
 | 
				
			||||||
 | 
					func (m Mailgun) Send(n *Notification, config v1alpha2.Notification) error {
 | 
				
			||||||
 | 
						secret := &corev1.Secret{}
 | 
				
			||||||
 | 
						i := n.Information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						selector := config.Mailgun.APIKeySecretKeySelector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := n.K8sClient.Get(context.TODO(), types.NamespacedName{Name: selector.Name, Namespace: n.Jenkins.Namespace}, secret)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secretValue := string(secret.Data[selector.Name])
 | 
				
			||||||
 | 
						if secretValue == "" {
 | 
				
			||||||
 | 
							return errors.Errorf("SecretValue %s is empty", selector.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mg := mailgun.NewMailgun(config.Mailgun.Domain, secretValue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						htmlMessage := fmt.Sprintf(content, getStatusColor(i.LogLevel, m), i.CrName, i.ConfigurationType, getStatusColor(i.LogLevel, m), string(i.LogLevel))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg := mg.NewMessage(fmt.Sprintf("Jenkins Operator Notifier <%s>", config.Mailgun.From), "Jenkins Operator Status", "", config.Mailgun.Recipient)
 | 
				
			||||||
 | 
						msg.SetHtml(htmlMessage)
 | 
				
			||||||
 | 
						ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
 | 
				
			||||||
 | 
						defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, _, err = mg.Send(ctx, msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,104 @@
 | 
				
			||||||
 | 
					package notifier
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Teams is Microsoft Teams Service
 | 
				
			||||||
 | 
					type Teams struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TeamsMessage is representation of json message structure
 | 
				
			||||||
 | 
					type TeamsMessage struct {
 | 
				
			||||||
 | 
						Type       string         `json:"@type"`
 | 
				
			||||||
 | 
						Context    string         `json:"@context"`
 | 
				
			||||||
 | 
						ThemeColor StatusColor    `json:"themeColor"`
 | 
				
			||||||
 | 
						Title      string         `json:"title"`
 | 
				
			||||||
 | 
						Sections   []TeamsSection `json:"sections"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TeamsSection is MS Teams message section
 | 
				
			||||||
 | 
					type TeamsSection struct {
 | 
				
			||||||
 | 
						Facts []TeamsFact `json:"facts"`
 | 
				
			||||||
 | 
						Text  string      `json:"text"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TeamsFact is field where we can put content
 | 
				
			||||||
 | 
					type TeamsFact struct {
 | 
				
			||||||
 | 
						Name  string `json:"name"`
 | 
				
			||||||
 | 
						Value string `json:"value"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send is function for sending directly to API
 | 
				
			||||||
 | 
					func (t Teams) Send(n *Notification, config v1alpha2.Notification) error {
 | 
				
			||||||
 | 
						secret := &corev1.Secret{}
 | 
				
			||||||
 | 
						i := n.Information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						selector := config.Teams.URLSecretKeySelector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := n.K8sClient.Get(context.TODO(), types.NamespacedName{Name: selector.Name, Namespace: n.Jenkins.Namespace}, secret)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg, err := json.Marshal(TeamsMessage{
 | 
				
			||||||
 | 
							Type:       "MessageCard",
 | 
				
			||||||
 | 
							Context:    "https://schema.org/extensions",
 | 
				
			||||||
 | 
							ThemeColor: getStatusColor(i.LogLevel, t),
 | 
				
			||||||
 | 
							Title:      titleText,
 | 
				
			||||||
 | 
							Sections: []TeamsSection{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Facts: []TeamsFact{
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Name:  crNameFieldName,
 | 
				
			||||||
 | 
											Value: i.CrName,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Name:  configurationTypeFieldName,
 | 
				
			||||||
 | 
											Value: i.ConfigurationType,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Name:  loggingLevelFieldName,
 | 
				
			||||||
 | 
											Value: string(i.LogLevel),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Name:  namespaceFieldName,
 | 
				
			||||||
 | 
											Value: i.Namespace,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Text: i.Message,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secretValue := string(secret.Data[selector.Key])
 | 
				
			||||||
 | 
						if secretValue == "" {
 | 
				
			||||||
 | 
							return errors.Errorf("SecretValue %s is empty", selector.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						request, err := http.NewRequest("POST", secretValue, bytes.NewBuffer(msg))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, err := client.Do(request)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer func() { _ = resp.Body.Close() }()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,99 @@
 | 
				
			||||||
 | 
					package notifier
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"sigs.k8s.io/controller-runtime/pkg/client/fake"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestTeams_Send(t *testing.T) {
 | 
				
			||||||
 | 
						fakeClient := fake.NewFakeClient()
 | 
				
			||||||
 | 
						testURLSelectorKeyName := "test-url-selector"
 | 
				
			||||||
 | 
						testSecretName := "test-secret"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i := Information{
 | 
				
			||||||
 | 
							ConfigurationType: testConfigurationType,
 | 
				
			||||||
 | 
							CrName:            testCrName,
 | 
				
			||||||
 | 
							Message:           testMessage,
 | 
				
			||||||
 | 
							MessageVerbose:    testMessageVerbose,
 | 
				
			||||||
 | 
							Namespace:         testNamespace,
 | 
				
			||||||
 | 
							LogLevel:          testLoggingLevel,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						notification := &Notification{
 | 
				
			||||||
 | 
							K8sClient:   fakeClient,
 | 
				
			||||||
 | 
							Information: i,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							var message TeamsMessage
 | 
				
			||||||
 | 
							decoder := json.NewDecoder(r.Body)
 | 
				
			||||||
 | 
							err := decoder.Decode(&message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, message.Title, titleText)
 | 
				
			||||||
 | 
							assert.Equal(t, message.ThemeColor, getStatusColor(i.LogLevel, Teams{}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mainSection := message.Sections[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, mainSection.Text, i.Message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, fact := range mainSection.Facts {
 | 
				
			||||||
 | 
								switch fact.Name {
 | 
				
			||||||
 | 
								case configurationTypeFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, fact.Value, i.ConfigurationType)
 | 
				
			||||||
 | 
								case crNameFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, fact.Value, i.CrName)
 | 
				
			||||||
 | 
								case messageFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, fact.Value, i.Message)
 | 
				
			||||||
 | 
								case loggingLevelFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, fact.Value, string(i.LogLevel))
 | 
				
			||||||
 | 
								case namespaceFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, fact.Value, i.Namespace)
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									t.Fail()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						teams := Teams{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secret := &corev1.Secret{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name: testSecretName,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Data: map[string][]byte{
 | 
				
			||||||
 | 
								testURLSelectorKeyName: []byte(server.URL),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := notification.K8sClient.Create(context.TODO(), secret)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = teams.Send(notification, v1alpha2.Notification{
 | 
				
			||||||
 | 
							Teams: v1alpha2.Teams{
 | 
				
			||||||
 | 
								URLSecretKeySelector: v1alpha2.SecretKeySelector{
 | 
				
			||||||
 | 
									LocalObjectReference: corev1.LocalObjectReference{
 | 
				
			||||||
 | 
										Name: testSecretName,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Key: testURLSelectorKeyName,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,140 @@
 | 
				
			||||||
 | 
					package notifier
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
 | 
				
			||||||
 | 
						"github.com/jenkinsci/kubernetes-operator/pkg/log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-logr/logr"
 | 
				
			||||||
 | 
						k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// LogWarn is warning log entry
 | 
				
			||||||
 | 
						LogWarn LoggingLevel = "warn"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// LogInfo is info log entry
 | 
				
			||||||
 | 
						LogInfo LoggingLevel = "info"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						titleText                  = "Operator reconciled."
 | 
				
			||||||
 | 
						messageFieldName           = "Message"
 | 
				
			||||||
 | 
						loggingLevelFieldName      = "Logging Level"
 | 
				
			||||||
 | 
						crNameFieldName            = "CR Name"
 | 
				
			||||||
 | 
						configurationTypeFieldName = "Configuration Type"
 | 
				
			||||||
 | 
						namespaceFieldName         = "Namespace"
 | 
				
			||||||
 | 
						footerContent              = "Powered by Jenkins Operator <3"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						testConfigurationType = "test-configuration"
 | 
				
			||||||
 | 
						testCrName            = "test-cr"
 | 
				
			||||||
 | 
						testNamespace         = "test-namespace"
 | 
				
			||||||
 | 
						testMessage           = "test-message"
 | 
				
			||||||
 | 
						testMessageVerbose    = "detail-test-message"
 | 
				
			||||||
 | 
						testLoggingLevel      = LogWarn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client = http.Client{}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StatusColor is useful for better UX
 | 
				
			||||||
 | 
					type StatusColor string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoggingLevel is type for selecting different logging levels
 | 
				
			||||||
 | 
					type LoggingLevel string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Information represents details about operator status
 | 
				
			||||||
 | 
					type Information struct {
 | 
				
			||||||
 | 
						ConfigurationType string
 | 
				
			||||||
 | 
						Namespace         string
 | 
				
			||||||
 | 
						CrName            string
 | 
				
			||||||
 | 
						LogLevel          LoggingLevel
 | 
				
			||||||
 | 
						Message           string
 | 
				
			||||||
 | 
						MessageVerbose    string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Notification contains message which will be sent
 | 
				
			||||||
 | 
					type Notification struct {
 | 
				
			||||||
 | 
						Jenkins     v1alpha2.Jenkins
 | 
				
			||||||
 | 
						K8sClient   k8sclient.Client
 | 
				
			||||||
 | 
						Logger      logr.Logger
 | 
				
			||||||
 | 
						Information Information
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Service is skeleton for additional services
 | 
				
			||||||
 | 
					type service interface {
 | 
				
			||||||
 | 
						Send(i *Notification, config v1alpha2.Notification) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Listen is goroutine that listens for incoming messages and sends it
 | 
				
			||||||
 | 
					func Listen(notification chan *Notification) {
 | 
				
			||||||
 | 
						for n := range notification {
 | 
				
			||||||
 | 
							for _, notificationConfig := range n.Jenkins.Spec.Notifications {
 | 
				
			||||||
 | 
								var err error
 | 
				
			||||||
 | 
								var svc service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if notificationConfig.Slack != (v1alpha2.Slack{}) {
 | 
				
			||||||
 | 
									svc = Slack{}
 | 
				
			||||||
 | 
								} else if notificationConfig.Teams != (v1alpha2.Teams{}) {
 | 
				
			||||||
 | 
									svc = Teams{}
 | 
				
			||||||
 | 
								} else if notificationConfig.Mailgun != (v1alpha2.Mailgun{}) {
 | 
				
			||||||
 | 
									svc = Mailgun{}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									n.Logger.V(log.VWarn).Info(fmt.Sprintf("Notification service in `%s` not found or not defined", notificationConfig.Name))
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err = notify(svc, n, notificationConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									n.Logger.V(log.VWarn).Info(fmt.Sprintf("Failed to send notifications. %+v", err))
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									n.Logger.V(log.VDebug).Info("Sent notification")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getStatusColor(logLevel LoggingLevel, svc service) StatusColor {
 | 
				
			||||||
 | 
						switch svc.(type) {
 | 
				
			||||||
 | 
						case Slack:
 | 
				
			||||||
 | 
							switch logLevel {
 | 
				
			||||||
 | 
							case LogInfo:
 | 
				
			||||||
 | 
								return "#439FE0"
 | 
				
			||||||
 | 
							case LogWarn:
 | 
				
			||||||
 | 
								return "danger"
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return "#c8c8c8"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case Teams:
 | 
				
			||||||
 | 
							switch logLevel {
 | 
				
			||||||
 | 
							case LogInfo:
 | 
				
			||||||
 | 
								return "439FE0"
 | 
				
			||||||
 | 
							case LogWarn:
 | 
				
			||||||
 | 
								return "E81123"
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return "C8C8C8"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case Mailgun:
 | 
				
			||||||
 | 
							switch logLevel {
 | 
				
			||||||
 | 
							case LogInfo:
 | 
				
			||||||
 | 
								return "blue"
 | 
				
			||||||
 | 
							case LogWarn:
 | 
				
			||||||
 | 
								return "red"
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return "gray"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return "#c8c8c8"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func notify(svc service, n *Notification, manifest v1alpha2.Notification) error {
 | 
				
			||||||
 | 
						if n.Information.LogLevel == LogInfo && string(manifest.LoggingLevel) == string(LogWarn) {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return svc.Send(n, manifest)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,114 @@
 | 
				
			||||||
 | 
					package notifier
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Slack is messaging service
 | 
				
			||||||
 | 
					type Slack struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SlackMessage is representation of json message
 | 
				
			||||||
 | 
					type SlackMessage struct {
 | 
				
			||||||
 | 
						Text        string            `json:"text"`
 | 
				
			||||||
 | 
						Attachments []SlackAttachment `json:"attachments"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SlackAttachment is representation of json attachment
 | 
				
			||||||
 | 
					type SlackAttachment struct {
 | 
				
			||||||
 | 
						Fallback string       `json:"fallback"`
 | 
				
			||||||
 | 
						Color    StatusColor  `json:"color"`
 | 
				
			||||||
 | 
						Pretext  string       `json:"pretext"`
 | 
				
			||||||
 | 
						Title    string       `json:"title"`
 | 
				
			||||||
 | 
						Text     string       `json:"text"`
 | 
				
			||||||
 | 
						Fields   []SlackField `json:"fields"`
 | 
				
			||||||
 | 
						Footer   string       `json:"footer"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SlackField is representation of json field.
 | 
				
			||||||
 | 
					type SlackField struct {
 | 
				
			||||||
 | 
						Title string `json:"title"`
 | 
				
			||||||
 | 
						Value string `json:"value"`
 | 
				
			||||||
 | 
						Short bool   `json:"short"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send is function for sending directly to API
 | 
				
			||||||
 | 
					func (s Slack) Send(n *Notification, config v1alpha2.Notification) error {
 | 
				
			||||||
 | 
						secret := &corev1.Secret{}
 | 
				
			||||||
 | 
						i := n.Information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						selector := config.Slack.URLSecretKeySelector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := n.K8sClient.Get(context.TODO(), types.NamespacedName{Name: selector.Name, Namespace: n.Jenkins.Namespace}, secret)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slackMessage, err := json.Marshal(SlackMessage{
 | 
				
			||||||
 | 
							Attachments: []SlackAttachment{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Fallback: "",
 | 
				
			||||||
 | 
									Color:    getStatusColor(i.LogLevel, s),
 | 
				
			||||||
 | 
									Text:     titleText,
 | 
				
			||||||
 | 
									Fields: []SlackField{
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Title: messageFieldName,
 | 
				
			||||||
 | 
											Value: i.Message,
 | 
				
			||||||
 | 
											Short: false,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Title: crNameFieldName,
 | 
				
			||||||
 | 
											Value: i.CrName,
 | 
				
			||||||
 | 
											Short: true,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Title: configurationTypeFieldName,
 | 
				
			||||||
 | 
											Value: i.ConfigurationType,
 | 
				
			||||||
 | 
											Short: true,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Title: loggingLevelFieldName,
 | 
				
			||||||
 | 
											Value: string(i.LogLevel),
 | 
				
			||||||
 | 
											Short: true,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											Title: namespaceFieldName,
 | 
				
			||||||
 | 
											Value: i.Namespace,
 | 
				
			||||||
 | 
											Short: true,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Footer: footerContent,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secretValue := string(secret.Data[selector.Key])
 | 
				
			||||||
 | 
						if secretValue == "" {
 | 
				
			||||||
 | 
							return errors.Errorf("SecretValue %s is empty", selector.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						request, err := http.NewRequest("POST", secretValue, bytes.NewBuffer(slackMessage))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp, err := client.Do(request)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer func() { _ = resp.Body.Close() }()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,99 @@
 | 
				
			||||||
 | 
					package notifier
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"sigs.k8s.io/controller-runtime/pkg/client/fake"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSlack_Send(t *testing.T) {
 | 
				
			||||||
 | 
						fakeClient := fake.NewFakeClient()
 | 
				
			||||||
 | 
						testURLSelectorKeyName := "test-url-selector"
 | 
				
			||||||
 | 
						testSecretName := "test-secret"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i := Information{
 | 
				
			||||||
 | 
							ConfigurationType: testConfigurationType,
 | 
				
			||||||
 | 
							CrName:            testCrName,
 | 
				
			||||||
 | 
							Message:           testMessage,
 | 
				
			||||||
 | 
							MessageVerbose:    testMessageVerbose,
 | 
				
			||||||
 | 
							Namespace:         testNamespace,
 | 
				
			||||||
 | 
							LogLevel:          testLoggingLevel,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						notification := &Notification{
 | 
				
			||||||
 | 
							K8sClient:   fakeClient,
 | 
				
			||||||
 | 
							Information: i,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							var message SlackMessage
 | 
				
			||||||
 | 
							decoder := json.NewDecoder(r.Body)
 | 
				
			||||||
 | 
							err := decoder.Decode(&message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mainAttachment := message.Attachments[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, mainAttachment.Text, titleText)
 | 
				
			||||||
 | 
							for _, field := range mainAttachment.Fields {
 | 
				
			||||||
 | 
								switch field.Title {
 | 
				
			||||||
 | 
								case configurationTypeFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, field.Value, i.ConfigurationType)
 | 
				
			||||||
 | 
								case crNameFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, field.Value, i.CrName)
 | 
				
			||||||
 | 
								case messageFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, field.Value, i.Message)
 | 
				
			||||||
 | 
								case loggingLevelFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, field.Value, string(i.LogLevel))
 | 
				
			||||||
 | 
								case namespaceFieldName:
 | 
				
			||||||
 | 
									assert.Equal(t, field.Value, i.Namespace)
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									t.Fail()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, mainAttachment.Footer, footerContent)
 | 
				
			||||||
 | 
							assert.Equal(t, mainAttachment.Color, getStatusColor(i.LogLevel, Slack{}))
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer server.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secret := &corev1.Secret{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name: testSecretName,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Data: map[string][]byte{
 | 
				
			||||||
 | 
								testURLSelectorKeyName: []byte(server.URL),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := notification.K8sClient.Create(context.TODO(), secret)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slack := Slack{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = slack.Send(notification, v1alpha2.Notification{
 | 
				
			||||||
 | 
							Slack: v1alpha2.Slack{
 | 
				
			||||||
 | 
								URLSecretKeySelector: v1alpha2.SecretKeySelector{
 | 
				
			||||||
 | 
									LocalObjectReference: corev1.LocalObjectReference{
 | 
				
			||||||
 | 
										Name: testSecretName,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Key: testURLSelectorKeyName,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,10 @@ type JenkinsSpec struct {
 | 
				
			||||||
	// +optional
 | 
						// +optional
 | 
				
			||||||
	SeedJobs []SeedJob `json:"seedJobs,omitempty"`
 | 
						SeedJobs []SeedJob `json:"seedJobs,omitempty"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Notifications defines services which are used to inform about Jenkins status
 | 
				
			||||||
 | 
						// Can be used to integrate chat services like Slack or Email services like Mailgun
 | 
				
			||||||
 | 
						Notifications []Notification `json:"notifications,omitempty"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Service is Kubernetes service of Jenkins master HTTP pod
 | 
						// Service is Kubernetes service of Jenkins master HTTP pod
 | 
				
			||||||
	// Defaults to :
 | 
						// Defaults to :
 | 
				
			||||||
	// port: 8080
 | 
						// port: 8080
 | 
				
			||||||
| 
						 | 
					@ -50,6 +54,44 @@ type JenkinsSpec struct {
 | 
				
			||||||
	ConfigurationAsCode ConfigurationAsCode `json:"configurationAsCode,omitempty"`
 | 
						ConfigurationAsCode ConfigurationAsCode `json:"configurationAsCode,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Notification is info sending service about Jenkins Operator
 | 
				
			||||||
 | 
					type Notification struct {
 | 
				
			||||||
 | 
						LoggingLevel JenkinsNotificationLogLevel `json:"loggingLevel"`
 | 
				
			||||||
 | 
						Verbose      bool                        `json:"verbose"`
 | 
				
			||||||
 | 
						Name         string                      `json:"name"`
 | 
				
			||||||
 | 
						Slack        Slack                       `json:"slack,omitempty"`
 | 
				
			||||||
 | 
						Teams        Teams                       `json:"teams,omitempty"`
 | 
				
			||||||
 | 
						Mailgun      Mailgun                     `json:"mailgun,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Slack is handler for Slack
 | 
				
			||||||
 | 
					type Slack struct {
 | 
				
			||||||
 | 
						// The web hook URL to Slack App
 | 
				
			||||||
 | 
						URLSecretKeySelector SecretKeySelector `json:"urlSecretKeySelector"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Teams is handler for Microsoft Teams
 | 
				
			||||||
 | 
					type Teams struct {
 | 
				
			||||||
 | 
						// The web hook URL to Teams App
 | 
				
			||||||
 | 
						URLSecretKeySelector SecretKeySelector `json:"urlSecretKeySelector"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mailgun is handler for Mailgun email service
 | 
				
			||||||
 | 
					type Mailgun struct {
 | 
				
			||||||
 | 
						Domain                  string            `json:"domain"`
 | 
				
			||||||
 | 
						APIKeySecretKeySelector SecretKeySelector `json:"apiKeySecretKeySelector"`
 | 
				
			||||||
 | 
						Recipient               string            `json:"recipient"`
 | 
				
			||||||
 | 
						From                    string            `json:"from"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SecretKeySelector selects a key of a Secret.
 | 
				
			||||||
 | 
					type SecretKeySelector struct {
 | 
				
			||||||
 | 
						// The name of the secret in the pod's namespace to select from.
 | 
				
			||||||
 | 
						corev1.LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
 | 
				
			||||||
 | 
						// The key of the secret to select from.  Must be a valid secret key.
 | 
				
			||||||
 | 
						Key string `json:"key" protobuf:"bytes,2,opt,name=key"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Container defines Kubernetes container attributes
 | 
					// Container defines Kubernetes container attributes
 | 
				
			||||||
type Container struct {
 | 
					type Container struct {
 | 
				
			||||||
	// Name of the container specified as a DNS_LABEL.
 | 
						// Name of the container specified as a DNS_LABEL.
 | 
				
			||||||
| 
						 | 
					@ -443,6 +485,17 @@ const (
 | 
				
			||||||
	UsernamePasswordCredentialType JenkinsCredentialType = "usernamePassword"
 | 
						UsernamePasswordCredentialType JenkinsCredentialType = "usernamePassword"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// JenkinsNotificationLogLevel defines type of Notification feature frequency of sending logger entries
 | 
				
			||||||
 | 
					type JenkinsNotificationLogLevel string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// LogLevelWarning - Only Warnings
 | 
				
			||||||
 | 
						LogLevelWarning JenkinsNotificationLogLevel = "warning"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// LogLevelInfo - Only info
 | 
				
			||||||
 | 
						LogLevelInfo JenkinsNotificationLogLevel = "info"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AllowedJenkinsCredentialMap contains all allowed Jenkins credentials types
 | 
					// AllowedJenkinsCredentialMap contains all allowed Jenkins credentials types
 | 
				
			||||||
var AllowedJenkinsCredentialMap = map[string]string{
 | 
					var AllowedJenkinsCredentialMap = map[string]string{
 | 
				
			||||||
	string(NoJenkinsCredentialCredentialType): "",
 | 
						string(NoJenkinsCredentialCredentialType): "",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -366,6 +366,11 @@ func (in *JenkinsSpec) DeepCopyInto(out *JenkinsSpec) {
 | 
				
			||||||
		*out = make([]SeedJob, len(*in))
 | 
							*out = make([]SeedJob, len(*in))
 | 
				
			||||||
		copy(*out, *in)
 | 
							copy(*out, *in)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if in.Notifications != nil {
 | 
				
			||||||
 | 
							in, out := &in.Notifications, &out.Notifications
 | 
				
			||||||
 | 
							*out = make([]Notification, len(*in))
 | 
				
			||||||
 | 
							copy(*out, *in)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	in.Service.DeepCopyInto(&out.Service)
 | 
						in.Service.DeepCopyInto(&out.Service)
 | 
				
			||||||
	in.SlaveService.DeepCopyInto(&out.SlaveService)
 | 
						in.SlaveService.DeepCopyInto(&out.SlaveService)
 | 
				
			||||||
	in.Backup.DeepCopyInto(&out.Backup)
 | 
						in.Backup.DeepCopyInto(&out.Backup)
 | 
				
			||||||
| 
						 | 
					@ -430,6 +435,42 @@ func (in *JenkinsStatus) DeepCopy() *JenkinsStatus {
 | 
				
			||||||
	return out
 | 
						return out
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
				
			||||||
 | 
					func (in *Mailgun) DeepCopyInto(out *Mailgun) {
 | 
				
			||||||
 | 
						*out = *in
 | 
				
			||||||
 | 
						out.APIKeySecretKeySelector = in.APIKeySecretKeySelector
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mailgun.
 | 
				
			||||||
 | 
					func (in *Mailgun) DeepCopy() *Mailgun {
 | 
				
			||||||
 | 
						if in == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out := new(Mailgun)
 | 
				
			||||||
 | 
						in.DeepCopyInto(out)
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
				
			||||||
 | 
					func (in *Notification) DeepCopyInto(out *Notification) {
 | 
				
			||||||
 | 
						*out = *in
 | 
				
			||||||
 | 
						out.Slack = in.Slack
 | 
				
			||||||
 | 
						out.Teams = in.Teams
 | 
				
			||||||
 | 
						out.Mailgun = in.Mailgun
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Notification.
 | 
				
			||||||
 | 
					func (in *Notification) DeepCopy() *Notification {
 | 
				
			||||||
 | 
						if in == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out := new(Notification)
 | 
				
			||||||
 | 
						in.DeepCopyInto(out)
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
					// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
				
			||||||
func (in *Plugin) DeepCopyInto(out *Plugin) {
 | 
					func (in *Plugin) DeepCopyInto(out *Plugin) {
 | 
				
			||||||
	*out = *in
 | 
						*out = *in
 | 
				
			||||||
| 
						 | 
					@ -463,6 +504,23 @@ func (in *Restore) DeepCopy() *Restore {
 | 
				
			||||||
	return out
 | 
						return out
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
				
			||||||
 | 
					func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) {
 | 
				
			||||||
 | 
						*out = *in
 | 
				
			||||||
 | 
						out.LocalObjectReference = in.LocalObjectReference
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeySelector.
 | 
				
			||||||
 | 
					func (in *SecretKeySelector) DeepCopy() *SecretKeySelector {
 | 
				
			||||||
 | 
						if in == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out := new(SecretKeySelector)
 | 
				
			||||||
 | 
						in.DeepCopyInto(out)
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
					// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
				
			||||||
func (in *SecretRef) DeepCopyInto(out *SecretRef) {
 | 
					func (in *SecretRef) DeepCopyInto(out *SecretRef) {
 | 
				
			||||||
	*out = *in
 | 
						*out = *in
 | 
				
			||||||
| 
						 | 
					@ -529,3 +587,37 @@ func (in *Service) DeepCopy() *Service {
 | 
				
			||||||
	in.DeepCopyInto(out)
 | 
						in.DeepCopyInto(out)
 | 
				
			||||||
	return out
 | 
						return out
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
				
			||||||
 | 
					func (in *Slack) DeepCopyInto(out *Slack) {
 | 
				
			||||||
 | 
						*out = *in
 | 
				
			||||||
 | 
						out.URLSecretKeySelector = in.URLSecretKeySelector
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Slack.
 | 
				
			||||||
 | 
					func (in *Slack) DeepCopy() *Slack {
 | 
				
			||||||
 | 
						if in == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out := new(Slack)
 | 
				
			||||||
 | 
						in.DeepCopyInto(out)
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
				
			||||||
 | 
					func (in *Teams) DeepCopyInto(out *Teams) {
 | 
				
			||||||
 | 
						*out = *in
 | 
				
			||||||
 | 
						out.URLSecretKeySelector = in.URLSecretKeySelector
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Teams.
 | 
				
			||||||
 | 
					func (in *Teams) DeepCopy() *Teams {
 | 
				
			||||||
 | 
						if in == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out := new(Teams)
 | 
				
			||||||
 | 
						in.DeepCopyInto(out)
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue