Repository: threefoldtech/zos
Pull request #518 (2020-02-12 08:21:47)

add staticcheck to CI

codecov[bot]: # [Codecov](https://codecov.io/gh/threefoldtech/zos/pull/518?src=pr&el=h1) Report
> Merging [#518](https://codecov.io/gh/threefoldtech/zos/pull/518?src=pr&el=desc) into [master](https://codecov.io/gh/threefoldtech/zos/commit/acf49fbc5780985fcba995838606fe8a35ab852f?src=pr&el=desc) will **increase** coverage by `0.13%`.
> The diff coverage is `37.5%`.

[![Impacted file tree graph](https://codecov.io/gh/threefoldtech/zos/pull/518/graphs/tree.svg?width=650&token=f7Vmq1OAEm&height=150&src=pr)](https://codecov.io/gh/threefoldtech/zos/pull/518?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #518      +/-   ##
==========================================
+ Coverage   36.13%   36.26%   +0.13%     
==========================================
  Files          75       75              
  Lines        5101     5066      -35     
==========================================
- Hits         1843     1837       -6     
+ Misses       2992     2963      -29     
  Partials      266      266
```


| [Impacted Files](https://codecov.io/gh/threefoldtech/zos/pull/518?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pkg/storage/filesystem/btrfs.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL3N0b3JhZ2UvZmlsZXN5c3RlbS9idHJmcy5nbw==) | `8.08% <ø> (+0.05%)` | :arrow_up: |
| [pkg/container/opts.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL2NvbnRhaW5lci9vcHRzLmdv) | `14.66% <ø> (+3.2%)` | :arrow_up: |
| [pkg/provision/zdb.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL3Byb3Zpc2lvbi96ZGIuZ28=) | `44.16% <ø> (-0.94%)` | :arrow_down: |
| [pkg/zinit/service.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL3ppbml0L3NlcnZpY2UuZ28=) | `26.66% <ø> (ø)` | :arrow_up: |
| [pkg/identity/store.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL2lkZW50aXR5L3N0b3JlLmdv) | `0% <ø> (ø)` | :arrow_up: |
| [pkg/schema/types.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL3NjaGVtYS90eXBlcy5nbw==) | `63.2% <ø> (ø)` | :arrow_up: |
| [pkg/gedis/commands\_provision.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL2dlZGlzL2NvbW1hbmRzX3Byb3Zpc2lvbi5nbw==) | `42.57% <0%> (+0.33%)` | :arrow_up: |
| [pkg/upgrade/upgrade.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL3VwZ3JhZGUvdXBncmFkZS5nbw==) | `0% <0%> (ø)` | :arrow_up: |
| [...ypes/provision/tfgrid\_workloads\_reservation\_k8s.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL2dlZGlzL3R5cGVzL3Byb3Zpc2lvbi90ZmdyaWRfd29ya2xvYWRzX3Jlc2VydmF0aW9uX2s4cy5nbw==) | `0% <0%> (ø)` | :arrow_up: |
| [pkg/flist/flist.go](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree#diff-cGtnL2ZsaXN0L2ZsaXN0Lmdv) | `52.81% <100%> (-0.21%)` | :arrow_down: |
| ... and [2 more](https://codecov.io/gh/threefoldtech/zos/pull/518/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/threefoldtech/zos/pull/518?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/threefoldtech/zos/pull/518?src=pr&el=footer). Last update [acf49fb...905ba71](https://codecov.io/gh/threefoldtech/zos/pull/518?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/go.mod b/go.mod
index 3ae428d7..ee22ac26 100644
--- a/go.mod
+++ b/go.mod
@@ -57,6 +57,7 @@ require (
 	github.com/stretchr/testify v1.4.0
 	github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect
 	github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8
+	github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae
 	github.com/threefoldtech/zbus v0.1.2
 	github.com/urfave/cli v1.22.1
 	github.com/vishvananda/netlink v1.0.0
diff --git a/go.sum b/go.sum
index 8df96da8..cc3fbe0f 100644
--- a/go.sum
+++ b/go.sum
@@ -324,6 +324,8 @@ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0
 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
 github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 h1:RB0v+/pc8oMzPsN97aZYEwNuJ6ouRJ2uhjxemJ9zvrY=
 github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8/go.mod h1:IlWNj9v/13q7xFbaK4mbyzMNwrZLaWSHx/aibKIZuIg=
+github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae h1:vgGSvdW5Lqg+I1aZOlG32uyE6xHpLdKhZzcTEktz5wM=
+github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae/go.mod h1:quDq6Se6jlGwiIKia/itDZxqC5rj6/8OdFyMMAwTxCs=
 github.com/threefoldtech/zbus v0.1.2 h1:fBuRy7FaI7UVdZNnJQUqumejm14IHtzEqYYtSxTsjSI=
 github.com/threefoldtech/zbus v0.1.2/go.mod h1:ZtiRpcqzEBJetVQDsEbw0p48h/AF3O1kf0tvd30I0BU=
 github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
@@ -430,6 +432,7 @@ golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBn
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 h1:4cFkmztxtMslUX2SctSl+blCyXfpzhGOy9LhKAqSMA4=
 golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.zx2c4.com/wireguard v0.0.20191012 h1:sdX+y3hrHkW8KJkjY7ZgzpT5Tqo8XnBkH55U1klphko=
@@ -469,4 +472,5 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/pkg/Makefile b/pkg/Makefile
index 40b9446d..94113338 100644
--- a/pkg/Makefile
+++ b/pkg/Makefile
@@ -13,7 +13,7 @@ getdeps:
 	@echo "Installing misspell" && go get -u github.com/client9/misspell/cmd/misspell
 	@echo "Installing ineffassign" && go get -u github.com/gordonklaus/ineffassign
 
-verifiers: vet fmt lint cyclo spelling #deadcode
+verifiers: vet fmt lint cyclo spelling static #deadcode
 
 vet:
 	@echo "Running $@"
@@ -42,6 +42,9 @@ deadcode:
 spelling:
 	@${GOPATH}/bin/misspell -i monitord -error `find .`
 
+static:
+	go run honnef.co/go/tools/cmd/staticcheck -- ./...
+
 # Builds minio, runs the verifiers then runs the tests.
 check: test
 test: verifiers build
diff --git a/pkg/app/boot.go b/pkg/app/boot.go
new file mode 100644
index 00000000..b35d2ebf
--- /dev/null
+++ b/pkg/app/boot.go
@@ -0,0 +1,27 @@
+package app
+
+import (
+	"os"
+	"path/filepath"
+)
+
+const bootedPath = "/var/run/modules"
+
+// MarkBooted creates a file in a memory
+// this file then can be used to check if "something" has been restared
+// if its the first time it starts
+func MarkBooted(name string) error {
+	if err := os.MkdirAll(bootedPath, 0770); err != nil {
+		return err
+	}
+	path := filepath.Join(bootedPath, name)
+	_, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE|os.O_TRUNC|os.O_EXCL, 0666)
+	return err
+}
+
+// IsFirstBoot checks if the a file has been created by MarkBooted function
+func IsFirstBoot(name string) bool {
+	path := filepath.Join(bootedPath, name)
+	_, err := os.Stat(path)
+	return !(err == nil)
+}
diff --git a/pkg/capacity/dmi/dmi.go b/pkg/capacity/dmi/dmi.go
index 99cbd1de..b220d55c 100644
--- a/pkg/capacity/dmi/dmi.go
+++ b/pkg/capacity/dmi/dmi.go
@@ -130,18 +130,6 @@ var dmitypeToString = map[Type]string{
 	TypeManagementControllerHostInterface: "ManagementControllerHostInterface",
 }
 
-var dmiKeywords = map[string]bool{
-	"bios":      true,
-	"system":    true,
-	"baseboard": true,
-	"chassis":   true,
-	"processor": true,
-	"memory":    true,
-	"cache":     true,
-	"connector": true,
-	"slot":      true,
-}
-
 var dmiTypeRegex = regexp.MustCompile("DMI type ([0-9]+)")
 var kvRegex = regexp.MustCompile("(.+?):(.*)")
 
@@ -157,7 +145,7 @@ func Decode() (*DMI, error) {
 
 // dmiTypeToString returns string representation of Type t
 func dmiTypeToString(t Type) string {
-	str, _ := dmitypeToString[t]
+	str := dmitypeToString[t]
 	if str == "" {
 		return fmt.Sprintf("Custom Type %d", t)
 	}
diff --git a/pkg/container/opts.go b/pkg/container/opts.go
index 71319595..ebe96ed8 100644
--- a/pkg/container/opts.go
+++ b/pkg/container/opts.go
@@ -25,41 +25,6 @@ func withNetworkNamespace(name string) oci.SpecOpts {
 	)
 }
 
-func withHooks(hooks specs.Hooks) oci.SpecOpts {
-	return func(_ context.Context, _ oci.Client, _ *containers.Container, spec *oci.Spec) error {
-		spec.Hooks = &hooks
-		return nil
-	}
-}
-
-func capsContain(caps []string, s string) bool {
-	for _, c := range caps {
-		if c == s {
-			return true
-		}
-	}
-	return false
-}
-
-func withAddedCapabilities(caps []string) oci.SpecOpts {
-	return func(_ context.Context, _ oci.Client, _ *containers.Container, s *oci.Spec) error {
-		// setCapabilities(s)
-		for _, c := range caps {
-			for _, cl := range []*[]string{
-				&s.Process.Capabilities.Bounding,
-				&s.Process.Capabilities.Effective,
-				&s.Process.Capabilities.Permitted,
-				&s.Process.Capabilities.Inheritable,
-			} {
-				if !capsContain(*cl, c) {
-					*cl = append(*cl, c)
-				}
-			}
-		}
-		return nil
-	}
-}
-
 func removeRunMount() oci.SpecOpts {
 	return func(_ context.Context, _ oci.Client, _ *containers.Container, s *oci.Spec) error {
 		for i, mount := range s.Mounts {
diff --git a/pkg/flist/flist.go b/pkg/flist/flist.go
index d179dc1e..94f4f591 100644
--- a/pkg/flist/flist.go
+++ b/pkg/flist/flist.go
@@ -414,7 +414,7 @@ func waitPidFile(timeout time.Duration, path string, exists bool) error {
 			// or check NOT exist but it is still there
 			// we try again
 			if (exists && os.IsNotExist(err)) || (!exists && err == nil) {
-				break //the select statement
+				time.Sleep(delay)
 			} else if err != nil && !os.IsNotExist(err) {
 				//another error that is NOT IsNotExist.
 				return err
@@ -422,8 +422,6 @@ func waitPidFile(timeout time.Duration, path string, exists bool) error {
 				return nil
 			}
 		}
-
-		<-time.After(delay)
 	}
 }
 
diff --git a/pkg/gedis/commands_provision.go b/pkg/gedis/commands_provision.go
index b6163742..8a068fbc 100644
--- a/pkg/gedis/commands_provision.go
+++ b/pkg/gedis/commands_provision.go
@@ -431,13 +431,8 @@ func k8sReservation(i interface{}, nodeID string) types.TfgridWorkloadsReservati
 		SSHKeys:       make([]string, 0, len(k.SSHKeys)),
 	}
 
-	for i, ip := range k.MasterIPs {
-		k8s.MasterIps[i] = ip
-	}
-
-	for i, key := range k.SSHKeys {
-		k8s.SSHKeys[i] = key
-	}
+	copy(k8s.MasterIps, k.MasterIPs)
+	copy(k8s.SSHKeys, k.SSHKeys)
 
 	return k8s
 }
diff --git a/pkg/gedis/types/directory/tfgrid_node_2.go b/pkg/gedis/types/directory/tfgrid_node_2.go
index 685cf407..9bded24f 100644
--- a/pkg/gedis/types/directory/tfgrid_node_2.go
+++ b/pkg/gedis/types/directory/tfgrid_node_2.go
@@ -22,7 +22,7 @@ type TfgridNode2 struct {
 	ReservedResources TfgridNodeResourceAmount1 `json:"reserved_resources"`
 	Proofs            []TfgridNodeProof1        `json:"proofs"`
 	Ifaces            []TfgridNodeIface1        `json:"ifaces"`
-	PublicConfig      *TfgridNodePublicIface1   `json:"public_config,omitemtpy"`
+	PublicConfig      *TfgridNodePublicIface1   `json:"public_config,omitempty"`
 	WGPorts           []uint                    `json:"wg_ports"`
 	Approved          bool                      `json:"approved"`
 	PublicKeyHex      string                    `json:"public_key_hex"`
diff --git a/pkg/gedis/types/provision/tfgrid_workloads_reservation_k8s.go b/pkg/gedis/types/provision/tfgrid_workloads_reservation_k8s.go
index c3e5d3d1..bb7016be 100644
--- a/pkg/gedis/types/provision/tfgrid_workloads_reservation_k8s.go
+++ b/pkg/gedis/types/provision/tfgrid_workloads_reservation_k8s.go
@@ -32,13 +32,8 @@ func (k TfgridWorkloadsReservationK8S1) ToProvisionType() (provision.Kubernetes,
 		SSHKeys:       make([]string, len(k.SSHKeys)),
 	}
 
-	for i, ip := range k.MasterIps {
-		k8s.MasterIPs[i] = ip
-	}
-
-	for i, key := range k.SSHKeys {
-		k8s.SSHKeys[i] = key
-	}
+	copy(k8s.MasterIPs, k.MasterIps)
+	copy(k8s.SSHKeys, k.SSHKeys)
 
 	return k8s, k.NodeID, nil
 }
diff --git a/pkg/identity/store.go b/pkg/identity/store.go
index bb972a10..851119a0 100644
--- a/pkg/identity/store.go
+++ b/pkg/identity/store.go
@@ -59,11 +59,6 @@ func (s *httpIDStore) RegisterNode(node pkg.Identifier, farm pkg.FarmID, version
 	return "(unknown)", nil
 }
 
-type farmRegisterReq struct {
-	ID   string `json:"id"`
-	Name string `json:"name"`
-}
-
 // RegisterFarm implements the IDStore interface
 func (s *httpIDStore) RegisterFarm(tid uint64, name string, email string, wallet []string) (pkg.FarmID, error) {
 	buf := bytes.Buffer{}
diff --git a/pkg/monitord/system.go b/pkg/monitord/system.go
index 448b71d3..1bf8cd58 100644
--- a/pkg/monitord/system.go
+++ b/pkg/monitord/system.go
@@ -78,6 +78,10 @@ func (m *systemMonitor) CPU(ctx context.Context) <-chan pkg.CPUTimesStat {
 				var result []pkg.TimesStat
 				now := time.Now()
 				times, err := cpu.Times(true)
+				if err != nil {
+					log.Error().Err(err).Msg("failed to read cpu usage times")
+					continue
+				}
 				for i, time := range times {
 					result = append(result, pkg.TimesStat{
 						TimesStat: time,
diff --git a/pkg/network/dhcp/dhcp.go b/pkg/network/dhcp/dhcp.go
index 024f15de..038bb3b4 100644
--- a/pkg/network/dhcp/dhcp.go
+++ b/pkg/network/dhcp/dhcp.go
@@ -57,7 +57,7 @@ func Probe(inf string, family int) (bool, error) {
 				return false, err
 			}
 			if hasGW {
-				break
+				return true, nil
 			}
 
 		}
diff --git a/pkg/network/ifaceutil/interface.go b/pkg/network/ifaceutil/interface.go
index 8fa85354..cf8eaa9e 100644
--- a/pkg/network/ifaceutil/interface.go
+++ b/pkg/network/ifaceutil/interface.go
@@ -47,17 +47,14 @@ func IsPlugged(inf string) bool {
 // IsPluggedTimeout is like IsPlugged but retry for duration time before returning
 func IsPluggedTimeout(name string, duration time.Duration) bool {
 	plugged := false
-	c := time.After(duration)
-	for out := false; out == false; {
+	for !plugged {
 		select {
-		case <-c:
-			out = true
-			break
+		case <-time.After(duration):
+			return false // timeout
 		default:
 			plugged = IsPlugged(name)
 			if plugged {
-				out = true
-				break
+				return true
 			}
 		}
 		time.Sleep(time.Second)
diff --git a/pkg/network/ifaceutil/mac.go b/pkg/network/ifaceutil/mac.go
index d9fef38d..19bc4862 100644
--- a/pkg/network/ifaceutil/mac.go
+++ b/pkg/network/ifaceutil/mac.go
@@ -45,7 +45,7 @@ func isHardwareAddrInValidRange(addr net.HardwareAddr) bool {
 	// possible range 2
 	if bytes.Compare(addr[hwMacAddress-len(macPUR2):], macPUR2[:]) <= 0 {
 		return bytes.Compare(macPLR2[:], addr[:len(macPLR2)]) <= 0 &&
-			bytes.Compare(macPR2EL[:], addr[:len(macPR2EL)]) != 0
+			!bytes.Equal(macPR2EL[:], addr[:len(macPR2EL)])
 	}
 	// possible (last) range 3
 	return bytes.Compare(addr[hwMacAddress-len(macPUR3):], macPUR3[:]) <= 0 &&
diff --git a/pkg/network/ndmz/ipam.go b/pkg/network/ndmz/ipam.go
index ba991b32..cce933a4 100644
--- a/pkg/network/ndmz/ipam.go
+++ b/pkg/network/ndmz/ipam.go
@@ -12,7 +12,7 @@ import (
 // in the network with netID, and NetResource.
 func allocateIPv4(networkID string) (*net.IPNet, error) {
 	// FIXME: path to the cache disk shouldn't be hardcoded here
-	store, err := disk.New("dmz", "/var/cache/modules/networkd/lease")
+	store, err := disk.New("ndmz", ipamPath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/network/ndmz/ndmz.go b/pkg/network/ndmz/ndmz.go
index 03c4378f..ad62145c 100644
--- a/pkg/network/ndmz/ndmz.go
+++ b/pkg/network/ndmz/ndmz.go
@@ -5,10 +5,13 @@ import (
 	"fmt"
 	"net"
 	"os"
+	"path/filepath"
 	"time"
 
 	"github.com/cenkalti/backoff/v3"
+	"github.com/termie/go-shutil"
 	"github.com/threefoldtech/zos/pkg"
+	"github.com/threefoldtech/zos/pkg/app"
 	"github.com/threefoldtech/zos/pkg/network/ifaceutil"
 	"github.com/threefoldtech/zos/pkg/network/types"
 
@@ -42,10 +45,46 @@ const (
 	PublicIfaceName = "public"
 )
 
+var ipamPath = "/var/cache/modules/networkd/lease"
+
 //Create create the NDMZ network namespace and configure its default routes and addresses
 func Create(nodeID pkg.Identifier) error {
+	path := filepath.Join(ipamPath, "ndmz")
+
+	if app.IsFirstBoot("networkd-dmz") {
+		log.Info().Msg("first boot, empty reservation cache")
+
+		if err := os.RemoveAll(path); err != nil {
+			return err
+		}
+
+		// TODO @zaibon: remove once all the network has applies this fix
+
+		// This check ensure we do not delete current lease from the node
+		// running a previous version of this code.
+		// if the old leases directory exists, we copy the content
+		// into the new location and delete the old directory
+		var err error
+		oldFolder := filepath.Join(ipamPath, "dmz")
+		if _, err = os.Stat(oldFolder); err == nil {
+			if err := shutil.CopyTree(oldFolder, path, nil); err != nil {
+				return err
+			}
+			err = os.RemoveAll(oldFolder)
+		} else {
+			err = os.MkdirAll(path, 0770)
+		}
+		if err != nil {
+			return err
+		}
 
-	os.RemoveAll("/var/cache/modules/networkd/lease/dmz/")
+		if err := app.MarkBooted("networkd-dmz"); err != nil {
+			return errors.Wrap(err, "fail to mark provisiond as booted")
+		}
+
+	} else {
+		log.Info().Msg("restart detected, keep IPAM lease cache intact")
+	}
 
 	netNS, err := namespace.GetByName(NetNSNDMZ)
 	if err != nil {
diff --git a/pkg/network/ndmz/public.go b/pkg/network/ndmz/public.go
deleted file mode 100644
index a6275b3f..00000000
--- a/pkg/network/ndmz/public.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package ndmz
-
-import (
-	"fmt"
-
-	"github.com/containernetworking/plugins/pkg/ns"
-	"github.com/pkg/errors"
-	"github.com/rs/zerolog/log"
-	"github.com/threefoldtech/zos/pkg/network/namespace"
-	"github.com/threefoldtech/zos/pkg/network/types"
-	"github.com/vishvananda/netlink"
-)
-
-func getPublicIface() (netlink.Link, error) {
-	var iface netlink.Link
-
-	pubNS, err := namespace.GetByName(types.PublicNamespace)
-
-	if err == nil { // there is a public namespace on the node
-		var ifaceIndex int
-
-		defer pubNS.Close()
-		// get the name of the public interface in the public namespace
-		if err := pubNS.Do(func(_ ns.NetNS) error {
-			// get the name of the interface connected to the public segment
-			public, err := netlink.LinkByName(types.PublicIface)
-			if err != nil {
-				return errors.Wrap(err, "failed to get public link")
-			}
-
-			ifaceIndex = public.Attrs().ParentIndex
-			return nil
-		}); err != nil {
-			return nil, err
-		}
-
-		master, err := netlink.LinkByIndex(ifaceIndex)
-		if err != nil {
-			return nil, errors.Wrapf(err, "failed to get link by index %d", ifaceIndex)
-		}
-		iface = master
-	} else {
-		zos, err := netlink.LinkByName("zos")
-		if err != nil {
-			return nil, errors.Wrap(err, "failed to get zos link")
-		}
-		// find the name of the interface attached to zos bridge
-		links, err := netlink.LinkList()
-		if err != nil {
-			return nil, errors.Wrap(err, "failed to list interfaces")
-		}
-		for _, link := range links {
-			if link.Attrs().MasterIndex == zos.Attrs().Index && link.Type() == "device" {
-				iface = link
-				break
-			}
-		}
-	}
-
-	if iface == nil {
-		return nil, fmt.Errorf("no interface with default gateway found")
-	}
-
-	log.Info().Str("iface", iface.Attrs().Name).Msg("interface with default gateway found")
-	return iface, nil
-}
diff --git a/pkg/network/networker.go b/pkg/network/networker.go
index 0c935bc6..f5e48ef8 100644
--- a/pkg/network/networker.go
+++ b/pkg/network/networker.go
@@ -73,7 +73,7 @@ func validateNetwork(n *pkg.Network) error {
 	}
 
 	if len(n.NetResources) < 1 {
-		return fmt.Errorf("Network needs at least one network resource")
+		return fmt.Errorf("network needs at least one network resource")
 	}
 
 	for _, nr := range n.NetResources {
@@ -101,7 +101,7 @@ func validateNR(nr pkg.NetResource) error {
 		return fmt.Errorf("network resource wireguard public key cannot empty")
 	}
 
-	if nr.WGListenPort <= 0 {
+	if nr.WGListenPort == 0 {
 		return fmt.Errorf("network resource wireguard listen port cannot empty")
 	}
 
diff --git a/pkg/network/nr/net_resource.go b/pkg/network/nr/net_resource.go
index 54f21edf..f26d608e 100644
--- a/pkg/network/nr/net_resource.go
+++ b/pkg/network/nr/net_resource.go
@@ -336,7 +336,7 @@ func (nr *NetResource) createNetNS() error {
 		}
 		return nil
 	})
-	return nil
+	return err
 }
 
 // attachToNRBridge creates a macvlan interface in the NR namespace, and attaches
diff --git a/pkg/provision/local_store.go b/pkg/provision/local_store.go
index 04aa2815..388cac71 100644
--- a/pkg/provision/local_store.go
+++ b/pkg/provision/local_store.go
@@ -12,6 +12,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/rs/zerolog/log"
 	"github.com/threefoldtech/zos/pkg"
+	"github.com/threefoldtech/zos/pkg/app"
 	"github.com/threefoldtech/zos/pkg/versioned"
 )
 
@@ -39,10 +40,6 @@ func (c *counterNop) Current() int64 {
 	return 0
 }
 
-var (
-	nop counterNop
-)
-
 // counterImpl value for safe increment/decrement
 type counterImpl int64
 
@@ -79,18 +76,7 @@ type FSStore struct {
 
 // NewFSStore creates a in memory reservation store
 func NewFSStore(root string) (*FSStore, error) {
-	if err := os.MkdirAll("/var/run/modules", 0770); err != nil {
-		return nil, err
-	}
-
-	_, err := os.OpenFile("/var/run/modules/provisiond", os.O_RDONLY|os.O_CREATE|os.O_TRUNC|os.O_EXCL, 0666)
-	// if we managed to create the file, this means the node
-	// has just booted and this is the first time provisiond starts since boot
-	// so we empty the reservation cache
-
-	// if the file is already present, this mean this is just a restart/update of provisiond
-	// so we need to keep the reservation cache as it is
-	if err == nil {
+	if app.IsFirstBoot("provisiond") {
 		log.Info().Msg("first boot, empty reservation cache")
 		if err := os.RemoveAll(root); err != nil {
 			return nil, err
@@ -98,6 +84,11 @@ func NewFSStore(root string) (*FSStore, error) {
 		if err := os.MkdirAll(root, 0770); err != nil {
 			return nil, err
 		}
+
+		if err := app.MarkBooted("provisiond"); err != nil {
+			return nil, errors.Wrap(err, "fail to mark provisiond as booted")
+		}
+
 	} else {
 		log.Info().Msg("restart detected, keep reservation cache intact")
 	}
@@ -135,7 +126,7 @@ func (s *FSStore) counterFor(typ ReservationType) Counter {
 		return &s.counters.vm
 	default:
 		// this will avoid nil pointer
-		return &nop
+		return &counterNop{}
 	}
 }
 
diff --git a/pkg/provision/zdb.go b/pkg/provision/zdb.go
index 179408fa..0b63e57d 100644
--- a/pkg/provision/zdb.go
+++ b/pkg/provision/zdb.go
@@ -6,7 +6,6 @@ import (
 	"fmt"
 	"net"
 	"os"
-	"path/filepath"
 	"strings"
 	"time"
 
@@ -334,10 +333,6 @@ func zdbDecommission(ctx context.Context, reservation *Reservation) error {
 	return nil
 }
 
-func zdbConnectionURL(ip string, port uint16) string {
-	return fmt.Sprintf("tcp://[%s]:%d", ip, port)
-}
-
 func socketDir(containerID pkg.ContainerID) string {
 	return fmt.Sprintf("/var/run/zdb_%s", containerID)
 }
@@ -360,12 +355,3 @@ func isPublic(ip net.IP) bool {
 		ip.IsLinkLocalMulticast() ||
 		ip.IsInterfaceLocalMulticast())
 }
-
-func findDataVolume(mounts []pkg.MountInfo) (string, error) {
-	for _, mount := range mounts {
-		if mount.Target == "/data" {
-			return filepath.Base(mount.Source), nil
-		}
-	}
-	return "", fmt.Errorf("data volume not found")
-}
diff --git a/pkg/schema/types.go b/pkg/schema/types.go
index 7e4ee264..e919b439 100644
--- a/pkg/schema/types.go
+++ b/pkg/schema/types.go
@@ -12,17 +12,7 @@ import (
 )
 
 var (
-	dateRe  = regexp.MustCompile(`^(?:(\d{2,4})/)?(\d{2})/(\d{2,4})(?:\s+(\d{1,2})(am|pm)?:(\d{1,2}))?$`)
-	boolMap = map[string]bool{
-		"true":  true,
-		"yes":   true,
-		"y":     true,
-		"1":     true,
-		"false": false,
-		"no":    false,
-		"n":     false,
-		"0":     false,
-	}
+	dateRe = regexp.MustCompile(`^(?:(\d{2,4})/)?(\d{2})/(\d{2,4})(?:\s+(\d{1,2})(am|pm)?:(\d{1,2}))?$`)
 )
 
 // Numeric type. this type is tricky so we just going to handle it as string
diff --git a/pkg/storage/filesystem/btrfs.go b/pkg/storage/filesystem/btrfs.go
index 128e3c23..3573edc9 100644
--- a/pkg/storage/filesystem/btrfs.go
+++ b/pkg/storage/filesystem/btrfs.go
@@ -52,10 +52,6 @@ func NewBtrfs(manager DeviceManager) Filesystem {
 	return newBtrfs(manager, executerFunc(run))
 }
 
-func (b *btrfs) btrfs(ctx context.Context, args ...string) ([]byte, error) {
-	return b.utils.run(ctx, "btrfs", args...)
-}
-
 func (b *btrfs) Create(ctx context.Context, name string, policy pkg.RaidProfile, devices ...*Device) (Pool, error) {
 	name = strings.TrimSpace(name)
 	if len(name) == 0 {
diff --git a/pkg/upgrade/upgrade.go b/pkg/upgrade/upgrade.go
index 3f69785b..9f368b31 100644
--- a/pkg/upgrade/upgrade.go
+++ b/pkg/upgrade/upgrade.go
@@ -19,8 +19,6 @@ import (
 	"github.com/threefoldtech/zos/pkg"
 )
 
-type hookType string
-
 var (
 	// ErrRestartNeeded is returned if upgraded requires a restart
 	ErrRestartNeeded = fmt.Errorf("restart needed")
@@ -358,7 +356,7 @@ func (u *Upgrader) applyUpgrade(from, to FListEvent) error {
 }
 
 func copyRecursive(source string, destination string, skip ...string) error {
-	return filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
+	return filepath.Walk(source, func(path string, info os.FileInfo, _ error) error {
 		rel, err := filepath.Rel(source, path)
 		if err != nil {
 			return err
diff --git a/pkg/vm/machine.go b/pkg/vm/machine.go
index 011ab57c..f662bc02 100644
--- a/pkg/vm/machine.go
+++ b/pkg/vm/machine.go
@@ -142,10 +142,6 @@ func (m *Machine) Start(ctx context.Context, base string) error {
 	return jailed.exec(ctx, base)
 }
 
-func (m *Machine) cfgFilePath(base string) string {
-	return filepath.Join(m.root(base), "config.json")
-}
-
 // Log returns machine log file path
 func (m *Machine) Log(base string) string {
 	return filepath.Join(m.root(base), "machine.log")
diff --git a/pkg/vm/manager.go b/pkg/vm/manager.go
index bf53944d..3e1a7db3 100644
--- a/pkg/vm/manager.go
+++ b/pkg/vm/manager.go
@@ -76,11 +76,7 @@ func (m *vmModuleImpl) socket(id string) string {
 
 func (m *vmModuleImpl) Exists(id string) bool {
 	_, err := m.find(id)
-	if err != nil {
-		return false
-	}
-
-	return true
+	return err == nil
 }
 
 func (m *vmModuleImpl) cleanFs(id string) error {
@@ -161,6 +157,9 @@ func (m *vmModuleImpl) tail(path string) (string, error) {
 
 	defer f.Close()
 	info, err := f.Stat()
+	if err != nil {
+		return "", errors.Wrapf(err, "fail to stat %s", f.Name())
+	}
 	offset := info.Size()
 	if offset > tail {
 		offset = tail
@@ -312,7 +311,7 @@ func (m *vmModuleImpl) find(name string) (int, error) {
 	)
 	idArg := name
 	result := 0
-	err := filepath.Walk(proc, func(path string, info os.FileInfo, err error) error {
+	err := filepath.Walk(proc, func(path string, info os.FileInfo, _ error) error {
 		if path == proc {
 			// assend into /proc
 			return nil
diff --git a/pkg/zinit/service.go b/pkg/zinit/service.go
index 640f3609..b79c5466 100644
--- a/pkg/zinit/service.go
+++ b/pkg/zinit/service.go
@@ -26,7 +26,7 @@ func (s *LogType) UnmarshalYAML(unmarshal func(interface{}) error) error {
 // All the type of logging supported by zinit
 const (
 	StdoutLogType LogType = "stdout"
-	RingLogType           = "ring"
+	RingLogType   LogType = "ring"
 )
 
 // InitService represent a Zinit service file
diff --git a/tools/Makefile b/tools/Makefile
index 99b31697..dee4c8e7 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -17,7 +17,7 @@ getdeps:
 	@echo "Installing misspell" && go install github.com/client9/misspell/cmd/misspell
 	@echo "Installing ineffassign" && go install github.com/gordonklaus/ineffassign
 
-verifiers: vet fmt lint cyclo spelling
+verifiers: vet fmt lint cyclo spelling static
 
 vet:
 	@echo "Running $@"
@@ -43,6 +43,9 @@ cyclo:
 spelling:
 	misspell -i monitord -error `find .`
 
+static:
+	go run honnef.co/go/tools/cmd/staticcheck -- ./...
+
 # Builds minio, runs the verifiers then runs the tests.
 check: test
 test: verifiers
diff --git a/tools/bcdb_mock/main.go b/tools/bcdb_mock/main.go
index a8caf6b6..ae1dcc5f 100644
--- a/tools/bcdb_mock/main.go
+++ b/tools/bcdb_mock/main.go
@@ -81,7 +81,7 @@ func main() {
 		Handler: r,
 	}
 
-	c := make(chan os.Signal)
+	c := make(chan os.Signal, 1)
 	signal.Notify(c, os.Interrupt)
 
 	go s.ListenAndServe()
diff --git a/tools/bcdb_mock/node_handlers.go b/tools/bcdb_mock/node_handlers.go
index 38ff1cac..3dfaf771 100644
--- a/tools/bcdb_mock/node_handlers.go
+++ b/tools/bcdb_mock/node_handlers.go
@@ -157,7 +157,7 @@ func (s *nodeStore) registerIfaces(w http.ResponseWriter, r *http.Request) {
 
 	input := []*types.IfaceInfo{}
 	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
-		log.Printf(err.Error())
+		log.Print(err.Error())
 		httpError(w, err, http.StatusBadRequest)
 		return
 	}
diff --git a/tools/bcdb_mock/nodes_store.go b/tools/bcdb_mock/nodes_store.go
index 72fb10ad..665fbcb6 100644
--- a/tools/bcdb_mock/nodes_store.go
+++ b/tools/bcdb_mock/nodes_store.go
@@ -101,12 +101,6 @@ func (s *nodeStore) Add(node directory.TfgridNode2) error {
 func (s *nodeStore) updateTotalCapacity(nodeID string, cap directory.TfgridNodeResourceAmount1) error {
 	return s.updateCapacity(nodeID, "total", cap)
 }
-func (s *nodeStore) updateReservedCapacity(nodeID string, cap directory.TfgridNodeResourceAmount1) error {
-	return s.updateCapacity(nodeID, "reserved", cap)
-}
-func (s *nodeStore) updateUsedCapacity(nodeID string, cap directory.TfgridNodeResourceAmount1) error {
-	return s.updateCapacity(nodeID, "used", cap)
-}
 
 func (s *nodeStore) updateCapacity(nodeID string, t string, cap directory.TfgridNodeResourceAmount1) error {
 	node, err := s.Get(nodeID)
diff --git a/tools/bcdb_mock/reservation_handlers.go b/tools/bcdb_mock/reservation_handlers.go
index a2d5af44..128671bc 100644
--- a/tools/bcdb_mock/reservation_handlers.go
+++ b/tools/bcdb_mock/reservation_handlers.go
@@ -50,7 +50,7 @@ func (s *reservationsStore) poll(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	output := []*provision.Reservation{}
+	var output []*provision.Reservation
 	if from == 0 {
 		// just get all reservation for this nodeID
 		output = s.GetReservations(nodeID, from)
diff --git a/tools/tffarmer/farm.go b/tools/tffarmer/farm.go
index 59bfec24..884518e4 100644
--- a/tools/tffarmer/farm.go
+++ b/tools/tffarmer/farm.go
@@ -10,7 +10,7 @@ func registerFarm(c *cli.Context) error {
 
 	name := c.Args().First()
 	if name == "" {
-		return fmt.Errorf("A farm name needs to be specified")
+		return fmt.Errorf("farm name needs to be specified")
 	}
 
 	tid := c.Uint64("tid")
diff --git a/tools/tffarmer/main.go b/tools/tffarmer/main.go
index 0d6091b2..b647cfa2 100644
--- a/tools/tffarmer/main.go
+++ b/tools/tffarmer/main.go
@@ -1,14 +1,11 @@
 package main
 
 import (
-	"path/filepath"
-
 	"github.com/rs/zerolog"
 	"github.com/rs/zerolog/log"
 
 	"os"
 
-	"github.com/threefoldtech/zos/pkg"
 	"github.com/threefoldtech/zos/pkg/identity"
 	"github.com/threefoldtech/zos/pkg/network"
 	"github.com/threefoldtech/zos/pkg/network/tnodb"
@@ -107,45 +104,3 @@ You can specify multime time the ip and gw flag to configure multiple IP on the
 		log.Fatal().Msg(err.Error())
 	}
 }
-
-func loadFarmID(seedPath string) (pkg.Identifier, error) {
-	if seedPath == "" {
-		cwd, err := os.Getwd()
-		if err != nil {
-			return nil, err
-		}
-		seedPath = filepath.Join(cwd, "farm.seed")
-	}
-
-	log.Debug().Msgf("loading seed from %s", seedPath)
-	farmID, err := identity.LoadKeyPair(seedPath)
-	if err != nil {
-		return nil, err
-	}
-
-	return farmID, nil
-}
-
-func generateKeyPair(seedPath string) (pkg.Identifier, error) {
-	log.Debug().Msg("generating new key pair")
-	keypair, err := identity.GenerateKeyPair()
-	if err != nil {
-		log.Error().Err(err).Msg("fail to generate key pair for farm identity")
-		return nil, err
-	}
-
-	if seedPath == "" {
-		cwd, err := os.Getwd()
-		if err != nil {
-			return nil, err
-		}
-		seedPath = filepath.Join(cwd, "farm.seed")
-	}
-
-	if err := keypair.Save(seedPath); err != nil {
-		log.Error().Err(err).Msg("fail to save identity seed on disk")
-		return nil, err
-	}
-
-	return keypair, nil
-}
diff --git a/tools/tfuser/cmds_provision.go b/tools/tfuser/cmds_provision.go
index 665112e5..c1b57279 100644
--- a/tools/tfuser/cmds_provision.go
+++ b/tools/tfuser/cmds_provision.go
@@ -92,16 +92,21 @@ func cmdsProvision(c *cli.Context) error {
 		nodeIDs  = c.StringSlice("node")
 		seedPath = c.String("seed")
 		d        = c.String("duration")
+		duration time.Duration
 		err      error
 	)
 
-	duration, err := time.ParseDuration(d)
-	if err != nil {
-		nrDays, err := strconv.Atoi(d)
+	if d == "" {
+		duration = defaultDuration
+	} else {
+		duration, err = time.ParseDuration(d)
 		if err != nil {
-			return errors.Wrap(err, "unsupported duration format")
+			nrDays, err := strconv.Atoi(d)
+			if err != nil {
+				return errors.Wrap(err, "unsupported duration format")
+			}
+			duration = time.Duration(nrDays) * day
 		}
-		duration = time.Duration(nrDays) * day
 	}
 
 	keypair, err := identity.LoadKeyPair(seedPath)
diff --git a/tools/tfuser/ui.go b/tools/tfuser/ui.go
index 987479b1..a5c6dc9e 100644
--- a/tools/tfuser/ui.go
+++ b/tools/tfuser/ui.go
@@ -2,34 +2,10 @@ package main
 
 import (
 	"encoding/json"
-	"fmt"
 	"io"
 	"os"
-	"strings"
-
-	"github.com/tcnksm/go-input"
 )
 
-func confirm(s string) (string, error) {
-	ans, err := input.DefaultUI().Ask(s, &input.Options{
-		Default:     "Y",
-		Required:    true,
-		Loop:        true,
-		HideDefault: true,
-		ValidateFunc: func(s string) error {
-			s = strings.ToLower(s)
-			if s != "y" && s != "n" {
-				return fmt.Errorf("input must be y or n")
-			}
-			return nil
-		},
-	})
-	if err != nil {
-		return "", err
-	}
-	return strings.ToLower(ans), nil
-}
-
 func output(name string, workload interface{}) (err error) {
 	var w io.Writer = os.Stdout
 	if name != "" {
diff --git a/tools/updatectl/jwt.go b/tools/updatectl/jwt.go
index ced24344..adb20223 100644
--- a/tools/updatectl/jwt.go
+++ b/tools/updatectl/jwt.go
@@ -27,10 +27,10 @@ func JWTUser(token string) (string, error) {
 	t, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
 		m, ok := token.Method.(*jwt.SigningMethodECDSA)
 		if !ok {
-			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
+			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
 		}
 		if token.Header["alg"] != m.Alg() {
-			return nil, fmt.Errorf("Unexpected signing algorithm: %v", token.Header["alg"])
+			return nil, fmt.Errorf("unexpected signing algorithm: %v", token.Header["alg"])
 		}
 		return pub, nil
 	})


Test patch:
diff --git a/pkg/crypto/crypto_test.go b/pkg/crypto/crypto_test.go
index 1856ac7d..db4fd822 100644
--- a/pkg/crypto/crypto_test.go
+++ b/pkg/crypto/crypto_test.go
@@ -19,6 +19,7 @@ func TestPublicKeyEncryption(t *testing.T) {
 	cipher, err := Encrypt(msg, pk)
 	require.NoError(t, err)
 	clear, err := Decrypt(cipher, sk)
+	require.NoError(t, err)
 	assert.Equal(t, clear, msg)
 }
 
diff --git a/pkg/network/ifaceutil/mac_test.go b/pkg/network/ifaceutil/mac_test.go
index 789c8aa4..1eaa99e0 100644
--- a/pkg/network/ifaceutil/mac_test.go
+++ b/pkg/network/ifaceutil/mac_test.go
@@ -10,7 +10,7 @@ import (
 func TestHardwareAddrFromInputBytes_Simple_Nil(t *testing.T) {
 	addr := HardwareAddrFromInputBytes(nil)
 	expectedAddr := net.HardwareAddr{0xd6, 0x1d, 0x8c, 0xd9, 0x8f, 0x00}
-	if bytes.Compare(addr[:], expectedAddr[:]) != 0 {
+	if !bytes.Equal(addr[:], expectedAddr[:]) {
 		t.Fatalf("%v != %v", addr, expectedAddr)
 	}
 }
@@ -18,7 +18,7 @@ func TestHardwareAddrFromInputBytes_Simple_Nil(t *testing.T) {
 func TestHardwareAddrFromInputBytes_Simple_42(t *testing.T) {
 	addr := HardwareAddrFromInputBytes([]byte{0x42})
 	expectedAddr := net.HardwareAddr{0x9e, 0x5e, 0xd6, 0x78, 0xfe, 0x57}
-	if bytes.Compare(addr[:], expectedAddr[:]) != 0 {
+	if !bytes.Equal(addr[:], expectedAddr[:]) {
 		t.Fatalf("%v != %v", addr, expectedAddr)
 	}
 }
@@ -27,7 +27,7 @@ func TestHardwareAddrFromInputBytes_Simple_FromNodeID(t *testing.T) {
 	const nodeID = "A34YUGenHKyhjDMAUKZe4cVDtJM2wQ4n4XRkfGUUEYdy"
 	addr := HardwareAddrFromInputBytes([]byte(nodeID[:]))
 	expectedAddr := net.HardwareAddr{0x8a, 0xd3, 0x36, 0x10, 0x7e, 0xe9}
-	if bytes.Compare(addr[:], expectedAddr[:]) != 0 {
+	if !bytes.Equal(addr[:], expectedAddr[:]) {
 		t.Fatalf("%v != %v", addr, expectedAddr)
 	}
 }
diff --git a/pkg/network/ndmz/ndmz_test.go b/pkg/network/ndmz/ndmz_test.go
index bc82c8c3..376f5e20 100644
--- a/pkg/network/ndmz/ndmz_test.go
+++ b/pkg/network/ndmz/ndmz_test.go
@@ -1,10 +1,14 @@
 package ndmz
 
 import (
+	"fmt"
+	"io/ioutil"
 	"net"
+	"sync"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestIpv6(t *testing.T) {
@@ -32,3 +36,66 @@ func TestIpv6(t *testing.T) {
 	}
 
 }
+
+func TestIPv4Allocate(t *testing.T) {
+	var (
+		err    error
+		origin = ipamPath
+	)
+
+	ipamPath, err = ioutil.TempDir("", "")
+	require.NoError(t, err)
+	defer func() { ipamPath = origin }()
+
+	addr, err := allocateIPv4("network1")
+	require.NoError(t, err)
+
+	addr2, err := allocateIPv4("network1")
+	require.NoError(t, err)
+
+	assert.Equal(t, addr.String(), addr2.String())
+
+	addr3, err := allocateIPv4("network2")
+	require.NoError(t, err)
+	assert.NotEqual(t, addr.String(), addr3.String())
+}
+
+func TestIPv4AllocateConcurent(t *testing.T) {
+	var (
+		err    error
+		origin = ipamPath
+	)
+
+	ipamPath, err = ioutil.TempDir("", "")
+	require.NoError(t, err)
+	defer func() { ipamPath = origin }()
+
+	wg := sync.WaitGroup{}
+	wg.Add(10)
+
+	c := make(chan *net.IPNet)
+
+	for i := 0; i < 10; i++ {
+		go func(c chan *net.IPNet, i int) {
+			defer wg.Done()
+			for y := 0; y < 10; y++ {
+				nw := fmt.Sprintf("network%d%d", i, y)
+				addr, err := allocateIPv4(nw)
+				require.NoError(t, err)
+				c <- addr
+			}
+		}(c, i)
+	}
+
+	go func() {
+		addrs := map[*net.IPNet]struct{}{}
+		for addr := range c {
+			_, exists := addrs[addr]
+			require.False(t, exists)
+			addrs[addr] = struct{}{}
+		}
+	}()
+
+	wg.Wait()
+	close(c)
+}
diff --git a/pkg/network/nr/net_resource_test.go b/pkg/network/nr/net_resource_test.go
index 2995574e..76aa4b73 100644
--- a/pkg/network/nr/net_resource_test.go
+++ b/pkg/network/nr/net_resource_test.go
@@ -111,15 +111,6 @@ func TestCreateBridge(t *testing.T) {
 	netlink.LinkDel(l)
 }
 
-func mustParseCIDR(cidr string) *net.IPNet {
-	ip, ipnet, err := net.ParseCIDR(cidr)
-	if err != nil {
-		panic(err)
-	}
-	ipnet.IP = ip
-	return ipnet
-}
-
 func Test_wgIP(t *testing.T) {
 	type args struct {
 		subnet *net.IPNet
diff --git a/pkg/network/portm/allocator_test.go b/pkg/network/portm/allocator_test.go
index 977d1380..1aa2c430 100644
--- a/pkg/network/portm/allocator_test.go
+++ b/pkg/network/portm/allocator_test.go
@@ -18,7 +18,7 @@ type testStore struct {
 func newTestStore() *testStore {
 	return &testStore{
 		reserved: make(map[string]mapset.Set),
-		last:     make(map[string]int, 0),
+		last:     make(map[string]int),
 	}
 }
 
diff --git a/pkg/provision/provision_test.go b/pkg/provision/provision_test.go
index 0ca26bea..5ea40534 100644
--- a/pkg/provision/provision_test.go
+++ b/pkg/provision/provision_test.go
@@ -27,9 +27,7 @@ func (t *TestClient) Request(module string, object zbus.ObjectID, method string,
 	inputs := []interface{}{
 		module, object, method,
 	}
-	for _, arg := range args {
-		inputs = append(inputs, arg)
-	}
+	inputs = append(inputs, args...)
 
 	return zbus.NewResponse("", "", t.Called(inputs...)...)
 }
diff --git a/pkg/provision/zdb_test.go b/pkg/provision/zdb_test.go
index 4870e8b3..6164d24f 100644
--- a/pkg/provision/zdb_test.go
+++ b/pkg/provision/zdb_test.go
@@ -5,6 +5,7 @@ import (
 	"encoding/hex"
 	"fmt"
 	"net"
+	"path/filepath"
 	"strings"
 	"testing"
 
@@ -283,3 +284,12 @@ func Test_findDataVolume(t *testing.T) {
 		})
 	}
 }
+
+func findDataVolume(mounts []pkg.MountInfo) (string, error) {
+	for _, mount := range mounts {
+		if mount.Target == "/data" {
+			return filepath.Base(mount.Source), nil
+		}
+	}
+	return "", fmt.Errorf("data volume not found")
+}
diff --git a/pkg/storage/filesystem/btrfs_ci_test.go b/pkg/storage/filesystem/btrfs_ci_test.go
index 5a6272d1..39b3dac7 100644
--- a/pkg/storage/filesystem/btrfs_ci_test.go
+++ b/pkg/storage/filesystem/btrfs_ci_test.go
@@ -318,6 +318,7 @@ func TestCLeanUpQgroupsCI(t *testing.T) {
 	pools, err := fs.List(context.Background(), func(p Pool) bool {
 		return strings.HasPrefix(p.Name(), "test-")
 	})
+	require.NoError(t, err)
 	pool := pools[0]
 
 	_, err = pool.Mount()