containerd: include zfs plugin by default
Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
parent
b307df2723
commit
f4113a903a
@ -20,4 +20,5 @@ import (
|
|||||||
_ "github.com/containerd/containerd/linux"
|
_ "github.com/containerd/containerd/linux"
|
||||||
_ "github.com/containerd/containerd/metrics/cgroups"
|
_ "github.com/containerd/containerd/metrics/cgroups"
|
||||||
_ "github.com/containerd/containerd/snapshots/overlay"
|
_ "github.com/containerd/containerd/snapshots/overlay"
|
||||||
|
_ "github.com/containerd/zfs"
|
||||||
)
|
)
|
||||||
|
@ -41,6 +41,7 @@ github.com/stevvooe/ttrpc d4528379866b0ce7e9d71f3eb96f0582fc374577
|
|||||||
github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
|
github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
|
||||||
github.com/gotestyourself/gotestyourself 44dbf532bbf5767611f6f2a61bded572e337010a
|
github.com/gotestyourself/gotestyourself 44dbf532bbf5767611f6f2a61bded572e337010a
|
||||||
github.com/google/go-cmp v0.1.0
|
github.com/google/go-cmp v0.1.0
|
||||||
|
|
||||||
# cri dependencies
|
# cri dependencies
|
||||||
github.com/containerd/cri 0c876040681ebe8a291fa2cebefdcc2796fa3fc8
|
github.com/containerd/cri 0c876040681ebe8a291fa2cebefdcc2796fa3fc8
|
||||||
github.com/blang/semver v3.1.0
|
github.com/blang/semver v3.1.0
|
||||||
@ -73,3 +74,8 @@ k8s.io/apiserver 8e45eac9dff86447a5c2effe6a3d2cba70121ebf
|
|||||||
k8s.io/client-go 33bd23f75b6de861994706a322b0afab824b2171
|
k8s.io/client-go 33bd23f75b6de861994706a322b0afab824b2171
|
||||||
k8s.io/kubernetes 05944b1d2ca7f60b09762a330425108f48f6b603
|
k8s.io/kubernetes 05944b1d2ca7f60b09762a330425108f48f6b603
|
||||||
k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e
|
k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e
|
||||||
|
|
||||||
|
# zfs dependencies
|
||||||
|
github.com/containerd/zfs 2e6f60521b5690bf2f265c416a42b251c2a3ec8e
|
||||||
|
github.com/mistifyio/go-zfs 166add352731e515512690329794ee593f1aaff2
|
||||||
|
github.com/pborman/uuid c65b2f87fee37d1c7854c9164a450713c28d50cd
|
||||||
|
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.
|
38
vendor/github.com/containerd/zfs/README.md
generated
vendored
Normal file
38
vendor/github.com/containerd/zfs/README.md
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# [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 ...`
|
322
vendor/github.com/containerd/zfs/zfs.go
generated
vendored
Normal file
322
vendor/github.com/containerd/zfs/zfs.go
generated
vendored
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
// +build linux freebsd
|
||||||
|
|
||||||
|
package zfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
"github.com/containerd/containerd/mount"
|
||||||
|
"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) {
|
||||||
|
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.Errorf("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 func(context.Context, snapshots.Info) error) error {
|
||||||
|
ctx, t, err := z.ms.TransactionContext(ctx, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer t.Rollback()
|
||||||
|
return storage.WalkInfo(ctx, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
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{})
|
||||||
|
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 {
|
||||||
|
datasetName += "@" + snapshotSuffix
|
||||||
|
}
|
||||||
|
dataset, err := zfs.GetDataset(datasetName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if k == snapshots.KindCommitted {
|
||||||
|
err = destroySnapshot(dataset)
|
||||||
|
} else {
|
||||||
|
err = destroy(dataset)
|
||||||
|
}
|
||||||
|
if 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/pborman/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()
|
||||||
|
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}, " "),
|
||||||
|
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
|
||||||
|
}
|
27
vendor/github.com/pborman/uuid/LICENSE
generated
vendored
Normal file
27
vendor/github.com/pborman/uuid/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009,2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
13
vendor/github.com/pborman/uuid/README.md
generated
vendored
Normal file
13
vendor/github.com/pborman/uuid/README.md
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
This project was automatically exported from code.google.com/p/go-uuid
|
||||||
|
|
||||||
|
# uuid 
|
||||||
|
The uuid package generates and inspects UUIDs based on [RFC 4122](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services.
|
||||||
|
|
||||||
|
###### Install
|
||||||
|
`go get github.com/pborman/uuid`
|
||||||
|
|
||||||
|
###### Documentation
|
||||||
|
[](http://godoc.org/github.com/pborman/uuid)
|
||||||
|
|
||||||
|
Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here:
|
||||||
|
http://godoc.org/github.com/pborman/uuid
|
84
vendor/github.com/pborman/uuid/dce.go
generated
vendored
Normal file
84
vendor/github.com/pborman/uuid/dce.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Domain represents a Version 2 domain
|
||||||
|
type Domain byte
|
||||||
|
|
||||||
|
// Domain constants for DCE Security (Version 2) UUIDs.
|
||||||
|
const (
|
||||||
|
Person = Domain(0)
|
||||||
|
Group = Domain(1)
|
||||||
|
Org = Domain(2)
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
||||||
|
//
|
||||||
|
// The domain should be one of Person, Group or Org.
|
||||||
|
// On a POSIX system the id should be the users UID for the Person
|
||||||
|
// domain and the users GID for the Group. The meaning of id for
|
||||||
|
// the domain Org or on non-POSIX systems is site defined.
|
||||||
|
//
|
||||||
|
// For a given domain/id pair the same token may be returned for up to
|
||||||
|
// 7 minutes and 10 seconds.
|
||||||
|
func NewDCESecurity(domain Domain, id uint32) UUID {
|
||||||
|
uuid := NewUUID()
|
||||||
|
if uuid != nil {
|
||||||
|
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
||||||
|
uuid[9] = byte(domain)
|
||||||
|
binary.BigEndian.PutUint32(uuid[0:], id)
|
||||||
|
}
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
||||||
|
// domain with the id returned by os.Getuid.
|
||||||
|
//
|
||||||
|
// NewDCEPerson(Person, uint32(os.Getuid()))
|
||||||
|
func NewDCEPerson() UUID {
|
||||||
|
return NewDCESecurity(Person, uint32(os.Getuid()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
||||||
|
// domain with the id returned by os.Getgid.
|
||||||
|
//
|
||||||
|
// NewDCEGroup(Group, uint32(os.Getgid()))
|
||||||
|
func NewDCEGroup() UUID {
|
||||||
|
return NewDCESecurity(Group, uint32(os.Getgid()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain returns the domain for a Version 2 UUID or false.
|
||||||
|
func (uuid UUID) Domain() (Domain, bool) {
|
||||||
|
if v, _ := uuid.Version(); v != 2 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return Domain(uuid[9]), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Id returns the id for a Version 2 UUID or false.
|
||||||
|
func (uuid UUID) Id() (uint32, bool) {
|
||||||
|
if v, _ := uuid.Version(); v != 2 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return binary.BigEndian.Uint32(uuid[0:4]), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Domain) String() string {
|
||||||
|
switch d {
|
||||||
|
case Person:
|
||||||
|
return "Person"
|
||||||
|
case Group:
|
||||||
|
return "Group"
|
||||||
|
case Org:
|
||||||
|
return "Org"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Domain%d", int(d))
|
||||||
|
}
|
8
vendor/github.com/pborman/uuid/doc.go
generated
vendored
Normal file
8
vendor/github.com/pborman/uuid/doc.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// The uuid package generates and inspects UUIDs.
|
||||||
|
//
|
||||||
|
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services.
|
||||||
|
package uuid
|
53
vendor/github.com/pborman/uuid/hash.go
generated
vendored
Normal file
53
vendor/github.com/pborman/uuid/hash.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Well known Name Space IDs and UUIDs
|
||||||
|
var (
|
||||||
|
NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NIL = Parse("00000000-0000-0000-0000-000000000000")
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewHash returns a new UUID derived from the hash of space concatenated with
|
||||||
|
// data generated by h. The hash should be at least 16 byte in length. The
|
||||||
|
// first 16 bytes of the hash are used to form the UUID. The version of the
|
||||||
|
// UUID will be the lower 4 bits of version. NewHash is used to implement
|
||||||
|
// NewMD5 and NewSHA1.
|
||||||
|
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
||||||
|
h.Reset()
|
||||||
|
h.Write(space)
|
||||||
|
h.Write([]byte(data))
|
||||||
|
s := h.Sum(nil)
|
||||||
|
uuid := make([]byte, 16)
|
||||||
|
copy(uuid, s)
|
||||||
|
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
|
||||||
|
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMD5 returns a new MD5 (Version 3) UUID based on the
|
||||||
|
// supplied name space and data.
|
||||||
|
//
|
||||||
|
// NewHash(md5.New(), space, data, 3)
|
||||||
|
func NewMD5(space UUID, data []byte) UUID {
|
||||||
|
return NewHash(md5.New(), space, data, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
|
||||||
|
// supplied name space and data.
|
||||||
|
//
|
||||||
|
// NewHash(sha1.New(), space, data, 5)
|
||||||
|
func NewSHA1(space UUID, data []byte) UUID {
|
||||||
|
return NewHash(sha1.New(), space, data, 5)
|
||||||
|
}
|
83
vendor/github.com/pborman/uuid/marshal.go
generated
vendored
Normal file
83
vendor/github.com/pborman/uuid/marshal.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
|
func (u UUID) MarshalText() ([]byte, error) {
|
||||||
|
if len(u) != 16 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var js [36]byte
|
||||||
|
encodeHex(js[:], u)
|
||||||
|
return js[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (u *UUID) UnmarshalText(data []byte) error {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
id := Parse(string(data))
|
||||||
|
if id == nil {
|
||||||
|
return errors.New("invalid UUID")
|
||||||
|
}
|
||||||
|
*u = id
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler.
|
||||||
|
func (u UUID) MarshalBinary() ([]byte, error) {
|
||||||
|
return u[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
|
||||||
|
func (u *UUID) UnmarshalBinary(data []byte) error {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(data) != 16 {
|
||||||
|
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
|
||||||
|
}
|
||||||
|
var id [16]byte
|
||||||
|
copy(id[:], data)
|
||||||
|
*u = id[:]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
|
func (u Array) MarshalText() ([]byte, error) {
|
||||||
|
var js [36]byte
|
||||||
|
encodeHex(js[:], u[:])
|
||||||
|
return js[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (u *Array) UnmarshalText(data []byte) error {
|
||||||
|
id := Parse(string(data))
|
||||||
|
if id == nil {
|
||||||
|
return errors.New("invalid UUID")
|
||||||
|
}
|
||||||
|
*u = id.Array()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler.
|
||||||
|
func (u Array) MarshalBinary() ([]byte, error) {
|
||||||
|
return u[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
|
||||||
|
func (u *Array) UnmarshalBinary(data []byte) error {
|
||||||
|
if len(data) != 16 {
|
||||||
|
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
|
||||||
|
}
|
||||||
|
copy(u[:], data)
|
||||||
|
return nil
|
||||||
|
}
|
108
vendor/github.com/pborman/uuid/node.go
generated
vendored
Normal file
108
vendor/github.com/pborman/uuid/node.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nodeMu sync.Mutex
|
||||||
|
ifname string // name of interface being used
|
||||||
|
nodeID []byte // hardware for version 1 UUIDs
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeInterface returns the name of the interface from which the NodeID was
|
||||||
|
// derived. The interface "user" is returned if the NodeID was set by
|
||||||
|
// SetNodeID.
|
||||||
|
func NodeInterface() string {
|
||||||
|
defer nodeMu.Unlock()
|
||||||
|
nodeMu.Lock()
|
||||||
|
return ifname
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
|
||||||
|
// If name is "" then the first usable interface found will be used or a random
|
||||||
|
// Node ID will be generated. If a named interface cannot be found then false
|
||||||
|
// is returned.
|
||||||
|
//
|
||||||
|
// SetNodeInterface never fails when name is "".
|
||||||
|
func SetNodeInterface(name string) bool {
|
||||||
|
defer nodeMu.Unlock()
|
||||||
|
nodeMu.Lock()
|
||||||
|
if nodeID != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return setNodeInterface(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNodeInterface(name string) bool {
|
||||||
|
|
||||||
|
iname, addr := getHardwareInterface(name) // null implementation for js
|
||||||
|
if iname != "" && setNodeID(addr) {
|
||||||
|
ifname = iname
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found no interfaces with a valid hardware address. If name
|
||||||
|
// does not specify a specific interface generate a random Node ID
|
||||||
|
// (section 4.1.6)
|
||||||
|
if name == "" {
|
||||||
|
if nodeID == nil {
|
||||||
|
nodeID = make([]byte, 6)
|
||||||
|
}
|
||||||
|
randomBits(nodeID)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
|
||||||
|
// if not already set.
|
||||||
|
func NodeID() []byte {
|
||||||
|
defer nodeMu.Unlock()
|
||||||
|
nodeMu.Lock()
|
||||||
|
if nodeID == nil {
|
||||||
|
setNodeInterface("")
|
||||||
|
}
|
||||||
|
nid := make([]byte, 6)
|
||||||
|
copy(nid, nodeID)
|
||||||
|
return nid
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
|
||||||
|
// of id are used. If id is less than 6 bytes then false is returned and the
|
||||||
|
// Node ID is not set.
|
||||||
|
func SetNodeID(id []byte) bool {
|
||||||
|
defer nodeMu.Unlock()
|
||||||
|
nodeMu.Lock()
|
||||||
|
if setNodeID(id) {
|
||||||
|
ifname = "user"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNodeID(id []byte) bool {
|
||||||
|
if len(id) < 6 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if nodeID == nil {
|
||||||
|
nodeID = make([]byte, 6)
|
||||||
|
}
|
||||||
|
copy(nodeID, id)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
|
||||||
|
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
|
||||||
|
func (uuid UUID) NodeID() []byte {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
node := make([]byte, 6)
|
||||||
|
copy(node, uuid[10:])
|
||||||
|
return node
|
||||||
|
}
|
12
vendor/github.com/pborman/uuid/node_js.go
generated
vendored
Normal file
12
vendor/github.com/pborman/uuid/node_js.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build js
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
// getHardwareInterface returns nil values for the JS version of the code.
|
||||||
|
// This remvoves the "net" dependency, because it is not used in the browser.
|
||||||
|
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
|
||||||
|
func getHardwareInterface(name string) (string, []byte) { return "", nil }
|
36
vendor/github.com/pborman/uuid/node_net.go
generated
vendored
Normal file
36
vendor/github.com/pborman/uuid/node_net.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !js
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
var interfaces []net.Interface // cached list of interfaces
|
||||||
|
|
||||||
|
// getHardwareInterface returns the name and hardware address of interface name.
|
||||||
|
// If name is "" then the name and hardware address of one of the system's
|
||||||
|
// interfaces is returned. If no interfaces are found (name does not exist or
|
||||||
|
// there are no interfaces) then "", nil is returned.
|
||||||
|
//
|
||||||
|
// Only addresses of at least 6 bytes are returned.
|
||||||
|
func getHardwareInterface(name string) (string, []byte) {
|
||||||
|
if interfaces == nil {
|
||||||
|
var err error
|
||||||
|
interfaces, err = net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ifs := range interfaces {
|
||||||
|
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
|
||||||
|
if setNodeID(ifs.HardwareAddr) {
|
||||||
|
ifname = ifs.Name
|
||||||
|
return ifname, nodeID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
66
vendor/github.com/pborman/uuid/sql.go
generated
vendored
Normal file
66
vendor/github.com/pborman/uuid/sql.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
|
||||||
|
// Currently, database types that map to string and []byte are supported. Please
|
||||||
|
// consult database-specific driver documentation for matching types.
|
||||||
|
func (uuid *UUID) Scan(src interface{}) error {
|
||||||
|
switch src.(type) {
|
||||||
|
case string:
|
||||||
|
// if an empty UUID comes from a table, we return a null UUID
|
||||||
|
if src.(string) == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// see uuid.Parse for required string format
|
||||||
|
parsed := Parse(src.(string))
|
||||||
|
|
||||||
|
if parsed == nil {
|
||||||
|
return errors.New("Scan: invalid UUID format")
|
||||||
|
}
|
||||||
|
|
||||||
|
*uuid = parsed
|
||||||
|
case []byte:
|
||||||
|
b := src.([]byte)
|
||||||
|
|
||||||
|
// if an empty UUID comes from a table, we return a null UUID
|
||||||
|
if len(b) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assumes a simple slice of bytes if 16 bytes
|
||||||
|
// otherwise attempts to parse
|
||||||
|
if len(b) == 16 {
|
||||||
|
*uuid = UUID(b)
|
||||||
|
} else {
|
||||||
|
u := Parse(string(b))
|
||||||
|
|
||||||
|
if u == nil {
|
||||||
|
return errors.New("Scan: invalid UUID format")
|
||||||
|
}
|
||||||
|
|
||||||
|
*uuid = u
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements sql.Valuer so that UUIDs can be written to databases
|
||||||
|
// transparently. Currently, UUIDs map to strings. Please consult
|
||||||
|
// database-specific driver documentation for matching types.
|
||||||
|
func (uuid UUID) Value() (driver.Value, error) {
|
||||||
|
return uuid.String(), nil
|
||||||
|
}
|
132
vendor/github.com/pborman/uuid/time.go
generated
vendored
Normal file
132
vendor/github.com/pborman/uuid/time.go
generated
vendored
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
|
||||||
|
// 1582.
|
||||||
|
type Time int64
|
||||||
|
|
||||||
|
const (
|
||||||
|
lillian = 2299160 // Julian day of 15 Oct 1582
|
||||||
|
unix = 2440587 // Julian day of 1 Jan 1970
|
||||||
|
epoch = unix - lillian // Days between epochs
|
||||||
|
g1582 = epoch * 86400 // seconds between epochs
|
||||||
|
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
timeMu sync.Mutex
|
||||||
|
lasttime uint64 // last time we returned
|
||||||
|
clock_seq uint16 // clock sequence for this run
|
||||||
|
|
||||||
|
timeNow = time.Now // for testing
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnixTime converts t the number of seconds and nanoseconds using the Unix
|
||||||
|
// epoch of 1 Jan 1970.
|
||||||
|
func (t Time) UnixTime() (sec, nsec int64) {
|
||||||
|
sec = int64(t - g1582ns100)
|
||||||
|
nsec = (sec % 10000000) * 100
|
||||||
|
sec /= 10000000
|
||||||
|
return sec, nsec
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
|
||||||
|
// clock sequence as well as adjusting the clock sequence as needed. An error
|
||||||
|
// is returned if the current time cannot be determined.
|
||||||
|
func GetTime() (Time, uint16, error) {
|
||||||
|
defer timeMu.Unlock()
|
||||||
|
timeMu.Lock()
|
||||||
|
return getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTime() (Time, uint16, error) {
|
||||||
|
t := timeNow()
|
||||||
|
|
||||||
|
// If we don't have a clock sequence already, set one.
|
||||||
|
if clock_seq == 0 {
|
||||||
|
setClockSequence(-1)
|
||||||
|
}
|
||||||
|
now := uint64(t.UnixNano()/100) + g1582ns100
|
||||||
|
|
||||||
|
// If time has gone backwards with this clock sequence then we
|
||||||
|
// increment the clock sequence
|
||||||
|
if now <= lasttime {
|
||||||
|
clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000
|
||||||
|
}
|
||||||
|
lasttime = now
|
||||||
|
return Time(now), clock_seq, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClockSequence returns the current clock sequence, generating one if not
|
||||||
|
// already set. The clock sequence is only used for Version 1 UUIDs.
|
||||||
|
//
|
||||||
|
// The uuid package does not use global static storage for the clock sequence or
|
||||||
|
// the last time a UUID was generated. Unless SetClockSequence a new random
|
||||||
|
// clock sequence is generated the first time a clock sequence is requested by
|
||||||
|
// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated
|
||||||
|
// for
|
||||||
|
func ClockSequence() int {
|
||||||
|
defer timeMu.Unlock()
|
||||||
|
timeMu.Lock()
|
||||||
|
return clockSequence()
|
||||||
|
}
|
||||||
|
|
||||||
|
func clockSequence() int {
|
||||||
|
if clock_seq == 0 {
|
||||||
|
setClockSequence(-1)
|
||||||
|
}
|
||||||
|
return int(clock_seq & 0x3fff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
|
||||||
|
// -1 causes a new sequence to be generated.
|
||||||
|
func SetClockSequence(seq int) {
|
||||||
|
defer timeMu.Unlock()
|
||||||
|
timeMu.Lock()
|
||||||
|
setClockSequence(seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setClockSequence(seq int) {
|
||||||
|
if seq == -1 {
|
||||||
|
var b [2]byte
|
||||||
|
randomBits(b[:]) // clock sequence
|
||||||
|
seq = int(b[0])<<8 | int(b[1])
|
||||||
|
}
|
||||||
|
old_seq := clock_seq
|
||||||
|
clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant
|
||||||
|
if old_seq != clock_seq {
|
||||||
|
lasttime = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||||
|
// uuid. It returns false if uuid is not valid. The time is only well defined
|
||||||
|
// for version 1 and 2 UUIDs.
|
||||||
|
func (uuid UUID) Time() (Time, bool) {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||||
|
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||||
|
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||||
|
return Time(time), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClockSequence returns the clock sequence encoded in uuid. It returns false
|
||||||
|
// if uuid is not valid. The clock sequence is only well defined for version 1
|
||||||
|
// and 2 UUIDs.
|
||||||
|
func (uuid UUID) ClockSequence() (int, bool) {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true
|
||||||
|
}
|
43
vendor/github.com/pborman/uuid/util.go
generated
vendored
Normal file
43
vendor/github.com/pborman/uuid/util.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// randomBits completely fills slice b with random data.
|
||||||
|
func randomBits(b []byte) {
|
||||||
|
if _, err := io.ReadFull(rander, b); err != nil {
|
||||||
|
panic(err.Error()) // rand should never fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// xvalues returns the value of a byte as a hexadecimal digit or 255.
|
||||||
|
var xvalues = [256]byte{
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
}
|
||||||
|
|
||||||
|
// xtob converts the the first two hex bytes of x into a byte.
|
||||||
|
func xtob(x string) (byte, bool) {
|
||||||
|
b1 := xvalues[x[0]]
|
||||||
|
b2 := xvalues[x[1]]
|
||||||
|
return (b1 << 4) | b2, b1 != 255 && b2 != 255
|
||||||
|
}
|
201
vendor/github.com/pborman/uuid/uuid.go
generated
vendored
Normal file
201
vendor/github.com/pborman/uuid/uuid.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Array is a pass-by-value UUID that can be used as an effecient key in a map.
|
||||||
|
type Array [16]byte
|
||||||
|
|
||||||
|
// UUID converts uuid into a slice.
|
||||||
|
func (uuid Array) UUID() UUID {
|
||||||
|
return uuid[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of uuid,
|
||||||
|
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
|
||||||
|
func (uuid Array) String() string {
|
||||||
|
return uuid.UUID().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
|
||||||
|
// 4122.
|
||||||
|
type UUID []byte
|
||||||
|
|
||||||
|
// A Version represents a UUIDs version.
|
||||||
|
type Version byte
|
||||||
|
|
||||||
|
// A Variant represents a UUIDs variant.
|
||||||
|
type Variant byte
|
||||||
|
|
||||||
|
// Constants returned by Variant.
|
||||||
|
const (
|
||||||
|
Invalid = Variant(iota) // Invalid UUID
|
||||||
|
RFC4122 // The variant specified in RFC4122
|
||||||
|
Reserved // Reserved, NCS backward compatibility.
|
||||||
|
Microsoft // Reserved, Microsoft Corporation backward compatibility.
|
||||||
|
Future // Reserved for future definition.
|
||||||
|
)
|
||||||
|
|
||||||
|
var rander = rand.Reader // random function
|
||||||
|
|
||||||
|
// New returns a new random (version 4) UUID as a string. It is a convenience
|
||||||
|
// function for NewRandom().String().
|
||||||
|
func New() string {
|
||||||
|
return NewRandom().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse decodes s into a UUID or returns nil. Both the UUID form of
|
||||||
|
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||||
|
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
|
||||||
|
func Parse(s string) UUID {
|
||||||
|
if len(s) == 36+9 {
|
||||||
|
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s = s[9:]
|
||||||
|
} else if len(s) != 36 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var uuid [16]byte
|
||||||
|
for i, x := range [16]int{
|
||||||
|
0, 2, 4, 6,
|
||||||
|
9, 11,
|
||||||
|
14, 16,
|
||||||
|
19, 21,
|
||||||
|
24, 26, 28, 30, 32, 34} {
|
||||||
|
if v, ok := xtob(s[x:]); !ok {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
uuid[i] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uuid[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if uuid1 and uuid2 are equal.
|
||||||
|
func Equal(uuid1, uuid2 UUID) bool {
|
||||||
|
return bytes.Equal(uuid1, uuid2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array returns an array representation of uuid that can be used as a map key.
|
||||||
|
// Array panics if uuid is not valid.
|
||||||
|
func (uuid UUID) Array() Array {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
panic("invalid uuid")
|
||||||
|
}
|
||||||
|
var a Array
|
||||||
|
copy(a[:], uuid)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
|
// , or "" if uuid is invalid.
|
||||||
|
func (uuid UUID) String() string {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var buf [36]byte
|
||||||
|
encodeHex(buf[:], uuid)
|
||||||
|
return string(buf[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// URN returns the RFC 2141 URN form of uuid,
|
||||||
|
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
|
||||||
|
func (uuid UUID) URN() string {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var buf [36 + 9]byte
|
||||||
|
copy(buf[:], "urn:uuid:")
|
||||||
|
encodeHex(buf[9:], uuid)
|
||||||
|
return string(buf[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeHex(dst []byte, uuid UUID) {
|
||||||
|
hex.Encode(dst[:], uuid[:4])
|
||||||
|
dst[8] = '-'
|
||||||
|
hex.Encode(dst[9:13], uuid[4:6])
|
||||||
|
dst[13] = '-'
|
||||||
|
hex.Encode(dst[14:18], uuid[6:8])
|
||||||
|
dst[18] = '-'
|
||||||
|
hex.Encode(dst[19:23], uuid[8:10])
|
||||||
|
dst[23] = '-'
|
||||||
|
hex.Encode(dst[24:], uuid[10:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variant returns the variant encoded in uuid. It returns Invalid if
|
||||||
|
// uuid is invalid.
|
||||||
|
func (uuid UUID) Variant() Variant {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return Invalid
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case (uuid[8] & 0xc0) == 0x80:
|
||||||
|
return RFC4122
|
||||||
|
case (uuid[8] & 0xe0) == 0xc0:
|
||||||
|
return Microsoft
|
||||||
|
case (uuid[8] & 0xe0) == 0xe0:
|
||||||
|
return Future
|
||||||
|
default:
|
||||||
|
return Reserved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version returns the version of uuid. It returns false if uuid is not
|
||||||
|
// valid.
|
||||||
|
func (uuid UUID) Version() (Version, bool) {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return Version(uuid[6] >> 4), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Version) String() string {
|
||||||
|
if v > 15 {
|
||||||
|
return fmt.Sprintf("BAD_VERSION_%d", v)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("VERSION_%d", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Variant) String() string {
|
||||||
|
switch v {
|
||||||
|
case RFC4122:
|
||||||
|
return "RFC4122"
|
||||||
|
case Reserved:
|
||||||
|
return "Reserved"
|
||||||
|
case Microsoft:
|
||||||
|
return "Microsoft"
|
||||||
|
case Future:
|
||||||
|
return "Future"
|
||||||
|
case Invalid:
|
||||||
|
return "Invalid"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("BadVariant%d", int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRand sets the random number generator to r, which implements io.Reader.
|
||||||
|
// If r.Read returns an error when the package requests random data then
|
||||||
|
// a panic will be issued.
|
||||||
|
//
|
||||||
|
// Calling SetRand with nil sets the random number generator to the default
|
||||||
|
// generator.
|
||||||
|
func SetRand(r io.Reader) {
|
||||||
|
if r == nil {
|
||||||
|
rander = rand.Reader
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rander = r
|
||||||
|
}
|
39
vendor/github.com/pborman/uuid/version1.go
generated
vendored
Normal file
39
vendor/github.com/pborman/uuid/version1.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
|
||||||
|
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||||
|
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||||
|
// be set NewUUID returns nil. If clock sequence has not been set by
|
||||||
|
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||||
|
// return the current NewUUID returns nil.
|
||||||
|
func NewUUID() UUID {
|
||||||
|
SetNodeInterface("")
|
||||||
|
|
||||||
|
now, seq, err := GetTime()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid := make([]byte, 16)
|
||||||
|
|
||||||
|
time_low := uint32(now & 0xffffffff)
|
||||||
|
time_mid := uint16((now >> 32) & 0xffff)
|
||||||
|
time_hi := uint16((now >> 48) & 0x0fff)
|
||||||
|
time_hi |= 0x1000 // Version 1
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(uuid[0:], time_low)
|
||||||
|
binary.BigEndian.PutUint16(uuid[4:], time_mid)
|
||||||
|
binary.BigEndian.PutUint16(uuid[6:], time_hi)
|
||||||
|
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||||
|
copy(uuid[10:], nodeID)
|
||||||
|
|
||||||
|
return uuid
|
||||||
|
}
|
25
vendor/github.com/pborman/uuid/version4.go
generated
vendored
Normal file
25
vendor/github.com/pborman/uuid/version4.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
// Random returns a Random (Version 4) UUID or panics.
|
||||||
|
//
|
||||||
|
// The strength of the UUIDs is based on the strength of the crypto/rand
|
||||||
|
// package.
|
||||||
|
//
|
||||||
|
// A note about uniqueness derived from from the UUID Wikipedia entry:
|
||||||
|
//
|
||||||
|
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
|
||||||
|
// hit by a meteorite is estimated to be one chance in 17 billion, that
|
||||||
|
// means the probability is about 0.00000000006 (6 × 10−11),
|
||||||
|
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||||
|
// year and having one duplicate.
|
||||||
|
func NewRandom() UUID {
|
||||||
|
uuid := make([]byte, 16)
|
||||||
|
randomBits([]byte(uuid))
|
||||||
|
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||||
|
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
||||||
|
return uuid
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user