bring back aufs and zfs
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
parent
4a1a1bc626
commit
a2688b972c
@ -17,14 +17,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// TODO: Needs updated snapshotter interface
|
_ "github.com/containerd/aufs"
|
||||||
//_ "github.com/containerd/aufs"
|
|
||||||
_ "github.com/containerd/containerd/metrics/cgroups"
|
_ "github.com/containerd/containerd/metrics/cgroups"
|
||||||
_ "github.com/containerd/containerd/runtime/v1/linux"
|
_ "github.com/containerd/containerd/runtime/v1/linux"
|
||||||
_ "github.com/containerd/containerd/runtime/v2"
|
_ "github.com/containerd/containerd/runtime/v2"
|
||||||
_ "github.com/containerd/containerd/runtime/v2/runc/options"
|
_ "github.com/containerd/containerd/runtime/v2/runc/options"
|
||||||
_ "github.com/containerd/containerd/snapshots/native"
|
_ "github.com/containerd/containerd/snapshots/native"
|
||||||
_ "github.com/containerd/containerd/snapshots/overlay"
|
_ "github.com/containerd/containerd/snapshots/overlay"
|
||||||
// TODO: Needs updated snapshotter interface
|
_ "github.com/containerd/zfs"
|
||||||
//_ "github.com/containerd/zfs"
|
|
||||||
)
|
)
|
||||||
|
10
vendor.conf
10
vendor.conf
@ -83,9 +83,9 @@ k8s.io/kubernetes v1.16.0-rc.2
|
|||||||
k8s.io/utils c2654d5206da6b7b6ace12841e8f359bb89b443c
|
k8s.io/utils c2654d5206da6b7b6ace12841e8f359bb89b443c
|
||||||
sigs.k8s.io/yaml v1.1.0
|
sigs.k8s.io/yaml v1.1.0
|
||||||
|
|
||||||
# zfs dependencies (temporarily removed)
|
# zfs dependencies
|
||||||
# github.com/containerd/zfs 2ceb2dbb8154202ed1b8fd32e4ea25b491d7b251
|
github.com/containerd/zfs 9abf673ca6ff9ab8d9bd776a4ceff8f6dc699c3d
|
||||||
# github.com/mistifyio/go-zfs f784269be439d704d3dfa1906f45dd848fed2beb
|
github.com/mistifyio/go-zfs f784269be439d704d3dfa1906f45dd848fed2beb
|
||||||
|
|
||||||
# aufs dependencies (temporarily removed)
|
# aufs dependencies
|
||||||
# github.com/containerd/aufs f894a800659b6e11c1a13084abd1712f346e349c
|
github.com/containerd/aufs 371312c1e31c210a21e49bf3dfd3f31729ed9f2f
|
||||||
|
201
vendor/github.com/containerd/aufs/LICENSE
generated
vendored
Normal file
201
vendor/github.com/containerd/aufs/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
33
vendor/github.com/containerd/aufs/README.md
generated
vendored
Normal file
33
vendor/github.com/containerd/aufs/README.md
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# aufs snapshotter
|
||||||
|
|
||||||
|
[](https://travis-ci.org/containerd/aufs)
|
||||||
|
|
||||||
|
[](https://codecov.io/gh/containerd/aufs)
|
||||||
|
|
||||||
|
|
||||||
|
AUFS implementation of the snapshot interface for containerd.
|
||||||
|
|
||||||
|
## Compile
|
||||||
|
|
||||||
|
To compile containerd with aufs support add the import into the `cmd/containerd/builtins_linux.go` file.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/containerd/aufs"
|
||||||
|
_ "github.com/containerd/containerd/linux"
|
||||||
|
_ "github.com/containerd/containerd/metrics/cgroups"
|
||||||
|
_ "github.com/containerd/containerd/snapshot/overlay"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project details
|
||||||
|
|
||||||
|
aufs is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
|
||||||
|
As a containerd sub-project, you will find the:
|
||||||
|
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
|
||||||
|
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
|
||||||
|
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
information in our [`containerd/project`](https://github.com/containerd/project) repository.
|
452
vendor/github.com/containerd/aufs/aufs.go
generated
vendored
Normal file
452
vendor/github.com/containerd/aufs/aufs.go
generated
vendored
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package aufs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
"github.com/containerd/containerd/platforms"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/snapshots"
|
||||||
|
"github.com/containerd/containerd/snapshots/storage"
|
||||||
|
"github.com/containerd/continuity/fs"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.SnapshotPlugin,
|
||||||
|
ID: "aufs",
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec())
|
||||||
|
ic.Meta.Exports["root"] = ic.Root
|
||||||
|
return New(ic.Root)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
dirperm sync.Once
|
||||||
|
dirpermEnabled bool
|
||||||
|
)
|
||||||
|
|
||||||
|
type snapshotter struct {
|
||||||
|
root string
|
||||||
|
ms *storage.MetaStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new snapshotter using aufs
|
||||||
|
func New(root string) (snapshots.Snapshotter, error) {
|
||||||
|
if err := supported(); err != nil {
|
||||||
|
return nil, errors.Wrap(plugin.ErrSkipPlugin, err.Error())
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(root, 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := os.Mkdir(filepath.Join(root, "snapshots"), 0700); err != nil && !os.IsExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &snapshotter{
|
||||||
|
root: root,
|
||||||
|
ms: ms,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
|
||||||
|
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
_, info, _, err := storage.GetInfo(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
|
||||||
|
ctx, t, err := o.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
info, err = storage.UpdateInfo(ctx, info, fieldpaths...)
|
||||||
|
if err != nil {
|
||||||
|
t.Rollback()
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
if err := t.Commit(); err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage returns the resources taken by the snapshot identified by key.
|
||||||
|
//
|
||||||
|
// For active snapshots, this will scan the usage of directory and may take some time.
|
||||||
|
//
|
||||||
|
// For committed snapshots, the value is returned from the metadata database.
|
||||||
|
func (o *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
|
||||||
|
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Usage{}, err
|
||||||
|
}
|
||||||
|
id, info, usage, err := storage.GetInfo(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Usage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
upperPath := o.upperPath(id)
|
||||||
|
t.Rollback() // transaction no longer needed at this point.
|
||||||
|
|
||||||
|
if info.Kind == snapshots.KindActive {
|
||||||
|
du, err := fs.DiskUsage(ctx, upperPath)
|
||||||
|
if err != nil {
|
||||||
|
// TODO(stevvooe): Consider not reporting an error in this case.
|
||||||
|
return snapshots.Usage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
usage = snapshots.Usage(du)
|
||||||
|
}
|
||||||
|
|
||||||
|
return usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
return o.createSnapshot(ctx, snapshots.KindActive, key, parent, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
return o.createSnapshot(ctx, snapshots.KindView, key, parent, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mounts returns the mounts for the transaction identified by key. Can be
|
||||||
|
// called on an read-write or readonly transaction.
|
||||||
|
//
|
||||||
|
// This can be used to recover mounts after calling View or Prepare.
|
||||||
|
func (o *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
|
||||||
|
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s, err := storage.GetSnapshot(ctx, key)
|
||||||
|
t.Rollback()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to get active mount")
|
||||||
|
}
|
||||||
|
return o.mounts(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
|
||||||
|
ctx, t, err := o.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if rerr := t.Rollback(); rerr != nil {
|
||||||
|
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// grab the existing id
|
||||||
|
id, _, _, err := storage.GetInfo(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
usage, err := fs.DiskUsage(ctx, o.upperPath(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = storage.CommitActive(ctx, key, name, snapshots.Usage(usage), opts...); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to commit snapshot")
|
||||||
|
}
|
||||||
|
return t.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove abandons the transaction identified by key. All resources
|
||||||
|
// associated with the key will be removed.
|
||||||
|
func (o *snapshotter) Remove(ctx context.Context, key string) (err error) {
|
||||||
|
ctx, t, err := o.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && t != nil {
|
||||||
|
if rerr := t.Rollback(); rerr != nil {
|
||||||
|
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
id, _, err := storage.Remove(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to remove")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := filepath.Join(o.root, "snapshots", id)
|
||||||
|
renamed := filepath.Join(o.root, "snapshots", "rm-"+id)
|
||||||
|
if err := os.Rename(path, renamed); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to rename")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.Commit()
|
||||||
|
t = nil
|
||||||
|
if err != nil {
|
||||||
|
if err1 := os.Rename(renamed, path); err1 != nil {
|
||||||
|
// May cause inconsistent data on disk
|
||||||
|
log.G(ctx).WithError(err1).WithField("path", renamed).Errorf("Failed to rename after failed commit")
|
||||||
|
}
|
||||||
|
return errors.Wrap(err, "failed to commit")
|
||||||
|
}
|
||||||
|
if err := os.RemoveAll(renamed); err != nil {
|
||||||
|
// Must be cleaned up, any "rm-*" could be removed if no active transactions
|
||||||
|
log.G(ctx).WithError(err).WithField("path", renamed).Warnf("Failed to remove root filesystem")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk the committed snapshots.
|
||||||
|
func (o *snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, filters ...string) error {
|
||||||
|
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
return storage.WalkInfo(ctx, fn, filters...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
var (
|
||||||
|
path string
|
||||||
|
snapshotDir = filepath.Join(o.root, "snapshots")
|
||||||
|
)
|
||||||
|
|
||||||
|
td, err := ioutil.TempDir(snapshotDir, "new-")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to create temp dir")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if td != "" {
|
||||||
|
if err1 := os.RemoveAll(td); err1 != nil {
|
||||||
|
err = errors.Wrapf(err, "remove failed: %v", err1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if path != "" {
|
||||||
|
if err1 := os.RemoveAll(path); err1 != nil {
|
||||||
|
err = errors.Wrapf(err, "failed to remove path: %v", err1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fs := filepath.Join(td, "fs")
|
||||||
|
if err = os.MkdirAll(fs, 0755); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, t, err := o.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...)
|
||||||
|
if err != nil {
|
||||||
|
if rerr := t.Rollback(); rerr != nil {
|
||||||
|
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||||
|
}
|
||||||
|
return nil, errors.Wrap(err, "failed to create active")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.ParentIDs) > 0 {
|
||||||
|
st, err := os.Stat(filepath.Join(o.upperPath(s.ParentIDs[0])))
|
||||||
|
if err != nil {
|
||||||
|
if rerr := t.Rollback(); rerr != nil {
|
||||||
|
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||||
|
}
|
||||||
|
return nil, errors.Wrap(err, "failed to stat parent")
|
||||||
|
}
|
||||||
|
|
||||||
|
stat := st.Sys().(*syscall.Stat_t)
|
||||||
|
|
||||||
|
if err := os.Lchown(fs, int(stat.Uid), int(stat.Gid)); err != nil {
|
||||||
|
if rerr := t.Rollback(); rerr != nil {
|
||||||
|
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||||
|
}
|
||||||
|
return nil, errors.Wrap(err, "failed to chown")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path = filepath.Join(snapshotDir, s.ID)
|
||||||
|
if err = os.Rename(td, path); err != nil {
|
||||||
|
if rerr := t.Rollback(); rerr != nil {
|
||||||
|
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||||
|
}
|
||||||
|
return nil, errors.Wrap(err, "failed to rename")
|
||||||
|
}
|
||||||
|
td = ""
|
||||||
|
|
||||||
|
if err = t.Commit(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "commit failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.mounts(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
|
||||||
|
if len(s.ParentIDs) == 0 {
|
||||||
|
// if we only have one layer/no parents then just return a bind mount
|
||||||
|
roFlag := "rw"
|
||||||
|
if s.Kind == snapshots.KindView {
|
||||||
|
roFlag = "ro"
|
||||||
|
}
|
||||||
|
|
||||||
|
return []mount.Mount{
|
||||||
|
{
|
||||||
|
Source: o.upperPath(s.ID),
|
||||||
|
Type: "bind",
|
||||||
|
Options: []string{
|
||||||
|
roFlag,
|
||||||
|
"rbind",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aufsOptions := []string{
|
||||||
|
"br",
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Kind == snapshots.KindActive {
|
||||||
|
aufsOptions = append(aufsOptions,
|
||||||
|
fmt.Sprintf("%s=rw", o.upperPath(s.ID)),
|
||||||
|
)
|
||||||
|
} else if len(s.ParentIDs) == 1 {
|
||||||
|
return []mount.Mount{
|
||||||
|
{
|
||||||
|
Source: o.upperPath(s.ParentIDs[0]),
|
||||||
|
Type: "bind",
|
||||||
|
Options: []string{
|
||||||
|
"ro",
|
||||||
|
"rbind",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range s.ParentIDs {
|
||||||
|
aufsOptions = append(aufsOptions, fmt.Sprintf("%s=ro+wh", o.upperPath(s.ParentIDs[i])))
|
||||||
|
}
|
||||||
|
options := []string{
|
||||||
|
"dio",
|
||||||
|
"xino=/dev/shm/aufs.xino",
|
||||||
|
}
|
||||||
|
if useDirperm() {
|
||||||
|
options = append(options, "dirperm1")
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, strings.Join(aufsOptions, ":"))
|
||||||
|
return []mount.Mount{
|
||||||
|
{
|
||||||
|
Type: "aufs",
|
||||||
|
Source: "none",
|
||||||
|
Options: options,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) upperPath(id string) string {
|
||||||
|
return filepath.Join(o.root, "snapshots", id, "fs")
|
||||||
|
}
|
||||||
|
|
||||||
|
func supported() error {
|
||||||
|
// modprobe the aufs module before checking
|
||||||
|
var probeError string
|
||||||
|
cmd := exec.Command("modprobe", "aufs")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
probeError = fmt.Sprintf(" (modprobe aufs failed: %v %q)", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open("/proc/filesystems")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
s := bufio.NewScanner(f)
|
||||||
|
for s.Scan() {
|
||||||
|
if strings.Contains(s.Text(), "aufs") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.Errorf("aufs is not supported" + probeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// useDirperm checks dirperm1 mount option can be used with the current
|
||||||
|
// version of aufs.
|
||||||
|
func useDirperm() bool {
|
||||||
|
dirperm.Do(func() {
|
||||||
|
base, err := ioutil.TempDir("", "docker-aufs-base")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(base)
|
||||||
|
|
||||||
|
union, err := ioutil.TempDir("", "docker-aufs-union")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(union)
|
||||||
|
|
||||||
|
opts := fmt.Sprintf("br:%s,dirperm1,xino=/dev/shm/aufs.xino", base)
|
||||||
|
if err := unix.Mount("none", union, "aufs", 0, opts); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dirpermEnabled = true
|
||||||
|
unix.Unmount(union, 0)
|
||||||
|
})
|
||||||
|
return dirpermEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) Close() error {
|
||||||
|
return o.ms.Close()
|
||||||
|
}
|
202
vendor/github.com/containerd/zfs/LICENSE
generated
vendored
Normal file
202
vendor/github.com/containerd/zfs/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
48
vendor/github.com/containerd/zfs/README.md
generated
vendored
Normal file
48
vendor/github.com/containerd/zfs/README.md
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# [containerd](https://github.com/containerd/containerd) ZFS snapshotter plugin
|
||||||
|
|
||||||
|
[](https://travis-ci.org/containerd/zfs)
|
||||||
|
[](https://codecov.io/gh/containerd/zfs)
|
||||||
|
|
||||||
|
ZFS snapshotter plugin for containerd.
|
||||||
|
|
||||||
|
This plugin is tested on Linux with Ubuntu. It should be compatible with FreeBSD.
|
||||||
|
|
||||||
|
|
||||||
|
## Compile
|
||||||
|
|
||||||
|
To compile containerd with ZFS support, add the import into the `$GOPATH/src/github.com/containerd/containerd/cmd/containerd/builtins_zfs.go` file.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// +build linux freebsd
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/containerd/zfs"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Please refer to [`.travis.yml`](.travis.yml) for the latest containerd version known to work with.
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Set up a ZFS filesystem.
|
||||||
|
The ZFS filesystem name is arbitrary but the mount point needs to be `/var/lib/containerd/io.containerd.snapshotter.v1.zfs`, when the containerd root is set to `/var/lib/containerd`.
|
||||||
|
```console
|
||||||
|
$ zfs create -o mountpoint=/var/lib/containerd/io.containerd.snapshotter.v1.zfs your-zpool/containerd
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Start containerd.
|
||||||
|
|
||||||
|
3. e.g. `ctr pull --snapshotter=zfs ...`
|
||||||
|
|
||||||
|
## Project details
|
||||||
|
|
||||||
|
The zfs plugin is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
|
||||||
|
As a containerd sub-project, you will find the:
|
||||||
|
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
|
||||||
|
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
|
||||||
|
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
information in our [`containerd/project`](https://github.com/containerd/project) repository.
|
343
vendor/github.com/containerd/zfs/zfs.go
generated
vendored
Normal file
343
vendor/github.com/containerd/zfs/zfs.go
generated
vendored
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
// +build linux freebsd
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package zfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
"github.com/containerd/containerd/platforms"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/snapshots"
|
||||||
|
"github.com/containerd/containerd/snapshots/storage"
|
||||||
|
zfs "github.com/mistifyio/go-zfs"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// snapshotSuffix is used as follows:
|
||||||
|
// active := filepath.Join(dataset.Name, id)
|
||||||
|
// committed := active + "@" + snapshotSuffix
|
||||||
|
snapshotSuffix = "snapshot"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.SnapshotPlugin,
|
||||||
|
ID: "zfs",
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec())
|
||||||
|
ic.Meta.Exports["root"] = ic.Root
|
||||||
|
return NewSnapshotter(ic.Root)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type snapshotter struct {
|
||||||
|
dataset *zfs.Dataset
|
||||||
|
ms *storage.MetaStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSnapshotter returns a Snapshotter using zfs. Uses the provided
|
||||||
|
// root directory for snapshots and stores the metadata in
|
||||||
|
// a file in the provided root.
|
||||||
|
// root needs to be a mount point of zfs.
|
||||||
|
func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
|
||||||
|
m, err := mount.Lookup(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if m.FSType != "zfs" {
|
||||||
|
return nil, errors.Wrapf(plugin.ErrSkipPlugin, "path %s must be a zfs filesystem to be used with the zfs snapshotter", root)
|
||||||
|
}
|
||||||
|
dataset, err := zfs.GetDataset(m.Source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &snapshotter{
|
||||||
|
dataset: dataset,
|
||||||
|
ms: ms,
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
zfsCreateProperties = map[string]string{
|
||||||
|
"mountpoint": "legacy",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// createFilesystem creates but not mount.
|
||||||
|
func createFilesystem(datasetName string) (*zfs.Dataset, error) {
|
||||||
|
return zfs.CreateFilesystem(datasetName, zfsCreateProperties)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloneFilesystem clones but not mount.
|
||||||
|
func cloneFilesystem(datasetName string, snapshot *zfs.Dataset) (*zfs.Dataset, error) {
|
||||||
|
return snapshot.Clone(datasetName, zfsCreateProperties)
|
||||||
|
}
|
||||||
|
|
||||||
|
func destroy(dataset *zfs.Dataset) error {
|
||||||
|
return dataset.Destroy(zfs.DestroyDefault)
|
||||||
|
}
|
||||||
|
|
||||||
|
func destroySnapshot(dataset *zfs.Dataset) error {
|
||||||
|
return dataset.Destroy(zfs.DestroyDeferDeletion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat returns the info for an active or committed snapshot by name or
|
||||||
|
// key.
|
||||||
|
//
|
||||||
|
// Should be used for parent resolution, existence checks and to discern
|
||||||
|
// the kind of snapshot.
|
||||||
|
func (z *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
|
||||||
|
ctx, t, err := z.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
_, info, _, err := storage.GetInfo(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage retrieves the disk usage of the top-level snapshot.
|
||||||
|
func (z *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
|
||||||
|
return snapshots.Usage{}, errors.New("zfs does not implement Usage() yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk the committed snapshots.
|
||||||
|
func (z *snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, filters ...string) error {
|
||||||
|
ctx, t, err := z.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
return storage.WalkInfo(ctx, fn, filters...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
return z.createSnapshot(ctx, snapshots.KindActive, key, parent, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
return z.createSnapshot(ctx, snapshots.KindView, key, parent, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
|
||||||
|
ctx, t, err := z.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && t != nil {
|
||||||
|
if rerr := t.Rollback(); rerr != nil {
|
||||||
|
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
a, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
targetName := filepath.Join(z.dataset.Name, a.ID)
|
||||||
|
var target *zfs.Dataset
|
||||||
|
if len(a.ParentIDs) == 0 {
|
||||||
|
target, err = createFilesystem(targetName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parent0Name := filepath.Join(z.dataset.Name, a.ParentIDs[0]) + "@" + snapshotSuffix
|
||||||
|
parent0, err := zfs.GetDataset(parent0Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
target, err = cloneFilesystem(targetName, parent0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.Commit()
|
||||||
|
t = nil
|
||||||
|
if err != nil {
|
||||||
|
if derr := destroy(target); derr != nil {
|
||||||
|
log.G(ctx).WithError(derr).WithField("targetName", targetName).Error("failed to delete dataset")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
readonly := kind == snapshots.KindView
|
||||||
|
return z.mounts(target, readonly)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *snapshotter) mounts(dataset *zfs.Dataset, readonly bool) ([]mount.Mount, error) {
|
||||||
|
var options []string
|
||||||
|
if readonly {
|
||||||
|
options = append(options, "ro")
|
||||||
|
}
|
||||||
|
return []mount.Mount{
|
||||||
|
{
|
||||||
|
Type: "zfs",
|
||||||
|
Source: dataset.Name,
|
||||||
|
Options: options,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) (err error) {
|
||||||
|
ctx, t, err := z.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && t != nil {
|
||||||
|
if rerr := t.Rollback(); rerr != nil {
|
||||||
|
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
id, err := storage.CommitActive(ctx, key, name, snapshots.Usage{}, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to commit")
|
||||||
|
}
|
||||||
|
|
||||||
|
activeName := filepath.Join(z.dataset.Name, id)
|
||||||
|
active, err := zfs.GetDataset(activeName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
snapshot, err := active.Snapshot(snapshotSuffix, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = t.Commit()
|
||||||
|
t = nil
|
||||||
|
if err != nil {
|
||||||
|
snapshotName := activeName + "@" + snapshotSuffix
|
||||||
|
if derr := destroySnapshot(snapshot); derr != nil {
|
||||||
|
log.G(ctx).WithError(derr).WithField("snapshotName", snapshotName).Error("failed to delete dataset")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mounts returns the mounts for the transaction identified by key. Can be
|
||||||
|
// called on an read-write or readonly transaction.
|
||||||
|
//
|
||||||
|
// This can be used to recover mounts after calling View or Prepare.
|
||||||
|
func (z *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
|
||||||
|
ctx, t, err := z.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s, err := storage.GetSnapshot(ctx, key)
|
||||||
|
t.Rollback()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to get active snapshot")
|
||||||
|
}
|
||||||
|
sName := filepath.Join(z.dataset.Name, s.ID)
|
||||||
|
sDataset, err := zfs.GetDataset(sName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return z.mounts(sDataset, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove abandons the transaction identified by key. All resources
|
||||||
|
// associated with the key will be removed.
|
||||||
|
func (z *snapshotter) Remove(ctx context.Context, key string) (err error) {
|
||||||
|
ctx, t, err := z.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && t != nil {
|
||||||
|
if rerr := t.Rollback(); rerr != nil {
|
||||||
|
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FIXME: rolling back the removed ZFS dataset is unsupported yet
|
||||||
|
}()
|
||||||
|
|
||||||
|
id, k, err := storage.Remove(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to remove snapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
datasetName := filepath.Join(z.dataset.Name, id)
|
||||||
|
if k == snapshots.KindCommitted {
|
||||||
|
snapshotName := datasetName + "@" + snapshotSuffix
|
||||||
|
snapshot, err := zfs.GetDataset(snapshotName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = destroySnapshot(snapshot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataset, err := zfs.GetDataset(datasetName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = destroy(dataset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = t.Commit()
|
||||||
|
t = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
|
||||||
|
ctx, t, err := o.ms.TransactionContext(ctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err = storage.UpdateInfo(ctx, info, fieldpaths...)
|
||||||
|
if err != nil {
|
||||||
|
t.Rollback()
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.Commit(); err != nil {
|
||||||
|
return snapshots.Info{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *snapshotter) Close() error {
|
||||||
|
return o.ms.Close()
|
||||||
|
}
|
201
vendor/github.com/mistifyio/go-zfs/LICENSE
generated
vendored
Normal file
201
vendor/github.com/mistifyio/go-zfs/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright (c) 2014, OmniTI Computer Consulting, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
54
vendor/github.com/mistifyio/go-zfs/README.md
generated
vendored
Normal file
54
vendor/github.com/mistifyio/go-zfs/README.md
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Go Wrapper for ZFS #
|
||||||
|
|
||||||
|
Simple wrappers for ZFS command line tools.
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/mistifyio/go-zfs)
|
||||||
|
|
||||||
|
## Requirements ##
|
||||||
|
|
||||||
|
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
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install ubuntu-zfs libzfs-dev
|
||||||
|
|
||||||
|
Developed using Go 1.3, but currently there isn't anything 1.3 specific. Don't use Ubuntu packages for Go, use http://golang.org/doc/install
|
||||||
|
|
||||||
|
Generally you need root privileges to use anything zfs related.
|
||||||
|
|
||||||
|
## Status ##
|
||||||
|
|
||||||
|
This has been only been tested on Ubuntu 14.04
|
||||||
|
|
||||||
|
In the future, we hope to work directly with libzfs.
|
||||||
|
|
||||||
|
# Hacking #
|
||||||
|
|
||||||
|
The tests have decent examples for most functions.
|
||||||
|
|
||||||
|
```go
|
||||||
|
//assuming a zpool named test
|
||||||
|
//error handling omitted
|
||||||
|
|
||||||
|
|
||||||
|
f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
|
||||||
|
ok(t, err)
|
||||||
|
|
||||||
|
s, err := f.Snapshot("test", nil)
|
||||||
|
ok(t, err)
|
||||||
|
|
||||||
|
// snapshot is named "test/snapshot-test@test"
|
||||||
|
|
||||||
|
c, err := s.Clone("test/clone-test", nil)
|
||||||
|
|
||||||
|
err := c.Destroy()
|
||||||
|
err := s.Destroy()
|
||||||
|
err := f.Destroy()
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Contributing #
|
||||||
|
|
||||||
|
See the [contributing guidelines](./CONTRIBUTING.md)
|
||||||
|
|
18
vendor/github.com/mistifyio/go-zfs/error.go
generated
vendored
Normal file
18
vendor/github.com/mistifyio/go-zfs/error.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package zfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error is an error which is returned when the `zfs` or `zpool` shell
|
||||||
|
// commands return with a non-zero exit code.
|
||||||
|
type Error struct {
|
||||||
|
Err error
|
||||||
|
Debug string
|
||||||
|
Stderr string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the string representation of an Error.
|
||||||
|
func (e Error) Error() string {
|
||||||
|
return fmt.Sprintf("%s: %q => %s", e.Err, e.Debug, e.Stderr)
|
||||||
|
}
|
360
vendor/github.com/mistifyio/go-zfs/utils.go
generated
vendored
Normal file
360
vendor/github.com/mistifyio/go-zfs/utils.go
generated
vendored
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
package zfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type command struct {
|
||||||
|
Command string
|
||||||
|
Stdin io.Reader
|
||||||
|
Stdout io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *command) Run(arg ...string) ([][]string, error) {
|
||||||
|
|
||||||
|
cmd := exec.Command(c.Command, arg...)
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
|
||||||
|
if c.Stdout == nil {
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
} else {
|
||||||
|
cmd.Stdout = c.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Stdin != nil {
|
||||||
|
cmd.Stdin = c.Stdin
|
||||||
|
|
||||||
|
}
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
id := uuid.New().String()
|
||||||
|
joinedArgs := strings.Join(cmd.Args, " ")
|
||||||
|
|
||||||
|
logger.Log([]string{"ID:" + id, "START", joinedArgs})
|
||||||
|
err := cmd.Run()
|
||||||
|
logger.Log([]string{"ID:" + id, "FINISH"})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, &Error{
|
||||||
|
Err: err,
|
||||||
|
Debug: strings.Join([]string{cmd.Path, joinedArgs[1:]}, " "),
|
||||||
|
Stderr: stderr.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assume if you passed in something for stdout, that you know what to do with it
|
||||||
|
if c.Stdout != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(stdout.String(), "\n")
|
||||||
|
|
||||||
|
//last line is always blank
|
||||||
|
lines = lines[0 : len(lines)-1]
|
||||||
|
output := make([][]string, len(lines))
|
||||||
|
|
||||||
|
for i, l := range lines {
|
||||||
|
output[i] = strings.Fields(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setString(field *string, value string) {
|
||||||
|
v := ""
|
||||||
|
if value != "-" {
|
||||||
|
v = value
|
||||||
|
}
|
||||||
|
*field = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUint(field *uint64, value string) error {
|
||||||
|
var v uint64
|
||||||
|
if value != "-" {
|
||||||
|
var err error
|
||||||
|
v, err = strconv.ParseUint(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*field = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds *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")
|
||||||
|
}
|
||||||
|
setString(&ds.Name, line[0])
|
||||||
|
setString(&ds.Origin, line[1])
|
||||||
|
|
||||||
|
if err = setUint(&ds.Used, line[2]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = setUint(&ds.Avail, line[3]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
setString(&ds.Mountpoint, line[4])
|
||||||
|
setString(&ds.Compression, line[5])
|
||||||
|
setString(&ds.Type, line[6])
|
||||||
|
|
||||||
|
if err = setUint(&ds.Volsize, line[7]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = setUint(&ds.Quota, line[8]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = setUint(&ds.Referenced, line[9]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS == "solaris" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = setUint(&ds.Written, line[10]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = setUint(&ds.Logicalused, line[11]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = setUint(&ds.Usedbydataset, line[12]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* from zfs diff`s escape function:
|
||||||
|
*
|
||||||
|
* Prints a file name out a character at a time. If the character is
|
||||||
|
* not in the range of what we consider "printable" ASCII, display it
|
||||||
|
* as an escaped 3-digit octal value. ASCII values less than a space
|
||||||
|
* are all control characters and we declare the upper end as the
|
||||||
|
* DELete character. This also is the last 7-bit ASCII character.
|
||||||
|
* We choose to treat all 8-bit ASCII as not printable for this
|
||||||
|
* application.
|
||||||
|
*/
|
||||||
|
func unescapeFilepath(path string) (string, error) {
|
||||||
|
buf := make([]byte, 0, len(path))
|
||||||
|
llen := len(path)
|
||||||
|
for i := 0; i < llen; {
|
||||||
|
if path[i] == '\\' {
|
||||||
|
if llen < i+4 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
buf = append(buf, byte(val))
|
||||||
|
i += 4
|
||||||
|
} else {
|
||||||
|
buf = append(buf, path[i])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var changeTypeMap = map[string]ChangeType{
|
||||||
|
"-": Removed,
|
||||||
|
"+": Created,
|
||||||
|
"M": Modified,
|
||||||
|
"R": Renamed,
|
||||||
|
}
|
||||||
|
var inodeTypeMap = map[string]InodeType{
|
||||||
|
"B": BlockDevice,
|
||||||
|
"C": CharacterDevice,
|
||||||
|
"/": Directory,
|
||||||
|
">": Door,
|
||||||
|
"|": NamedPipe,
|
||||||
|
"@": SymbolicLink,
|
||||||
|
"P": EventPort,
|
||||||
|
"=": Socket,
|
||||||
|
"F": File,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 strconv.Atoi(matches[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInodeChange(line []string) (*InodeChange, error) {
|
||||||
|
llen := len(line)
|
||||||
|
if llen < 1 {
|
||||||
|
return nil, fmt.Errorf("Empty line passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
changeType := changeTypeMap[line[0]]
|
||||||
|
if changeType == 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)
|
||||||
|
}
|
||||||
|
case Modified:
|
||||||
|
if llen != 4 && llen != 3 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inodeType := inodeTypeMap[line[1]]
|
||||||
|
if inodeType == 0 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPath string
|
||||||
|
var referenceCount int
|
||||||
|
switch changeType {
|
||||||
|
case Renamed:
|
||||||
|
newPath, err = unescapeFilepath(line[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to parse filename: %v", 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
newPath = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return &InodeChange{
|
||||||
|
Change: changeType,
|
||||||
|
Type: inodeType,
|
||||||
|
Path: path,
|
||||||
|
NewPath: newPath,
|
||||||
|
ReferenceCountChange: referenceCount,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// example input
|
||||||
|
//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)
|
||||||
|
}
|
||||||
|
changes[i] = c
|
||||||
|
}
|
||||||
|
return changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listByType(t, filter string) ([]*Dataset, error) {
|
||||||
|
args := []string{"list", "-rHp", "-t", t, "-o", dsPropListOptions}
|
||||||
|
|
||||||
|
if filter != "" {
|
||||||
|
args = append(args, filter)
|
||||||
|
}
|
||||||
|
out, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var datasets []*Dataset
|
||||||
|
|
||||||
|
name := ""
|
||||||
|
var ds *Dataset
|
||||||
|
for _, line := range out {
|
||||||
|
if name != line[0] {
|
||||||
|
name = line[0]
|
||||||
|
ds = &Dataset{Name: name}
|
||||||
|
datasets = append(datasets, ds)
|
||||||
|
}
|
||||||
|
if err := ds.parseLine(line); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return datasets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func propsSlice(properties map[string]string) []string {
|
||||||
|
args := make([]string, 0, len(properties)*3)
|
||||||
|
for k, v := range properties {
|
||||||
|
args = append(args, "-o")
|
||||||
|
args = append(args, fmt.Sprintf("%s=%s", k, v))
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Zpool) parseLine(line []string) error {
|
||||||
|
prop := line[1]
|
||||||
|
val := line[2]
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch prop {
|
||||||
|
case "name":
|
||||||
|
setString(&z.Name, val)
|
||||||
|
case "health":
|
||||||
|
setString(&z.Health, val)
|
||||||
|
case "allocated":
|
||||||
|
err = setUint(&z.Allocated, val)
|
||||||
|
case "size":
|
||||||
|
err = setUint(&z.Size, val)
|
||||||
|
case "free":
|
||||||
|
err = setUint(&z.Free, val)
|
||||||
|
case "fragmentation":
|
||||||
|
// Trim trailing "%" before parsing uint
|
||||||
|
i := strings.Index(val, "%")
|
||||||
|
if i < 0 {
|
||||||
|
i = len(val)
|
||||||
|
}
|
||||||
|
err = setUint(&z.Fragmentation, val[:i])
|
||||||
|
case "readonly":
|
||||||
|
z.ReadOnly = val == "on"
|
||||||
|
case "freeing":
|
||||||
|
err = setUint(&z.Freeing, val)
|
||||||
|
case "leaked":
|
||||||
|
err = setUint(&z.Leaked, val)
|
||||||
|
case "dedupratio":
|
||||||
|
// Trim trailing "x" before parsing float64
|
||||||
|
z.DedupRatio, err = strconv.ParseFloat(val[:len(val)-1], 64)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
17
vendor/github.com/mistifyio/go-zfs/utils_notsolaris.go
generated
vendored
Normal file
17
vendor/github.com/mistifyio/go-zfs/utils_notsolaris.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// +build !solaris
|
||||||
|
|
||||||
|
package zfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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"}
|
||||||
|
|
||||||
|
var dsPropListOptions = strings.Join(dsPropList, ",")
|
||||||
|
|
||||||
|
// 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}
|
17
vendor/github.com/mistifyio/go-zfs/utils_solaris.go
generated
vendored
Normal file
17
vendor/github.com/mistifyio/go-zfs/utils_solaris.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// +build solaris
|
||||||
|
|
||||||
|
package zfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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"}
|
||||||
|
|
||||||
|
var dsPropListOptions = strings.Join(dsPropList, ",")
|
||||||
|
|
||||||
|
// 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}
|
452
vendor/github.com/mistifyio/go-zfs/zfs.go
generated
vendored
Normal file
452
vendor/github.com/mistifyio/go-zfs/zfs.go
generated
vendored
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
// Package zfs provides wrappers around the ZFS command line tools.
|
||||||
|
package zfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// The field definitions can be found in the ZFS manual:
|
||||||
|
// http://www.freebsd.org/cgi/man.cgi?zfs(8).
|
||||||
|
type Dataset struct {
|
||||||
|
Name string
|
||||||
|
Origin string
|
||||||
|
Used uint64
|
||||||
|
Avail uint64
|
||||||
|
Mountpoint string
|
||||||
|
Compression string
|
||||||
|
Type string
|
||||||
|
Written uint64
|
||||||
|
Volsize uint64
|
||||||
|
Logicalused uint64
|
||||||
|
Usedbydataset uint64
|
||||||
|
Quota uint64
|
||||||
|
Referenced uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// InodeType is the type of inode as reported by Diff
|
||||||
|
type InodeType int
|
||||||
|
|
||||||
|
// Types of Inodes
|
||||||
|
const (
|
||||||
|
_ = iota // 0 == unknown type
|
||||||
|
BlockDevice InodeType = iota
|
||||||
|
CharacterDevice
|
||||||
|
Directory
|
||||||
|
Door
|
||||||
|
NamedPipe
|
||||||
|
SymbolicLink
|
||||||
|
EventPort
|
||||||
|
Socket
|
||||||
|
File
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChangeType is the type of inode change as reported by Diff
|
||||||
|
type ChangeType int
|
||||||
|
|
||||||
|
// Types of Changes
|
||||||
|
const (
|
||||||
|
_ = iota // 0 == unknown type
|
||||||
|
Removed ChangeType = iota
|
||||||
|
Created
|
||||||
|
Modified
|
||||||
|
Renamed
|
||||||
|
)
|
||||||
|
|
||||||
|
// DestroyFlag is the options flag passed to Destroy
|
||||||
|
type DestroyFlag int
|
||||||
|
|
||||||
|
// Valid destroy options
|
||||||
|
const (
|
||||||
|
DestroyDefault DestroyFlag = 1 << iota
|
||||||
|
DestroyRecursive = 1 << iota
|
||||||
|
DestroyRecursiveClones = 1 << iota
|
||||||
|
DestroyDeferDeletion = 1 << iota
|
||||||
|
DestroyForceUmount = 1 << iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// InodeChange represents a change as reported by Diff
|
||||||
|
type InodeChange struct {
|
||||||
|
Change ChangeType
|
||||||
|
Type InodeType
|
||||||
|
Path string
|
||||||
|
NewPath string
|
||||||
|
ReferenceCountChange int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger can be used to log commands/actions
|
||||||
|
type Logger interface {
|
||||||
|
Log(cmd []string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultLogger struct{}
|
||||||
|
|
||||||
|
func (*defaultLogger) Log(cmd []string) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var logger Logger = &defaultLogger{}
|
||||||
|
|
||||||
|
// SetLogger set a log handler to log all commands including arguments before
|
||||||
|
// they are executed
|
||||||
|
func SetLogger(l Logger) {
|
||||||
|
if l != nil {
|
||||||
|
logger = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// zfs is a helper function to wrap typical calls to zfs.
|
||||||
|
func zfs(arg ...string) ([][]string, error) {
|
||||||
|
c := command{Command: "zfs"}
|
||||||
|
return c.Run(arg...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
func GetDataset(name string) (*Dataset, error) {
|
||||||
|
out, err := zfs("list", "-Hp", "-o", dsPropListOptions, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ds := &Dataset{Name: name}
|
||||||
|
for _, line := range out {
|
||||||
|
if err := ds.parseLine(line); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone clones a ZFS snapshot and returns a clone dataset.
|
||||||
|
// An error will be returned if the input dataset is not of snapshot type.
|
||||||
|
func (d *Dataset) Clone(dest string, properties map[string]string) (*Dataset, error) {
|
||||||
|
if d.Type != DatasetSnapshot {
|
||||||
|
return nil, errors.New("can only clone snapshots")
|
||||||
|
}
|
||||||
|
args := make([]string, 2, 4)
|
||||||
|
args[0] = "clone"
|
||||||
|
args[1] = "-p"
|
||||||
|
if properties != nil {
|
||||||
|
args = append(args, propsSlice(properties)...)
|
||||||
|
}
|
||||||
|
args = append(args, []string{d.Name, dest}...)
|
||||||
|
_, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return GetDataset(dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount unmounts currently mounted ZFS file systems.
|
||||||
|
func (d *Dataset) Unmount(force bool) (*Dataset, error) {
|
||||||
|
if d.Type == DatasetSnapshot {
|
||||||
|
return nil, errors.New("cannot unmount snapshots")
|
||||||
|
}
|
||||||
|
args := make([]string, 1, 3)
|
||||||
|
args[0] = "umount"
|
||||||
|
if force {
|
||||||
|
args = append(args, "-f")
|
||||||
|
}
|
||||||
|
args = append(args, d.Name)
|
||||||
|
_, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return GetDataset(d.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount mounts ZFS file systems.
|
||||||
|
func (d *Dataset) Mount(overlay bool, options []string) (*Dataset, error) {
|
||||||
|
if d.Type == DatasetSnapshot {
|
||||||
|
return nil, errors.New("cannot mount snapshots")
|
||||||
|
}
|
||||||
|
args := make([]string, 1, 5)
|
||||||
|
args[0] = "mount"
|
||||||
|
if overlay {
|
||||||
|
args = append(args, "-O")
|
||||||
|
}
|
||||||
|
if options != nil {
|
||||||
|
args = append(args, "-o")
|
||||||
|
args = append(args, strings.Join(options, ","))
|
||||||
|
}
|
||||||
|
args = append(args, d.Name)
|
||||||
|
_, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
func ReceiveSnapshot(input io.Reader, name string) (*Dataset, error) {
|
||||||
|
c := command{Command: "zfs", Stdin: input}
|
||||||
|
_, err := c.Run("receive", name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return GetDataset(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendSnapshot sends a ZFS stream of a snapshot to the input io.Writer.
|
||||||
|
// An error will be returned if the input dataset is not of snapshot type.
|
||||||
|
func (d *Dataset) SendSnapshot(output io.Writer) error {
|
||||||
|
if d.Type != DatasetSnapshot {
|
||||||
|
return errors.New("can only send snapshots")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := command{Command: "zfs", Stdout: output}
|
||||||
|
_, err := c.Run("send", d.Name)
|
||||||
|
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:
|
||||||
|
// 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)
|
||||||
|
args[0] = "create"
|
||||||
|
args[1] = "-p"
|
||||||
|
args[2] = "-V"
|
||||||
|
args[3] = strconv.FormatUint(size, 10)
|
||||||
|
if properties != nil {
|
||||||
|
args = append(args, propsSlice(properties)...)
|
||||||
|
}
|
||||||
|
args = append(args, name)
|
||||||
|
_, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
func (d *Dataset) Destroy(flags DestroyFlag) error {
|
||||||
|
args := make([]string, 1, 3)
|
||||||
|
args[0] = "destroy"
|
||||||
|
if flags&DestroyRecursive != 0 {
|
||||||
|
args = append(args, "-r")
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags&DestroyRecursiveClones != 0 {
|
||||||
|
args = append(args, "-R")
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags&DestroyDeferDeletion != 0 {
|
||||||
|
args = append(args, "-d")
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags&DestroyForceUmount != 0 {
|
||||||
|
args = append(args, "-f")
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, d.Name)
|
||||||
|
_, err := zfs(args...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetProperty sets a ZFS property on the receiving dataset.
|
||||||
|
// A full list of available ZFS properties may be found here:
|
||||||
|
// 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)
|
||||||
|
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).
|
||||||
|
func (d *Dataset) GetProperty(key string) (string, error) {
|
||||||
|
out, err := zfs("get", "-H", key, d.Name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out[0][2], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename renames a dataset.
|
||||||
|
func (d *Dataset) Rename(name string, createParent bool, recursiveRenameSnapshots bool) (*Dataset, error) {
|
||||||
|
args := make([]string, 3, 5)
|
||||||
|
args[0] = "rename"
|
||||||
|
args[1] = d.Name
|
||||||
|
args[2] = name
|
||||||
|
if createParent {
|
||||||
|
args = append(args, "-p")
|
||||||
|
}
|
||||||
|
if recursiveRenameSnapshots {
|
||||||
|
args = append(args, "-r")
|
||||||
|
}
|
||||||
|
_, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return d, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetDataset(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshots returns a slice of all ZFS snapshots of a given dataset.
|
||||||
|
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:
|
||||||
|
// https://www.freebsd.org/cgi/man.cgi?zfs(8).
|
||||||
|
func CreateFilesystem(name string, properties map[string]string) (*Dataset, error) {
|
||||||
|
args := make([]string, 1, 4)
|
||||||
|
args[0] = "create"
|
||||||
|
|
||||||
|
if properties != nil {
|
||||||
|
args = append(args, propsSlice(properties)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, name)
|
||||||
|
_, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
func (d *Dataset) Snapshot(name string, recursive bool) (*Dataset, error) {
|
||||||
|
args := make([]string, 1, 4)
|
||||||
|
args[0] = "snapshot"
|
||||||
|
if recursive {
|
||||||
|
args = append(args, "-r")
|
||||||
|
}
|
||||||
|
snapName := fmt.Sprintf("%s@%s", d.Name, name)
|
||||||
|
args = append(args, snapName)
|
||||||
|
_, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return GetDataset(snapName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// 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 {
|
||||||
|
return errors.New("can only rollback snapshots")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]string, 1, 3)
|
||||||
|
args[0] = "rollback"
|
||||||
|
if destroyMoreRecent {
|
||||||
|
args = append(args, "-r")
|
||||||
|
}
|
||||||
|
args = append(args, d.Name)
|
||||||
|
|
||||||
|
_, err := zfs(args...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (d *Dataset) Children(depth uint64) ([]*Dataset, error) {
|
||||||
|
args := []string{"list"}
|
||||||
|
if depth > 0 {
|
||||||
|
args = append(args, "-d")
|
||||||
|
args = append(args, strconv.FormatUint(depth, 10))
|
||||||
|
} else {
|
||||||
|
args = append(args, "-r")
|
||||||
|
}
|
||||||
|
args = append(args, "-t", "all", "-Hp", "-o", dsPropListOptions)
|
||||||
|
args = append(args, d.Name)
|
||||||
|
|
||||||
|
out, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var datasets []*Dataset
|
||||||
|
name := ""
|
||||||
|
var ds *Dataset
|
||||||
|
for _, line := range out {
|
||||||
|
if name != line[0] {
|
||||||
|
name = line[0]
|
||||||
|
ds = &Dataset{Name: name}
|
||||||
|
datasets = append(datasets, ds)
|
||||||
|
}
|
||||||
|
if err := ds.parseLine(line); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return datasets[1:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (d *Dataset) Diff(snapshot string) ([]*InodeChange, error) {
|
||||||
|
args := []string{"diff", "-FH", snapshot, d.Name}[:]
|
||||||
|
out, err := zfs(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inodeChanges, err := parseInodeChanges(out)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return inodeChanges, nil
|
||||||
|
}
|
112
vendor/github.com/mistifyio/go-zfs/zpool.go
generated
vendored
Normal file
112
vendor/github.com/mistifyio/go-zfs/zpool.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
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:
|
||||||
|
// https://docs.oracle.com/cd/E19253-01/819-5461/gamno/index.html.
|
||||||
|
const (
|
||||||
|
ZpoolOnline = "ONLINE"
|
||||||
|
ZpoolDegraded = "DEGRADED"
|
||||||
|
ZpoolFaulted = "FAULTED"
|
||||||
|
ZpoolOffline = "OFFLINE"
|
||||||
|
ZpoolUnavail = "UNAVAIL"
|
||||||
|
ZpoolRemoved = "REMOVED"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
Allocated uint64
|
||||||
|
Size uint64
|
||||||
|
Free uint64
|
||||||
|
Fragmentation uint64
|
||||||
|
ReadOnly bool
|
||||||
|
Freeing uint64
|
||||||
|
Leaked uint64
|
||||||
|
DedupRatio float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// zpool is a helper function to wrap typical calls to zpool.
|
||||||
|
func zpool(arg ...string) ([][]string, error) {
|
||||||
|
c := command{Command: "zpool"}
|
||||||
|
return c.Run(arg...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetZpool retrieves a single ZFS zpool by name.
|
||||||
|
func GetZpool(name string) (*Zpool, error) {
|
||||||
|
args := zpoolArgs
|
||||||
|
args = append(args, name)
|
||||||
|
out, err := zpool(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// there is no -H
|
||||||
|
out = out[1:]
|
||||||
|
|
||||||
|
z := &Zpool{Name: name}
|
||||||
|
for _, line := range out {
|
||||||
|
if err := z.parseLine(line); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return z, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datasets returns a slice of all ZFS datasets in a zpool.
|
||||||
|
func (z *Zpool) Datasets() ([]*Dataset, error) {
|
||||||
|
return Datasets(z.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshots returns a slice of all ZFS snapshots in a zpool.
|
||||||
|
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).
|
||||||
|
func CreateZpool(name string, properties map[string]string, args ...string) (*Zpool, error) {
|
||||||
|
cli := make([]string, 1, 4)
|
||||||
|
cli[0] = "create"
|
||||||
|
if properties != nil {
|
||||||
|
cli = append(cli, propsSlice(properties)...)
|
||||||
|
}
|
||||||
|
cli = append(cli, name)
|
||||||
|
cli = append(cli, args...)
|
||||||
|
_, err := zpool(cli...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Zpool{Name: name}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy destroys a ZFS zpool by name.
|
||||||
|
func (z *Zpool) Destroy() error {
|
||||||
|
_, 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...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pools []*Zpool
|
||||||
|
|
||||||
|
for _, line := range out {
|
||||||
|
z, err := GetZpool(line[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pools = append(pools, z)
|
||||||
|
}
|
||||||
|
return pools, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user