diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 16a9a56..206154e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ jobs: run: curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.26.0 - name: Run golangci-lint - run: $(go env GOPATH)/bin/golangci-lint run + run: $(go env GOPATH)/bin/golangci-lint run -p bugs - name: build and test run: | diff --git a/go.mod b/go.mod index 1df4426..ac5f0af 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,14 @@ module github.com/metal-stack/go-ipam -go 1.15 +go 1.16 require ( github.com/avast/retry-go v3.0.0+incompatible github.com/jmoiron/sqlx v1.3.1 github.com/lib/pq v1.9.0 - github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 github.com/testcontainers/testcontainers-go v0.9.0 - golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect - golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect - inet.af/netaddr v0.0.0-20210129021658-06debf945877 + golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect + inet.af/netaddr v0.0.0-20210222205655-a1ec2b7b8c44 ) diff --git a/go.sum b/go.sum index a610ab4..fa48a75 100644 --- a/go.sum +++ b/go.sum @@ -14,7 +14,6 @@ github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPK github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -27,7 +26,6 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dvyukov/go-fuzz v0.0.0-20201127111758-49e582c6c23d/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= @@ -55,7 +53,6 @@ github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE= github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= @@ -80,9 +77,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -99,14 +94,10 @@ github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -116,22 +107,19 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg= go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e h1:ExUmGi0ZsQmiVo9giDQqXkr7vreeXPMkOGIusfsfbzI= go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc= go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -140,7 +128,6 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -148,9 +135,7 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -165,14 +150,10 @@ google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -181,5 +162,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0= gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -inet.af/netaddr v0.0.0-20210129021658-06debf945877 h1:kiGwZoOS5kyefrTgIAvPzyfTuVfensOlU73GJN9OQKY= -inet.af/netaddr v0.0.0-20210129021658-06debf945877/go.mod h1:I2i9ONCXRZDnG1+7O8fSuYzjcPxHQXrIfzD/IkR87x4= +inet.af/netaddr v0.0.0-20210222205655-a1ec2b7b8c44 h1:p7fX77zWzZMuNdJUhniBsmN1OvFOrW9SOtvgnzqUZX4= +inet.af/netaddr v0.0.0-20210222205655-a1ec2b7b8c44/go.mod h1:I2i9ONCXRZDnG1+7O8fSuYzjcPxHQXrIfzD/IkR87x4= diff --git a/memory.go b/memory.go index b382a63..373df8d 100644 --- a/memory.go +++ b/memory.go @@ -3,8 +3,6 @@ package ipam import ( "fmt" "sync" - - "github.com/pkg/errors" ) type memory struct { @@ -38,7 +36,7 @@ func (m *memory) ReadPrefix(prefix string) (Prefix, error) { result, ok := m.prefixes[prefix] if !ok { - return Prefix{}, errors.Errorf("Prefix %s not found", prefix) + return Prefix{}, fmt.Errorf("prefix %s not found", prefix) } return *result.deepCopy(), nil } diff --git a/memory_test.go b/memory_test.go index 2668800..7fbc277 100644 --- a/memory_test.go +++ b/memory_test.go @@ -13,7 +13,7 @@ func Test_ReadPrefix(t *testing.T) { // Prefix p, err := m.ReadPrefix("12.0.0.0/8") require.NotNil(t, err) - require.Equal(t, "Prefix 12.0.0.0/8 not found", err.Error()) + require.Equal(t, "prefix 12.0.0.0/8 not found", err.Error()) require.Empty(t, p) prefix := Prefix{Cidr: "12.0.0.0/16"} diff --git a/postgres.go b/postgres.go index d4a5bc2..3de81a4 100644 --- a/postgres.go +++ b/postgres.go @@ -54,7 +54,7 @@ func NewPostgresStorage(host, port, user, password, dbname string, sslmode SSLMo func newPostgres(host, port, user, password, dbname string, sslmode SSLMode) (*sql, error) { db, err := sqlx.Connect("postgres", dataSource(host, port, user, password, dbname, sslmode)) if err != nil { - return nil, fmt.Errorf("unable to connect to database:%v", err) + return nil, fmt.Errorf("unable to connect to database:%w", err) } db.MustExec(postgresSchema) return &sql{ diff --git a/prefix.go b/prefix.go index a394a88..1fbdc20 100644 --- a/prefix.go +++ b/prefix.go @@ -3,13 +3,13 @@ package ipam import ( "bytes" "encoding/gob" + "errors" "fmt" "math" "net" "strings" "github.com/avast/retry-go" - "github.com/pkg/errors" "inet.af/netaddr" ) @@ -20,6 +20,8 @@ var ( ErrNoIPAvailable NoIPAvailableError // ErrAlreadyAllocated is returned if the requested address is not available ErrAlreadyAllocated AlreadyAllocatedError + // ErrOptimisticLockError is returned if insert or update conflicts with the existing data + ErrOptimisticLockError OptimisticLockError ) // Prefix is a expression of a ip with length and forms a classless network. @@ -160,7 +162,7 @@ func (i *ipamer) DeletePrefix(cidr string) (*Prefix, error) { } prefix, err := i.storage.DeletePrefix(*p) if err != nil { - return nil, fmt.Errorf("delete prefix:%s %v", cidr, err) + return nil, fmt.Errorf("delete prefix:%s %w", cidr, err) } return &prefix, nil @@ -192,7 +194,7 @@ func (i *ipamer) acquireChildPrefixInternal(parentCidr string, length uint8) (*P return nil, fmt.Errorf("prefix %s has ips, acquire child prefix not possible", parent.Cidr) } - var ipset netaddr.IPSet + var ipset netaddr.IPSetBuilder ipset.AddPrefix(ipprefix) for cp, available := range parent.availableChildPrefixes { if available { @@ -205,9 +207,9 @@ func (i *ipamer) acquireChildPrefixInternal(parentCidr string, length uint8) (*P ipset.RemovePrefix(cpipprefix) } - cp, ok := ipset.RemoveFreePrefix(length) + cp, _, ok := ipset.IPSet().RemoveFreePrefix(length) if !ok { - pfxs := ipset.Prefixes() + pfxs := ipset.IPSet().Prefixes() if len(pfxs) == 0 { return nil, fmt.Errorf("no prefix found in %s with length:%d", parentCidr, length) } @@ -234,15 +236,15 @@ func (i *ipamer) acquireChildPrefixInternal(parentCidr string, length uint8) (*P _, err = i.storage.UpdatePrefix(*parent) if err != nil { - return nil, errors.Wrapf(err, "unable to update parent prefix:%v", parent) + return nil, fmt.Errorf("unable to update parent prefix:%v error:%w", parent, err) } child, err = i.newPrefix(child.Cidr, parentCidr) if err != nil { - return nil, fmt.Errorf("unable to persist created child:%v", err) + return nil, fmt.Errorf("unable to persist created child:%w", err) } _, err = i.storage.CreatePrefix(*child) if err != nil { - return nil, errors.Wrapf(err, "unable to update parent prefix:%v", child) + return nil, fmt.Errorf("unable to update parent prefix:%v error:%w", child, err) } return child, nil @@ -268,11 +270,11 @@ func (i *ipamer) releaseChildPrefixInternal(child *Prefix) error { parent.availableChildPrefixes[child.Cidr] = true _, err := i.DeletePrefix(child.Cidr) if err != nil { - return fmt.Errorf("unable to release prefix %v:%v", child, err) + return fmt.Errorf("unable to release prefix %v:%w", child, err) } _, err = i.storage.UpdatePrefix(*parent) if err != nil { - return fmt.Errorf("unable to release prefix %v:%v", child, err) + return fmt.Errorf("unable to release prefix %v:%w", child, err) } return nil } @@ -340,7 +342,7 @@ func (i *ipamer) acquireSpecificIPInternal(prefixCidr, specificIP string) (*IP, prefix.ips[ipstring] = true _, err := i.storage.UpdatePrefix(*prefix) if err != nil { - return nil, errors.Wrapf(err, "unable to persist acquired ip:%v", prefix) + return nil, fmt.Errorf("unable to persist acquired ip:%v error:%w", prefix, err) } return acquired, nil } @@ -387,12 +389,12 @@ func (i *ipamer) PrefixesOverlapping(existingPrefixes []string, newPrefixes []st for _, ep := range existingPrefixes { eip, err := netaddr.ParseIPPrefix(ep) if err != nil { - return fmt.Errorf("parsing prefix %s failed:%v", ep, err) + return fmt.Errorf("parsing prefix %s failed:%w", ep, err) } for _, np := range newPrefixes { nip, err := netaddr.ParseIPPrefix(np) if err != nil { - return fmt.Errorf("parsing prefix %s failed:%v", np, err) + return fmt.Errorf("parsing prefix %s failed:%w", np, err) } if eip.Overlaps(nip) || nip.Overlaps(eip) { return fmt.Errorf("%s overlaps %s", np, ep) @@ -406,7 +408,7 @@ func (i *ipamer) PrefixesOverlapping(existingPrefixes []string, newPrefixes []st func (i *ipamer) newPrefix(cidr, parentCidr string) (*Prefix, error) { ipnet, err := netaddr.ParseIPPrefix(cidr) if err != nil { - return nil, fmt.Errorf("unable to parse cidr:%s %v", cidr, err) + return nil, fmt.Errorf("unable to parse cidr:%s %w", cidr, err) } p := &Prefix{ Cidr: cidr, @@ -486,7 +488,7 @@ func (p *Prefix) availablePrefixes() (uint64, []string) { if err != nil { return 0, nil } - var ipset netaddr.IPSet + var ipset netaddr.IPSetBuilder ipset.AddPrefix(prefix) for cp, available := range p.availableChildPrefixes { if available { @@ -500,7 +502,7 @@ func (p *Prefix) availablePrefixes() (uint64, []string) { } // Only 2 Bit Prefixes are usable, set max bits available 2 less than max in family maxBits := prefix.IP.BitLen() - 2 - pfxs := ipset.Prefixes() + pfxs := ipset.IPSet().Prefixes() totalAvailable := uint64(0) availablePrefixes := []string{} for _, pfx := range pfxs { @@ -555,6 +557,15 @@ func (o NotFoundError) Error() string { return "NotFound" } +// OptimisticLockError indicates that the operation could not be executed because the dataset to update has changed in the meantime. +// clients can decide to read the current dataset and retry the operation. +type OptimisticLockError struct { +} + +func (o OptimisticLockError) Error() string { + return "OptimisticLockError" +} + // AlreadyAllocatedError is raised if the given address is already in use type AlreadyAllocatedError struct { } @@ -571,8 +582,7 @@ func retryOnOptimisticLock(retryableFunc retry.RetryableFunc) error { return retry.Do( retryableFunc, retry.RetryIf(func(err error) bool { - _, isOptimisticLock := errors.Cause(err).(OptimisticLockError) - return isOptimisticLock + return errors.Is(err, ErrOptimisticLockError) }), retry.Attempts(10), retry.DelayType(retry.CombineDelay(retry.BackOffDelay, retry.RandomDelay)), diff --git a/prefix_benchmark_test.go b/prefix_benchmark_test.go index 84e323f..8a191fb 100644 --- a/prefix_benchmark_test.go +++ b/prefix_benchmark_test.go @@ -27,16 +27,17 @@ func BenchmarkNewPrefix(b *testing.B) { {name: "Cockroach", ipam: cockipam}, } for _, bm := range benchmarks { - b.Run(bm.name, func(b *testing.B) { + test := bm + b.Run(test.name, func(b *testing.B) { for n := 0; n < b.N; n++ { - p, err := bm.ipam.NewPrefix("192.168.0.0/24") + p, err := test.ipam.NewPrefix("192.168.0.0/24") if err != nil { panic(err) } if p == nil { panic("Prefix nil") } - _, err = bm.ipam.DeletePrefix(p.Cidr) + _, err = test.ipam.DeletePrefix(p.Cidr) if err != nil { panic(err) } @@ -68,25 +69,26 @@ func BenchmarkAcquireIP(b *testing.B) { {name: "Cockroach", ipam: cockipam, cidr: "10.0.0.0/16"}, } for _, bm := range benchmarks { - b.Run(bm.name, func(b *testing.B) { - p, err := bm.ipam.NewPrefix(bm.cidr) + test := bm + b.Run(test.name, func(b *testing.B) { + p, err := test.ipam.NewPrefix(test.cidr) if err != nil { panic(err) } for n := 0; n < b.N; n++ { - ip, err := bm.ipam.AcquireIP(p.Cidr) + ip, err := test.ipam.AcquireIP(p.Cidr) if err != nil { panic(err) } if ip == nil { panic("IP nil") } - p, err = bm.ipam.ReleaseIP(ip) + p, err = test.ipam.ReleaseIP(ip) if err != nil { panic(err) } } - _, err = bm.ipam.DeletePrefix(bm.cidr) + _, err = test.ipam.DeletePrefix(test.cidr) if err != nil { b.Fatalf("error deleting prefix:%v", err) } @@ -112,14 +114,15 @@ func BenchmarkAcquireChildPrefix(b *testing.B) { {name: "16/26", parentLength: 16, childLength: 26}, } for _, bm := range benchmarks { - b.Run(bm.name, func(b *testing.B) { + test := bm + b.Run(test.name, func(b *testing.B) { ipam := New() - p, err := ipam.NewPrefix(fmt.Sprintf("192.168.0.0/%d", bm.parentLength)) + p, err := ipam.NewPrefix(fmt.Sprintf("192.168.0.0/%d", test.parentLength)) if err != nil { panic(err) } for n := 0; n < b.N; n++ { - p, err := ipam.AcquireChildPrefix(p.Cidr, bm.childLength) + p, err := ipam.AcquireChildPrefix(p.Cidr, test.childLength) if err != nil { panic(err) } diff --git a/prefix_test.go b/prefix_test.go index 10bb992..36241dc 100644 --- a/prefix_test.go +++ b/prefix_test.go @@ -5,7 +5,8 @@ import ( "strings" "testing" - "github.com/pkg/errors" + "errors" + "github.com/stretchr/testify/require" "inet.af/netaddr" ) @@ -118,12 +119,13 @@ func TestIpamer_AcquireIP(t *testing.T) { } for _, tt := range tests { + test := tt testWithBackends(t, func(t *testing.T, ipam *ipamer) { - p, err := ipam.NewPrefix(tt.fields.prefixCIDR) + p, err := ipam.NewPrefix(test.fields.prefixCIDR) if err != nil { t.Errorf("Could not create prefix: %v", err) } - for _, ipString := range tt.fields.existingips { + for _, ipString := range test.fields.existingips { p.ips[ipString] = true } @@ -133,13 +135,13 @@ func TestIpamer_AcquireIP(t *testing.T) { t.Errorf("Could not update prefix: %v", err) } got, _ := ipam.AcquireIP(updatedPrefix.Cidr) - if tt.want == nil || got == nil { - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Ipamer.AcquireIP() want or got is nil, got %v, want %v", got, tt.want) + if test.want == nil || got == nil { + if !reflect.DeepEqual(got, test.want) { + t.Errorf("Ipamer.AcquireIP() want or got is nil, got %v, want %v", got, test.want) } } else { - if tt.want.IP.Compare(got.IP) != 0 { - t.Errorf("Ipamer.AcquireIP() got %v, want %v", got, tt.want) + if test.want.IP.Compare(got.IP) != 0 { + t.Errorf("Ipamer.AcquireIP() got %v, want %v", got, test.want) } } }) @@ -767,12 +769,13 @@ func TestPrefix_Availableips(t *testing.T) { }, } for _, tt := range tests { + test := tt t.Run(tt.name, func(t *testing.T) { p := &Prefix{ - Cidr: tt.Cidr, + Cidr: test.Cidr, } - if got := p.availableips(); got != tt.want { - t.Errorf("Prefix.Availableips() = %v, want %v", got, tt.want) + if got := p.availableips(); got != test.want { + t.Errorf("Prefix.Availableips() = %v, want %v", got, test.want) } }) } @@ -838,8 +841,9 @@ func TestIpamer_PrefixesOverlapping(t *testing.T) { }, } for _, tt := range tests { + test := tt testWithBackends(t, func(t *testing.T, ipam *ipamer) { - for _, ep := range tt.existingPrefixes { + for _, ep := range test.existingPrefixes { p, err := ipam.NewPrefix(ep) if err != nil { t.Errorf("Newprefix on ExistingPrefix failed:%v", err) @@ -848,15 +852,15 @@ func TestIpamer_PrefixesOverlapping(t *testing.T) { t.Errorf("Newprefix on ExistingPrefix returns nil") } } - err := ipam.PrefixesOverlapping(tt.existingPrefixes, tt.newPrefixes) - if tt.wantErr && err == nil { + err := ipam.PrefixesOverlapping(test.existingPrefixes, test.newPrefixes) + if test.wantErr && err == nil { t.Errorf("Ipamer.PrefixesOverlapping() expected error but err was nil") } - if tt.wantErr && err != nil && err.Error() != tt.errorString { - t.Errorf("Ipamer.PrefixesOverlapping() error = %v, wantErr %v, errorString = %v", err, tt.wantErr, tt.errorString) + if test.wantErr && err != nil && err.Error() != test.errorString { + t.Errorf("Ipamer.PrefixesOverlapping() error = %v, wantErr %v, errorString = %v", err, test.wantErr, test.errorString) } - if !tt.wantErr && err != nil { - t.Errorf("Ipamer.PrefixesOverlapping() error = %v, wantErr %v", err, tt.wantErr) + if !test.wantErr && err != nil { + t.Errorf("Ipamer.PrefixesOverlapping() error = %v, wantErr %v", err, test.wantErr) } }) } @@ -902,27 +906,27 @@ func TestIpamer_NewPrefix(t *testing.T) { }, } for _, tt := range tests { - + test := tt testWithBackends(t, func(t *testing.T, ipam *ipamer) { - got, err := ipam.NewPrefix(tt.cidr) - if (err != nil) != tt.wantErr { - t.Errorf("Ipamer.NewPrefix() error = %v, wantErr %v", err, tt.wantErr) + got, err := ipam.NewPrefix(test.cidr) + if (err != nil) != test.wantErr { + t.Errorf("Ipamer.NewPrefix() error = %v, wantErr %v", err, test.wantErr) return } - if (err != nil) && tt.errorString != err.Error() { - t.Errorf("Ipamer.NewPrefix() error = %v, errorString %v", err, tt.errorString) + if (err != nil) && test.errorString != err.Error() { + t.Errorf("Ipamer.NewPrefix() error = %v, errorString %v", err, test.errorString) return } if err != nil { return } - if got.Cidr != tt.cidr { - t.Errorf("Ipamer.NewPrefix() = %v, want %v", got.Cidr, tt.cidr) + if got.Cidr != test.cidr { + t.Errorf("Ipamer.NewPrefix() = %v, want %v", got.Cidr, test.cidr) } - if got.ParentCidr != tt.parentCidr { - t.Errorf("Ipamer.NewPrefix() = %v, want %v", got.ParentCidr, tt.parentCidr) + if got.ParentCidr != test.parentCidr { + t.Errorf("Ipamer.NewPrefix() = %v, want %v", got.ParentCidr, test.parentCidr) } }) } @@ -990,29 +994,19 @@ func TestIpamerAcquireIP(t *testing.T) { testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "10.0.0.0/16" p, err := ipam.NewPrefix(cidr) - if err != nil { - panic(err) - } + require.NoError(t, err) for n := 0; n < 10; n++ { if len(p.ips) != 2 { t.Fatalf("expected 2 ips in prefix, got %d", len(p.ips)) } ip, err := ipam.AcquireIP(p.Cidr) - if err != nil { - panic(err) - } - if ip == nil { - panic("IP nil") - } + require.NoError(t, err) + require.NotNil(t, ip, "IP is nil") p, err = ipam.ReleaseIP(ip) - if err != nil { - panic(err) - } + require.NoError(t, err) } _, err = ipam.DeletePrefix(cidr) - if err != nil { - t.Errorf("error deleting prefix:%v", err) - } + require.NoError(t, err, "error deleting prefix:%v", err) }) } @@ -1021,29 +1015,19 @@ func TestIpamerAcquireIPv6(t *testing.T) { testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "2001:0db8:85a3::/120" p, err := ipam.NewPrefix(cidr) - if err != nil { - panic(err) - } + require.NoError(t, err) for n := 0; n < 10; n++ { if len(p.ips) != 1 { t.Fatalf("expected 1 ips in prefix, got %d", len(p.ips)) } ip, err := ipam.AcquireIP(p.Cidr) - if err != nil { - panic(err) - } - if ip == nil { - panic("IP nil") - } + require.NoError(t, err) + require.NotNil(t, ip, "IP is nil") p, err = ipam.ReleaseIP(ip) - if err != nil { - panic(err) - } + require.NoError(t, err) } _, err = ipam.DeletePrefix(cidr) - if err != nil { - t.Errorf("error deleting prefix:%v", err) - } + require.NoError(t, err, "error deleting prefix:%v", err) }) } func TestIpamerAcquireAlreadyAquiredIPv4(t *testing.T) { @@ -1090,9 +1074,7 @@ func TestGetHostAddresses(t *testing.T) { testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "4.1.0.0/24" ips, err := ipam.getHostAddresses(cidr) - if err != nil { - panic(err) - } + require.NoError(t, err) require.NotNil(t, ips) require.Equal(t, 254, len(ips)) @@ -1105,9 +1087,7 @@ func TestGetHostAddresses(t *testing.T) { cidr = "3.1.0.0/26" ips, err = ipam.getHostAddresses(cidr) - if err != nil { - panic(err) - } + require.NoError(t, err) require.NotNil(t, ips) require.Equal(t, 62, len(ips)) @@ -1124,9 +1104,7 @@ func TestGetHostAddressesIPv6(t *testing.T) { testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "2001:0db8:85a3::/120" ips, err := ipam.getHostAddresses(cidr) - if err != nil { - panic(err) - } + require.NoError(t, err) require.NotNil(t, ips) require.Equal(t, 255, len(ips)) @@ -1139,9 +1117,7 @@ func TestGetHostAddressesIPv6(t *testing.T) { cidr = "2001:0db8:95a3::/122" ips, err = ipam.getHostAddresses(cidr) - if err != nil { - panic(err) - } + require.NoError(t, err) require.NotNil(t, ips) require.Equal(t, 63, len(ips)) @@ -1230,10 +1206,11 @@ func TestPrefix_availablePrefixes(t *testing.T) { }, } for _, tt := range tests { + test := tt t.Run(tt.name, func(t *testing.T) { p := &Prefix{ - Cidr: tt.cidr, - availableChildPrefixes: tt.availableChildPrefixes, + Cidr: test.cidr, + availableChildPrefixes: test.availableChildPrefixes, } got, avpfxs := p.availablePrefixes() for _, pfx := range avpfxs { @@ -1244,13 +1221,13 @@ func TestPrefix_availablePrefixes(t *testing.T) { t.Logf("available prefix:%s smallest left:%d", pfx, smallest) } - if tt.want != got { - t.Errorf("Prefix.availablePrefixes() = %d, want %d", got, tt.want) + if test.want != got { + t.Errorf("Prefix.availablePrefixes() = %d, want %d", got, test.want) } got2 := p.Usage().AvailableSmallestPrefixes - if tt.want != got2 { - t.Errorf("Prefix.availablePrefixes() = %d, want %d", got2, tt.want) + if test.want != got2 { + t.Errorf("Prefix.availablePrefixes() = %d, want %d", got2, test.want) } }) } diff --git a/sql.go b/sql.go index 830319f..7d2118f 100644 --- a/sql.go +++ b/sql.go @@ -69,13 +69,16 @@ func (s *sql) CreatePrefix(prefix Prefix) (Prefix, error) { prefix.version = int64(0) pj, err := json.Marshal(prefix.toPrefixJSON()) if err != nil { - return Prefix{}, fmt.Errorf("unable to marshal prefix:%v", err) + return Prefix{}, fmt.Errorf("unable to marshal prefix:%w", err) } tx, err := s.db.Beginx() if err != nil { - return Prefix{}, fmt.Errorf("unable to start transaction:%v", err) + return Prefix{}, fmt.Errorf("unable to start transaction:%w", err) + } + _, err = tx.Exec("INSERT INTO prefixes (cidr, prefix) VALUES ($1, $2)", prefix.Cidr, pj) + if err != nil { + return Prefix{}, fmt.Errorf("unable to insert prefix:%w", err) } - tx.MustExec("INSERT INTO prefixes (cidr, prefix) VALUES ($1, $2)", prefix.Cidr, pj) return prefix, tx.Commit() } @@ -83,12 +86,12 @@ func (s *sql) ReadPrefix(prefix string) (Prefix, error) { var result []byte err := s.db.Get(&result, "SELECT prefix FROM prefixes WHERE cidr=$1", prefix) if err != nil { - return Prefix{}, fmt.Errorf("unable to read prefix:%v", err) + return Prefix{}, fmt.Errorf("unable to read prefix:%w", err) } var pre prefixJSON err = json.Unmarshal(result, &pre) if err != nil { - return Prefix{}, fmt.Errorf("unable to unmarshal prefix:%v", err) + return Prefix{}, fmt.Errorf("unable to unmarshal prefix:%w", err) } return pre.toPrefix(), nil } @@ -97,7 +100,7 @@ func (s *sql) ReadAllPrefixes() ([]Prefix, error) { var prefixes [][]byte err := s.db.Select(&prefixes, "SELECT prefix FROM prefixes") if err != nil { - return nil, fmt.Errorf("unable to read prefixes:%v", err) + return nil, fmt.Errorf("unable to read prefixes:%w", err) } result := []Prefix{} @@ -105,7 +108,7 @@ func (s *sql) ReadAllPrefixes() ([]Prefix, error) { var pre prefixJSON err = json.Unmarshal(v, &pre) if err != nil { - return nil, fmt.Errorf("unable to unmarshal prefix:%v", err) + return nil, fmt.Errorf("unable to unmarshal prefix:%w", err) } result = append(result, pre.toPrefix()) } @@ -119,35 +122,37 @@ func (s *sql) UpdatePrefix(prefix Prefix) (Prefix, error) { prefix.version = oldVersion + 1 pn, err := json.Marshal(prefix.toPrefixJSON()) if err != nil { - return Prefix{}, fmt.Errorf("unable to marshal prefix:%v", err) + return Prefix{}, fmt.Errorf("unable to marshal prefix:%w", err) } tx, err := s.db.Beginx() if err != nil { - return Prefix{}, fmt.Errorf("unable to start transaction:%v", err) + return Prefix{}, fmt.Errorf("unable to start transaction:%w", err) + } + result, err := tx.Exec("SELECT prefix FROM prefixes WHERE cidr=$1 AND prefix->>'Version'=$2 FOR UPDATE", prefix.Cidr, oldVersion) + if err != nil { + return Prefix{}, fmt.Errorf("%w: unable to select for update prefix:%s", ErrOptimisticLockError, prefix.Cidr) } - result := tx.MustExec("SELECT prefix FROM prefixes WHERE cidr=$1 AND prefix->>'Version'=$2 FOR UPDATE", prefix.Cidr, oldVersion) rows, err := result.RowsAffected() if err != nil { return Prefix{}, err } if rows == 0 { - err := tx.Rollback() - if err != nil { - return Prefix{}, newOptimisticLockError("select for update did not effect any row, but rollback did not work:" + err.Error()) - } - return Prefix{}, newOptimisticLockError("select for update did not effect any row") + // Rollback, but ignore error, if rollback is ommited, updatePrefix sometimes stucks forever, dunno why. + _ = tx.Rollback() + return Prefix{}, fmt.Errorf("%w: select for update did not effect any row", ErrOptimisticLockError) + } + result, err = tx.Exec("UPDATE prefixes SET prefix=$1 WHERE cidr=$2 AND prefix->>'Version'=$3", pn, prefix.Cidr, oldVersion) + if err != nil { + return Prefix{}, fmt.Errorf("%w: unable to update prefix:%s", ErrOptimisticLockError, prefix.Cidr) } - result = tx.MustExec("UPDATE prefixes SET prefix=$1 WHERE cidr=$2 AND prefix->>'Version'=$3", pn, prefix.Cidr, oldVersion) rows, err = result.RowsAffected() if err != nil { return Prefix{}, err } if rows == 0 { - err := tx.Rollback() - if err != nil { - return Prefix{}, newOptimisticLockError("updatePrefix did not effect any row, but rollback did not work:" + err.Error()) - } - return Prefix{}, newOptimisticLockError("updatePrefix did not effect any row") + // Rollback, but ignore error, if rollback is ommited, updatePrefix sometimes stucks forever, dunno why. + _ = tx.Rollback() + return Prefix{}, fmt.Errorf("%w: updatePrefix did not effect any row", ErrOptimisticLockError) } return prefix, tx.Commit() } @@ -155,8 +160,11 @@ func (s *sql) UpdatePrefix(prefix Prefix) (Prefix, error) { func (s *sql) DeletePrefix(prefix Prefix) (Prefix, error) { tx, err := s.db.Beginx() if err != nil { - return Prefix{}, fmt.Errorf("unable to start transaction:%v", err) + return Prefix{}, fmt.Errorf("unable to start transaction:%w", err) + } + _, err = tx.Exec("DELETE from prefixes WHERE cidr=$1", prefix.Cidr) + if err != nil { + return Prefix{}, fmt.Errorf("unable delete prefix:%w", err) } - tx.MustExec("DELETE from prefixes WHERE cidr=$1", prefix.Cidr) return prefix, tx.Commit() } diff --git a/storage.go b/storage.go index 4b59d50..cc7971a 100644 --- a/storage.go +++ b/storage.go @@ -8,17 +8,3 @@ type Storage interface { UpdatePrefix(prefix Prefix) (Prefix, error) DeletePrefix(prefix Prefix) (Prefix, error) } - -// OptimisticLockError indicates that the operation could not be executed because the dataset to update has changed in the meantime. -// clients can decide to read the current dataset and retry the operation. -type OptimisticLockError struct { - msg string -} - -func (o OptimisticLockError) Error() string { - return o.msg -} - -func newOptimisticLockError(msg string) OptimisticLockError { - return OptimisticLockError{msg: msg} -}