From dc55f27918a1945cab00fa3d916f2e7a134496cf Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 17:58:24 -0500 Subject: [PATCH 01/12] Add nix-shell and direnv goodness --- .envrc | 4 ++++ shell.nix | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 .envrc create mode 100644 shell.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..f310aea --- /dev/null +++ b/.envrc @@ -0,0 +1,4 @@ +has nix && use nix +dotenv_if_exists +PATH_add bin +path_add GOBIN bin diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..27ad73a --- /dev/null +++ b/shell.nix @@ -0,0 +1,14 @@ +let _pkgs = import { }; +in { pkgs ? import (_pkgs.fetchFromGitHub { + owner = "NixOS"; + repo = "nixpkgs"; + #branch@date: 21.11@2022-02-13 + rev = "560ad8a2f89586ab1a14290f128ad6a393046065"; + sha256 = "0s0dv1clfpjyzy4p6ywxvzmwx9ddbr2yl77jf1wqdbr0x1206hb8"; +}) { } }: + +with pkgs; + +mkShell { + buildInputs = [ git gnumake gnused go nixfmt nodePackages.prettier vagrant ]; +} From 8a2b0b6caa6cdb9073ddc6119946b71729d437ee Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 18:13:06 -0500 Subject: [PATCH 02/12] prettierify all the files --- .travis.yml | 36 ++++++++++++++++++------------------ CONTRIBUTING.md | 31 +++++++++++++++++-------------- README.md | 13 ++++++------- 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/.travis.yml b/.travis.yml index acbd39c..c7f82b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,25 +18,25 @@ go: - "1.10.x" - master -before_install: - - export MAKEFLAGS=-j$(($(grep -c '^processor' /proc/cpuinfo) * 2 + 1)) - - export PATH=/usr/lib/ccache:$PATH - - go get github.com/alecthomas/gometalinter - - gometalinter --install --update - - sudo apt-get update -y && sudo apt-get install -y libattr1-dev libblkid-dev linux-headers-$(uname -r) tree uuid-dev - - mkdir -p $HOME/zfs - - cd $HOME/zfs - - [[ -d spl-$rel.tar.gz ]] || curl -L https://github.com/zfsonlinux/zfs/releases/download/zfs-$rel/spl-$rel.tar.gz | tar xz - - [[ -d zfs-$rel.tar.gz ]] || curl -L https://github.com/zfsonlinux/zfs/releases/download/zfs-$rel/zfs-$rel.tar.gz | tar xz - - (cd spl-$rel && ./configure --prefix=/usr && make && sudo make install) - - (cd zfs-$rel && ./configure --prefix=/usr && make && sudo make install) - - sudo modprobe zfs - - cd $TRAVIS_BUILD_DIR +before_install: | + export MAKEFLAGS=-j$(($(grep -c '^processor' /proc/cpuinfo) * 2 + 1)) + export PATH=/usr/lib/ccache:$PATH + go get github.com/alecthomas/gometalinter + gometalinter --install --update + sudo apt-get update -y && sudo apt-get install -y libattr1-dev libblkid-dev linux-headers-$(uname -r) tree uuid-dev + mkdir -p $HOME/zfs + cd $HOME/zfs + [[ -d spl-$rel.tar.gz ]] || curl -L https://github.com/zfsonlinux/zfs/releases/download/zfs-$rel/spl-$rel.tar.gz | tar xz + [[ -d zfs-$rel.tar.gz ]] || curl -L https://github.com/zfsonlinux/zfs/releases/download/zfs-$rel/zfs-$rel.tar.gz | tar xz + (cd spl-$rel && ./configure --prefix=/usr && make && sudo make install) + (cd zfs-$rel && ./configure --prefix=/usr && make && sudo make install) + sudo modprobe zfs + cd $TRAVIS_BUILD_DIR -script: - - sudo -E $(which go) test -v ./... - - gometalinter --vendor --vendored-linters ./... || true - - gometalinter --errors --vendor --vendored-linters ./... +script: | + sudo -E $(which go) test -v ./... + gometalinter --vendor --vendored-linters ./... || true + gometalinter --errors --vendor --vendored-linters ./... notifications: email: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f1880c1..ebfe905 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,18 +1,18 @@ -## How to Contribute ## +## How to Contribute We always welcome contributions to help make `go-zfs` better. Please take a moment to read this document if you would like to contribute. -### Reporting issues ### +### Reporting issues We use [Github issues](https://github.com/mistifyio/go-zfs/issues) to track bug reports, feature requests, and submitting pull requests. If you find a bug: -* Use the GitHub issue search to check whether the bug has already been reported. -* If the issue has been fixed, try to reproduce the issue using the latest `master` branch of the repository. -* If the issue still reproduces or has not yet been reported, try to isolate the problem before opening an issue, if possible. Also provide the steps taken to reproduce the bug. +- Use the GitHub issue search to check whether the bug has already been reported. +- If the issue has been fixed, try to reproduce the issue using the latest `master` branch of the repository. +- If the issue still reproduces or has not yet been reported, try to isolate the problem before opening an issue, if possible. Also provide the steps taken to reproduce the bug. -### Pull requests ### +### Pull requests We welcome bug fixes, improvements, and new features. Before embarking on making significant changes, please open an issue and ask first so that you do not risk duplicating efforts or spending time working on something that may be out of scope. For minor items, just open a pull request. @@ -42,19 +42,22 @@ Push your feature branch to your fork. [Open a Pull Request](https://help.github.com/articles/using-pull-requests) against the upstream master branch. Please give your pull request a clear title and description and note which issue(s) your pull request fixes. -* All Go code should be formatted using [gofmt](http://golang.org/cmd/gofmt/). -* Every exported function should have [documentation](http://blog.golang.org/godoc-documenting-go-code) and corresponding [tests](http://golang.org/doc/code.html#Testing). +- All Go code should be formatted using [gofmt](http://golang.org/cmd/gofmt/). +- Every exported function should have [documentation](http://blog.golang.org/godoc-documenting-go-code) and corresponding [tests](http://golang.org/doc/code.html#Testing). **Important:** By submitting a patch, you agree to allow the project owners to license your work under the [Apache 2.0 License](./LICENSE). -### Go Tools ### +### Go Tools + For consistency and to catch minor issues for all of go code, please run the following: -* goimports -* go vet -* golint -* errcheck + +- goimports +- go vet +- golint +- errcheck Many editors can execute the above on save. ----- +--- + Guidelines based on http://azkaban.github.io/contributing.html diff --git a/README.md b/README.md index fef80d7..c911833 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# Go Wrapper for ZFS # +# Go Wrapper for ZFS Simple wrappers for ZFS command line tools. [![GoDoc](https://godoc.org/github.com/mistifyio/go-zfs?status.svg)](https://godoc.org/github.com/mistifyio/go-zfs) -## Requirements ## +## Requirements -You need a working ZFS setup. To use on Ubuntu 14.04, setup ZFS: +You need a working ZFS setup. To use on Ubuntu 14.04, setup ZFS: sudo apt-get install python-software-properties sudo apt-add-repository ppa:zfs-native/stable @@ -17,13 +17,13 @@ Developed using Go 1.3, but currently there isn't anything 1.3 specific. Don't u Generally you need root privileges to use anything zfs related. -## Status ## +## Status This has been only been tested on Ubuntu 14.04 In the future, we hope to work directly with libzfs. -# Hacking # +# Hacking The tests have decent examples for most functions. @@ -48,7 +48,6 @@ err := f.Destroy() ``` -# Contributing # +# Contributing See the [contributing guidelines](./CONTRIBUTING.md) - From a769b80495651f6c14074fc025fa3a6ad13c3dea Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 18:23:20 -0500 Subject: [PATCH 03/12] Add go based tools Tools are in a separate go module to avoid the tool's dependencies influencing go mod's version decision when go-zfs is pulled into a different project. From: https://github.com/mistifyio/go-zfs/pull/79#discussion_r805883432 > @thaJeztah wrote: > > @mmlb wrote: > > > it doesn't really have much of an effect on projects importing go-zfs > Unfortunately, it does; go modules looks at what's defined in a dependency's > go.mod and uses that to resolve what dependencies should be considered (and > what version), even if the dependency is not used by the code you consume > from the module you depend on disappointed. We ran into issues because of > that in various projects (e.g. the Cobra CLI-framework having some dependency > on a grpc version, now forcing all projects to update their version). --- tooling/go.mod | 19 +++++++++++++++ tooling/go.sum | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ tooling/tools.go | 9 +++++++ 3 files changed, 90 insertions(+) create mode 100644 tooling/go.mod create mode 100644 tooling/go.sum create mode 100644 tooling/tools.go diff --git a/tooling/go.mod b/tooling/go.mod new file mode 100644 index 0000000..717e11e --- /dev/null +++ b/tooling/go.mod @@ -0,0 +1,19 @@ +module github.com/mistifyio/go-zfs/tooling/v3 + +go 1.17 + +require ( + github.com/tinkerbell/lint-install v0.0.0-20220113213936-9b6f0db005b0 + mvdan.cc/gofumpt v0.2.1 +) + +require ( + github.com/go-logr/logr v0.4.0 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/hexops/gotextdiff v1.0.3 // indirect + github.com/karrick/godirwalk v1.16.1 // indirect + golang.org/x/mod v0.5.1 // indirect + golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 // indirect + golang.org/x/tools v0.1.8 // indirect + k8s.io/klog/v2 v2.10.0 // indirect +) diff --git a/tooling/go.sum b/tooling/go.sum new file mode 100644 index 0000000..cddad8e --- /dev/null +++ b/tooling/go.sum @@ -0,0 +1,62 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= +github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/tinkerbell/lint-install v0.0.0-20220113213936-9b6f0db005b0 h1:wArDwmgA90A4klrkNhV7vTZfNiP1Z7O7NWhpOl4bDZ8= +github.com/tinkerbell/lint-install v0.0.0-20220113213936-9b6f0db005b0/go.mod h1:0h2KsALaQLNkoVeV+G+HjBWWCnp0COFYhJdRd5WCQPM= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +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/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 h1:A0Qkn7Z/n8zC1xd9LTw17AiKlBRK64tw3ejWQiEqca0= +golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +k8s.io/klog/v2 v2.10.0 h1:R2HDMDJsHVTHA2n4RjwbeYXdOcBymXdX/JRb1v0VGhE= +k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +mvdan.cc/gofumpt v0.2.1 h1:7jakRGkQcLAJdT+C8Bwc9d0BANkVPSkHZkzNv07pJAs= +mvdan.cc/gofumpt v0.2.1/go.mod h1:a/rvZPhsNaedOJBzqRD9omnwVwHZsBdJirXHa9Gh9Ig= diff --git a/tooling/tools.go b/tooling/tools.go new file mode 100644 index 0000000..4ae09ba --- /dev/null +++ b/tooling/tools.go @@ -0,0 +1,9 @@ +//go:build tools +// +build tools + +package tools + +import ( + _ "github.com/tinkerbell/lint-install" + _ "mvdan.cc/gofumpt" +) From fd5e3b2bea8281f963babebbcb7b9b65b9f0bbcc Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 18:23:29 -0500 Subject: [PATCH 04/12] Add Makefile and rules.mk files --- .gitignore | 1 + Makefile | 18 ++++++++++++++++++ rules.mk | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 Makefile create mode 100644 rules.mk diff --git a/.gitignore b/.gitignore index 8000dd9..4fc84c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +bin .vagrant diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..256ac72 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +help: ## Print this help + @grep --no-filename -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sed 's/:.*## /·/' | sort | column -t -W 2 -s '·' -c $(shell tput cols) + +all: test ## Run tests + +-include rules.mk + +test: ## Run tests + go test ./... + +verify: gofumpt prettier lint ## Verify code style, is lint free, freshness ... + git diff | (! grep .) + +fix: gofumpt-fix prettier-fix ## Fix code formatting errors + +tools: ${toolsBins} ## Build Go based build tools + +.PHONY: all help test tools verify diff --git a/rules.mk b/rules.mk new file mode 100644 index 0000000..4746c97 --- /dev/null +++ b/rules.mk @@ -0,0 +1,49 @@ +# Only use the recipes defined in these makefiles +MAKEFLAGS += --no-builtin-rules +.SUFFIXES: +# Delete target files if there's an error +# This avoids a failure to then skip building on next run if the output is created by shell redirection for example +# Not really necessary for now, but just good to have already if it becomes necessary later. +.DELETE_ON_ERROR: +# Treat the whole recipe as a one shell script/invocation instead of one-per-line +.ONESHELL: +# Use bash instead of plain sh +SHELL := bash +.SHELLFLAGS := -o pipefail -euc + +version := $(shell git rev-parse --short HEAD) +tag := $(shell git tag --points-at HEAD) +ifneq (,$(tag)) +version := $(tag)-$(version) +endif +LDFLAGS := -ldflags "-X main.version=$(version)" +export CGO_ENABLED := 0 + +ifeq ($(origin GOBIN), undefined) +GOBIN := ${PWD}/bin +export GOBIN +PATH := ${GOBIN}:${PATH} +export PATH +endif + +toolsBins := $(addprefix bin/,$(notdir $(shell grep '^\s*_' tooling/tools.go | awk -F'"' '{print $$2}'))) + +# installs cli tools defined in tools.go +$(toolsBins): tooling/go.mod tooling/go.sum tooling/tools.go +$(toolsBins): CMD=$(shell awk -F'"' '/$(@F)"/ {print $$2}' tooling/tools.go) +$(toolsBins): + cd tooling && go install $(CMD) + +.PHONY: gofumpt +gofumpt: bin/gofumpt + gofumpt -s -d . + +gofumpt-fix: bin/gofumpt + gofumpt -s -w . + +.PHONY: prettier prettier-fix +prettier: + prettier --list-different --ignore-path .gitignore . + +prettier-fix: + prettier --write --ignore-path .gitignore . From 53636cc983d5d87ba136e2c9143b131b50df3b08 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 18:28:47 -0500 Subject: [PATCH 05/12] gofumptize the code base --- error_test.go | 2 +- utils.go | 5 ++--- utils_notsolaris.go | 22 ++++++++++++---------- utils_solaris.go | 22 ++++++++++++---------- zfs.go | 2 +- zfs_test.go | 14 ++++++-------- 6 files changed, 34 insertions(+), 33 deletions(-) diff --git a/error_test.go b/error_test.go index 323980e..84b19cf 100644 --- a/error_test.go +++ b/error_test.go @@ -7,7 +7,7 @@ import ( ) func TestError(t *testing.T) { - var tests = []struct { + tests := []struct { err error debug string stderr string diff --git a/utils.go b/utils.go index c18c2c3..af95011 100644 --- a/utils.go +++ b/utils.go @@ -21,7 +21,6 @@ type command struct { } func (c *command) Run(arg ...string) ([][]string, error) { - cmd := exec.Command(c.Command, arg...) var stdout, stderr bytes.Buffer @@ -34,7 +33,6 @@ func (c *command) Run(arg ...string) ([][]string, error) { if c.Stdin != nil { cmd.Stdin = c.Stdin - } cmd.Stderr = &stderr @@ -60,7 +58,7 @@ func (c *command) Run(arg ...string) ([][]string, error) { lines := strings.Split(stdout.String(), "\n") - //last line is always blank + // last line is always blank lines = lines[0 : len(lines)-1] output := make([][]string, len(lines)) @@ -179,6 +177,7 @@ var changeTypeMap = map[string]ChangeType{ "M": Modified, "R": Renamed, } + var inodeTypeMap = map[string]InodeType{ "B": BlockDevice, "C": CharacterDevice, diff --git a/utils_notsolaris.go b/utils_notsolaris.go index a46f730..198f2b7 100644 --- a/utils_notsolaris.go +++ b/utils_notsolaris.go @@ -1,17 +1,19 @@ +//go:build !solaris // +build !solaris package zfs -import ( - "strings" -) +import "strings" + +var ( + // List of ZFS properties to retrieve from zfs list command on a non-Solaris platform + dsPropList = []string{"name", "origin", "used", "available", "mountpoint", "compression", "type", "volsize", "quota", "referenced", "written", "logicalused", "usedbydataset"} -// List of ZFS properties to retrieve from zfs list command on a non-Solaris platform -var dsPropList = []string{"name", "origin", "used", "available", "mountpoint", "compression", "type", "volsize", "quota", "referenced", "written", "logicalused", "usedbydataset"} + dsPropListOptions = strings.Join(dsPropList, ",") -var dsPropListOptions = strings.Join(dsPropList, ",") + // List of Zpool properties to retrieve from zpool list command on a non-Solaris platform + zpoolPropList = []string{"name", "health", "allocated", "size", "free", "readonly", "dedupratio", "fragmentation", "freeing", "leaked"} -// List of Zpool properties to retrieve from zpool list command on a non-Solaris platform -var zpoolPropList = []string{"name", "health", "allocated", "size", "free", "readonly", "dedupratio", "fragmentation", "freeing", "leaked"} -var zpoolPropListOptions = strings.Join(zpoolPropList, ",") -var zpoolArgs = []string{"get", "-p", zpoolPropListOptions} + zpoolPropListOptions = strings.Join(zpoolPropList, ",") + zpoolArgs = []string{"get", "-p", zpoolPropListOptions} +) diff --git a/utils_solaris.go b/utils_solaris.go index 0a7e90f..c6bf6d8 100644 --- a/utils_solaris.go +++ b/utils_solaris.go @@ -1,17 +1,19 @@ +//go:build solaris // +build solaris package zfs -import ( - "strings" -) +import "strings" + +var ( + // List of ZFS properties to retrieve from zfs list command on a Solaris platform + dsPropList = []string{"name", "origin", "used", "available", "mountpoint", "compression", "type", "volsize", "quota", "referenced"} -// List of ZFS properties to retrieve from zfs list command on a Solaris platform -var dsPropList = []string{"name", "origin", "used", "available", "mountpoint", "compression", "type", "volsize", "quota", "referenced"} + dsPropListOptions = strings.Join(dsPropList, ",") -var dsPropListOptions = strings.Join(dsPropList, ",") + // List of Zpool properties to retrieve from zpool list command on a non-Solaris platform + zpoolPropList = []string{"name", "health", "allocated", "size", "free", "readonly", "dedupratio"} -// List of Zpool properties to retrieve from zpool list command on a non-Solaris platform -var zpoolPropList = []string{"name", "health", "allocated", "size", "free", "readonly", "dedupratio"} -var zpoolPropListOptions = strings.Join(zpoolPropList, ",") -var zpoolArgs = []string{"get", "-p", zpoolPropListOptions} + zpoolPropListOptions = strings.Join(zpoolPropList, ",") + zpoolArgs = []string{"get", "-p", zpoolPropListOptions} +) diff --git a/zfs.go b/zfs.go index 0c63e3d..a1b5ff4 100644 --- a/zfs.go +++ b/zfs.go @@ -329,7 +329,7 @@ func (d *Dataset) GetProperty(key string) (string, error) { } // Rename renames a dataset. -func (d *Dataset) Rename(name string, createParent bool, recursiveRenameSnapshots bool) (*Dataset, error) { +func (d *Dataset) Rename(name string, createParent, recursiveRenameSnapshots bool) (*Dataset, error) { args := make([]string, 3, 5) args[0] = "rename" args[1] = d.Name diff --git a/zfs_test.go b/zfs_test.go index 6f2deba..8b6b495 100644 --- a/zfs_test.go +++ b/zfs_test.go @@ -22,7 +22,7 @@ func pow2(x int) int64 { return int64(math.Pow(2, float64(x))) } -//https://github.com/benbjohnson/testing +// https://github.com/benbjohnson/testing // assert fails the test if the condition is false. func assert(tb testing.TB, condition bool, msg string, v ...interface{}) { if !condition { @@ -75,7 +75,6 @@ func zpoolTest(t *testing.T, fn func()) { defer pool.Destroy() ok(t, err) fn() - } func TestDatasets(t *testing.T) { @@ -109,7 +108,6 @@ func TestDatasetGetProperty(t *testing.T) { } func TestSnapshots(t *testing.T) { - zpoolTest(t, func() { snapshots, err := zfs.Snapshots("") ok(t, err) @@ -365,27 +363,27 @@ func TestDiff(t *testing.T) { unicodePath := "/test/origin/i\x040\x1c2\x135\x144\x040unicode" wants := map[string]*zfs.InodeChange{ - "/test/origin/linked": &zfs.InodeChange{ + "/test/origin/linked": { Type: zfs.File, Change: zfs.Modified, ReferenceCountChange: 1, }, - "/test/origin/file": &zfs.InodeChange{ + "/test/origin/file": { Type: zfs.File, Change: zfs.Renamed, NewPath: "/test/origin/file-new", }, - "/test/origin/i ❤ unicode": &zfs.InodeChange{ + "/test/origin/i ❤ unicode": { Path: "❤❤ unicode ❤❤", Type: zfs.File, Change: zfs.Created, }, - unicodePath: &zfs.InodeChange{ + unicodePath: { Path: "❤❤ unicode ❤❤", Type: zfs.File, Change: zfs.Created, }, - "/test/origin/": &zfs.InodeChange{ + "/test/origin/": { Type: zfs.Directory, Change: zfs.Modified, }, From 5a8395c8d7623336b9c2beed5ed0d0eaf06f0d89 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 19:34:14 -0500 Subject: [PATCH 06/12] Use tinkerbell/lint-install to setup linters The nicest thing lint-install brings is the versioned linter installs and opinionated golangci-linter config file that was lint-clean on the go stdlib. --- .gitignore | 3 + .golangci.yml | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++ .yamllint | 16 ++++ Makefile | 1 + lint.mk | 58 ++++++++++++++ shell.nix | 12 ++- 6 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 .golangci.yml create mode 100644 .yamllint create mode 100644 lint.mk diff --git a/.gitignore b/.gitignore index 4fc84c8..aad2426 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ bin .vagrant + +# added by lint-install +out/ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..499c3ec --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,207 @@ +run: + # The default runtime timeout is 1m, which doesn't work well on Github Actions. + timeout: 4m + +# NOTE: This file is populated by the lint-install tool. Local adjustments may be overwritten. +linters-settings: + cyclop: + # NOTE: This is a very high transitional threshold + max-complexity: 37 + package-average: 34.0 + skip-tests: true + + gocognit: + # NOTE: This is a very high transitional threshold + min-complexity: 98 + + dupl: + threshold: 200 + + goconst: + min-len: 4 + min-occurrences: 5 + ignore-tests: true + + gosec: + excludes: + - G107 # Potential HTTP request made with variable url + - G204 # Subprocess launched with function call as argument or cmd arguments + - G404 # Use of weak random number generator (math/rand instead of crypto/rand + + errorlint: + # these are still common in Go: for instance, exit errors. + asserts: false + + exhaustive: + default-signifies-exhaustive: true + + nestif: + min-complexity: 8 + + nolintlint: + require-explanation: true + allow-unused: false + require-specific: true + + revive: + ignore-generated-header: true + severity: warning + rules: + - name: atomic + - name: blank-imports + - name: bool-literal-in-expr + - name: confusing-naming + - name: constant-logical-expr + - name: context-as-argument + - name: context-keys-type + - name: deep-exit + - name: defer + - name: range-val-in-closure + - name: range-val-address + - name: dot-imports + - name: error-naming + - name: error-return + - name: error-strings + - name: errorf + - name: exported + - name: identical-branches + - name: if-return + - name: import-shadowing + - name: increment-decrement + - name: indent-error-flow + - name: indent-error-flow + - name: package-comments + - name: range + - name: receiver-naming + - name: redefines-builtin-id + - name: superfluous-else + - name: struct-tag + - name: time-naming + - name: unexported-naming + - name: unexported-return + - name: unnecessary-stmt + - name: unreachable-code + - name: unused-parameter + - name: var-declaration + - name: var-naming + - name: unconditional-recursion + - name: waitgroup-by-value + + staticcheck: + go: "1.16" + + unused: + go: "1.16" + +output: + sort-results: true + +linters: + disable-all: true + enable: + - asciicheck + - bodyclose + - cyclop + - deadcode + - dogsled + - dupl + - durationcheck + - errcheck + - errname + - errorlint + - exhaustive + - exportloopref + - forcetypeassert + - gocognit + - goconst + - gocritic + - godot + - gofmt + - gofumpt + - gosec + - goheader + - goimports + - goprintffuncname + - gosimple + - govet + - ifshort + - importas + - ineffassign + - makezero + - misspell + - nakedret + - nestif + - nilerr + - noctx + - nolintlint + - predeclared + # disabling for the initial iteration of the linting tool + # - promlinter + - revive + - rowserrcheck + - sqlclosecheck + - staticcheck + - structcheck + - stylecheck + - thelper + - tparallel + - typecheck + - unconvert + - unparam + - unused + - varcheck + - wastedassign + - whitespace + + # Disabled linters, due to being misaligned with Go practices + # - exhaustivestruct + # - gochecknoglobals + # - gochecknoinits + # - goconst + # - godox + # - goerr113 + # - gomnd + # - lll + # - nlreturn + # - testpackage + # - wsl + # Disabled linters, due to not being relevant to our code base: + # - maligned + # - prealloc "For most programs usage of prealloc will be a premature optimization." + # Disabled linters due to bad error messages or bugs + # - tagliatelle + +issues: + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + - path: _test\.go + linters: + - dupl + - errcheck + - forcetypeassert + - gocyclo + - gosec + - noctx + + - path: .*cmd.* + linters: + - noctx + + - path: main\.go + linters: + - noctx + + - path: .*cmd.* + text: "deep-exit" + + - path: main\.go + text: "deep-exit" + + # This check is of questionable value + - linters: + - tparallel + text: "call t.Parallel on the top level as well as its subtests" + + # Don't hide lint issues just because there are many of them + max-same-issues: 0 + max-issues-per-linter: 0 diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..9a08ad1 --- /dev/null +++ b/.yamllint @@ -0,0 +1,16 @@ +--- +extends: default + +rules: + braces: + max-spaces-inside: 1 + brackets: + max-spaces-inside: 1 + comments: disable + comments-indentation: disable + document-start: disable + line-length: + level: warning + max: 160 + allow-non-breakable-inline-mappings: true + truthy: disable diff --git a/Makefile b/Makefile index 256ac72..1c5f55e 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ help: ## Print this help all: test ## Run tests -include rules.mk +-include lint.mk test: ## Run tests go test ./... diff --git a/lint.mk b/lint.mk new file mode 100644 index 0000000..138cede --- /dev/null +++ b/lint.mk @@ -0,0 +1,58 @@ +# BEGIN: lint-install -makefile lint.mk . +# http://github.com/tinkerbell/lint-install + +.PHONY: lint +lint: _lint + +LINT_ARCH := $(shell uname -m) +LINT_OS := $(shell uname) +LINT_OS_LOWER := $(shell echo $(LINT_OS) | tr '[:upper:]' '[:lower:]') +LINT_ROOT := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) + +# shellcheck and hadolint lack arm64 native binaries: rely on x86-64 emulation +ifeq ($(LINT_OS),Darwin) + ifeq ($(LINT_ARCH),arm64) + LINT_ARCH=x86_64 + endif +endif + +LINTERS := +FIXERS := + +GOLANGCI_LINT_CONFIG := $(LINT_ROOT)/.golangci.yml +GOLANGCI_LINT_VERSION ?= v1.43.0 +GOLANGCI_LINT_BIN := out/linters/golangci-lint-$(GOLANGCI_LINT_VERSION)-$(LINT_ARCH) +$(GOLANGCI_LINT_BIN): + mkdir -p out/linters + rm -rf out/linters/golangci-lint-* + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b out/linters $(GOLANGCI_LINT_VERSION) + mv out/linters/golangci-lint $@ + +LINTERS += golangci-lint-lint +golangci-lint-lint: $(GOLANGCI_LINT_BIN) + find . -name go.mod -execdir "$(GOLANGCI_LINT_BIN)" run -c "$(GOLINT_CONFIG)" \; + +FIXERS += golangci-lint-fix +golangci-lint-fix: $(GOLANGCI_LINT_BIN) + find . -name go.mod -execdir "$(GOLANGCI_LINT_BIN)" run -c "$(GOLINT_CONFIG)" --fix \; + +YAMLLINT_VERSION ?= 1.26.3 +YAMLLINT_ROOT := out/linters/yamllint-$(YAMLLINT_VERSION) +YAMLLINT_BIN := $(YAMLLINT_ROOT)/dist/bin/yamllint +$(YAMLLINT_BIN): + mkdir -p out/linters + rm -rf out/linters/yamllint-* + curl -sSfL https://github.com/adrienverge/yamllint/archive/refs/tags/v$(YAMLLINT_VERSION).tar.gz | tar -C out/linters -zxf - + cd $(YAMLLINT_ROOT) && pip3 install --target dist . + +LINTERS += yamllint-lint +yamllint-lint: $(YAMLLINT_BIN) + PYTHONPATH=$(YAMLLINT_ROOT)/dist $(YAMLLINT_ROOT)/dist/bin/yamllint . + +.PHONY: _lint $(LINTERS) +_lint: $(LINTERS) + +.PHONY: fix $(FIXERS) +fix: $(FIXERS) + +# END: lint-install -makefile lint.mk . diff --git a/shell.nix b/shell.nix index 27ad73a..fb1c46f 100644 --- a/shell.nix +++ b/shell.nix @@ -10,5 +10,15 @@ in { pkgs ? import (_pkgs.fetchFromGitHub { with pkgs; mkShell { - buildInputs = [ git gnumake gnused go nixfmt nodePackages.prettier vagrant ]; + buildInputs = [ + git + gnumake + gnused + go + nixfmt + nodePackages.prettier + python3Packages.pip + python3Packages.setuptools + vagrant + ]; } From d41d935ef9d17274401462de56d680670e0d46ef Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 19:34:46 -0500 Subject: [PATCH 07/12] make golangci-lint happy --- utils.go | 85 +++++++++++++++++++++------------------------ utils_notsolaris.go | 4 +-- zfs.go | 76 +++++++++++++++++++--------------------- zfs_test.go | 32 +++++++++++------ zpool.go | 17 +++++---- 5 files changed, 111 insertions(+), 103 deletions(-) diff --git a/utils.go b/utils.go index af95011..0c2cce7 100644 --- a/utils.go +++ b/utils.go @@ -40,16 +40,14 @@ func (c *command) Run(arg ...string) ([][]string, error) { joinedArgs := strings.Join(cmd.Args, " ") logger.Log([]string{"ID:" + id, "START", joinedArgs}) - err := cmd.Run() - logger.Log([]string{"ID:" + id, "FINISH"}) - - if err != nil { + if err := cmd.Run(); err != nil { return nil, &Error{ Err: err, Debug: strings.Join([]string{cmd.Path, joinedArgs[1:]}, " "), Stderr: stderr.String(), } } + logger.Log([]string{"ID:" + id, "FINISH"}) // assume if you passed in something for stdout, that you know what to do with it if c.Stdout != nil { @@ -90,33 +88,33 @@ func setUint(field *uint64, value string) error { return nil } -func (ds *Dataset) parseLine(line []string) error { +func (d *Dataset) parseLine(line []string) error { var err error if len(line) != len(dsPropList) { - return errors.New("Output does not match what is expected on this platform") + return errors.New("output does not match what is expected on this platform") } - setString(&ds.Name, line[0]) - setString(&ds.Origin, line[1]) + setString(&d.Name, line[0]) + setString(&d.Origin, line[1]) - if err = setUint(&ds.Used, line[2]); err != nil { + if err = setUint(&d.Used, line[2]); err != nil { return err } - if err = setUint(&ds.Avail, line[3]); err != nil { + if err = setUint(&d.Avail, line[3]); err != nil { return err } - setString(&ds.Mountpoint, line[4]) - setString(&ds.Compression, line[5]) - setString(&ds.Type, line[6]) + setString(&d.Mountpoint, line[4]) + setString(&d.Compression, line[5]) + setString(&d.Type, line[6]) - if err = setUint(&ds.Volsize, line[7]); err != nil { + if err = setUint(&d.Volsize, line[7]); err != nil { return err } - if err = setUint(&ds.Quota, line[8]); err != nil { + if err = setUint(&d.Quota, line[8]); err != nil { return err } - if err = setUint(&ds.Referenced, line[9]); err != nil { + if err = setUint(&d.Referenced, line[9]); err != nil { return err } @@ -124,17 +122,13 @@ func (ds *Dataset) parseLine(line []string) error { return nil } - if err = setUint(&ds.Written, line[10]); err != nil { + if err = setUint(&d.Written, line[10]); err != nil { return err } - if err = setUint(&ds.Logicalused, line[11]); err != nil { + if err = setUint(&d.Logicalused, line[11]); err != nil { return err } - if err = setUint(&ds.Usedbydataset, line[12]); err != nil { - return err - } - - return nil + return setUint(&d.Usedbydataset, line[12]) } /* @@ -154,12 +148,12 @@ func unescapeFilepath(path string) (string, error) { for i := 0; i < llen; { if path[i] == '\\' { if llen < i+4 { - return "", fmt.Errorf("Invalid octal code: too short") + return "", fmt.Errorf("invalid octal code: too short") } octalCode := path[(i + 1):(i + 4)] val, err := strconv.ParseUint(octalCode, 8, 8) if err != nil { - return "", fmt.Errorf("Invalid octal code: %v", err) + return "", fmt.Errorf("invalid octal code: %w", err) } buf = append(buf, byte(val)) i += 4 @@ -190,51 +184,51 @@ var inodeTypeMap = map[string]InodeType{ "F": File, } -// matches (+1) or (-1) -var referenceCountRegex = regexp.MustCompile("\\(([+-]\\d+?)\\)") +// matches (+1) or (-1). +var referenceCountRegex = regexp.MustCompile(`\(([+-]\d+?)\)`) func parseReferenceCount(field string) (int, error) { matches := referenceCountRegex.FindStringSubmatch(field) if matches == nil { - return 0, fmt.Errorf("Regexp does not match") + return 0, fmt.Errorf("regexp does not match") } return strconv.Atoi(matches[1]) } func parseInodeChange(line []string) (*InodeChange, error) { - llen := len(line) + llen := len(line) // nolint:ifshort // llen *is* actually used if llen < 1 { - return nil, fmt.Errorf("Empty line passed") + return nil, fmt.Errorf("empty line passed") } changeType := changeTypeMap[line[0]] if changeType == 0 { - return nil, fmt.Errorf("Unknown change type '%s'", line[0]) + return nil, fmt.Errorf("unknown change type '%s'", line[0]) } switch changeType { case Renamed: if llen != 4 { - return nil, fmt.Errorf("Mismatching number of fields: expect 4, got: %d", llen) + return nil, fmt.Errorf("mismatching number of fields: expect 4, got: %d", llen) } case Modified: if llen != 4 && llen != 3 { - return nil, fmt.Errorf("Mismatching number of fields: expect 3..4, got: %d", llen) + return nil, fmt.Errorf("mismatching number of fields: expect 3..4, got: %d", llen) } default: if llen != 3 { - return nil, fmt.Errorf("Mismatching number of fields: expect 3, got: %d", llen) + return nil, fmt.Errorf("mismatching number of fields: expect 3, got: %d", llen) } } inodeType := inodeTypeMap[line[1]] if inodeType == 0 { - return nil, fmt.Errorf("Unknown inode type '%s'", line[1]) + return nil, fmt.Errorf("unknown inode type '%s'", line[1]) } path, err := unescapeFilepath(line[2]) if err != nil { - return nil, fmt.Errorf("Failed to parse filename: %v", err) + return nil, fmt.Errorf("failed to parse filename: %w", err) } var newPath string @@ -243,13 +237,13 @@ func parseInodeChange(line []string) (*InodeChange, error) { case Renamed: newPath, err = unescapeFilepath(line[3]) if err != nil { - return nil, fmt.Errorf("Failed to parse filename: %v", err) + return nil, fmt.Errorf("failed to parse filename: %w", err) } case Modified: if llen == 4 { referenceCount, err = parseReferenceCount(line[3]) if err != nil { - return nil, fmt.Errorf("Failed to parse reference count: %v", err) + return nil, fmt.Errorf("failed to parse reference count: %w", err) } } default: @@ -265,18 +259,19 @@ func parseInodeChange(line []string) (*InodeChange, error) { }, nil } -// example input -//M / /testpool/bar/ -//+ F /testpool/bar/hello.txt -//M / /testpool/bar/hello.txt (+1) -//M / /testpool/bar/hello-hardlink +// example input for parseInodeChanges +// M / /testpool/bar/ +// + F /testpool/bar/hello.txt +// M / /testpool/bar/hello.txt (+1) +// M / /testpool/bar/hello-hardlink + func parseInodeChanges(lines [][]string) ([]*InodeChange, error) { changes := make([]*InodeChange, len(lines)) for i, line := range lines { c, err := parseInodeChange(line) if err != nil { - return nil, fmt.Errorf("Failed to parse line %d of zfs diff: %v, got: '%s'", i, err, line) + return nil, fmt.Errorf("failed to parse line %d of zfs diff: %w, got: '%s'", i, err, line) } changes[i] = c } @@ -289,7 +284,7 @@ func listByType(t, filter string) ([]*Dataset, error) { if filter != "" { args = append(args, filter) } - out, err := zfs(args...) + out, err := zfsOutput(args...) if err != nil { return nil, err } diff --git a/utils_notsolaris.go b/utils_notsolaris.go index 198f2b7..ef1beac 100644 --- a/utils_notsolaris.go +++ b/utils_notsolaris.go @@ -6,12 +6,12 @@ package zfs import "strings" var ( - // List of ZFS properties to retrieve from zfs list command on a non-Solaris platform + // List of ZFS properties to retrieve from zfs list command on a non-Solaris platform. dsPropList = []string{"name", "origin", "used", "available", "mountpoint", "compression", "type", "volsize", "quota", "referenced", "written", "logicalused", "usedbydataset"} dsPropListOptions = strings.Join(dsPropList, ",") - // List of Zpool properties to retrieve from zpool list command on a non-Solaris platform + // List of Zpool properties to retrieve from zpool list command on a non-Solaris platform. zpoolPropList = []string{"name", "health", "allocated", "size", "free", "readonly", "dedupratio", "fragmentation", "freeing", "leaked"} zpoolPropListOptions = strings.Join(zpoolPropList, ",") diff --git a/zfs.go b/zfs.go index a1b5ff4..3cfe68f 100644 --- a/zfs.go +++ b/zfs.go @@ -9,16 +9,15 @@ import ( "strings" ) -// ZFS dataset types, which can indicate if a dataset is a filesystem, -// snapshot, or volume. +// ZFS dataset types, which can indicate if a dataset is a filesystem, snapshot, or volume. const ( DatasetFilesystem = "filesystem" DatasetSnapshot = "snapshot" DatasetVolume = "volume" ) -// Dataset is a ZFS dataset. A dataset could be a clone, filesystem, snapshot, -// or volume. The Type struct member can be used to determine a dataset's type. +// Dataset is a ZFS dataset. A dataset could be a clone, filesystem, snapshot, or volume. +// The Type struct member can be used to determine a dataset's type. // // The field definitions can be found in the ZFS manual: // http://www.freebsd.org/cgi/man.cgi?zfs(8). @@ -38,10 +37,10 @@ type Dataset struct { Referenced uint64 } -// InodeType is the type of inode as reported by Diff +// InodeType is the type of inode as reported by Diff. type InodeType int -// Types of Inodes +// Types of Inodes. const ( _ = iota // 0 == unknown type BlockDevice InodeType = iota @@ -55,10 +54,10 @@ const ( File ) -// ChangeType is the type of inode change as reported by Diff +// ChangeType is the type of inode change as reported by Diff. type ChangeType int -// Types of Changes +// Types of Changes. const ( _ = iota // 0 == unknown type Removed ChangeType = iota @@ -67,10 +66,10 @@ const ( Renamed ) -// DestroyFlag is the options flag passed to Destroy +// DestroyFlag is the options flag passed to Destroy. type DestroyFlag int -// Valid destroy options +// Valid destroy options. const ( DestroyDefault DestroyFlag = 1 << iota DestroyRecursive = 1 << iota @@ -79,7 +78,7 @@ const ( DestroyForceUmount = 1 << iota ) -// InodeChange represents a change as reported by Diff +// InodeChange represents a change as reported by Diff. type InodeChange struct { Change ChangeType Type InodeType @@ -88,29 +87,34 @@ type InodeChange struct { ReferenceCountChange int } -// Logger can be used to log commands/actions +// Logger can be used to log commands/actions. type Logger interface { Log(cmd []string) } type defaultLogger struct{} -func (*defaultLogger) Log(cmd []string) { - return +func (*defaultLogger) Log([]string) { } var logger Logger = &defaultLogger{} // SetLogger set a log handler to log all commands including arguments before -// they are executed +// they are executed. func SetLogger(l Logger) { if l != nil { logger = l } } +// zfs is a helper function to wrap typical calls to zfs that ignores stdout. +func zfs(arg ...string) error { + _, err := zfsOutput(arg...) + return err +} + // zfs is a helper function to wrap typical calls to zfs. -func zfs(arg ...string) ([][]string, error) { +func zfsOutput(arg ...string) ([][]string, error) { c := command{Command: "zfs"} return c.Run(arg...) } @@ -146,7 +150,7 @@ func Volumes(filter string) ([]*Dataset, error) { // GetDataset retrieves a single ZFS dataset by name. This dataset could be // any valid ZFS dataset type, such as a clone, filesystem, snapshot, or volume. func GetDataset(name string) (*Dataset, error) { - out, err := zfs("list", "-Hp", "-o", dsPropListOptions, name) + out, err := zfsOutput("list", "-Hp", "-o", dsPropListOptions, name) if err != nil { return nil, err } @@ -174,8 +178,7 @@ func (d *Dataset) Clone(dest string, properties map[string]string) (*Dataset, er args = append(args, propsSlice(properties)...) } args = append(args, []string{d.Name, dest}...) - _, err := zfs(args...) - if err != nil { + if err := zfs(args...); err != nil { return nil, err } return GetDataset(dest) @@ -192,8 +195,7 @@ func (d *Dataset) Unmount(force bool) (*Dataset, error) { args = append(args, "-f") } args = append(args, d.Name) - _, err := zfs(args...) - if err != nil { + if err := zfs(args...); err != nil { return nil, err } return GetDataset(d.Name) @@ -214,8 +216,7 @@ func (d *Dataset) Mount(overlay bool, options []string) (*Dataset, error) { args = append(args, strings.Join(options, ",")) } args = append(args, d.Name) - _, err := zfs(args...) - if err != nil { + if err := zfs(args...); err != nil { return nil, err } return GetDataset(d.Name) @@ -226,8 +227,7 @@ func (d *Dataset) Mount(overlay bool, options []string) (*Dataset, error) { // newly-created snapshot. func ReceiveSnapshot(input io.Reader, name string) (*Dataset, error) { c := command{Command: "zfs", Stdin: input} - _, err := c.Run("receive", name) - if err != nil { + if _, err := c.Run("receive", name); err != nil { return nil, err } return GetDataset(name) @@ -271,8 +271,7 @@ func CreateVolume(name string, size uint64, properties map[string]string) (*Data args = append(args, propsSlice(properties)...) } args = append(args, name) - _, err := zfs(args...) - if err != nil { + if err := zfs(args...); err != nil { return nil, err } return GetDataset(name) @@ -302,7 +301,7 @@ func (d *Dataset) Destroy(flags DestroyFlag) error { } args = append(args, d.Name) - _, err := zfs(args...) + err := zfs(args...) return err } @@ -311,7 +310,7 @@ func (d *Dataset) Destroy(flags DestroyFlag) error { // https://www.freebsd.org/cgi/man.cgi?zfs(8). func (d *Dataset) SetProperty(key, val string) error { prop := strings.Join([]string{key, val}, "=") - _, err := zfs("set", prop, d.Name) + err := zfs("set", prop, d.Name) return err } @@ -320,7 +319,7 @@ func (d *Dataset) SetProperty(key, val string) error { // A full list of available ZFS properties may be found here: // https://www.freebsd.org/cgi/man.cgi?zfs(8). func (d *Dataset) GetProperty(key string) (string, error) { - out, err := zfs("get", "-H", key, d.Name) + out, err := zfsOutput("get", "-H", key, d.Name) if err != nil { return "", err } @@ -340,8 +339,7 @@ func (d *Dataset) Rename(name string, createParent, recursiveRenameSnapshots boo if recursiveRenameSnapshots { args = append(args, "-r") } - _, err := zfs(args...) - if err != nil { + if err := zfs(args...); err != nil { return d, err } @@ -366,8 +364,7 @@ func CreateFilesystem(name string, properties map[string]string) (*Dataset, erro } args = append(args, name) - _, err := zfs(args...) - if err != nil { + if err := zfs(args...); err != nil { return nil, err } return GetDataset(name) @@ -384,8 +381,7 @@ func (d *Dataset) Snapshot(name string, recursive bool) (*Dataset, error) { } snapName := fmt.Sprintf("%s@%s", d.Name, name) args = append(args, snapName) - _, err := zfs(args...) - if err != nil { + if err := zfs(args...); err != nil { return nil, err } return GetDataset(snapName) @@ -408,7 +404,7 @@ func (d *Dataset) Rollback(destroyMoreRecent bool) error { } args = append(args, d.Name) - _, err := zfs(args...) + err := zfs(args...) return err } @@ -426,7 +422,7 @@ func (d *Dataset) Children(depth uint64) ([]*Dataset, error) { args = append(args, "-t", "all", "-Hp", "-o", dsPropListOptions) args = append(args, d.Name) - out, err := zfs(args...) + out, err := zfsOutput(args...) if err != nil { return nil, err } @@ -451,8 +447,8 @@ func (d *Dataset) Children(depth uint64) ([]*Dataset, error) { // The snapshot name must include the filesystem part as it is possible to // compare clones with their origin snapshots. func (d *Dataset) Diff(snapshot string) ([]*InodeChange, error) { - args := []string{"diff", "-FH", snapshot, d.Name}[:] - out, err := zfs(args...) + args := []string{"diff", "-FH", snapshot, d.Name} + out, err := zfsOutput(args...) if err != nil { return nil, err } diff --git a/zfs_test.go b/zfs_test.go index 8b6b495..5f3bf95 100644 --- a/zfs_test.go +++ b/zfs_test.go @@ -24,50 +24,60 @@ func pow2(x int) int64 { // https://github.com/benbjohnson/testing // assert fails the test if the condition is false. -func assert(tb testing.TB, condition bool, msg string, v ...interface{}) { +func assert(t *testing.T, condition bool, msg string, v ...interface{}) { + t.Helper() + if !condition { _, file, line, _ := runtime.Caller(1) fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...) - tb.FailNow() + t.FailNow() } } // ok fails the test if an err is not nil. -func ok(tb testing.TB, err error) { +func ok(t *testing.T, err error) { + t.Helper() + if err != nil { _, file, line, _ := runtime.Caller(1) fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error()) - tb.FailNow() + t.FailNow() } } // nok fails the test if an err is nil. -func nok(tb testing.TB, err error) { +func nok(t *testing.T, err error) { + t.Helper() + if err == nil { _, file, line, _ := runtime.Caller(1) fmt.Printf("\033[31m%s:%d: expected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error()) - tb.FailNow() + t.FailNow() } } // equals fails the test if exp is not equal to act. -func equals(tb testing.TB, exp, act interface{}) { +func equals(t *testing.T, exp, act interface{}) { + t.Helper() + if !reflect.DeepEqual(exp, act) { _, file, line, _ := runtime.Caller(1) fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act) - tb.FailNow() + t.FailNow() } } func zpoolTest(t *testing.T, fn func()) { + t.Helper() + tempfiles := make([]string, 3) for i := range tempfiles { f, _ := ioutil.TempFile("/tmp/", "zfs-") - defer f.Close() err := f.Truncate(pow2(30)) + f.Close() ok(t, err) tempfiles[i] = f.Name() - defer os.Remove(f.Name()) + defer os.Remove(f.Name()) // nolint:revive // its ok to defer to end of func } pool, err := zfs.CreateZpool("test", nil, tempfiles...) @@ -78,6 +88,8 @@ func zpoolTest(t *testing.T, fn func()) { } func TestDatasets(t *testing.T) { + t.Helper() + zpoolTest(t, func() { _, err := zfs.Datasets("") ok(t, err) diff --git a/zpool.go b/zpool.go index d8db945..6de1d32 100644 --- a/zpool.go +++ b/zpool.go @@ -27,8 +27,14 @@ type Zpool struct { DedupRatio float64 } +// zpool is a helper function to wrap typical calls to zpool and ignores stdout. +func zpool(arg ...string) error { + _, err := zpoolOutput(arg...) + return err +} + // zpool is a helper function to wrap typical calls to zpool. -func zpool(arg ...string) ([][]string, error) { +func zpoolOutput(arg ...string) ([][]string, error) { c := command{Command: "zpool"} return c.Run(arg...) } @@ -37,7 +43,7 @@ func zpool(arg ...string) ([][]string, error) { func GetZpool(name string) (*Zpool, error) { args := zpoolArgs args = append(args, name) - out, err := zpool(args...) + out, err := zpoolOutput(args...) if err != nil { return nil, err } @@ -77,8 +83,7 @@ func CreateZpool(name string, properties map[string]string, args ...string) (*Zp } cli = append(cli, name) cli = append(cli, args...) - _, err := zpool(cli...) - if err != nil { + if err := zpool(cli...); err != nil { return nil, err } @@ -87,14 +92,14 @@ func CreateZpool(name string, properties map[string]string, args ...string) (*Zp // Destroy destroys a ZFS zpool by name. func (z *Zpool) Destroy() error { - _, err := zpool("destroy", z.Name) + err := zpool("destroy", z.Name) return err } // ListZpools list all ZFS zpools accessible on the current system. func ListZpools() ([]*Zpool, error) { args := []string{"list", "-Ho", "name"} - out, err := zpool(args...) + out, err := zpoolOutput(args...) if err != nil { return nil, err } From 61ebb26ce8c0a81ff08f36470bf9e1a133850bc9 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 19:40:06 -0500 Subject: [PATCH 08/12] Update CONTRIBUTING.md with make based approach --- CONTRIBUTING.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ebfe905..9f625d5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,7 @@ ## How to Contribute -We always welcome contributions to help make `go-zfs` better. Please take a moment to read this document if you would like to contribute. +We always welcome contributions to help make `go-zfs` better. +Please take a moment to read this document if you would like to contribute. ### Reporting issues @@ -14,7 +15,9 @@ If you find a bug: ### Pull requests -We welcome bug fixes, improvements, and new features. Before embarking on making significant changes, please open an issue and ask first so that you do not risk duplicating efforts or spending time working on something that may be out of scope. For minor items, just open a pull request. +We welcome bug fixes, improvements, and new features. +Before embarking on making significant changes, please open an issue and ask first so that you do not risk duplicating efforts or spending time working on something that may be out of scope. +For minor items, just open a pull request. [Fork the project](https://help.github.com/articles/fork-a-repo), clone your fork, and add the upstream to your remote: @@ -28,11 +31,13 @@ If you need to pull new changes committed upstream: $ git fetch upstream $ git merge upstream/master -Don' work directly on master as this makes it harder to merge later. Create a feature branch for your fix or new feature: +Don' work directly on master as this makes it harder to merge later. +Create a feature branch for your fix or new feature: $ git checkout -b -Please try to commit your changes in logical chunks. Ideally, you should include the issue number in the commit message. +Please try to commit your changes in logical chunks. +Ideally, you should include the issue number in the commit message. $ git commit -m "Issue # - " @@ -40,21 +45,17 @@ Push your feature branch to your fork. $ git push origin -[Open a Pull Request](https://help.github.com/articles/using-pull-requests) against the upstream master branch. Please give your pull request a clear title and description and note which issue(s) your pull request fixes. +[Open a Pull Request](https://help.github.com/articles/using-pull-requests) against the upstream master branch. +Please give your pull request a clear title and description and note which issue(s) your pull request fixes. -- All Go code should be formatted using [gofmt](http://golang.org/cmd/gofmt/). +- All linters should be happy (can be run with `make verify`). - Every exported function should have [documentation](http://blog.golang.org/godoc-documenting-go-code) and corresponding [tests](http://golang.org/doc/code.html#Testing). **Important:** By submitting a patch, you agree to allow the project owners to license your work under the [Apache 2.0 License](./LICENSE). ### Go Tools -For consistency and to catch minor issues for all of go code, please run the following: - -- goimports -- go vet -- golint -- errcheck +For consistency and to catch minor issues for all of go code, please run `make verify`. Many editors can execute the above on save. From 2d9274b17855287a047f25c3c2c8de7404df524b Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 19:48:58 -0500 Subject: [PATCH 09/12] Add GitHub Actions Based off of tinkerbell/boots. --- .github/workflows/ci.yaml | 40 +++++++++++++++++++++++++++++++++ .github/workflows/formatters.sh | 25 +++++++++++++++++++++ lint.mk | 17 ++++++++++++++ shell.nix | 1 + 4 files changed, 83 insertions(+) create mode 100644 .github/workflows/ci.yaml create mode 100755 .github/workflows/formatters.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..2e9e9ef --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,40 @@ +name: For each commit and PR +on: + push: + pull_request: + +jobs: + validation: + runs-on: [ubuntu-latest] + env: + CGO_ENABLED: 0 + steps: + - name: Setup Dynamic Env + run: | + echo "MAKEFLAGS=-j$(nproc)" | tee $GITHUB_ENV + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install nix + uses: cachix/install-nix-action@018abf956a0a15673dae4932ae26f0f071ac0944 + with: + nix_path: nixpkgs=channel:nixpkgs-unstable + + - name: Fetch Nix Derivations + run: nix-shell --command true + + - name: Install Go tools + run: nix-shell --run 'make tools' + + - name: Linters and Go Formatting + run: nix-shell --run 'make verify' + + - name: Non Go Formatters + run: ./.github/workflows/formatters.sh + + - name: Install ZFS + run: sudo apt-get -y update && sudo apt-get -y install zfsutils-linux + + - name: Tests + run: nix-shell --run 'sudo make test' diff --git a/.github/workflows/formatters.sh b/.github/workflows/formatters.sh new file mode 100755 index 0000000..781b41e --- /dev/null +++ b/.github/workflows/formatters.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash ../../shell.nix +#shellcheck shell=bash + +set -eux + +failed=0 + +if ! git ls-files '*.md' '*.yaml' '*.yml' | xargs prettier --list-different --write; then + failed=1 +fi + +if ! shfmt -f . | xargs shfmt -l -d; then + failed=1 +fi + +if ! nixfmt shell.nix; then + failed=1 +fi + +if ! git diff | (! grep .); then + failed=1 +fi + +exit "$failed" diff --git a/lint.mk b/lint.mk index 138cede..a1e0a4f 100644 --- a/lint.mk +++ b/lint.mk @@ -19,6 +19,23 @@ endif LINTERS := FIXERS := +SHELLCHECK_VERSION ?= v0.8.0 +SHELLCHECK_BIN := out/linters/shellcheck-$(SHELLCHECK_VERSION)-$(LINT_ARCH) +$(SHELLCHECK_BIN): + mkdir -p out/linters + rm -rf out/linters/shellcheck-* + curl -sSfL https://github.com/koalaman/shellcheck/releases/download/$(SHELLCHECK_VERSION)/shellcheck-$(SHELLCHECK_VERSION).$(LINT_OS_LOWER).$(LINT_ARCH).tar.xz | tar -C out/linters -xJf - + mv out/linters/shellcheck-$(SHELLCHECK_VERSION)/shellcheck $@ + rm -rf out/linters/shellcheck-$(SHELLCHECK_VERSION)/shellcheck + +LINTERS += shellcheck-lint +shellcheck-lint: $(SHELLCHECK_BIN) + $(SHELLCHECK_BIN) $(shell find . -name "*.sh") + +FIXERS += shellcheck-fix +shellcheck-fix: $(SHELLCHECK_BIN) + $(SHELLCHECK_BIN) $(shell find . -name "*.sh") -f diff | { read -t 1 line || exit 0; { echo "$$line" && cat; } | git apply -p2; } + GOLANGCI_LINT_CONFIG := $(LINT_ROOT)/.golangci.yml GOLANGCI_LINT_VERSION ?= v1.43.0 GOLANGCI_LINT_BIN := out/linters/golangci-lint-$(GOLANGCI_LINT_VERSION)-$(LINT_ARCH) diff --git a/shell.nix b/shell.nix index fb1c46f..185498a 100644 --- a/shell.nix +++ b/shell.nix @@ -19,6 +19,7 @@ mkShell { nodePackages.prettier python3Packages.pip python3Packages.setuptools + shfmt vagrant ]; } From b0b30816fd15a8116a7e92206982ab061f92c8b2 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Sun, 13 Feb 2022 20:57:25 -0500 Subject: [PATCH 10/12] Drop Travis CI Its defunct? Isn't running anymore...? --- .travis.yml | 43 ------------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c7f82b4..0000000 --- a/.travis.yml +++ /dev/null @@ -1,43 +0,0 @@ -language: go -dist: trusty -sudo: required -cache: - directories: - - $HOME/.ccache - - $HOME/zfs - -branches: - only: - - master - -env: - - rel=0.6.5.11 - - rel=0.7.6 - -go: - - "1.10.x" - - master - -before_install: | - export MAKEFLAGS=-j$(($(grep -c '^processor' /proc/cpuinfo) * 2 + 1)) - export PATH=/usr/lib/ccache:$PATH - go get github.com/alecthomas/gometalinter - gometalinter --install --update - sudo apt-get update -y && sudo apt-get install -y libattr1-dev libblkid-dev linux-headers-$(uname -r) tree uuid-dev - mkdir -p $HOME/zfs - cd $HOME/zfs - [[ -d spl-$rel.tar.gz ]] || curl -L https://github.com/zfsonlinux/zfs/releases/download/zfs-$rel/spl-$rel.tar.gz | tar xz - [[ -d zfs-$rel.tar.gz ]] || curl -L https://github.com/zfsonlinux/zfs/releases/download/zfs-$rel/zfs-$rel.tar.gz | tar xz - (cd spl-$rel && ./configure --prefix=/usr && make && sudo make install) - (cd zfs-$rel && ./configure --prefix=/usr && make && sudo make install) - sudo modprobe zfs - cd $TRAVIS_BUILD_DIR - -script: | - sudo -E $(which go) test -v ./... - gometalinter --vendor --vendored-linters ./... || true - gometalinter --errors --vendor --vendored-linters ./... - -notifications: - email: false - irc: "chat.freenode.net#cerana" From 916f67b82b55f7a255360fe0a23317ef1cbb9503 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Mon, 14 Feb 2022 08:31:23 -0500 Subject: [PATCH 11/12] One sentence per line For better git diffs and treating each sentence as its own thing. --- zfs.go | 73 ++++++++++++++++++++++++-------------------------------- zpool.go | 17 ++++++------- 2 files changed, 40 insertions(+), 50 deletions(-) diff --git a/zfs.go b/zfs.go index 3cfe68f..2c77faf 100644 --- a/zfs.go +++ b/zfs.go @@ -99,8 +99,7 @@ func (*defaultLogger) Log([]string) { var logger Logger = &defaultLogger{} -// SetLogger set a log handler to log all commands including arguments before -// they are executed. +// SetLogger set a log handler to log all commands including arguments before they are executed. func SetLogger(l Logger) { if l != nil { logger = l @@ -120,35 +119,31 @@ func zfsOutput(arg ...string) ([][]string, error) { } // Datasets returns a slice of ZFS datasets, regardless of type. -// A filter argument may be passed to select a dataset with the matching name, -// or empty string ("") may be used to select all datasets. +// A filter argument may be passed to select a dataset with the matching name, or empty string ("") may be used to select all datasets. func Datasets(filter string) ([]*Dataset, error) { return listByType("all", filter) } // Snapshots returns a slice of ZFS snapshots. -// A filter argument may be passed to select a snapshot with the matching name, -// or empty string ("") may be used to select all snapshots. +// A filter argument may be passed to select a snapshot with the matching name, or empty string ("") may be used to select all snapshots. func Snapshots(filter string) ([]*Dataset, error) { return listByType(DatasetSnapshot, filter) } // Filesystems returns a slice of ZFS filesystems. -// A filter argument may be passed to select a filesystem with the matching name, -// or empty string ("") may be used to select all filesystems. +// A filter argument may be passed to select a filesystem with the matching name, or empty string ("") may be used to select all filesystems. func Filesystems(filter string) ([]*Dataset, error) { return listByType(DatasetFilesystem, filter) } // Volumes returns a slice of ZFS volumes. -// A filter argument may be passed to select a volume with the matching name, -// or empty string ("") may be used to select all volumes. +// A filter argument may be passed to select a volume with the matching name, or empty string ("") may be used to select all volumes. func Volumes(filter string) ([]*Dataset, error) { return listByType(DatasetVolume, filter) } -// GetDataset retrieves a single ZFS dataset by name. This dataset could be -// any valid ZFS dataset type, such as a clone, filesystem, snapshot, or volume. +// GetDataset retrieves a single ZFS dataset by name. +// This dataset could be any valid ZFS dataset type, such as a clone, filesystem, snapshot, or volume. func GetDataset(name string) (*Dataset, error) { out, err := zfsOutput("list", "-Hp", "-o", dsPropListOptions, name) if err != nil { @@ -222,9 +217,8 @@ func (d *Dataset) Mount(overlay bool, options []string) (*Dataset, error) { return GetDataset(d.Name) } -// ReceiveSnapshot receives a ZFS stream from the input io.Reader, creates a -// new snapshot with the specified name, and streams the input data into the -// newly-created snapshot. +// ReceiveSnapshot receives a ZFS stream from the input io.Reader. +// A new snapshot is created with the specified name, and streams the input data into the newly-created snapshot. func ReceiveSnapshot(input io.Reader, name string) (*Dataset, error) { c := command{Command: "zfs", Stdin: input} if _, err := c.Run("receive", name); err != nil { @@ -245,8 +239,7 @@ func (d *Dataset) SendSnapshot(output io.Writer) error { return err } -// IncrementalSend sends a ZFS stream of a snapshot to the input io.Writer -// using the baseSnapshot as the starting point. +// IncrementalSend sends a ZFS stream of a snapshot to the input io.Writer using the baseSnapshot as the starting point. // An error will be returned if the input dataset is not of snapshot type. func (d *Dataset) IncrementalSend(baseSnapshot *Dataset, output io.Writer) error { if d.Type != DatasetSnapshot || baseSnapshot.Type != DatasetSnapshot { @@ -257,9 +250,9 @@ func (d *Dataset) IncrementalSend(baseSnapshot *Dataset, output io.Writer) error return err } -// CreateVolume creates a new ZFS volume with the specified name, size, and -// properties. -// A full list of available ZFS properties may be found here: +// CreateVolume creates a new ZFS volume with the specified name, size, and properties. +// +// A full list of available ZFS properties may be found in the ZFS manual: // https://www.freebsd.org/cgi/man.cgi?zfs(8). func CreateVolume(name string, size uint64, properties map[string]string) (*Dataset, error) { args := make([]string, 4, 5) @@ -277,10 +270,9 @@ func CreateVolume(name string, size uint64, properties map[string]string) (*Data return GetDataset(name) } -// Destroy destroys a ZFS dataset. If the destroy bit flag is set, any -// descendents of the dataset will be recursively destroyed, including snapshots. -// If the deferred bit flag is set, the snapshot is marked for deferred -// deletion. +// Destroy destroys a ZFS dataset. +// If the destroy bit flag is set, any descendents of the dataset will be recursively destroyed, including snapshots. +// If the deferred bit flag is set, the snapshot is marked for deferred deletion. func (d *Dataset) Destroy(flags DestroyFlag) error { args := make([]string, 1, 3) args[0] = "destroy" @@ -306,7 +298,8 @@ func (d *Dataset) Destroy(flags DestroyFlag) error { } // SetProperty sets a ZFS property on the receiving dataset. -// A full list of available ZFS properties may be found here: +// +// A full list of available ZFS properties may be found in the ZFS manual: // https://www.freebsd.org/cgi/man.cgi?zfs(8). func (d *Dataset) SetProperty(key, val string) error { prop := strings.Join([]string{key, val}, "=") @@ -314,10 +307,10 @@ func (d *Dataset) SetProperty(key, val string) error { return err } -// GetProperty returns the current value of a ZFS property from the -// receiving dataset. -// A full list of available ZFS properties may be found here: -// https://www.freebsd.org/cgi/man.cgi?zfs(8). +// GetProperty returns the current value of a ZFS property from the receiving dataset. +// +// A full list of available ZFS properties may be found in the ZFS manual: +// https://openzfs.github.io/openzfs-docs/man/7/zfsprops.7.html. func (d *Dataset) GetProperty(key string) (string, error) { out, err := zfsOutput("get", "-H", key, d.Name) if err != nil { @@ -351,9 +344,9 @@ func (d *Dataset) Snapshots() ([]*Dataset, error) { return Snapshots(d.Name) } -// CreateFilesystem creates a new ZFS filesystem with the specified name and -// properties. -// A full list of available ZFS properties may be found here: +// CreateFilesystem creates a new ZFS filesystem with the specified name and properties. +// +// A full list of available ZFS properties may be found in the ZFS manual: // https://www.freebsd.org/cgi/man.cgi?zfs(8). func CreateFilesystem(name string, properties map[string]string) (*Dataset, error) { args := make([]string, 1, 4) @@ -370,9 +363,8 @@ func CreateFilesystem(name string, properties map[string]string) (*Dataset, erro return GetDataset(name) } -// Snapshot creates a new ZFS snapshot of the receiving dataset, using the -// specified name. Optionally, the snapshot can be taken recursively, creating -// snapshots of all descendent filesystems in a single, atomic operation. +// Snapshot creates a new ZFS snapshot of the receiving dataset, using the specified name. +// Optionally, the snapshot can be taken recursively, creating snapshots of all descendent filesystems in a single, atomic operation. func (d *Dataset) Snapshot(name string, recursive bool) (*Dataset, error) { args := make([]string, 1, 4) args[0] = "snapshot" @@ -388,9 +380,8 @@ func (d *Dataset) Snapshot(name string, recursive bool) (*Dataset, error) { } // Rollback rolls back the receiving ZFS dataset to a previous snapshot. -// Optionally, intermediate snapshots can be destroyed. A ZFS snapshot -// rollback cannot be completed without this option, if more recent -// snapshots exist. +// Optionally, intermediate snapshots can be destroyed. +// A ZFS snapshot rollback cannot be completed without this option, if more recent snapshots exist. // An error will be returned if the input dataset is not of snapshot type. func (d *Dataset) Rollback(destroyMoreRecent bool) error { if d.Type != DatasetSnapshot { @@ -409,8 +400,7 @@ func (d *Dataset) Rollback(destroyMoreRecent bool) error { } // Children returns a slice of children of the receiving ZFS dataset. -// A recursion depth may be specified, or a depth of 0 allows unlimited -// recursion. +// A recursion depth may be specified, or a depth of 0 allows unlimited recursion. func (d *Dataset) Children(depth uint64) ([]*Dataset, error) { args := []string{"list"} if depth > 0 { @@ -444,8 +434,7 @@ func (d *Dataset) Children(depth uint64) ([]*Dataset, error) { } // Diff returns changes between a snapshot and the given ZFS dataset. -// The snapshot name must include the filesystem part as it is possible to -// compare clones with their origin snapshots. +// The snapshot name must include the filesystem part as it is possible to compare clones with their origin snapshots. func (d *Dataset) Diff(snapshot string) ([]*InodeChange, error) { args := []string{"diff", "-FH", snapshot, d.Name} out, err := zfsOutput(args...) diff --git a/zpool.go b/zpool.go index 6de1d32..5edfa71 100644 --- a/zpool.go +++ b/zpool.go @@ -1,7 +1,8 @@ package zfs -// ZFS zpool states, which can indicate if a pool is online, offline, -// degraded, etc. More information regarding zpool states can be found here: +// ZFS zpool states, which can indicate if a pool is online, offline, degraded, etc. +// +// More information regarding zpool states can be found in the ZFS manual: // https://docs.oracle.com/cd/E19253-01/819-5461/gamno/index.html. const ( ZpoolOnline = "ONLINE" @@ -12,8 +13,8 @@ const ( ZpoolRemoved = "REMOVED" ) -// Zpool is a ZFS zpool. A pool is a top-level structure in ZFS, and can -// contain many descendent datasets. +// Zpool is a ZFS zpool. +// A pool is a top-level structure in ZFS, and can contain many descendent datasets. type Zpool struct { Name string Health string @@ -71,10 +72,10 @@ func (z *Zpool) Snapshots() ([]*Dataset, error) { return Snapshots(z.Name) } -// CreateZpool creates a new ZFS zpool with the specified name, properties, -// and optional arguments. -// A full list of available ZFS properties and command-line arguments may be -// found here: https://www.freebsd.org/cgi/man.cgi?zfs(8). +// CreateZpool creates a new ZFS zpool with the specified name, properties, and optional arguments. +// +// A full list of available ZFS properties and command-line arguments may be found in the ZFS manual: +// https://www.freebsd.org/cgi/man.cgi?zfs(8). func CreateZpool(name string, properties map[string]string, args ...string) (*Zpool, error) { cli := make([]string, 1, 4) cli[0] = "create" From 0cefd768476a68ac63bf33707aae4c0c6d9bcc08 Mon Sep 17 00:00:00 2001 From: Manuel Mendez Date: Mon, 14 Feb 2022 08:32:19 -0500 Subject: [PATCH 12/12] Update documentation links to openzfs-docs pages --- zfs.go | 8 ++++---- zpool.go | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/zfs.go b/zfs.go index 2c77faf..1166bdc 100644 --- a/zfs.go +++ b/zfs.go @@ -20,7 +20,7 @@ const ( // The Type struct member can be used to determine a dataset's type. // // The field definitions can be found in the ZFS manual: -// http://www.freebsd.org/cgi/man.cgi?zfs(8). +// https://openzfs.github.io/openzfs-docs/man/7/zfsprops.7.html. type Dataset struct { Name string Origin string @@ -253,7 +253,7 @@ func (d *Dataset) IncrementalSend(baseSnapshot *Dataset, output io.Writer) error // CreateVolume creates a new ZFS volume with the specified name, size, and properties. // // A full list of available ZFS properties may be found in the ZFS manual: -// https://www.freebsd.org/cgi/man.cgi?zfs(8). +// https://openzfs.github.io/openzfs-docs/man/7/zfsprops.7.html. func CreateVolume(name string, size uint64, properties map[string]string) (*Dataset, error) { args := make([]string, 4, 5) args[0] = "create" @@ -300,7 +300,7 @@ func (d *Dataset) Destroy(flags DestroyFlag) error { // SetProperty sets a ZFS property on the receiving dataset. // // A full list of available ZFS properties may be found in the ZFS manual: -// https://www.freebsd.org/cgi/man.cgi?zfs(8). +// https://openzfs.github.io/openzfs-docs/man/7/zfsprops.7.html. func (d *Dataset) SetProperty(key, val string) error { prop := strings.Join([]string{key, val}, "=") err := zfs("set", prop, d.Name) @@ -347,7 +347,7 @@ func (d *Dataset) Snapshots() ([]*Dataset, error) { // CreateFilesystem creates a new ZFS filesystem with the specified name and properties. // // A full list of available ZFS properties may be found in the ZFS manual: -// https://www.freebsd.org/cgi/man.cgi?zfs(8). +// https://openzfs.github.io/openzfs-docs/man/7/zfsprops.7.html. func CreateFilesystem(name string, properties map[string]string) (*Dataset, error) { args := make([]string, 1, 4) args[0] = "create" diff --git a/zpool.go b/zpool.go index 5edfa71..2f70713 100644 --- a/zpool.go +++ b/zpool.go @@ -3,7 +3,7 @@ package zfs // ZFS zpool states, which can indicate if a pool is online, offline, degraded, etc. // // More information regarding zpool states can be found in the ZFS manual: -// https://docs.oracle.com/cd/E19253-01/819-5461/gamno/index.html. +// https://openzfs.github.io/openzfs-docs/man/7/zpoolconcepts.7.html#Device_Failure_and_Recovery const ( ZpoolOnline = "ONLINE" ZpoolDegraded = "DEGRADED" @@ -75,7 +75,8 @@ func (z *Zpool) Snapshots() ([]*Dataset, error) { // CreateZpool creates a new ZFS zpool with the specified name, properties, and optional arguments. // // A full list of available ZFS properties and command-line arguments may be found in the ZFS manual: -// https://www.freebsd.org/cgi/man.cgi?zfs(8). +// https://openzfs.github.io/openzfs-docs/man/7/zfsprops.7.html. +// https://openzfs.github.io/openzfs-docs/man/8/zpool-create.8.html func CreateZpool(name string, properties map[string]string, args ...string) (*Zpool, error) { cli := make([]string, 1, 4) cli[0] = "create"