diff --git a/images/storage-network-discoverer/src/go.mod b/images/storage-network-discoverer/src/go.mod new file mode 100644 index 00000000..76f4890a --- /dev/null +++ b/images/storage-network-discoverer/src/go.mod @@ -0,0 +1,65 @@ +module storage-network-controller + +go 1.22.7 + +require ( + github.com/go-logr/logr v1.4.2 + github.com/square/exit v1.1.0 + k8s.io/api v0.31.0 + k8s.io/apimachinery v0.31.0 // indirect + k8s.io/client-go v0.31.0 + k8s.io/klog/v2 v2.130.1 + sigs.k8s.io/controller-runtime v0.19.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/x448/float16 v0.8.4 // indirect + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.3.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.31.0 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/images/storage-network-discoverer/src/go.sum b/images/storage-network-discoverer/src/go.sum new file mode 100644 index 00000000..da136f9e --- /dev/null +++ b/images/storage-network-discoverer/src/go.sum @@ -0,0 +1,196 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +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/square/exit v1.1.0 h1:ECTN4HPgsWQ4Komnhk535AuVSM+z1IUHzNbf2DYo8JA= +github.com/square/exit v1.1.0/go.mod h1:w6hlsf9QOH7EcFtKczbLWwcyRXE1gwZH2y5kJF5ferk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= +k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= +k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= +k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= +k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= +k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= +k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= +sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/images/storage-network-discoverer/src/internal/config/config.go b/images/storage-network-discoverer/src/internal/config/config.go new file mode 100644 index 00000000..fac45712 --- /dev/null +++ b/images/storage-network-discoverer/src/internal/config/config.go @@ -0,0 +1,132 @@ +/* +Copyright 2024 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "flag" + "fmt" + "net" + "os" + "strconv" + "strings" + + "storage-network-controller/internal/logger" +) + +const ( + ControllerNamespaceEnv = "CONTROLLER_NAMESPACE" + DiscoverySecEnv = "DISCOVERY_INTERVAL_SEC" + CacheSecEnv = "CACHE_TTL_SEC" + DefaultDiscoverySec = 15 + DefaultCacheTTLSec = 30 + HardcodedControllerNS = "d8-sds-replicated-volume" + LogLevelEnv = "LOG_LEVEL" +) + +type StorageNetworkCIDR []string + +type Options struct { + ControllerNamespace string + DiscoverySec int + CacheTTLSec int + Loglevel logger.Verbosity + StorageNetworkCIDR StorageNetworkCIDR +} + +// String is an implementation of the flag.Value interface +func (i *StorageNetworkCIDR) String() string { + return strings.Join(*i, " ") +} + +// Set is an implementation of the flag.Value interface +func (i *StorageNetworkCIDR) Set(value string) error { + ip, _, err := net.ParseCIDR(value) + + if err != nil { + return err + } + + if !ip.IsPrivate() { + return fmt.Errorf("IP %s must be in private ranges", value) + } + + *i = append(*i, value) + return nil +} + +func NewConfig() (*Options, error) { + var opts Options + + loglevel := os.Getenv(LogLevelEnv) + if loglevel == "" { + opts.Loglevel = logger.DebugLevel + } else { + opts.Loglevel = logger.Verbosity(loglevel) + } + + discoverySec := os.Getenv(DiscoverySecEnv) + if discoverySec == "" { + opts.DiscoverySec = DefaultDiscoverySec + } else { + i, err := strconv.Atoi(discoverySec) + if err != nil { + fmt.Printf("Failed to convert value of env var %s to integer: %s", DiscoverySecEnv, err.Error()) + fmt.Printf("Using default %d seconds", DefaultDiscoverySec) + opts.DiscoverySec = DefaultDiscoverySec + } else { + opts.DiscoverySec = i + } + } + + cacheTTLSec := os.Getenv(CacheSecEnv) + if cacheTTLSec == "" { + opts.CacheTTLSec = DefaultCacheTTLSec + } else { + i, err := strconv.Atoi(cacheTTLSec) + if err != nil { + fmt.Printf("Failed to convert value of env var %s to integer: %s", CacheSecEnv, err.Error()) + fmt.Printf("Using default %d seconds", DefaultCacheTTLSec) + opts.CacheTTLSec = DefaultCacheTTLSec + } else { + opts.CacheTTLSec = i + } + } + + opts.ControllerNamespace = os.Getenv(ControllerNamespaceEnv) + if opts.ControllerNamespace == "" { + namespace, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace") + if err != nil { + fmt.Printf("Failed to get namespace from filesystem: %s", err.Error()) + fmt.Printf("Using hardcoded namespace: %s", HardcodedControllerNS) + opts.ControllerNamespace = HardcodedControllerNS + } else { + fmt.Printf("Got namespace from filesystem: %s", string(namespace)) + opts.ControllerNamespace = string(namespace) + } + } + + fl := flag.NewFlagSet(os.Args[0], flag.ExitOnError) + fl.Var(&opts.StorageNetworkCIDR, "storage-network-cidr", "Set storage network CIDR blocks. Can be passed multiple times.") + + err := fl.Parse(os.Args[1:]) + if err != nil { + fmt.Printf("error parsing flags, err: %s\n", err.Error()) + os.Exit(1) + } + + return &opts, nil +} diff --git a/images/storage-network-discoverer/src/internal/logger/logger.go b/images/storage-network-discoverer/src/internal/logger/logger.go new file mode 100644 index 00000000..792082b0 --- /dev/null +++ b/images/storage-network-discoverer/src/internal/logger/logger.go @@ -0,0 +1,103 @@ +/* +Copyright 2023 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logger + +import ( + "context" + "fmt" + "strconv" + + "github.com/go-logr/logr" + "k8s.io/klog/v2/textlogger" +) + +const ( + ErrorLevel Verbosity = "0" + WarningLevel Verbosity = "1" + InfoLevel Verbosity = "2" + DebugLevel Verbosity = "3" + TraceLevel Verbosity = "4" +) + +const ( + warnLvl = iota + 1 + infoLvl + debugLvl + traceLvl +) + +type ( + Verbosity string + // struct just to be unique in context + loggerKey struct{} +) + +type Logger struct { + log logr.Logger +} + +func NewLogger(level Verbosity) (*Logger, error) { + v, err := strconv.Atoi(string(level)) + if err != nil { + return nil, err + } + + log := textlogger.NewLogger(textlogger.NewConfig(textlogger.Verbosity(v))).WithCallDepth(1) + + return &Logger{log: log}, nil +} + +func WithLogger(ctx context.Context, logger *Logger) context.Context { + return context.WithValue(ctx, loggerKey{}, logger) +} + +func FromContext(ctx context.Context) *Logger { + if logger, ok := ctx.Value(loggerKey{}).(*Logger); ok { + return logger + } + + // WARNING! There will be a panic here if the logger was not initialized in the context + return nil +} + +func (l Logger) GetLogger() logr.Logger { + return l.log +} + +func (l Logger) Error(err error, message string, keysAndValues ...interface{}) { + l.log.Error(err, fmt.Sprintf("ERROR %s", message), keysAndValues...) +} + +func (l Logger) Warning(message string, keysAndValues ...interface{}) { + l.log.V(warnLvl).Info(fmt.Sprintf("WARNING %s", message), keysAndValues...) +} + +func (l Logger) Info(message string, keysAndValues ...interface{}) { + l.log.V(infoLvl).Info(fmt.Sprintf("INFO %s", message), keysAndValues...) +} + +func (l Logger) Debug(message string, keysAndValues ...interface{}) { + l.log.V(debugLvl).Info(fmt.Sprintf("DEBUG %s", message), keysAndValues...) +} + +func (l Logger) Trace(message string, keysAndValues ...interface{}) { + l.log.V(traceLvl).Info(fmt.Sprintf("TRACE %s", message), keysAndValues...) +} + +func (l *Logger) Printf(format string, args ...interface{}) { + l.log.V(traceLvl).Info("%s", fmt.Sprintf(format, args...)) +} diff --git a/images/storage-network-discoverer/src/internal/utils/utils.go b/images/storage-network-discoverer/src/internal/utils/utils.go new file mode 100644 index 00000000..a389930f --- /dev/null +++ b/images/storage-network-discoverer/src/internal/utils/utils.go @@ -0,0 +1,41 @@ +/* +Copyright 2024 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils + +import ( + "fmt" + "net/netip" + + "storage-network-controller/internal/logger" +) + +func IPInCIDR(networks []netip.Prefix, ip string, log *logger.Logger) bool { + netIP, err := netip.ParseAddr(ip) + + if err != nil { + log.Error(err, fmt.Sprintf("IPInCIDR: cannot parse IP %s", ip)) + return false + } + + for _, network := range networks { + if network.Contains(netIP) { + return true + } + } + + return false +} diff --git a/images/storage-network-discoverer/src/main.go b/images/storage-network-discoverer/src/main.go new file mode 100644 index 00000000..8d4289d6 --- /dev/null +++ b/images/storage-network-discoverer/src/main.go @@ -0,0 +1,110 @@ +/* +Copyright 2024 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "os" + goruntime "runtime" + + "github.com/square/exit" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/manager" + "storage-network-controller/internal/config" + "storage-network-controller/internal/logger" + "storage-network-controller/pkg/discoverer" +) + +func KubernetesDefaultConfigCreate() (*rest.Config, error) { + clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), + &clientcmd.ConfigOverrides{}, + ) + // Get a config to talk to API server + config, err := clientConfig.ClientConfig() + if err != nil { + return nil, fmt.Errorf("config kubernetes error %w", err) + } + return config, nil +} + +// have a separate function so we can return an error without skipping defers +func runController(ctx context.Context, cfg *config.Options, log *logger.Logger) error { + log.Info(fmt.Sprintf("Go Version:%s ", goruntime.Version())) + log.Info(fmt.Sprintf("OS/Arch:Go OS/Arch:%s/%s ", goruntime.GOOS, goruntime.GOARCH)) + + // Create default config Kubernetes client + kConfig, err := KubernetesDefaultConfigCreate() + if err != nil { + log.Error(err, "error reading a kubernetes configuration") + return err + } + log.Info("read Kubernetes config") + + cacheOpt := cache.Options{} + + managerOpts := manager.Options{ + Cache: cacheOpt, + LeaderElection: false, + } + + mgr, err := ctrl.NewManager(kConfig, managerOpts) + if err != nil { + log.Error(err, "failed to create a manager") + return err + } + + log.Info("created kubernetes manager in namespace: " + cfg.ControllerNamespace) + + ctrl.SetLogger(log.GetLogger()) + + err = discoverer.DiscoveryLoop(ctx, cfg, mgr) + if err != nil { + log.Error(err, "failed to run discovery mode") + return err + } + + log.Info("Shutdown successful") + return nil +} + +func main() { + cfg, err := config.NewConfig() + if err != nil { + fmt.Println("unable to create NewConfig " + err.Error()) + os.Exit(exit.NotOK) + } + + log, err := logger.NewLogger(cfg.Loglevel) + if err != nil { + fmt.Printf("unable to create NewLogger, err: %v\n", err) + os.Exit(exit.NotOK) + } + + // make context from controller-runtime with signals (SIGINT, SIGTERM) handling + ctx := ctrl.SetupSignalHandler() + // add logger to root context to make it available everywhere, where root context is used + ctx = logger.WithLogger(ctx, log) + + err = runController(ctx, cfg, log) + // for err == nil exit.FromError return code 0, so we just pass err without additional 'if err != nil' here + os.Exit(exit.FromError(err)) +} diff --git a/images/storage-network-discoverer/src/pkg/cache/ttl_cache.go b/images/storage-network-discoverer/src/pkg/cache/ttl_cache.go new file mode 100644 index 00000000..a639238d --- /dev/null +++ b/images/storage-network-discoverer/src/pkg/cache/ttl_cache.go @@ -0,0 +1,147 @@ +/* +Copyright 2024 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache + +import ( + "context" + "sync" + "time" +) + +// item represents a cache item with a value and an expiration time. +type item[V any] struct { + value V + expiry time.Time +} + +// isExpired checks if the cache item has expired. +func (i item[V]) isExpired() bool { + return time.Now().After(i.expiry) +} + +// TTLCache is a generic cache implementation with support for time-to-live +// (TTL) expiration. +type TTLCache[K comparable, V any] struct { + items map[K]item[V] // The map storing cache items. + mu sync.Mutex // Mutex for controlling concurrent access to the cache. +} + +// NewTTL creates a new TTLCache instance and starts a goroutine to periodically +// remove expired items every 5 seconds. +func NewTTL[K comparable, V any](ctx context.Context) *TTLCache[K, V] { + c := &TTLCache[K, V]{ + items: make(map[K]item[V]), + } + + // starting up remove expired items loop + go c.removeExpiredLoop(ctx) + + return c +} + +// Iterate over the cache items every 5 sec and delete expired ones. +func (c *TTLCache[K, V]) removeExpiredLoop(ctx context.Context) { + // endless loop + for { + select { + case <-ctx.Done(): + // do nothing in case of cancel, just return from this goroutine + return + + default: + c.mu.Lock() + + // Iterate over the cache items and delete expired ones. + for key, item := range c.items { + if item.isExpired() { + delete(c.items, key) + } + } + + c.mu.Unlock() + + // sleep for 5 sec + time.Sleep(5 * time.Second) + } + } +} + +// Set adds a new item to the cache with the specified key, value, and +// time-to-live (TTL). +func (c *TTLCache[K, V]) Set(key K, value V, ttl time.Duration) { + c.mu.Lock() + defer c.mu.Unlock() + + c.items[key] = item[V]{ + value: value, + expiry: time.Now().Add(ttl), + } +} + +// Get retrieves the value associated with the given key from the cache. +func (c *TTLCache[K, V]) Get(key K) (V, bool) { + c.mu.Lock() + defer c.mu.Unlock() + + item, found := c.items[key] + if !found { + // If the key is not found, return the nil value for V and false. + return item.value, false + } + + if item.isExpired() { + // If the item has expired, remove it from the cache and return the + // value and false. + delete(c.items, key) + return item.value, false + } + + // Otherwise return the value and true. + return item.value, true +} + +// Remove removes the item with the specified key from the cache. +func (c *TTLCache[K, V]) Remove(key K) { + c.mu.Lock() + defer c.mu.Unlock() + + // Delete the item with the given key from the cache. + delete(c.items, key) +} + +// Pop removes and returns the item with the specified key from the cache. +func (c *TTLCache[K, V]) Pop(key K) (V, bool) { + c.mu.Lock() + defer c.mu.Unlock() + + item, found := c.items[key] + if !found { + // If the key is not found, return the zero value for V and false. + return item.value, false + } + + // If the key is found, delete the item from the cache. + delete(c.items, key) + + if item.isExpired() { + // If the item has expired, return the value and false. + return item.value, false + } + + // Otherwise return the value and true. + return item.value, true +} diff --git a/images/storage-network-discoverer/src/pkg/discoverer/discoverer.go b/images/storage-network-discoverer/src/pkg/discoverer/discoverer.go new file mode 100644 index 00000000..3ce46709 --- /dev/null +++ b/images/storage-network-discoverer/src/pkg/discoverer/discoverer.go @@ -0,0 +1,207 @@ +/* +Copyright 2024 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package discoverer + +import ( + "context" + "errors" + "fmt" + "net" + "net/netip" + "os" + "slices" + "strings" + "time" + + "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" + "storage-network-controller/internal/config" + "storage-network-controller/internal/logger" + "storage-network-controller/internal/utils" + "storage-network-controller/pkg/cache" +) + +type discoveredIPs []string + +const ( + // this type will be added to node's status.addresses field + RVStorageIPType = "SDSRVStorageIP" +) + +var DiscoveryCache *cache.TTLCache[string, string] + +func DiscoveryLoop(ctx context.Context, cfg *config.Options, mgr manager.Manager) error { + // just syntactic sugar for logger + log := logger.FromContext(ctx) + + storageNetworks, err := parseCIDRs(cfg.StorageNetworkCIDR) + if err != nil { + log.Error(err, "Cannot parse storage-network-cidr") + return err + } + + myNodeName := os.Getenv("NODE_NAME") + if myNodeName == "" { + return errors.New("cannot get node name because no NODE_NAME env variable") + } + + // we cannot do `mgr.GetClient()` because its require a cache, but we do not want cache feature + // so, create our own kubeclient WITHOUT cache + cl, err := client.New(mgr.GetConfig(), client.Options{}) + if err != nil { + log.Error(err, "Cannot create k8s client") + return err + } + + log.Info("Discoverer settings:") + log.Info(fmt.Sprintf("* Log level: %s", cfg.Loglevel)) + log.Info(fmt.Sprintf("* Discovery every %d seconds", cfg.DiscoverySec)) + log.Info(fmt.Sprintf("* Cache founded IP(s) for %d seconds", cfg.CacheTTLSec)) + log.Info(fmt.Sprintf("* Storage network CIDRs: %s", storageNetworks)) + log.Info(fmt.Sprintf("* I discovery IPs on node %s", myNodeName)) + + // create a new DiscoveryCache with TTL (item expiring) capabilities + DiscoveryCache = cache.NewTTL[string, string](ctx) + + // discoverer loop + for { + select { + case <-ctx.Done(): + // do nothing in case of context canceling and just return from the loop + return nil + + default: + if err := discovery(ctx, myNodeName, storageNetworks, &cl, *cfg); err != nil { + log.Error(err, "Discovery error occurred") + return err + } + } + + time.Sleep(time.Duration(cfg.DiscoverySec) * time.Second) + } +} + +func parseCIDRs(cidrs config.StorageNetworkCIDR) ([]netip.Prefix, error) { + networks := make([]netip.Prefix, len(cidrs)) + + var err error + + for i, cidr := range cidrs { + if networks[i], err = netip.ParsePrefix(cidr); err != nil { + return nil, err + } + } + + return networks, nil +} + +func discovery(ctx context.Context, nodeName string, storageNetworks []netip.Prefix, cl *client.Client, cfg config.Options) error { + log := logger.FromContext(ctx) + + addrs, err := net.InterfaceAddrs() + if err != nil { + log.Error(err, "Cannot get network interfaces") + return err + } + + var foundedIP discoveredIPs + + for _, address := range addrs { + // check the address type: it should be not a loopback and in a private range + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && ipnet.IP.IsPrivate() { + if ipnet.IP.To4() != nil { + ipStr := ipnet.IP.String() + + if utils.IPInCIDR(storageNetworks, ipStr, log) { + log.Debug(fmt.Sprintf("IP '%s' found in CIDR blocks", ipStr)) + foundedIP = append(foundedIP, ipStr) + } else { + log.Trace(fmt.Sprintf("IP '%s' not founded in any CIDR blocks", ipStr)) + } + } + } + } + + if len(foundedIP) > 0 { + log.Info(fmt.Sprintf("Founded %d storage network IPs: %s", len(foundedIP), strings.Join(foundedIP, ", "))) + + // If there is 2 or more IPs founded we get only FIRST IP and warning about others + if len(foundedIP) > 1 { + log.Warning(fmt.Sprintf("Founded more than one storage IP: %s. Use first.", strings.Join(foundedIP, ", "))) + } + ip := foundedIP[0] + + // check node status only if no IP in cache + if _, found := DiscoveryCache.Get(ip); !found { + err := updateNodeStatusIfNeeded(ctx, nodeName, ip, *cl) + if err != nil { + log.Error(err, "cannot update node status field for now. Waiting for next reconciliation") + return nil + } + DiscoveryCache.Set(ip, "", time.Duration(cfg.CacheTTLSec)*time.Second) + } + } + + return nil +} + +func updateNodeStatusIfNeeded(ctx context.Context, nodeName string, ip string, cl client.Client) error { + log := logger.FromContext(ctx) + + node := &v1.Node{} + err := cl.Get(ctx, client.ObjectKey{ + Name: nodeName, + }, node) + + if err != nil { + log.Error(err, "cannot get my node info") + return err + } + + addresses := node.Status.Addresses + + // index of address with type SDSRVStorageIP (if will founded in node addresses) + storageAddrIdx := slices.IndexFunc(addresses, func(addr v1.NodeAddress) bool { return addr.Type == RVStorageIPType }) + + if storageAddrIdx == -1 { + // no address on node status yet + log.Trace(fmt.Sprintf("Append %s with IP %s to status.addresses", RVStorageIPType, ip)) + addresses = append(addresses, v1.NodeAddress{Type: RVStorageIPType, Address: ip}) + } else { + // if is same address, then just return and do nothing + if addresses[storageAddrIdx].Address == ip { + return nil + } + + // address already exists in node.status and it differrent from address in 'ip' + log.Trace(fmt.Sprintf("Change %s from %s to %s in status.addresses", RVStorageIPType, addresses[storageAddrIdx].Address, ip)) + addresses[storageAddrIdx].Address = ip + } + + log.Info(fmt.Sprintf("[updateNodeStatusIfNeeded] update node '%s' and set %s=%s", node.Name, RVStorageIPType, ip)) + + node.Status.Addresses = addresses + err = cl.Status().Update(ctx, node) + + if err != nil { + log.Error(err, "cannot update node status addresses") + return err + } + + return nil +} diff --git a/images/storage-network-discoverer/werf.inc.yaml b/images/storage-network-discoverer/werf.inc.yaml new file mode 100644 index 00000000..a191192c --- /dev/null +++ b/images/storage-network-discoverer/werf.inc.yaml @@ -0,0 +1,35 @@ +--- +image: {{ $.ImageName }}-golang-artifact +from: {{ $.Root.BASE_GOLANG_1_22 }} +final: false + +git: + - add: / + to: / + includePaths: + # - api + - images/{{ $.ImageName }}/src + stageDependencies: + setup: + - "**/*" +mount: + - fromPath: ~/go-pkg-cache + to: /go/pkg +shell: + setup: + - cd /images/{{ $.ImageName }}/src + - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o {{ $.ImageName }} + - mv {{ $.ImageName }} /{{ $.ImageName }} + - chmod +x /{{ $.ImageName }} +--- +image: {{ $.ImageName }} +from: {{ $.Root.BASE_SCRATCH }} + +import: + - image: {{ $.ImageName }}-golang-artifact + add: /{{ $.ImageName }} + to: /{{ $.ImageName }} + before: setup + +docker: + ENTRYPOINT: ["/{{ $.ImageName }}"] \ No newline at end of file diff --git a/openapi/config-values.yaml b/openapi/config-values.yaml index 24c959cd..af99edae 100644 --- a/openapi/config-values.yaml +++ b/openapi/config-values.yaml @@ -20,3 +20,14 @@ properties: type: boolean default: false description: Allow thin LVM volumes usage + storageNetworkCIDRs: + description: Storage network CIDR blocks. Use only if your cluster's nodes haves dedicated network interface attached. + type: array + items: + type: string + pattern: '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$' + default: [] + example: ["10.1.1.0/24"] + x-examples: + - [] + - ["1.2.3.0/24", "3.2.1.0/16"] diff --git a/openapi/doc-ru-config-values.yaml b/openapi/doc-ru-config-values.yaml index 963152f5..a195c61b 100644 --- a/openapi/doc-ru-config-values.yaml +++ b/openapi/doc-ru-config-values.yaml @@ -6,3 +6,9 @@ properties: description: Уровень логирования для приложений модуля enableThinProvisioning: description: Включить поддержку thin LVM томов + storageNetworkCIDRs: + description: CIDR-блоки для выделенной сети Storage. Используйте этот параметр только если к узлам Вашего кластера подключена выделенная сеть для системы хранения данных (СХД). + example: ["10.1.1.0/24"] + x-examples: + - [] + - ["1.2.3.0/24", "3.2.1.0/16"] diff --git a/templates/storage-network-discoverer/daemonset.yaml b/templates/storage-network-discoverer/daemonset.yaml new file mode 100644 index 00000000..bdbe2c86 --- /dev/null +++ b/templates/storage-network-discoverer/daemonset.yaml @@ -0,0 +1,116 @@ +{{- define "storage_network_discoverer_resources" }} +cpu: 50m +memory: 25Mi +{{- end }} + +{{- if .Values.sdsReplicatedVolume.storageNetworkCIDRs }} + +{{- if (.Values.global.enabledModules | has "vertical-pod-autoscaler-crd") }} +--- +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: storage-network-discoverer + namespace: d8-{{ .Chart.Name }} + {{- include "helm_lib_module_labels" (list . (dict "app" "storage-network-discoverer" "workload-resource-policy.deckhouse.io" "every-node")) | nindent 2 }} +spec: + targetRef: + apiVersion: "apps/v1" + kind: DaemonSet + name: storage-network-discoverer + updatePolicy: + updateMode: "Auto" + resourcePolicy: + containerPolicies: + - containerName: discoverer + minAllowed: + {{- include "storage_network_discoverer_resources" . | nindent 8 }} + maxAllowed: + cpu: 200m + memory: 512Mi +{{- end }} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: storage-network-discoverer + namespace: d8-{{ .Chart.Name }} + {{- include "helm_lib_module_labels" (list . (dict "app" "storage-network-discoverer")) | nindent 2 }} +spec: + selector: + matchLabels: + app: storage-network-discoverer + template: + metadata: + name: storage-network-discoverer + namespace: d8-{{ .Chart.Name }} + labels: + app: storage-network-discoverer + annotations: + kubectl.kubernetes.io/default-container: discoverer + spec: + {{- include "helm_lib_priority_class" (tuple . "cluster-medium") | nindent 6 }} + {{- include "helm_lib_tolerations" (tuple . "any-node" "storage-problems") | nindent 6 }} + {{- /* include "helm_lib_module_pod_security_context_run_as_user_root" . | nindent 6 */}} + nodeSelector: + storage.deckhouse.io/sds-replicated-volume-node: "" + imagePullSecrets: + - name: {{ .Chart.Name }}-module-registry + affinity: {} + serviceAccount: storage-network-discoverer + serviceAccountName: storage-network-discoverer + hostNetwork: true + containers: + - name: discoverer + image: {{ include "helm_lib_module_image" (list . "storageNetworkDiscoverer") }} + imagePullPolicy: IfNotPresent + args: + {{- range $cidr := .Values.sdsReplicatedVolume.storageNetworkCIDRs }} + - --storage-network-cidr + - {{ $cidr | quote }} + {{- end }} + env: + - name: LOG_LEVEL +{{- if eq .Values.sdsReplicatedVolume.logLevel "ERROR" }} + value: "0" +{{- else if eq .Values.sdsReplicatedVolume.logLevel "WARN" }} + value: "1" +{{- else if eq .Values.sdsReplicatedVolume.logLevel "INFO" }} + value: "2" +{{- else if eq .Values.sdsReplicatedVolume.logLevel "DEBUG" }} + value: "3" +{{- else if eq .Values.sdsReplicatedVolume.logLevel "TRACE" }} + value: "4" +{{- end }} + # try discover storage IPs on each node every 30s + - name: DISCOVERY_INTERVAL_SEC + value: "30" + # If founded, storage IP will be set to node's status and cached for CACHE_TTL_SEC + # Discoverer DO NOT attempt to update node before this cache will expired + # So, do not set this value too high. (2 * DISCOVERY_INTERVAL_SEC) should be enough. + - name: CACHE_TTL_SEC + value: "60" + # NODE_NAME REQUIRED by discoverer + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + resources: + requests: + {{- include "helm_lib_module_ephemeral_storage_only_logs" . | nindent 12 }} +{{- if not ( .Values.global.enabledModules | has "vertical-pod-autoscaler-crd") }} + {{- include "storage_network_discoverer_resources" . | nindent 12 }} +{{- end }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + updateStrategy: + rollingUpdate: + maxSurge: 0 + maxUnavailable: 100% + type: RollingUpdate + +{{- end }} diff --git a/templates/storage-network-discoverer/rbac-for-us.yaml b/templates/storage-network-discoverer/rbac-for-us.yaml new file mode 100644 index 00000000..cec2035e --- /dev/null +++ b/templates/storage-network-discoverer/rbac-for-us.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: storage-network-discoverer + namespace: d8-{{ .Chart.Name }} + {{- include "helm_lib_module_labels" (list . (dict "app" "storage-network-discoverer")) | nindent 2 }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: d8:{{ .Chart.Name }}:storage-network-discoverer + {{- include "helm_lib_module_labels" (list . (dict "app" "storage-network-discoverer")) | nindent 2 }} +rules: + - apiGroups: + - deckhouse.io + resources: + - moduleconfigs + verbs: + - get + - list + - watch + + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch + + - apiGroups: + - "" + resources: + - nodes/status + verbs: + - get + - list + - patch + - update + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: d8:{{ .Chart.Name }}:storage-network-discoverer + {{- include "helm_lib_module_labels" (list . (dict "app" "storage-network-discoverer")) | nindent 2 }} +subjects: + - kind: ServiceAccount + name: storage-network-discoverer + namespace: d8-{{ .Chart.Name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: d8:{{ .Chart.Name }}:storage-network-discoverer