Merge pull request #3789 from AkihiroSuda/vendor-20191030
bring back aufs and zfs
This commit is contained in:
commit
342ce3e602
@ -17,14 +17,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
// TODO: Needs updated snapshotter interface
|
||||
//_ "github.com/containerd/aufs"
|
||||
_ "github.com/containerd/aufs"
|
||||
_ "github.com/containerd/containerd/metrics/cgroups"
|
||||
_ "github.com/containerd/containerd/runtime/v1/linux"
|
||||
_ "github.com/containerd/containerd/runtime/v2"
|
||||
_ "github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
_ "github.com/containerd/containerd/snapshots/native"
|
||||
_ "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
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
|
||||
# zfs dependencies (temporarily removed)
|
||||
# github.com/containerd/zfs 2ceb2dbb8154202ed1b8fd32e4ea25b491d7b251
|
||||
# github.com/mistifyio/go-zfs f784269be439d704d3dfa1906f45dd848fed2beb
|
||||
# zfs dependencies
|
||||
github.com/containerd/zfs 9abf673ca6ff9ab8d9bd776a4ceff8f6dc699c3d
|
||||
github.com/mistifyio/go-zfs f784269be439d704d3dfa1906f45dd848fed2beb
|
||||
|
||||
# aufs dependencies (temporarily removed)
|
||||
# github.com/containerd/aufs f894a800659b6e11c1a13084abd1712f346e349c
|
||||
# aufs dependencies
|
||||
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