diff --git a/go.mod b/go.mod index 4705508..66487fc 100644 --- a/go.mod +++ b/go.mod @@ -2,15 +2,15 @@ module github.com/ironcore-dev/fedhcp go 1.24.0 -toolchain go1.24.1 +toolchain go1.24.3 require ( github.com/coredhcp/coredhcp v0.0.0-20240908184240-576af8676ffa github.com/google/addlicense v1.1.1 github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 - github.com/ironcore-dev/controller-utils v0.9.7 - github.com/ironcore-dev/ipam v0.2.2 - github.com/ironcore-dev/metal-operator v0.0.0-20240910120000-bbd70c2a0eb0 + github.com/ironcore-dev/controller-utils v0.9.9 + github.com/ironcore-dev/ipam v0.2.3 + github.com/ironcore-dev/metal-operator v0.0.0-20250513095753-f66aeecc9435 github.com/mdlayher/netx v0.0.0-20230430222610-7e21880baee8 github.com/onsi/ginkgo/v2 v2.23.4 github.com/onsi/gomega v1.37.0 @@ -21,7 +21,7 @@ require ( k8s.io/api v0.33.1 k8s.io/apimachinery v0.33.1 k8s.io/client-go v0.33.1 - sigs.k8s.io/controller-runtime v0.19.4 + sigs.k8s.io/controller-runtime v0.20.4 ) replace github.com/coredhcp/coredhcp => github.com/damyan/coredhcp v0.0.0-20240911115402-66f9c25a305e @@ -29,15 +29,12 @@ replace github.com/coredhcp/coredhcp => github.com/damyan/coredhcp v0.0.0-202409 replace github.com/insomniacslk/dhcp => github.com/damyan/dhcp v0.0.0-20250326181646-a882567e66e8 require ( - github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.14.3 // indirect github.com/bmatcuk/doublestar/v4 v4.2.0 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chappjc/logrus-prefix v0.0.0-20180227015900-3a1d64819adb // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect @@ -55,7 +52,6 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -70,16 +66,14 @@ require ( github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/prometheus/client_golang v1.20.3 // indirect - github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.59.1 // indirect - github.com/prometheus/procfs v0.15.1 // indirect github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.7.0 // indirect - github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect @@ -88,22 +82,21 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.36.0 // indirect + golang.org/x/crypto v0.37.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/net v0.38.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.12.0 // indirect + golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect + golang.org/x/term v0.31.0 // indirect + golang.org/x/text v0.24.0 // indirect golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.31.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - k8s.io/apiextensions-apiserver v0.31.4 // indirect + k8s.io/apiextensions-apiserver v0.32.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect diff --git a/go.sum b/go.sum index d584059..b94deeb 100644 --- a/go.sum +++ b/go.sum @@ -19,10 +19,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 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.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -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/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= @@ -52,8 +50,6 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= @@ -62,12 +58,12 @@ 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/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/ironcore-dev/controller-utils v0.9.7 h1:ywNcB6lDeOe6UWxJaptbdgb9qb6cDx4+jxBcIGkMQD8= -github.com/ironcore-dev/controller-utils v0.9.7/go.mod h1:7X6JUmq75o4KFe05zUa9rEXnS39dSrlXqUnt9Wuiug0= -github.com/ironcore-dev/ipam v0.2.2 h1:mens+psDYnw2Eakf33vNq+6OS2DebZtilMzI+Gx9NRQ= -github.com/ironcore-dev/ipam v0.2.2/go.mod h1:B9+Q+s9tXDJc+ha2J4CrjlxCuqASgcIlrTMs6ZfKb+o= -github.com/ironcore-dev/metal-operator v0.0.0-20240910120000-bbd70c2a0eb0 h1:uka+TDFFXOVdJurwROD+S8crX1Zb1i/d7+4VKrbmHtA= -github.com/ironcore-dev/metal-operator v0.0.0-20240910120000-bbd70c2a0eb0/go.mod h1:WKHotrH3wiLey9PQcQJErK57J+l/g+XddKtm2PqbsVw= +github.com/ironcore-dev/controller-utils v0.9.9 h1:SRVMjj+jh9yDZj//1hGG7U7S4fRuUVICAiXu7JBHJyo= +github.com/ironcore-dev/controller-utils v0.9.9/go.mod h1:hNqQjd6JkRmKYH1MY1XkfT4Eew3P7etHTJjaHj+vK9Y= +github.com/ironcore-dev/ipam v0.2.3 h1:ELQqMA//eOoeSdZ+m98MFmw5d3OaJJX65F2O5BjlTHY= +github.com/ironcore-dev/ipam v0.2.3/go.mod h1:NSAq8rNgjWAFfJQWmYDm90rzJZLI283kfSFqLBplOJk= +github.com/ironcore-dev/metal-operator v0.0.0-20250513095753-f66aeecc9435 h1:ykVWXFwgMzYatwM6R15+fNiYMg+54AeXTl1PhnsQhrM= +github.com/ironcore-dev/metal-operator v0.0.0-20250513095753-f66aeecc9435/go.mod h1:cpeP3tM5BsxEs1u3gidh8QRoaHH+dMLz+Qv6XjTQS/I= 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/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= @@ -82,8 +78,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -155,8 +149,8 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA= -github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -189,8 +183,8 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/W 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/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -208,8 +202,8 @@ golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT 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/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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= @@ -219,12 +213,12 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= 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.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -260,8 +254,8 @@ 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.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= -k8s.io/apiextensions-apiserver v0.31.4 h1:FxbqzSvy92Ca9DIs5jqot883G0Ln/PGXfm/07t39LS0= -k8s.io/apiextensions-apiserver v0.31.4/go.mod h1:hIW9YU8UsqZqIWGG99/gsdIU0Ar45Qd3A12QOe/rvpg= +k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY= +k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss= k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= @@ -272,8 +266,8 @@ k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUy k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= -sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU= +sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= diff --git a/plugins/ipam/k8s.go b/plugins/ipam/k8s.go index 51091c6..07bd5a5 100644 --- a/plugins/ipam/k8s.go +++ b/plugins/ipam/k8s.go @@ -12,15 +12,15 @@ import ( "os" "reflect" "strings" + "time" + + "k8s.io/apimachinery/pkg/util/wait" "github.com/ironcore-dev/fedhcp/internal/kubernetes" ipamv1alpha1 "github.com/ironcore-dev/ipam/api/ipam/v1alpha1" - ipam "github.com/ironcore-dev/ipam/clientgo/ipam" - "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes/scheme" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/record" @@ -33,10 +33,8 @@ const ( type K8sClient struct { Client client.Client - Clientset ipam.Clientset Namespace string SubnetNames []string - Ctx context.Context EventRecorder record.EventRecorder } @@ -44,11 +42,6 @@ func NewK8sClient(namespace string, subnetNames []string) (*K8sClient, error) { cfg := kubernetes.GetConfig() cl := kubernetes.GetClient() - clientset, err := ipam.NewForConfig(cfg) - if err != nil { - return nil, fmt.Errorf("failed to create IPAM clientset: %w", err) - } - corev1Client, err := corev1client.NewForConfig(cfg) if err != nil { return nil, fmt.Errorf("failed to create core client: %w", err) @@ -66,20 +59,18 @@ func NewK8sClient(namespace string, subnetNames []string) (*K8sClient, error) { k8sClient := K8sClient{ Client: cl, - Clientset: *clientset, Namespace: namespace, SubnetNames: subnetNames, - Ctx: context.Background(), EventRecorder: recorder, } return &k8sClient, nil } -func (k K8sClient) createIpamIP(ipaddr net.IP, mac net.HardwareAddr) error { +func (k K8sClient) createIpamIP(ctx context.Context, ipaddr net.IP, mac net.HardwareAddr) error { // select the subnet matching the CIDR of the request subnetMatch := false for _, subnetName := range k.SubnetNames { - subnet, err := k.getMatchingSubnet(subnetName, ipaddr) + subnet, err := k.getMatchingSubnet(ctx, subnetName, ipaddr) if err != nil { return err } @@ -90,12 +81,12 @@ func (k K8sClient) createIpamIP(ipaddr net.IP, mac net.HardwareAddr) error { subnetMatch = true var ipamIP *ipamv1alpha1.IP - ipamIP, err = k.prepareCreateIpamIP(subnetName, ipaddr, mac) + ipamIP, err = k.prepareCreateIpamIP(ctx, subnetName, ipaddr, mac) if err != nil { return err } if ipamIP != nil { - err = k.doCreateIpamIP(ipamIP) + err = k.doCreateIpamIP(ctx, ipamIP) if err != nil { return err } @@ -111,7 +102,7 @@ func (k K8sClient) createIpamIP(ipaddr net.IP, mac net.HardwareAddr) error { return nil } -func (k K8sClient) getMatchingSubnet(subnetName string, ipaddr net.IP) (*ipamv1alpha1.Subnet, error) { +func (k K8sClient) getMatchingSubnet(ctx context.Context, subnetName string, ipaddr net.IP) (*ipamv1alpha1.Subnet, error) { subnet := &ipamv1alpha1.Subnet{ ObjectMeta: metav1.ObjectMeta{ Name: subnetName, @@ -119,7 +110,7 @@ func (k K8sClient) getMatchingSubnet(subnetName string, ipaddr net.IP) (*ipamv1a }, } existingSubnet := subnet.DeepCopy() - err := k.Client.Get(k.Ctx, client.ObjectKeyFromObject(subnet), existingSubnet) + err := k.Client.Get(ctx, client.ObjectKeyFromObject(subnet), existingSubnet) if err != nil && !apierrors.IsNotFound(err) { return nil, fmt.Errorf("failed to get subnet %s/%s: %w", k.Namespace, subnetName, err) } @@ -135,10 +126,7 @@ func (k K8sClient) getMatchingSubnet(subnetName string, ipaddr net.IP) (*ipamv1a return subnet, nil } -func (k K8sClient) prepareCreateIpamIP( - subnetName string, - ipaddr net.IP, - mac net.HardwareAddr) (*ipamv1alpha1.IP, error) { +func (k K8sClient) prepareCreateIpamIP(ctx context.Context, subnetName string, ipaddr net.IP, mac net.HardwareAddr) (*ipamv1alpha1.IP, error) { ip, err := ipamv1alpha1.IPAddrFromString(ipaddr.String()) if err != nil { return nil, fmt.Errorf("failed to parse IP %s: %w", ipaddr, err) @@ -169,7 +157,7 @@ func (k K8sClient) prepareCreateIpamIP( } existingIpamIP := ipamIP.DeepCopy() - err = k.Client.Get(k.Ctx, client.ObjectKeyFromObject(ipamIP), existingIpamIP) + err = k.Client.Get(ctx, client.ObjectKeyFromObject(ipamIP), existingIpamIP) if err != nil && !apierrors.IsNotFound(err) { return nil, fmt.Errorf("failed to get IP %s/%s: %w", existingIpamIP.Namespace, existingIpamIP.Name, err) } @@ -183,14 +171,18 @@ func (k K8sClient) prepareCreateIpamIP( prettyFormat(ipamIP.Spec)) log.Infof("Deleting old IP %s/%s", existingIpamIP.Namespace, existingIpamIP.Name) // delete old IP object - err = k.Client.Delete(k.Ctx, existingIpamIP) + err = k.Client.Delete(ctx, existingIpamIP) if err != nil { return nil, fmt.Errorf("failed to delete IP %s/%s: %w", existingIpamIP.Namespace, existingIpamIP.Name, err) } - err = k.waitForDeletion(existingIpamIP) - if err != nil { + if err := wait.PollUntilContextTimeout(ctx, 1*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) { + if err := k.Client.Get(ctx, client.ObjectKeyFromObject(existingIpamIP), existingIpamIP); !apierrors.IsNotFound(err) { + return false, err + } + return true, nil + }); err != nil { return nil, fmt.Errorf("failed to delete IP %s/%s: %w", existingIpamIP.Namespace, existingIpamIP.Name, err) } @@ -208,37 +200,8 @@ func (k K8sClient) prepareCreateIpamIP( return ipamIP, nil } -func (k K8sClient) waitForDeletion(ipamIP *ipamv1alpha1.IP) error { - // Define the namespace and resource name (if you want to watch a specific resource) - namespace := ipamIP.Namespace - resourceName := ipamIP.Name - fieldSelector := "metadata.name=" + resourceName + ",metadata.namespace=" + namespace - timeout := int64(5) - - // watch for deletion finished event - watcher, err := k.Clientset.IpamV1alpha1().IPs(namespace).Watch(context.TODO(), metav1.ListOptions{ - FieldSelector: fieldSelector, - TimeoutSeconds: &timeout, - }) - if err != nil { - log.Errorf("Error watching for IP: %v", err) - } - - log.Debugf("Watching for changes to IP %s/%s...", namespace, resourceName) - - for event := range watcher.ResultChan() { - log.Debugf("Type: %s, Object: %v\n", event.Type, event.Object) - foundIpamIP := event.Object.(*ipamv1alpha1.IP) - if event.Type == watch.Deleted && reflect.DeepEqual(ipamIP.Spec, foundIpamIP.Spec) { - log.Infof("IP %s/%s deleted", foundIpamIP.Namespace, foundIpamIP.Name) - return nil - } - } - return errors.New("timeout reached, IP not deleted") -} - -func (k K8sClient) doCreateIpamIP(ipamIP *ipamv1alpha1.IP) error { - err := k.Client.Create(k.Ctx, ipamIP) +func (k K8sClient) doCreateIpamIP(ctx context.Context, ipamIP *ipamv1alpha1.IP) error { + err := k.Client.Create(ctx, ipamIP) if err != nil && !apierrors.IsAlreadyExists(err) { return fmt.Errorf("failed to create IP %s/%s: %w", ipamIP.Namespace, ipamIP.Name, err) } diff --git a/plugins/ipam/plugin.go b/plugins/ipam/plugin.go index 4680cc8..086abf5 100644 --- a/plugins/ipam/plugin.go +++ b/plugins/ipam/plugin.go @@ -4,6 +4,7 @@ package ipam import ( + "context" "fmt" "github.com/coredhcp/coredhcp/handler" @@ -94,8 +95,10 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) { copy(ipaddr, relayMsg.LinkAddr) ipaddr[len(ipaddr)-1] += 1 + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() log.Infof("Generated IP address %s for mac %s", ipaddr.String(), mac.String()) - err = k8sClient.createIpamIP(ipaddr, mac) + err = k8sClient.createIpamIP(ctx, ipaddr, mac) if err != nil { log.Errorf("Could not create IPAM IP: %s", err) return nil, true diff --git a/plugins/metal/plugin.go b/plugins/metal/plugin.go index 573de3f..294ee4d 100644 --- a/plugins/metal/plugin.go +++ b/plugins/metal/plugin.go @@ -157,7 +157,7 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) { return nil, true } - if err := ApplyEndpointForMACAddress(mac, ipamv1alpha1.CIPv6SubnetType); err != nil { + if err := ApplyEndpointForMACAddress(mac, ipamv1alpha1.IPv6SubnetType); err != nil { log.Errorf("Could not apply endpoint for mac %s: %s", mac.String(), err) return resp, false } @@ -171,7 +171,7 @@ func handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) { mac := req.ClientHWAddr - if err := ApplyEndpointForMACAddress(mac, ipamv1alpha1.CIPv4SubnetType); err != nil { + if err := ApplyEndpointForMACAddress(mac, ipamv1alpha1.IPv4SubnetType); err != nil { log.Errorf("Could not apply peer address: %s", err) return resp, false } diff --git a/plugins/metal/plugin_test.go b/plugins/metal/plugin_test.go index 4866fb2..186a0ee 100644 --- a/plugins/metal/plugin_test.go +++ b/plugins/metal/plugin_test.go @@ -307,7 +307,7 @@ var _ = Describe("Endpoint", func() { It("Should not return an IP address for a known machine without IP address", func(ctx SpecContext) { mac, _ := net.ParseMAC(machineWithoutIPAddressMACAddress) - ip, err := GetIPAMIPAddressForMACAddress(mac, ipamv1alpha1.CIPv6SubnetType) + ip, err := GetIPAMIPAddressForMACAddress(mac, ipamv1alpha1.IPv6SubnetType) Eventually(err).ShouldNot(HaveOccurred()) Eventually(ip).Should(BeNil()) }) diff --git a/plugins/oob/k8s.go b/plugins/oob/k8s.go index 63f5baa..cd89313 100644 --- a/plugins/oob/k8s.go +++ b/plugins/oob/k8s.go @@ -9,18 +9,18 @@ import ( "fmt" "net" "os" - "reflect" "strings" + "time" - "github.com/ironcore-dev/fedhcp/internal/kubernetes" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/watch" + "github.com/ironcore-dev/fedhcp/internal/kubernetes" ipamv1alpha1 "github.com/ironcore-dev/ipam/api/ipam/v1alpha1" - ipam "github.com/ironcore-dev/ipam/clientgo/ipam" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" @@ -34,22 +34,20 @@ const ( type K8sClient struct { Client client.Client - Clientset ipam.Clientset Namespace string - OobLabel string - Ctx context.Context + oobLabelValue string + oobLabelKey string EventRecorder record.EventRecorder } func NewK8sClient(namespace string, oobLabel string) (*K8sClient, error) { + if !strings.Contains(oobLabel, "=") { + return nil, fmt.Errorf("invalid subnet label: %s, should be 'key=value'", oobLabel) + } + cfg := kubernetes.GetConfig() cl := kubernetes.GetClient() - clientset, err := ipam.NewForConfig(cfg) - if err != nil { - return nil, fmt.Errorf("failed to create IPAM clientset %w", err) - } - corev1Client, err := corev1client.NewForConfig(cfg) if err != nil { return nil, fmt.Errorf("failed to create core client %w", err) @@ -65,12 +63,12 @@ func NewK8sClient(namespace string, oobLabel string) (*K8sClient, error) { recorder := broadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: id}) broadcaster.StartRecordingToSink(&corev1client.EventSinkImpl{Interface: corev1Client.Events("")}) + labelKev, labelValue := strings.Split(oobLabel, "=")[0], strings.Split(oobLabel, "=")[1] k8sClient := K8sClient{ Client: cl, - Clientset: *clientset, Namespace: namespace, - OobLabel: oobLabel, - Ctx: context.Background(), + oobLabelKey: labelKev, + oobLabelValue: labelValue, EventRecorder: recorder, } @@ -78,6 +76,7 @@ func NewK8sClient(namespace string, oobLabel string) (*K8sClient, error) { } func (k K8sClient) getIp( + ctx context.Context, ipaddr net.IP, mac net.HardwareAddr, exactIP bool, @@ -85,16 +84,22 @@ func (k K8sClient) getIp( var ipamIP *ipamv1alpha1.IP macKey := strings.ReplaceAll(mac.String(), ":", "") - subnetNames := k.getOOBNetworks(subnetType) + subnetNames, err := k.getOOBNetworks(ctx, subnetType) + if err != nil { + return nil, fmt.Errorf("failed to get OOB networks: %w", err) + } if len(subnetNames) == 0 { return nil, errors.New("No OOB subnets found") } else { log.Debugf("%d OOB subnets found: %s", len(subnetNames), strings.Join(subnetNames, " ")) subnetMatch := false + var errorList []error for _, subnetName := range subnetNames { - subnet, err := k.getMatchingSubnet(subnetName, ipaddr) + subnet, err := k.getMatchingSubnet(ctx, subnetName, ipaddr) if err != nil { - return nil, err + errorList = append(errorList, err) + log.Debugf("Error getting subnet %s/%s: %v", k.Namespace, subnetName, err) + continue } if subnet == nil { continue @@ -102,19 +107,21 @@ func (k K8sClient) getIp( log.Debugf("Selecting subnet %s/%s", k.Namespace, subnetName) subnetMatch = true - ipamIP, err = k.prepareCreateIpamIP(subnetName, macKey) + ipamIP, err = k.prepareCreateIpamIP(ctx, subnetName, macKey) if err != nil { return nil, err } if ipamIP == nil { - ipamIP, err = k.doCreateIpamIP(subnetName, macKey, ipaddr, exactIP) + ipamIP, err = k.doCreateIpamIP(ctx, subnetName, macKey, ipaddr, exactIP) if err != nil { return nil, err } } else { log.Infof("Reserved IP %s (%s/%s) already exists in subnet %s", ipamIP.Status.Reserved.String(), ipamIP.Namespace, ipamIP.Name, ipamIP.Spec.Subnet.Name) - k.applySubnetLabel(ipamIP) + if err := k.applySubnetLabel(ctx, ipamIP); err != nil { + return nil, err + } } // break at first subnet match, there can be only one break @@ -131,41 +138,30 @@ func (k K8sClient) getIp( } } -func (k K8sClient) prepareCreateIpamIP(subnetName string, macKey string) (*ipamv1alpha1.IP, error) { - namespace := k.Namespace - fieldSelector := "metadata.namespace=" + namespace - // https://github.com/ironcore-dev/ipam/issues/307 - // fieldSelector += ",spec.subnet.name=" + subnetName - labelSelector := "mac=" + macKey - //labelSelector += ",origin=" + origin - timeout := int64(5) - - ipList, err := k.Clientset.IpamV1alpha1().IPs(namespace).List(context.TODO(), metav1.ListOptions{ - FieldSelector: fieldSelector, - LabelSelector: labelSelector, - TimeoutSeconds: &timeout, - }) - if err != nil { +func (k K8sClient) prepareCreateIpamIP(ctx context.Context, subnetName string, macKey string) (*ipamv1alpha1.IP, error) { + ipList := &ipamv1alpha1.IPList{} + if err := k.Client.List(ctx, ipList, client.InNamespace(k.Namespace), client.MatchingLabels{ + "mac": macKey, + }); err != nil { return nil, fmt.Errorf("error listing IPs with MAC %v: %w", macKey, err) } + for _, existingIpamIP := range ipList.Items { if existingIpamIP.Spec.Subnet.Name != subnetName { // IP with that MAC is assigned to a different subnet (v4 vs v6?) log.Debugf("IPAM IP with MAC %v and wrong subnet %s/%s found, ignoring", macKey, existingIpamIP.Namespace, existingIpamIP.Spec.Subnet.Name) continue - } else if existingIpamIP.Status.State == ipamv1alpha1.CFailedIPState { + } else if existingIpamIP.Status.State == ipamv1alpha1.FailedIPState { log.Infof("Failed IP %s/%s in subnet %s found, deleting", existingIpamIP.Namespace, existingIpamIP.Name, existingIpamIP.Spec.Subnet.Name) log.Debugf("Deleting old IP %s/%s:\n%v", existingIpamIP.Namespace, existingIpamIP.Name, prettyFormat(existingIpamIP.Status)) - err = k.Client.Delete(k.Ctx, &existingIpamIP) - if err != nil { + if err := k.Client.Delete(ctx, &existingIpamIP); err != nil { return nil, fmt.Errorf("failed to delete IP %s/%s: %w", existingIpamIP.Namespace, existingIpamIP.Name, err) } - err = k.waitForDeletion(&existingIpamIP) - if err != nil { + if err := k.waitForDeletion(ctx, &existingIpamIP); err != nil { return nil, fmt.Errorf("failed to delete IP %s/%s: %w", existingIpamIP.Namespace, existingIpamIP.Name, err) } @@ -181,23 +177,15 @@ func (k K8sClient) prepareCreateIpamIP(subnetName string, macKey string) (*ipamv return nil, nil } -func (k K8sClient) doCreateIpamIP( - subnetName string, - macKey string, - ipaddr net.IP, - exactIP bool) (*ipamv1alpha1.IP, error) { - oobLabelKey := strings.Split(k.OobLabel, "=")[0] - oobLabelValue := strings.Split(k.OobLabel, "=")[1] - var ipamIP *ipamv1alpha1.IP - - ipamIP = &ipamv1alpha1.IP{ +func (k K8sClient) doCreateIpamIP(ctx context.Context, subnetName string, macKey string, ipaddr net.IP, exactIP bool) (*ipamv1alpha1.IP, error) { + ipamIP := &ipamv1alpha1.IP{ ObjectMeta: metav1.ObjectMeta{ GenerateName: macKey + "-" + origin + "-", Namespace: k.Namespace, Labels: map[string]string{ - "mac": macKey, - "origin": origin, - oobLabelKey: oobLabelValue, + "mac": macKey, + "origin": origin, + k.oobLabelKey: k.oobLabelValue, }, }, Spec: ipamv1alpha1.IPSpec{ @@ -212,7 +200,7 @@ func (k K8sClient) doCreateIpamIP( ipamIP.Spec.IP = ip } - if err := k.Client.Create(k.Ctx, ipamIP); err != nil { + if err := k.Client.Create(ctx, ipamIP); err != nil { if !apierrors.IsAlreadyExists(err) { return nil, fmt.Errorf("failed to create IP %s/%s: %w", ipamIP.Namespace, ipamIP.Name, err) } else { @@ -221,7 +209,7 @@ func (k K8sClient) doCreateIpamIP( } } - ipamIP, err := k.waitForCreation(ipamIP) + ipamIP, err := k.waitForCreation(ctx, ipamIP) if err != nil { return nil, fmt.Errorf("failed to create IP %w", err) } else { @@ -231,7 +219,7 @@ func (k K8sClient) doCreateIpamIP( // update IP attributes createdIpamIP := ipamIP.DeepCopy() - err := k.Client.Get(k.Ctx, client.ObjectKeyFromObject(createdIpamIP), createdIpamIP) + err := k.Client.Get(ctx, client.ObjectKeyFromObject(createdIpamIP), createdIpamIP) if err != nil && !apierrors.IsNotFound(err) { return nil, fmt.Errorf("failed to get IP %s/%s: %w", createdIpamIP.Namespace, createdIpamIP.Name, err) } @@ -239,144 +227,72 @@ func (k K8sClient) doCreateIpamIP( } } -func (k K8sClient) waitForDeletion(ipamIP *ipamv1alpha1.IP) error { - // Define the namespace and resource name (if you want to watch a specific resource) - namespace := ipamIP.Namespace - resourceName := ipamIP.Name - fieldSelector := "metadata.name=" + resourceName + ",metadata.namespace=" + namespace - timeout := int64(5) - - // watch for deletion finished event - watcher, err := k.Clientset.IpamV1alpha1().IPs(namespace).Watch(context.TODO(), metav1.ListOptions{ - FieldSelector: fieldSelector, - TimeoutSeconds: &timeout, - }) - if err != nil { - log.Errorf("Error watching for IP: %v", err) - } - - log.Tracef("Watching for changes to IP %s/%s...", namespace, resourceName) - - for event := range watcher.ResultChan() { - log.Tracef("Type: %s, Object: %v\n", event.Type, event.Object) - existingIpamIP := event.Object.(*ipamv1alpha1.IP) - if event.Type == watch.Deleted && reflect.DeepEqual(ipamIP.Spec, existingIpamIP.Spec) { - log.Infof("IP %s/%s deleted", existingIpamIP.Namespace, existingIpamIP.Name) - return nil +func (k K8sClient) waitForDeletion(ctx context.Context, ipamIP *ipamv1alpha1.IP) error { + if err := wait.PollUntilContextTimeout(ctx, 1*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) { + if err := k.Client.Get(ctx, client.ObjectKeyFromObject(ipamIP), ipamIP); !apierrors.IsNotFound(err) { + return false, err } - } - return errors.New("Timeout reached, IP not deleted") -} - -func (k K8sClient) waitForCreation(ipamIP *ipamv1alpha1.IP) (*ipamv1alpha1.IP, error) { - // Define the namespace and resource name (if you want to watch a specific resource) - namespace := ipamIP.Namespace - resourceName := ipamIP.Name - fieldSelector := "metadata.name=" + resourceName + ",metadata.namespace=" + namespace - timeout := int64(10) - - // watch for creation finished event - watcher, err := k.Clientset.IpamV1alpha1().IPs(namespace).Watch(context.TODO(), metav1.ListOptions{ - FieldSelector: fieldSelector, - TimeoutSeconds: &timeout, - }) - if err != nil { - log.Errorf("Error watching for IP: %v", err) + return true, nil + }); err != nil { + return fmt.Errorf("failed to delete IP %s/%s: %w", ipamIP.Namespace, ipamIP.Name, err) } - log.Tracef("Watching for changes to IP %s/%s...", namespace, resourceName) + return nil +} - for event := range watcher.ResultChan() { - log.Tracef("Type: %s, Object: %v\n", event.Type, event.Object) - createdIpamIP := event.Object.(*ipamv1alpha1.IP) - if event.Type == watch.Added || event.Type == watch.Modified { - if createdIpamIP.Status.State == ipamv1alpha1.CFinishedIPState { - log.Debug("IP creation finished") - return createdIpamIP, nil - } else if createdIpamIP.Status.State == ipamv1alpha1.CProcessingIPState { - continue - } else if createdIpamIP.Status.State == ipamv1alpha1.CFailedIPState { - return nil, errors.New("Failed to create IP address") - } +func (k K8sClient) waitForCreation(ctx context.Context, ipamIP *ipamv1alpha1.IP) (*ipamv1alpha1.IP, error) { + if err := wait.PollUntilContextTimeout(ctx, 1*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) { + if err := k.Client.Get(ctx, client.ObjectKeyFromObject(ipamIP), ipamIP); apierrors.IsNotFound(err) { + return false, err } + return true, nil + }); err != nil { + return nil, fmt.Errorf("failed to get IP %s/%s: %w", ipamIP.Namespace, ipamIP.Name, err) } - return nil, errors.New("Timeout reached, IP not created") -} -func (k K8sClient) getOOBNetworks(subnetType ipamv1alpha1.SubnetAddressType) []string { - timeout := int64(5) + return ipamIP, nil +} - subnetList, err := k.Clientset.IpamV1alpha1().Subnets(k.Namespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: k.OobLabel, - TimeoutSeconds: &timeout, - }) - if err != nil { - log.Errorf("Error listing OOB subnets: %v", err) +func (k K8sClient) getOOBNetworks(ctx context.Context, subnetType ipamv1alpha1.SubnetAddressType) ([]string, error) { + subnetList := &ipamv1alpha1.SubnetList{} + if err := k.Client.List(ctx, subnetList, client.InNamespace(k.Namespace), client.MatchingLabels{ + k.oobLabelKey: k.oobLabelValue, + }); err != nil { + return nil, fmt.Errorf("error listing OOB subnets: %w", err) } - oobSubnetNames := []string{} + var oobSubnetNames []string for _, subnet := range subnetList.Items { if subnet.Status.Type == subnetType { oobSubnetNames = append(oobSubnetNames, subnet.Name) } } - return oobSubnetNames + return oobSubnetNames, nil } -func (k K8sClient) getMatchingSubnet(subnetName string, ipaddr net.IP) (*ipamv1alpha1.Subnet, error) { - subnet := &ipamv1alpha1.Subnet{ - ObjectMeta: metav1.ObjectMeta{ - Name: subnetName, - Namespace: k.Namespace, - }, - } - existingSubnet := subnet.DeepCopy() - err := k.Client.Get(k.Ctx, client.ObjectKeyFromObject(subnet), existingSubnet) - if err != nil && !apierrors.IsNotFound(err) { - return nil, fmt.Errorf("failed to get subnet %s/%s: %w", k.Namespace, subnetName, err) +func (k K8sClient) getMatchingSubnet(ctx context.Context, subnetName string, ipaddr net.IP) (*ipamv1alpha1.Subnet, error) { + subnet := &ipamv1alpha1.Subnet{} + if err := k.Client.Get(ctx, types.NamespacedName{Name: subnetName, Namespace: k.Namespace}, subnet); apierrors.IsNotFound(err) { + return nil, fmt.Errorf("cannot select subnet %s, does not exist", client.ObjectKeyFromObject(subnet)) + } else if !apierrors.IsNotFound(err) { + return nil, fmt.Errorf("failed to get subnet %s: %w", client.ObjectKeyFromObject(subnet), err) } - if apierrors.IsNotFound(err) { - log.Debugf("Cannot select subnet %s/%s, does not exist", k.Namespace, subnetName) - return nil, nil - } - if !checkIPInCIDR(ipaddr, existingSubnet.Status.Reserved.String()) && ipaddr.String() != UNKNOWN_IP { - log.Debugf("Cannot select subnet %s/%s, CIDR mismatch", k.Namespace, subnetName) - return nil, nil + + if !checkIPInCIDR(ipaddr, subnet.Status.Reserved.String()) && ipaddr.String() != UNKNOWN_IP { + return nil, fmt.Errorf("cannot select subnet %s, CIDR mismatch", client.ObjectKeyFromObject(subnet)) } return subnet, nil } -func (k K8sClient) applySubnetLabel(ipamIP *ipamv1alpha1.IP) { - oobLabelKey := strings.Split(k.OobLabel, "=")[0] - oobLabelValue := strings.Split(k.OobLabel, "=")[1] - - log.Debugf("Current labels: %v", ipamIP.Labels) - - _, exists := ipamIP.Labels[oobLabelKey] - if exists && ipamIP.Labels[oobLabelKey] == oobLabelValue { - log.Debug("Subnet label up-to-date") - } else { - if !exists { - ipamIP, err := k.Clientset.IpamV1alpha1().IPs(ipamIP.Namespace).Get(context.TODO(), ipamIP.Name, metav1.GetOptions{}) - if err != nil { - log.Errorf("Error applying subnet label to IPAM IP %s: %v\n", ipamIP.Name, err) - } else { - if ipamIP.Labels == nil { - ipamIP.Labels = make(map[string]string) - } - } - } - - ipamIP.Labels[oobLabelKey] = oobLabelValue - _, err := k.Clientset.IpamV1alpha1().IPs(ipamIP.Namespace).Update(context.TODO(), ipamIP, metav1.UpdateOptions{}) - if err != nil { - log.Errorf("Error applying label to IPAM IP %s: %v\n", ipamIP.Name, err) - } else { - log.Debugf("Subnet label applied to IPAM IP %s\n", ipamIP.Name) - } +func (k K8sClient) applySubnetLabel(ctx context.Context, ipamIP *ipamv1alpha1.IP) error { + ipamIPBase := ipamIP.DeepCopy() + ipamIP.Labels[k.oobLabelKey] = k.oobLabelValue + if err := k.Client.Patch(ctx, ipamIP, client.MergeFrom(ipamIPBase)); err != nil { + return fmt.Errorf("failed to patch IP %s/%s: %w", ipamIP.Namespace, ipamIP.Name, err) } + return nil } func prettyFormat(ipSpec interface{}) string { diff --git a/plugins/oob/plugin.go b/plugins/oob/plugin.go index 928c8f1..04ad544 100644 --- a/plugins/oob/plugin.go +++ b/plugins/oob/plugin.go @@ -4,6 +4,7 @@ package oob import ( + "context" "fmt" "net" "os" @@ -107,8 +108,10 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) { ipaddr := make(net.IP, len(relayMsg.LinkAddr)) copy(ipaddr, relayMsg.LinkAddr) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() log.Infof("Requested IP address from relay %s for mac %s", ipaddr.String(), mac.String()) - leaseIP, err := k8sClient.getIp(ipaddr, mac, false, ipamv1alpha1.CIPv6SubnetType) + leaseIP, err := k8sClient.getIp(ctx, ipaddr, mac, false, ipamv1alpha1.IPv6SubnetType) if err != nil { log.Errorf("Could not get IPAM IP: %s", err) return nil, true @@ -189,8 +192,10 @@ func handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) { exactIP = false } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() log.Debugf("IP: %v", ipaddr) - leaseIP, err := k8sClient.getIp(ipaddr, mac, exactIP, ipamv1alpha1.CIPv4SubnetType) + leaseIP, err := k8sClient.getIp(ctx, ipaddr, mac, exactIP, ipamv1alpha1.IPv4SubnetType) if err != nil { log.Errorf("Could not get IPAM IP: %s", err) return nil, true