From 603f5a535acd46d6c4a7e7b741b7d6f858a8838e Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Mon, 15 Jun 2020 19:46:47 +0200 Subject: [PATCH] Kubernetes provider --- go.mod | 3 + go.sum | 30 ++++++ internal/app/diun.go | 6 ++ internal/config/config_test.go | 4 + internal/config/fixtures/config.test.yml | 2 + internal/model/provider_kubernetes.go | 30 ++++++ internal/model/providers.go | 7 +- internal/provider/kubernetes/kubernetes.go | 49 +++++++++ internal/provider/kubernetes/pod.go | 48 +++++++++ pkg/k8s/client.go | 114 +++++++++++++++++++++ pkg/k8s/pod.go | 22 ++++ 11 files changed, 312 insertions(+), 3 deletions(-) create mode 100644 internal/model/provider_kubernetes.go create mode 100644 internal/provider/kubernetes/kubernetes.go create mode 100644 internal/provider/kubernetes/pod.go create mode 100644 pkg/k8s/client.go create mode 100644 pkg/k8s/pod.go diff --git a/go.mod b/go.mod index 96df5e0d..902fdbd4 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,9 @@ require ( gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect gopkg.in/yaml.v2 v2.3.0 + k8s.io/api v0.18.2 + k8s.io/apimachinery v0.18.2 + k8s.io/client-go v0.18.2 ) // Docker v19.03.6 diff --git a/go.sum b/go.sum index b361da10..f0c9f5aa 100644 --- a/go.sum +++ b/go.sum @@ -182,6 +182,7 @@ github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6 github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= @@ -277,6 +278,8 @@ github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv3 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -290,6 +293,7 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= @@ -364,6 +368,7 @@ github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBv github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -438,9 +443,11 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -464,6 +471,7 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -569,6 +577,7 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= @@ -656,6 +665,7 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= @@ -729,6 +739,7 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -766,6 +777,7 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -853,6 +865,7 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -899,6 +912,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -931,17 +945,27 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0= +k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= +k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= +k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA= +k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ= +k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE= +k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= @@ -952,5 +976,11 @@ mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/internal/app/diun.go b/internal/app/diun.go index 2d8c24b8..03ef439d 100644 --- a/internal/app/diun.go +++ b/internal/app/diun.go @@ -12,6 +12,7 @@ import ( "github.com/crazy-max/diun/v4/internal/notif" dockerPrd "github.com/crazy-max/diun/v4/internal/provider/docker" filePrd "github.com/crazy-max/diun/v4/internal/provider/file" + kubernetesPrd "github.com/crazy-max/diun/v4/internal/provider/kubernetes" swarmPrd "github.com/crazy-max/diun/v4/internal/provider/swarm" "github.com/crazy-max/diun/v4/pkg/registry" "github.com/hako/durafmt" @@ -123,6 +124,11 @@ func (di *Diun) Run() { di.createJob(job) } + // Kubernetes provider + for _, job := range kubernetesPrd.New(di.cfg.Providers.Kubernetes).ListJob() { + di.createJob(job) + } + // File provider for _, job := range filePrd.New(di.cfg.Providers.File).ListJob() { di.createJob(job) diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 807b92bf..941b5ef8 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -125,6 +125,10 @@ func TestLoadFile(t *testing.T) { TLSVerify: utl.NewTrue(), WatchByDefault: utl.NewFalse(), }, + Kubernetes: &model.PrdKubernetes{ + TLSInsecure: utl.NewFalse(), + WatchByDefault: utl.NewTrue(), + }, File: &model.PrdFile{ Filename: "./fixtures/dummy.yml", }, diff --git a/internal/config/fixtures/config.test.yml b/internal/config/fixtures/config.test.yml index 79d51008..88c0d0d0 100644 --- a/internal/config/fixtures/config.test.yml +++ b/internal/config/fixtures/config.test.yml @@ -67,5 +67,7 @@ providers: watchByDefault: true watchStopped: true swarm: {} + kubernetes: + watchByDefault: true file: filename: ./fixtures/dummy.yml diff --git a/internal/model/provider_kubernetes.go b/internal/model/provider_kubernetes.go new file mode 100644 index 00000000..0da71816 --- /dev/null +++ b/internal/model/provider_kubernetes.go @@ -0,0 +1,30 @@ +package model + +import ( + "github.com/crazy-max/diun/v4/pkg/utl" +) + +// PrdKubernetes holds kubernetes provider configuration +type PrdKubernetes struct { + Endpoint string `yaml:"endpoint" json:"endpoint,omitempty" validate:"omitempty"` + Token string `yaml:"token,omitempty" json:"token,omitempty" validate:"omitempty"` + TokenFile string `yaml:"tokenFile,omitempty" json:"tokenFile,omitempty" validate:"omitempty,file"` + ConfigFile string `yaml:"configFile" json:"configFile,omitempty" validate:"omitempty,file"` + Namespaces []string `yaml:"namespaces" json:"namespaces,omitempty" validate:"omitempty"` + TLSCAFile string `yaml:"tlsCaFile" json:"tlsCaFile,omitempty" validate:"omitempty"` + TLSInsecure *bool `yaml:"tlsInsecure" json:"tlsInsecure,omitempty" validate:"required"` + WatchByDefault *bool `yaml:"watchByDefault" json:"watchByDefault,omitempty" validate:"required"` +} + +// GetDefaults gets the default values +func (s *PrdKubernetes) GetDefaults() *PrdKubernetes { + n := &PrdKubernetes{} + n.SetDefaults() + return n +} + +// SetDefaults sets the default values +func (s *PrdKubernetes) SetDefaults() { + s.TLSInsecure = utl.NewFalse() + s.WatchByDefault = utl.NewFalse() +} diff --git a/internal/model/providers.go b/internal/model/providers.go index 4fb3fd96..d3977406 100644 --- a/internal/model/providers.go +++ b/internal/model/providers.go @@ -2,9 +2,10 @@ package model // Providers represents a provider configuration type Providers struct { - Docker *PrdDocker `yaml:"docker,omitempty" json:"docker,omitempty" label:"allowEmpty"` - Swarm *PrdSwarm `yaml:"swarm,omitempty" json:"swarm,omitempty" label:"allowEmpty"` - File *PrdFile `yaml:"file,omitempty" json:"file,omitempty"` + Docker *PrdDocker `yaml:"docker,omitempty" json:"docker,omitempty" label:"allowEmpty"` + Swarm *PrdSwarm `yaml:"swarm,omitempty" json:"swarm,omitempty" label:"allowEmpty"` + Kubernetes *PrdKubernetes `yaml:"kubernetes,omitempty" json:"kubernetes,omitempty" label:"allowEmpty"` + File *PrdFile `yaml:"file,omitempty" json:"file,omitempty"` } // GetDefaults gets the default values diff --git a/internal/provider/kubernetes/kubernetes.go b/internal/provider/kubernetes/kubernetes.go new file mode 100644 index 00000000..c5f83bc7 --- /dev/null +++ b/internal/provider/kubernetes/kubernetes.go @@ -0,0 +1,49 @@ +package kubernetes + +import ( + "github.com/crazy-max/diun/v4/internal/model" + "github.com/crazy-max/diun/v4/internal/provider" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +// Client represents an active kubernetes provider object +type Client struct { + *provider.Client + config *model.PrdKubernetes + logger zerolog.Logger +} + +// New creates new kubernetes provider instance +func New(config *model.PrdKubernetes) *provider.Client { + return &provider.Client{ + Handler: &Client{ + config: config, + logger: log.With().Str("provider", "k8s").Logger(), + }, + } +} + +// ListJob returns job list to process +func (c *Client) ListJob() []model.Job { + if c.config == nil { + return []model.Job{} + } + + images := c.listPodImage() + if len(images) == 0 { + log.Warn().Msg("No image found") + return []model.Job{} + } + + c.logger.Info().Msgf("Found %d image(s) to analyze", len(images)) + var list []model.Job + for _, image := range images { + list = append(list, model.Job{ + Provider: "k8s", + Image: image, + }) + } + + return list +} diff --git a/internal/provider/kubernetes/pod.go b/internal/provider/kubernetes/pod.go new file mode 100644 index 00000000..f9641e39 --- /dev/null +++ b/internal/provider/kubernetes/pod.go @@ -0,0 +1,48 @@ +package kubernetes + +import ( + "reflect" + + "github.com/crazy-max/diun/v4/internal/model" + "github.com/crazy-max/diun/v4/internal/provider" + "github.com/crazy-max/diun/v4/pkg/k8s" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (c *Client) listPodImage() []model.Image { + cli, err := k8s.New(k8s.Options{ + Endpoint: c.config.Endpoint, + Token: c.config.Token, + TokenFile: c.config.TokenFile, + Namespaces: c.config.Namespaces, + TLSCAFile: c.config.TLSCAFile, + TLSInsecure: c.config.TLSInsecure, + }) + if err != nil { + c.logger.Error().Err(err).Msg("Cannot create Kubernetes client") + return []model.Image{} + } + + pods, err := cli.PodList(metav1.ListOptions{}) + if err != nil { + c.logger.Error().Err(err).Msg("Cannot list Kubernetes pods") + return []model.Image{} + } + + var list []model.Image + for _, pod := range pods { + for _, ctn := range pod.Spec.Containers { + image, err := provider.ValidateContainerImage(ctn.Image, pod.Labels, *c.config.WatchByDefault) + if err != nil { + c.logger.Error().Err(err).Msgf("Cannot get image from container %s (pod %s)", ctn.Name, pod.Name) + continue + } else if reflect.DeepEqual(image, model.Image{}) { + c.logger.Debug().Msgf("Watch disabled for container %s (pod %s)", ctn.Name, pod.Name) + continue + } + list = append(list, image) + } + } + + return list +} diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go new file mode 100644 index 00000000..e07aa272 --- /dev/null +++ b/pkg/k8s/client.go @@ -0,0 +1,114 @@ +package k8s + +import ( + "context" + "io/ioutil" + "os" + + "github.com/crazy-max/diun/v4/pkg/utl" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +// Client represents an active kubernetes object +type Client struct { + ctx context.Context + API *kubernetes.Clientset +} + +// Options holds kubernetes client object options +type Options struct { + Endpoint string + Token string + TokenFile string + TLSCAFile string + TLSInsecure *bool +} + +// New initializes a new Kubernetes client +func New(opts Options) (*Client, error) { + var err error + var cl *kubernetes.Clientset + + switch { + case os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "": + log.Debug().Msgf("Creating in-cluster Kubernetes provider client %s", opts.Endpoint) + cl, err = newInClusterClient(opts) + case os.Getenv("KUBECONFIG") != "": + log.Debug().Msgf("Creating cluster-external Kubernetes provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG")) + cl, err = newExternalClusterClientFromFile(opts, os.Getenv("KUBECONFIG")) + default: + log.Debug().Msgf("Creating cluster-external Kubernetes provider client %s", opts.Endpoint) + cl, err = newExternalClusterClient(opts) + } + + return &Client{ + ctx: context.Background(), + API: cl, + }, err +} + +func newInClusterClient(opts Options) (*kubernetes.Clientset, error) { + config, err := rest.InClusterConfig() + if err != nil { + return nil, errors.Wrap(err, "failed to create in-cluster configuration") + } + + if opts.Endpoint != "" { + config.Host = opts.Endpoint + } + if opts.TLSInsecure != nil { + config.TLSClientConfig.Insecure = *opts.TLSInsecure + } + + return kubernetes.NewForConfig(config) +} + +func newExternalClusterClientFromFile(opts Options, file string) (*kubernetes.Clientset, error) { + configFromFlags, err := clientcmd.BuildConfigFromFlags("", file) + if err != nil { + return nil, err + } + if opts.TLSInsecure != nil { + configFromFlags.TLSClientConfig.Insecure = *opts.TLSInsecure + } + + configFromFlags.TLSClientConfig.Insecure = true + return kubernetes.NewForConfig(configFromFlags) +} + +func newExternalClusterClient(opts Options) (*kubernetes.Clientset, error) { + var err error + + if opts.Endpoint == "" { + return nil, errors.New("Endpoint missing for external cluster client") + } + + opts.Token, err = utl.GetSecret(opts.Token, opts.TokenFile) + if err != nil { + return nil, errors.Wrap(err, "Cannot retrieve bearer token") + } + + config := &rest.Config{ + Host: opts.Endpoint, + BearerToken: opts.Token, + } + + if opts.TLSCAFile != "" { + caData, err := ioutil.ReadFile(opts.TLSCAFile) + if err != nil { + return nil, errors.Wrap(err, "Failed to read CA file") + } + config.TLSClientConfig = rest.TLSClientConfig{ + CAData: caData, + } + } + if opts.TLSInsecure != nil { + config.TLSClientConfig.Insecure = *opts.TLSInsecure + } + + return kubernetes.NewForConfig(config) +} diff --git a/pkg/k8s/pod.go b/pkg/k8s/pod.go new file mode 100644 index 00000000..2e52ad92 --- /dev/null +++ b/pkg/k8s/pod.go @@ -0,0 +1,22 @@ +package k8s + +import ( + "sort" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PodList returns Kubernetes pods +func (c *Client) PodList(opts metav1.ListOptions) ([]v1.Pod, error) { + pods, err := c.API.CoreV1().Pods("").List(c.ctx, opts) + if err != nil { + return nil, err + } + + sort.Slice(pods.Items, func(i, j int) bool { + return pods.Items[i].Name < pods.Items[j].Name + }) + + return pods.Items, nil +}