mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 17:57:51 +00:00
Add vendor folder to git
This commit is contained in:
parent
66cf5eaafb
commit
183585f56f
6916 changed files with 2629581 additions and 1 deletions
1271
vendor/github.com/coreos/etcd/etcdctl/README.md
generated
vendored
Normal file
1271
vendor/github.com/coreos/etcd/etcdctl/README.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
341
vendor/github.com/coreos/etcd/etcdctl/READMEv2.md
generated
vendored
Normal file
341
vendor/github.com/coreos/etcd/etcdctl/READMEv2.md
generated
vendored
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
etcdctl
|
||||
========
|
||||
|
||||
`etcdctl` is a command line client for [etcd][etcd].
|
||||
It can be used in scripts or for administrators to explore an etcd cluster.
|
||||
|
||||
## Getting etcdctl
|
||||
|
||||
The latest release is available as a binary at [Github][github-release] along with etcd.
|
||||
|
||||
You can also build etcdctl from source using the build script found in the parent directory.
|
||||
|
||||
## Configuration
|
||||
### --debug
|
||||
+ output cURL commands which can be used to reproduce the request
|
||||
|
||||
### --no-sync
|
||||
+ don't synchronize cluster information before sending request
|
||||
+ Use this to access non-published client endpoints
|
||||
+ Without this flag, values from `--endpoint` flag will be overwritten by etcd cluster when it does internal sync.
|
||||
|
||||
### --output, -o
|
||||
+ output response in the given format (`simple`, `extended` or `json`)
|
||||
+ default: `"simple"`
|
||||
|
||||
### --discovery-srv, -D
|
||||
+ domain name to query for SRV records describing cluster endpoints
|
||||
+ default: none
|
||||
+ env variable: ETCDCTL_DISCOVERY_SRV
|
||||
|
||||
### --peers
|
||||
+ a comma-delimited list of machine addresses in the cluster
|
||||
+ default: `"http://127.0.0.1:2379"`
|
||||
+ env variable: ETCDCTL_PEERS
|
||||
|
||||
### --endpoint
|
||||
+ a comma-delimited list of machine addresses in the cluster
|
||||
+ default: `"http://127.0.0.1:2379"`
|
||||
+ env variable: ETCDCTL_ENDPOINT
|
||||
+ Without `--no-sync` flag, this will be overwritten by etcd cluster when it does internal sync.
|
||||
|
||||
### --cert-file
|
||||
+ identify HTTPS client using this SSL certificate file
|
||||
+ default: none
|
||||
+ env variable: ETCDCTL_CERT_FILE
|
||||
|
||||
### --key-file
|
||||
+ identify HTTPS client using this SSL key file
|
||||
+ default: none
|
||||
+ env variable: ETCDCTL_KEY_FILE
|
||||
|
||||
### --ca-file
|
||||
+ verify certificates of HTTPS-enabled servers using this CA bundle
|
||||
+ default: none
|
||||
+ env variable: ETCDCTL_CA_FILE
|
||||
|
||||
### --username, -u
|
||||
+ provide username[:password] and prompt if password is not supplied
|
||||
+ default: none
|
||||
+ env variable: ETCDCTL_USERNAME
|
||||
|
||||
### --timeout
|
||||
+ connection timeout per request
|
||||
+ default: `"1s"`
|
||||
|
||||
### --total-timeout
|
||||
+ timeout for the command execution (except watch)
|
||||
+ default: `"5s"`
|
||||
|
||||
## Usage
|
||||
|
||||
### Setting Key Values
|
||||
|
||||
Set a value on the `/foo/bar` key:
|
||||
|
||||
```sh
|
||||
$ etcdctl set /foo/bar "Hello world"
|
||||
Hello world
|
||||
```
|
||||
|
||||
Set a value on the `/foo/bar` key with a value that expires in 60 seconds:
|
||||
|
||||
```sh
|
||||
$ etcdctl set /foo/bar "Hello world" --ttl 60
|
||||
Hello world
|
||||
```
|
||||
|
||||
Conditionally set a value on `/foo/bar` if the previous value was "Hello world":
|
||||
|
||||
```sh
|
||||
$ etcdctl set /foo/bar "Goodbye world" --swap-with-value "Hello world"
|
||||
Goodbye world
|
||||
```
|
||||
|
||||
Conditionally set a value on `/foo/bar` if the previous etcd index was 12:
|
||||
|
||||
```sh
|
||||
$ etcdctl set /foo/bar "Goodbye world" --swap-with-index 12
|
||||
Goodbye world
|
||||
```
|
||||
|
||||
Create a new key `/foo/bar`, only if the key did not previously exist:
|
||||
|
||||
```sh
|
||||
$ etcdctl mk /foo/new_bar "Hello world"
|
||||
Hello world
|
||||
```
|
||||
|
||||
Create a new in-order key under dir `/fooDir`:
|
||||
|
||||
```sh
|
||||
$ etcdctl mk --in-order /fooDir "Hello world"
|
||||
```
|
||||
|
||||
Create a new dir `/fooDir`, only if the key did not previously exist:
|
||||
|
||||
```sh
|
||||
$ etcdctl mkdir /fooDir
|
||||
```
|
||||
|
||||
Update an existing key `/foo/bar`, only if the key already existed:
|
||||
|
||||
```sh
|
||||
$ etcdctl update /foo/bar "Hola mundo"
|
||||
Hola mundo
|
||||
```
|
||||
|
||||
Create or update a directory called `/mydir`:
|
||||
|
||||
```sh
|
||||
$ etcdctl setdir /mydir
|
||||
```
|
||||
|
||||
|
||||
### Retrieving a key value
|
||||
|
||||
Get the current value for a single key in the local etcd node:
|
||||
|
||||
```sh
|
||||
$ etcdctl get /foo/bar
|
||||
Hello world
|
||||
```
|
||||
|
||||
Get the value of a key with additional metadata in a parseable format:
|
||||
|
||||
```sh
|
||||
$ etcdctl -o extended get /foo/bar
|
||||
Key: /foo/bar
|
||||
Modified-Index: 72
|
||||
TTL: 0
|
||||
Etcd-Index: 72
|
||||
Raft-Index: 5611
|
||||
Raft-Term: 1
|
||||
|
||||
Hello World
|
||||
```
|
||||
|
||||
### Listing a directory
|
||||
|
||||
Explore the keyspace using the `ls` command
|
||||
|
||||
```sh
|
||||
$ etcdctl ls
|
||||
/akey
|
||||
/adir
|
||||
$ etcdctl ls /adir
|
||||
/adir/key1
|
||||
/adir/key2
|
||||
```
|
||||
|
||||
Add `--recursive` to recursively list subdirectories encountered.
|
||||
|
||||
```sh
|
||||
$ etcdctl ls --recursive
|
||||
/akey
|
||||
/adir
|
||||
/adir/key1
|
||||
/adir/key2
|
||||
```
|
||||
|
||||
Directories can also have a trailing `/` added to output using `-p`.
|
||||
|
||||
```sh
|
||||
$ etcdctl ls -p
|
||||
/akey
|
||||
/adir/
|
||||
```
|
||||
|
||||
### Deleting a key
|
||||
|
||||
Delete a key:
|
||||
|
||||
```sh
|
||||
$ etcdctl rm /foo/bar
|
||||
```
|
||||
|
||||
Delete an empty directory or a key-value pair
|
||||
|
||||
```sh
|
||||
$ etcdctl rmdir /path/to/dir
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```sh
|
||||
$ etcdctl rm /path/to/dir --dir
|
||||
```
|
||||
|
||||
Recursively delete a key and all child keys:
|
||||
|
||||
```sh
|
||||
$ etcdctl rm /path/to/dir --recursive
|
||||
```
|
||||
|
||||
Conditionally delete `/foo/bar` if the previous value was "Hello world":
|
||||
|
||||
```sh
|
||||
$ etcdctl rm /foo/bar --with-value "Hello world"
|
||||
```
|
||||
|
||||
Conditionally delete `/foo/bar` if the previous etcd index was 12:
|
||||
|
||||
```sh
|
||||
$ etcdctl rm /foo/bar --with-index 12
|
||||
```
|
||||
|
||||
### Watching for changes
|
||||
|
||||
Watch for only the next change on a key:
|
||||
|
||||
```sh
|
||||
$ etcdctl watch /foo/bar
|
||||
Hello world
|
||||
```
|
||||
|
||||
Continuously watch a key:
|
||||
|
||||
```sh
|
||||
$ etcdctl watch /foo/bar --forever
|
||||
Hello world
|
||||
.... client hangs forever until ctrl+C printing values as key change
|
||||
```
|
||||
|
||||
Continuously watch a key, starting with a given etcd index:
|
||||
|
||||
```sh
|
||||
$ etcdctl watch /foo/bar --forever --index 12
|
||||
Hello world
|
||||
.... client hangs forever until ctrl+C printing values as key change
|
||||
```
|
||||
|
||||
Continuously watch a key and exec a program:
|
||||
|
||||
```sh
|
||||
$ etcdctl exec-watch /foo/bar -- sh -c "env | grep ETCD"
|
||||
ETCD_WATCH_ACTION=set
|
||||
ETCD_WATCH_VALUE=My configuration stuff
|
||||
ETCD_WATCH_MODIFIED_INDEX=1999
|
||||
ETCD_WATCH_KEY=/foo/bar
|
||||
ETCD_WATCH_ACTION=set
|
||||
ETCD_WATCH_VALUE=My new configuration stuff
|
||||
ETCD_WATCH_MODIFIED_INDEX=2000
|
||||
ETCD_WATCH_KEY=/foo/bar
|
||||
```
|
||||
|
||||
Continuously and recursively watch a key and exec a program:
|
||||
```sh
|
||||
$ etcdctl exec-watch --recursive /foo -- sh -c "env | grep ETCD"
|
||||
ETCD_WATCH_ACTION=set
|
||||
ETCD_WATCH_VALUE=My configuration stuff
|
||||
ETCD_WATCH_MODIFIED_INDEX=1999
|
||||
ETCD_WATCH_KEY=/foo/bar
|
||||
ETCD_WATCH_ACTION=set
|
||||
ETCD_WATCH_VALUE=My new configuration stuff
|
||||
ETCD_WATCH_MODIFIED_INDEX=2000
|
||||
ETCD_WATCH_KEY=/foo/barbar
|
||||
```
|
||||
|
||||
## Return Codes
|
||||
|
||||
The following exit codes can be returned from etcdctl:
|
||||
|
||||
```
|
||||
0 Success
|
||||
1 Malformed etcdctl arguments
|
||||
2 Failed to connect to host
|
||||
3 Failed to auth (client cert rejected, ca validation failure, etc)
|
||||
4 400 error from etcd
|
||||
5 500 error from etcd
|
||||
```
|
||||
|
||||
## Endpoint
|
||||
|
||||
If your etcd cluster isn't available on `http://127.0.0.1:2379` you can specify
|
||||
a `--endpoint` flag or `ETCDCTL_ENDPOINT` environment variable. You can list one endpoint,
|
||||
or a comma-separated list of endpoints. This option is ignored if the `--discovery-srv`
|
||||
option is provided.
|
||||
|
||||
```sh
|
||||
ETCDCTL_ENDPOINT="http://10.0.28.1:4002" etcdctl set my-key to-a-value
|
||||
ETCDCTL_ENDPOINT="http://10.0.28.1:4002,http://10.0.28.2:4002,http://10.0.28.3:4002" etcdctl set my-key to-a-value
|
||||
etcdctl --endpoint http://10.0.28.1:4002 my-key to-a-value
|
||||
etcdctl --endpoint http://10.0.28.1:4002,http://10.0.28.2:4002,http://10.0.28.3:4002 etcdctl set my-key to-a-value
|
||||
```
|
||||
|
||||
## Username and Password
|
||||
|
||||
If your etcd cluster is protected by [authentication][authentication], you can specify username and password using the [`--username`][username-flag] or `ETCDCTL_USERNAME` environment variable. When `--username` flag or `ETCDCTL_USERNAME` environment variable doesn't contain password, etcdctl will prompt password in interactive mode.
|
||||
|
||||
```sh
|
||||
ETCDCTL_USERNAME="root:password" etcdctl set my-key to-a-value
|
||||
```
|
||||
|
||||
## DNS Discovery
|
||||
|
||||
If you want to discover your etcd cluster through domain SRV records you can specify
|
||||
a `--discovery-srv` flag or `ETCDCTL_DISCOVERY_SRV` environment variable. This option takes
|
||||
precedence over the `--endpoint` flag.
|
||||
|
||||
```sh
|
||||
ETCDCTL_DISCOVERY_SRV="some-domain" etcdctl set my-key to-a-value
|
||||
etcdctl --discovery-srv some-domain set my-key to-a-value
|
||||
```
|
||||
|
||||
## Project Details
|
||||
|
||||
### Versioning
|
||||
|
||||
etcdctl uses [semantic versioning][semver].
|
||||
Releases will follow lockstep with the etcd release cycle.
|
||||
|
||||
### License
|
||||
|
||||
etcdctl is under the Apache 2.0 license. See the [LICENSE][license] file for details.
|
||||
|
||||
[authentication]: ../Documentation/v2/authentication.md
|
||||
[etcd]: https://github.com/coreos/etcd
|
||||
[github-release]: https://github.com/coreos/etcd/releases/
|
||||
[license]: https://github.com/coreos/etcdctl/blob/master/LICENSE
|
||||
[semver]: http://semver.org/
|
||||
[username-flag]: #--username--u
|
||||
90
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/auth_commands.go
generated
vendored
Normal file
90
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/auth_commands.go
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func NewAuthCommands() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "auth",
|
||||
Usage: "overall auth controls",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "enable",
|
||||
Usage: "enable auth access controls",
|
||||
ArgsUsage: " ",
|
||||
Action: actionAuthEnable,
|
||||
},
|
||||
{
|
||||
Name: "disable",
|
||||
Usage: "disable auth access controls",
|
||||
ArgsUsage: " ",
|
||||
Action: actionAuthDisable,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func actionAuthEnable(c *cli.Context) error {
|
||||
authEnableDisable(c, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionAuthDisable(c *cli.Context) error {
|
||||
authEnableDisable(c, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustNewAuthAPI(c *cli.Context) client.AuthAPI {
|
||||
hc := mustNewClient(c)
|
||||
|
||||
if c.GlobalBool("debug") {
|
||||
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
|
||||
}
|
||||
|
||||
return client.NewAuthAPI(hc)
|
||||
}
|
||||
|
||||
func authEnableDisable(c *cli.Context, enable bool) {
|
||||
if len(c.Args()) != 0 {
|
||||
fmt.Fprintln(os.Stderr, "No arguments accepted")
|
||||
os.Exit(1)
|
||||
}
|
||||
s := mustNewAuthAPI(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
var err error
|
||||
if enable {
|
||||
err = s.Enable(ctx)
|
||||
} else {
|
||||
err = s.Disable(ctx)
|
||||
}
|
||||
cancel()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if enable {
|
||||
fmt.Println("Authentication Enabled")
|
||||
} else {
|
||||
fmt.Println("Authentication Disabled")
|
||||
}
|
||||
}
|
||||
118
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/backup_command.go
generated
vendored
Normal file
118
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/backup_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"github.com/coreos/etcd/pkg/fileutil"
|
||||
"github.com/coreos/etcd/pkg/idutil"
|
||||
"github.com/coreos/etcd/pkg/pbutil"
|
||||
"github.com/coreos/etcd/snap"
|
||||
"github.com/coreos/etcd/wal"
|
||||
"github.com/coreos/etcd/wal/walpb"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func NewBackupCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "backup",
|
||||
Usage: "backup an etcd directory",
|
||||
ArgsUsage: " ",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "data-dir", Value: "", Usage: "Path to the etcd data dir"},
|
||||
cli.StringFlag{Name: "wal-dir", Value: "", Usage: "Path to the etcd wal dir"},
|
||||
cli.StringFlag{Name: "backup-dir", Value: "", Usage: "Path to the backup dir"},
|
||||
cli.StringFlag{Name: "backup-wal-dir", Value: "", Usage: "Path to the backup wal dir"},
|
||||
},
|
||||
Action: handleBackup,
|
||||
}
|
||||
}
|
||||
|
||||
// handleBackup handles a request that intends to do a backup.
|
||||
func handleBackup(c *cli.Context) error {
|
||||
var srcWAL string
|
||||
var destWAL string
|
||||
|
||||
srcSnap := filepath.Join(c.String("data-dir"), "member", "snap")
|
||||
destSnap := filepath.Join(c.String("backup-dir"), "member", "snap")
|
||||
|
||||
if c.String("wal-dir") != "" {
|
||||
srcWAL = c.String("wal-dir")
|
||||
} else {
|
||||
srcWAL = filepath.Join(c.String("data-dir"), "member", "wal")
|
||||
}
|
||||
|
||||
if c.String("backup-wal-dir") != "" {
|
||||
destWAL = c.String("backup-wal-dir")
|
||||
} else {
|
||||
destWAL = filepath.Join(c.String("backup-dir"), "member", "wal")
|
||||
}
|
||||
|
||||
if err := fileutil.CreateDirAll(destSnap); err != nil {
|
||||
log.Fatalf("failed creating backup snapshot dir %v: %v", destSnap, err)
|
||||
}
|
||||
ss := snap.New(srcSnap)
|
||||
snapshot, err := ss.Load()
|
||||
if err != nil && err != snap.ErrNoSnapshot {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var walsnap walpb.Snapshot
|
||||
if snapshot != nil {
|
||||
walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
|
||||
newss := snap.New(destSnap)
|
||||
if err = newss.SaveSnap(*snapshot); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
w, err := wal.OpenForRead(srcWAL, walsnap)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer w.Close()
|
||||
wmetadata, state, ents, err := w.ReadAll()
|
||||
switch err {
|
||||
case nil:
|
||||
case wal.ErrSnapshotNotFound:
|
||||
fmt.Printf("Failed to find the match snapshot record %+v in wal %v.", walsnap, srcWAL)
|
||||
fmt.Printf("etcdctl will add it back. Start auto fixing...")
|
||||
default:
|
||||
log.Fatal(err)
|
||||
}
|
||||
var metadata etcdserverpb.Metadata
|
||||
pbutil.MustUnmarshal(&metadata, wmetadata)
|
||||
idgen := idutil.NewGenerator(0, time.Now())
|
||||
metadata.NodeID = idgen.Next()
|
||||
metadata.ClusterID = idgen.Next()
|
||||
|
||||
neww, err := wal.Create(destWAL, pbutil.MustMarshal(&metadata))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer neww.Close()
|
||||
if err := neww.Save(state, ents); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := neww.SaveSnapshot(walsnap); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
137
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/cluster_health.go
generated
vendored
Normal file
137
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/cluster_health.go
generated
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func NewClusterHealthCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "cluster-health",
|
||||
Usage: "check the health of the etcd cluster",
|
||||
ArgsUsage: " ",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "forever, f", Usage: "forever check the health every 10 second until CTRL+C"},
|
||||
},
|
||||
Action: handleClusterHealth,
|
||||
}
|
||||
}
|
||||
|
||||
func handleClusterHealth(c *cli.Context) error {
|
||||
forever := c.Bool("forever")
|
||||
if forever {
|
||||
sigch := make(chan os.Signal, 1)
|
||||
signal.Notify(sigch, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
<-sigch
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
||||
|
||||
tr, err := getTransport(c)
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
|
||||
hc := http.Client{
|
||||
Transport: tr,
|
||||
}
|
||||
|
||||
cln := mustNewClientNoSync(c)
|
||||
mi := client.NewMembersAPI(cln)
|
||||
ms, err := mi.List(context.TODO())
|
||||
if err != nil {
|
||||
fmt.Println("cluster may be unhealthy: failed to list members")
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
|
||||
for {
|
||||
health := false
|
||||
for _, m := range ms {
|
||||
if len(m.ClientURLs) == 0 {
|
||||
fmt.Printf("member %s is unreachable: no available published client urls\n", m.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
checked := false
|
||||
for _, url := range m.ClientURLs {
|
||||
resp, err := hc.Get(url + "/health")
|
||||
if err != nil {
|
||||
fmt.Printf("failed to check the health of member %s on %s: %v\n", m.ID, url, err)
|
||||
continue
|
||||
}
|
||||
|
||||
result := struct{ Health string }{}
|
||||
nresult := struct{ Health bool }{}
|
||||
bytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to check the health of member %s on %s: %v\n", m.ID, url, err)
|
||||
continue
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
err = json.Unmarshal(bytes, &result)
|
||||
if err != nil {
|
||||
err = json.Unmarshal(bytes, &nresult)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf("failed to check the health of member %s on %s: %v\n", m.ID, url, err)
|
||||
continue
|
||||
}
|
||||
|
||||
checked = true
|
||||
if result.Health == "true" || nresult.Health {
|
||||
health = true
|
||||
fmt.Printf("member %s is healthy: got healthy result from %s\n", m.ID, url)
|
||||
} else {
|
||||
fmt.Printf("member %s is unhealthy: got unhealthy result from %s\n", m.ID, url)
|
||||
}
|
||||
break
|
||||
}
|
||||
if !checked {
|
||||
fmt.Printf("member %s is unreachable: %v are all unreachable\n", m.ID, m.ClientURLs)
|
||||
}
|
||||
}
|
||||
if health {
|
||||
fmt.Println("cluster is healthy")
|
||||
} else {
|
||||
fmt.Println("cluster is unhealthy")
|
||||
}
|
||||
|
||||
if !forever {
|
||||
if health {
|
||||
os.Exit(ExitSuccess)
|
||||
return nil
|
||||
}
|
||||
os.Exit(ExitClusterNotHealthy)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("\nnext check after 10 second...\n\n")
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
16
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/doc.go
generated
vendored
Normal file
16
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package command is a set of libraries for etcdctl commands.
|
||||
package command
|
||||
52
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/error.go
generated
vendored
Normal file
52
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/error.go
generated
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
ExitSuccess = iota
|
||||
ExitBadArgs
|
||||
ExitBadConnection
|
||||
ExitBadAuth
|
||||
ExitServerError
|
||||
ExitClusterNotHealthy
|
||||
)
|
||||
|
||||
func handleError(c *cli.Context, code int, err error) {
|
||||
if c.GlobalString("output") == "json" {
|
||||
if err, ok := err.(*client.Error); ok {
|
||||
b, err := json.Marshal(err)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, string(b))
|
||||
os.Exit(code)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "Error: ", err)
|
||||
if cerr, ok := err.(*client.ClusterError); ok {
|
||||
fmt.Fprintln(os.Stderr, cerr.Detail())
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
128
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/exec_watch_command.go
generated
vendored
Normal file
128
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/exec_watch_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// NewExecWatchCommand returns the CLI command for "exec-watch".
|
||||
func NewExecWatchCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "exec-watch",
|
||||
Usage: "watch a key for changes and exec an executable",
|
||||
ArgsUsage: "<key> <command> [args...]",
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "after-index", Value: 0, Usage: "watch after the given index"},
|
||||
cli.BoolFlag{Name: "recursive, r", Usage: "watch all values for key and child keys"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
execWatchCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// execWatchCommandFunc executes the "exec-watch" command.
|
||||
func execWatchCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
args := c.Args()
|
||||
argslen := len(args)
|
||||
|
||||
if argslen < 2 {
|
||||
handleError(c, ExitBadArgs, errors.New("key and command to exec required"))
|
||||
}
|
||||
|
||||
var (
|
||||
key string
|
||||
cmdArgs []string
|
||||
)
|
||||
|
||||
foundSep := false
|
||||
for i := range args {
|
||||
if args[i] == "--" && i != 0 {
|
||||
foundSep = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if foundSep {
|
||||
key = args[0]
|
||||
cmdArgs = args[2:]
|
||||
} else {
|
||||
// If no flag is parsed, the order of key and cmdArgs will be switched and
|
||||
// args will not contain `--`.
|
||||
key = args[argslen-1]
|
||||
cmdArgs = args[:argslen-1]
|
||||
}
|
||||
|
||||
index := 0
|
||||
if c.Int("after-index") != 0 {
|
||||
index = c.Int("after-index")
|
||||
}
|
||||
|
||||
recursive := c.Bool("recursive")
|
||||
|
||||
sigch := make(chan os.Signal, 1)
|
||||
signal.Notify(sigch, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
<-sigch
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
w := ki.Watcher(key, &client.WatcherOptions{AfterIndex: uint64(index), Recursive: recursive})
|
||||
|
||||
for {
|
||||
resp, err := w.Next(context.TODO())
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
if resp.Node.Dir {
|
||||
fmt.Fprintf(os.Stderr, "Ignored dir %s change\n", resp.Node.Key)
|
||||
continue
|
||||
}
|
||||
|
||||
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||||
cmd.Env = environResponse(resp, os.Environ())
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
go func() {
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
cmd.Wait()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func environResponse(resp *client.Response, env []string) []string {
|
||||
env = append(env, "ETCD_WATCH_ACTION="+resp.Action)
|
||||
env = append(env, "ETCD_WATCH_MODIFIED_INDEX="+fmt.Sprintf("%d", resp.Node.ModifiedIndex))
|
||||
env = append(env, "ETCD_WATCH_KEY="+resp.Node.Key)
|
||||
env = append(env, "ETCD_WATCH_VALUE="+resp.Node.Value)
|
||||
return env
|
||||
}
|
||||
60
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/format.go
generated
vendored
Normal file
60
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/format.go
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
)
|
||||
|
||||
// printResponseKey only supports to print key correctly.
|
||||
func printResponseKey(resp *client.Response, format string) {
|
||||
// Format the result.
|
||||
switch format {
|
||||
case "simple":
|
||||
if resp.Action != "delete" {
|
||||
fmt.Println(resp.Node.Value)
|
||||
} else {
|
||||
fmt.Println("PrevNode.Value:", resp.PrevNode.Value)
|
||||
}
|
||||
case "extended":
|
||||
// Extended prints in a rfc2822 style format
|
||||
fmt.Println("Key:", resp.Node.Key)
|
||||
fmt.Println("Created-Index:", resp.Node.CreatedIndex)
|
||||
fmt.Println("Modified-Index:", resp.Node.ModifiedIndex)
|
||||
|
||||
if resp.PrevNode != nil {
|
||||
fmt.Println("PrevNode.Value:", resp.PrevNode.Value)
|
||||
}
|
||||
|
||||
fmt.Println("TTL:", resp.Node.TTL)
|
||||
fmt.Println("Index:", resp.Index)
|
||||
if resp.Action != "delete" {
|
||||
fmt.Println("")
|
||||
fmt.Println(resp.Node.Value)
|
||||
}
|
||||
case "json":
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Unsupported output format:", format)
|
||||
}
|
||||
}
|
||||
66
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/get_command.go
generated
vendored
Normal file
66
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/get_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewGetCommand returns the CLI command for "get".
|
||||
func NewGetCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "get",
|
||||
Usage: "retrieve the value of a key",
|
||||
ArgsUsage: "<key>",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "sort", Usage: "returns result in sorted order"},
|
||||
cli.BoolFlag{Name: "quorum, q", Usage: "require quorum for get request"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
getCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// getCommandFunc executes the "get" command.
|
||||
func getCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
if len(c.Args()) == 0 {
|
||||
handleError(c, ExitBadArgs, errors.New("key required"))
|
||||
}
|
||||
|
||||
key := c.Args()[0]
|
||||
sorted := c.Bool("sort")
|
||||
quorum := c.Bool("quorum")
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
resp, err := ki.Get(ctx, key, &client.GetOptions{Sort: sorted, Quorum: quorum})
|
||||
cancel()
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
|
||||
if resp.Node.Dir {
|
||||
fmt.Fprintln(os.Stderr, fmt.Sprintf("%s: is a directory", resp.Node.Key))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
}
|
||||
90
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/ls_command.go
generated
vendored
Normal file
90
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/ls_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func NewLsCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "retrieve a directory",
|
||||
ArgsUsage: "[key]",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "sort", Usage: "returns result in sorted order"},
|
||||
cli.BoolFlag{Name: "recursive, r", Usage: "returns all key names recursively for the given path"},
|
||||
cli.BoolFlag{Name: "p", Usage: "append slash (/) to directories"},
|
||||
cli.BoolFlag{Name: "quorum, q", Usage: "require quorum for get request"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
lsCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// lsCommandFunc executes the "ls" command.
|
||||
func lsCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
key := "/"
|
||||
if len(c.Args()) != 0 {
|
||||
key = c.Args()[0]
|
||||
}
|
||||
|
||||
sort := c.Bool("sort")
|
||||
recursive := c.Bool("recursive")
|
||||
quorum := c.Bool("quorum")
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
resp, err := ki.Get(ctx, key, &client.GetOptions{Sort: sort, Recursive: recursive, Quorum: quorum})
|
||||
cancel()
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
|
||||
printLs(c, resp)
|
||||
}
|
||||
|
||||
// printLs writes a response out in a manner similar to the `ls` command in unix.
|
||||
// Non-empty directories list their contents and files list their name.
|
||||
func printLs(c *cli.Context, resp *client.Response) {
|
||||
if c.GlobalString("output") == "simple" {
|
||||
if !resp.Node.Dir {
|
||||
fmt.Println(resp.Node.Key)
|
||||
}
|
||||
for _, node := range resp.Node.Nodes {
|
||||
rPrint(c, node)
|
||||
}
|
||||
} else {
|
||||
// user wants JSON or extended output
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
}
|
||||
}
|
||||
|
||||
// rPrint recursively prints out the nodes in the node structure.
|
||||
func rPrint(c *cli.Context, n *client.Node) {
|
||||
if n.Dir && c.Bool("p") {
|
||||
fmt.Println(fmt.Sprintf("%v/", n.Key))
|
||||
} else {
|
||||
fmt.Println(n.Key)
|
||||
}
|
||||
|
||||
for _, node := range n.Nodes {
|
||||
rPrint(c, node)
|
||||
}
|
||||
}
|
||||
207
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/member_commands.go
generated
vendored
Normal file
207
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/member_commands.go
generated
vendored
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func NewMemberCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "member",
|
||||
Usage: "member add, remove and list subcommands",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "list",
|
||||
Usage: "enumerate existing cluster members",
|
||||
ArgsUsage: " ",
|
||||
Action: actionMemberList,
|
||||
},
|
||||
{
|
||||
Name: "add",
|
||||
Usage: "add a new member to the etcd cluster",
|
||||
ArgsUsage: "<name> <peerURL>",
|
||||
Action: actionMemberAdd,
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Usage: "remove an existing member from the etcd cluster",
|
||||
ArgsUsage: "<memberID>",
|
||||
Action: actionMemberRemove,
|
||||
},
|
||||
{
|
||||
Name: "update",
|
||||
Usage: "update an existing member in the etcd cluster",
|
||||
ArgsUsage: "<memberID> <peerURLs>",
|
||||
Action: actionMemberUpdate,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func actionMemberList(c *cli.Context) error {
|
||||
if len(c.Args()) != 0 {
|
||||
fmt.Fprintln(os.Stderr, "No arguments accepted")
|
||||
os.Exit(1)
|
||||
}
|
||||
mAPI := mustNewMembersAPI(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
defer cancel()
|
||||
|
||||
members, err := mAPI.List(ctx)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
leader, err := mAPI.Leader(ctx)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Failed to get leader: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, m := range members {
|
||||
isLeader := false
|
||||
if m.ID == leader.ID {
|
||||
isLeader = true
|
||||
}
|
||||
if len(m.Name) == 0 {
|
||||
fmt.Printf("%s[unstarted]: peerURLs=%s\n", m.ID, strings.Join(m.PeerURLs, ","))
|
||||
} else {
|
||||
fmt.Printf("%s: name=%s peerURLs=%s clientURLs=%s isLeader=%v\n", m.ID, m.Name, strings.Join(m.PeerURLs, ","), strings.Join(m.ClientURLs, ","), isLeader)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionMemberAdd(c *cli.Context) error {
|
||||
args := c.Args()
|
||||
if len(args) != 2 {
|
||||
fmt.Fprintln(os.Stderr, "Provide a name and a single member peerURL")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
mAPI := mustNewMembersAPI(c)
|
||||
|
||||
url := args[1]
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
defer cancel()
|
||||
|
||||
m, err := mAPI.Add(ctx, url)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
newID := m.ID
|
||||
newName := args[0]
|
||||
fmt.Printf("Added member named %s with ID %s to cluster\n", newName, newID)
|
||||
|
||||
members, err := mAPI.List(ctx)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
conf := []string{}
|
||||
for _, memb := range members {
|
||||
for _, u := range memb.PeerURLs {
|
||||
n := memb.Name
|
||||
if memb.ID == newID {
|
||||
n = newName
|
||||
}
|
||||
conf = append(conf, fmt.Sprintf("%s=%s", n, u))
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Print("\n")
|
||||
fmt.Printf("ETCD_NAME=%q\n", newName)
|
||||
fmt.Printf("ETCD_INITIAL_CLUSTER=%q\n", strings.Join(conf, ","))
|
||||
fmt.Printf("ETCD_INITIAL_CLUSTER_STATE=\"existing\"\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionMemberRemove(c *cli.Context) error {
|
||||
args := c.Args()
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintln(os.Stderr, "Provide a single member ID")
|
||||
os.Exit(1)
|
||||
}
|
||||
removalID := args[0]
|
||||
|
||||
mAPI := mustNewMembersAPI(c)
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
defer cancel()
|
||||
// Get the list of members.
|
||||
members, err := mAPI.List(ctx)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error while verifying ID against known members:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
// Sanity check the input.
|
||||
foundID := false
|
||||
for _, m := range members {
|
||||
if m.ID == removalID {
|
||||
foundID = true
|
||||
}
|
||||
if m.Name == removalID {
|
||||
// Note that, so long as it's not ambiguous, we *could* do the right thing by name here.
|
||||
fmt.Fprintf(os.Stderr, "Found a member named %s; if this is correct, please use its ID, eg:\n\tetcdctl member remove %s\n", m.Name, m.ID)
|
||||
fmt.Fprintf(os.Stderr, "For more details, read the documentation at https://github.com/coreos/etcd/blob/master/Documentation/runtime-configuration.md#remove-a-member\n\n")
|
||||
}
|
||||
}
|
||||
if !foundID {
|
||||
fmt.Fprintf(os.Stderr, "Couldn't find a member in the cluster with an ID of %s.\n", removalID)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Actually attempt to remove the member.
|
||||
err = mAPI.Remove(ctx, removalID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Received an error trying to remove member %s: %s", removalID, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Removed member %s from cluster\n", removalID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionMemberUpdate(c *cli.Context) error {
|
||||
args := c.Args()
|
||||
if len(args) != 2 {
|
||||
fmt.Fprintln(os.Stderr, "Provide an ID and a list of comma separated peerURL (0xabcd http://example.com,http://example1.com)")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
mAPI := mustNewMembersAPI(c)
|
||||
|
||||
mid := args[0]
|
||||
urls := args[1]
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
err := mAPI.Update(ctx, mid, strings.Split(urls, ","))
|
||||
cancel()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Updated member with ID %s in cluster\n", mid)
|
||||
return nil
|
||||
}
|
||||
76
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/mk_command.go
generated
vendored
Normal file
76
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/mk_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewMakeCommand returns the CLI command for "mk".
|
||||
func NewMakeCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "mk",
|
||||
Usage: "make a new key with a given value",
|
||||
ArgsUsage: "<key> <value>",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "in-order", Usage: "create in-order key under directory <key>"},
|
||||
cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live in seconds"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
mkCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// mkCommandFunc executes the "mk" command.
|
||||
func mkCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
if len(c.Args()) == 0 {
|
||||
handleError(c, ExitBadArgs, errors.New("key required"))
|
||||
}
|
||||
key := c.Args()[0]
|
||||
value, err := argOrStdin(c.Args(), os.Stdin, 1)
|
||||
if err != nil {
|
||||
handleError(c, ExitBadArgs, errors.New("value required"))
|
||||
}
|
||||
|
||||
ttl := c.Int("ttl")
|
||||
inorder := c.Bool("in-order")
|
||||
|
||||
var resp *client.Response
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
if !inorder {
|
||||
// Since PrevNoExist means that the Node must not exist previously,
|
||||
// this Set method always creates a new key. Therefore, mk command
|
||||
// succeeds only if the key did not previously exist, and the command
|
||||
// prevents one from overwriting values accidentally.
|
||||
resp, err = ki.Set(ctx, key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevExist: client.PrevNoExist})
|
||||
} else {
|
||||
// If in-order flag is specified then create an inorder key under
|
||||
// the directory identified by the key argument.
|
||||
resp, err = ki.CreateInOrder(ctx, key, value, &client.CreateInOrderOptions{TTL: time.Duration(ttl) * time.Second})
|
||||
}
|
||||
cancel()
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
}
|
||||
59
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/mkdir_command.go
generated
vendored
Normal file
59
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/mkdir_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewMakeDirCommand returns the CLI command for "mkdir".
|
||||
func NewMakeDirCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "mkdir",
|
||||
Usage: "make a new directory",
|
||||
ArgsUsage: "<key>",
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live in seconds"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
mkdirCommandFunc(c, mustNewKeyAPI(c), client.PrevNoExist)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// mkdirCommandFunc executes the "mkdir" command.
|
||||
func mkdirCommandFunc(c *cli.Context, ki client.KeysAPI, prevExist client.PrevExistType) {
|
||||
if len(c.Args()) == 0 {
|
||||
handleError(c, ExitBadArgs, errors.New("key required"))
|
||||
}
|
||||
|
||||
key := c.Args()[0]
|
||||
ttl := c.Int("ttl")
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
resp, err := ki.Set(ctx, key, "", &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: prevExist})
|
||||
cancel()
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
if c.GlobalString("output") != "simple" {
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
}
|
||||
}
|
||||
63
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/rm_command.go
generated
vendored
Normal file
63
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/rm_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewRemoveCommand returns the CLI command for "rm".
|
||||
func NewRemoveCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a key or a directory",
|
||||
ArgsUsage: "<key>",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "dir", Usage: "removes the key if it is an empty directory or a key-value pair"},
|
||||
cli.BoolFlag{Name: "recursive, r", Usage: "removes the key and all child keys(if it is a directory)"},
|
||||
cli.StringFlag{Name: "with-value", Value: "", Usage: "previous value"},
|
||||
cli.IntFlag{Name: "with-index", Value: 0, Usage: "previous index"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
rmCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// rmCommandFunc executes the "rm" command.
|
||||
func rmCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
if len(c.Args()) == 0 {
|
||||
handleError(c, ExitBadArgs, errors.New("key required"))
|
||||
}
|
||||
key := c.Args()[0]
|
||||
recursive := c.Bool("recursive")
|
||||
dir := c.Bool("dir")
|
||||
prevValue := c.String("with-value")
|
||||
prevIndex := c.Int("with-index")
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
resp, err := ki.Delete(ctx, key, &client.DeleteOptions{PrevIndex: uint64(prevIndex), PrevValue: prevValue, Dir: dir, Recursive: recursive})
|
||||
cancel()
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
if !resp.Node.Dir || c.GlobalString("output") != "simple" {
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
}
|
||||
}
|
||||
54
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/rmdir_command.go
generated
vendored
Normal file
54
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/rmdir_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewRemoveDirCommand returns the CLI command for "rmdir".
|
||||
func NewRemoveDirCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "rmdir",
|
||||
Usage: "removes the key if it is an empty directory or a key-value pair",
|
||||
ArgsUsage: "<key>",
|
||||
Action: func(c *cli.Context) error {
|
||||
rmdirCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// rmdirCommandFunc executes the "rmdir" command.
|
||||
func rmdirCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
if len(c.Args()) == 0 {
|
||||
handleError(c, ExitBadArgs, errors.New("key required"))
|
||||
}
|
||||
key := c.Args()[0]
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
resp, err := ki.Delete(ctx, key, &client.DeleteOptions{Dir: true})
|
||||
cancel()
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
|
||||
if !resp.Node.Dir || c.GlobalString("output") != "simple" {
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
}
|
||||
}
|
||||
255
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/role_commands.go
generated
vendored
Normal file
255
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/role_commands.go
generated
vendored
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/coreos/etcd/pkg/pathutil"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func NewRoleCommands() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "role",
|
||||
Usage: "role add, grant and revoke subcommands",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "add",
|
||||
Usage: "add a new role for the etcd cluster",
|
||||
ArgsUsage: "<role> ",
|
||||
Action: actionRoleAdd,
|
||||
},
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "get details for a role",
|
||||
ArgsUsage: "<role>",
|
||||
Action: actionRoleGet,
|
||||
},
|
||||
{
|
||||
Name: "list",
|
||||
Usage: "list all roles",
|
||||
ArgsUsage: " ",
|
||||
Action: actionRoleList,
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Usage: "remove a role from the etcd cluster",
|
||||
ArgsUsage: "<role>",
|
||||
Action: actionRoleRemove,
|
||||
},
|
||||
{
|
||||
Name: "grant",
|
||||
Usage: "grant path matches to an etcd role",
|
||||
ArgsUsage: "<role>",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "path", Value: "", Usage: "Path granted for the role to access"},
|
||||
cli.BoolFlag{Name: "read", Usage: "Grant read-only access"},
|
||||
cli.BoolFlag{Name: "write", Usage: "Grant write-only access"},
|
||||
cli.BoolFlag{Name: "readwrite, rw", Usage: "Grant read-write access"},
|
||||
},
|
||||
Action: actionRoleGrant,
|
||||
},
|
||||
{
|
||||
Name: "revoke",
|
||||
Usage: "revoke path matches for an etcd role",
|
||||
ArgsUsage: "<role>",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "path", Value: "", Usage: "Path revoked for the role to access"},
|
||||
cli.BoolFlag{Name: "read", Usage: "Revoke read access"},
|
||||
cli.BoolFlag{Name: "write", Usage: "Revoke write access"},
|
||||
cli.BoolFlag{Name: "readwrite, rw", Usage: "Revoke read-write access"},
|
||||
},
|
||||
Action: actionRoleRevoke,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func mustNewAuthRoleAPI(c *cli.Context) client.AuthRoleAPI {
|
||||
hc := mustNewClient(c)
|
||||
|
||||
if c.GlobalBool("debug") {
|
||||
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
|
||||
}
|
||||
|
||||
return client.NewAuthRoleAPI(hc)
|
||||
}
|
||||
|
||||
func actionRoleList(c *cli.Context) error {
|
||||
if len(c.Args()) != 0 {
|
||||
fmt.Fprintln(os.Stderr, "No arguments accepted")
|
||||
os.Exit(1)
|
||||
}
|
||||
r := mustNewAuthRoleAPI(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
roles, err := r.ListRoles(ctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, role := range roles {
|
||||
fmt.Printf("%s\n", role)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionRoleAdd(c *cli.Context) error {
|
||||
api, role := mustRoleAPIAndName(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
defer cancel()
|
||||
currentRole, err := api.GetRole(ctx, role)
|
||||
if currentRole != nil {
|
||||
fmt.Fprintf(os.Stderr, "Role %s already exists\n", role)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = api.AddRole(ctx, role)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Role %s created\n", role)
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionRoleRemove(c *cli.Context) error {
|
||||
api, role := mustRoleAPIAndName(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
err := api.RemoveRole(ctx, role)
|
||||
cancel()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Role %s removed\n", role)
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionRoleGrant(c *cli.Context) error {
|
||||
roleGrantRevoke(c, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionRoleRevoke(c *cli.Context) error {
|
||||
roleGrantRevoke(c, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func roleGrantRevoke(c *cli.Context, grant bool) {
|
||||
path := c.String("path")
|
||||
if path == "" {
|
||||
fmt.Fprintln(os.Stderr, "No path specified; please use `--path`")
|
||||
os.Exit(1)
|
||||
}
|
||||
if pathutil.CanonicalURLPath(path) != path {
|
||||
fmt.Fprintf(os.Stderr, "Not canonical path; please use `--path=%s`\n", pathutil.CanonicalURLPath(path))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
read := c.Bool("read")
|
||||
write := c.Bool("write")
|
||||
rw := c.Bool("readwrite")
|
||||
permcount := 0
|
||||
for _, v := range []bool{read, write, rw} {
|
||||
if v {
|
||||
permcount++
|
||||
}
|
||||
}
|
||||
if permcount != 1 {
|
||||
fmt.Fprintln(os.Stderr, "Please specify exactly one of --read, --write or --readwrite")
|
||||
os.Exit(1)
|
||||
}
|
||||
var permType client.PermissionType
|
||||
switch {
|
||||
case read:
|
||||
permType = client.ReadPermission
|
||||
case write:
|
||||
permType = client.WritePermission
|
||||
case rw:
|
||||
permType = client.ReadWritePermission
|
||||
}
|
||||
|
||||
api, role := mustRoleAPIAndName(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
defer cancel()
|
||||
currentRole, err := api.GetRole(ctx, role)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
var newRole *client.Role
|
||||
if grant {
|
||||
newRole, err = api.GrantRoleKV(ctx, role, []string{path}, permType)
|
||||
} else {
|
||||
newRole, err = api.RevokeRoleKV(ctx, role, []string{path}, permType)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if reflect.DeepEqual(newRole, currentRole) {
|
||||
if grant {
|
||||
fmt.Printf("Role unchanged; already granted")
|
||||
} else {
|
||||
fmt.Printf("Role unchanged; already revoked")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Role %s updated\n", role)
|
||||
}
|
||||
|
||||
func actionRoleGet(c *cli.Context) error {
|
||||
api, rolename := mustRoleAPIAndName(c)
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
role, err := api.GetRole(ctx, rolename)
|
||||
cancel()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Role: %s\n", role.Role)
|
||||
fmt.Printf("KV Read:\n")
|
||||
for _, v := range role.Permissions.KV.Read {
|
||||
fmt.Printf("\t%s\n", v)
|
||||
}
|
||||
fmt.Printf("KV Write:\n")
|
||||
for _, v := range role.Permissions.KV.Write {
|
||||
fmt.Printf("\t%s\n", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustRoleAPIAndName(c *cli.Context) (client.AuthRoleAPI, string) {
|
||||
args := c.Args()
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintln(os.Stderr, "Please provide a role name")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
api := mustNewAuthRoleAPI(c)
|
||||
return api, name
|
||||
}
|
||||
73
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/set_command.go
generated
vendored
Normal file
73
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/set_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewSetCommand returns the CLI command for "set".
|
||||
func NewSetCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "set",
|
||||
Usage: "set the value of a key",
|
||||
ArgsUsage: "<key> <value>",
|
||||
Description: `Set sets the value of a key.
|
||||
|
||||
When <value> begins with '-', <value> is interpreted as a flag.
|
||||
Insert '--' for workaround:
|
||||
|
||||
$ set -- <key> <value>`,
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live in seconds"},
|
||||
cli.StringFlag{Name: "swap-with-value", Value: "", Usage: "previous value"},
|
||||
cli.IntFlag{Name: "swap-with-index", Value: 0, Usage: "previous index"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
setCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// setCommandFunc executes the "set" command.
|
||||
func setCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
if len(c.Args()) == 0 {
|
||||
handleError(c, ExitBadArgs, errors.New("key required"))
|
||||
}
|
||||
key := c.Args()[0]
|
||||
value, err := argOrStdin(c.Args(), os.Stdin, 1)
|
||||
if err != nil {
|
||||
handleError(c, ExitBadArgs, errors.New("value required"))
|
||||
}
|
||||
|
||||
ttl := c.Int("ttl")
|
||||
prevValue := c.String("swap-with-value")
|
||||
prevIndex := c.Int("swap-with-index")
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
resp, err := ki.Set(ctx, key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevIndex: uint64(prevIndex), PrevValue: prevValue})
|
||||
cancel()
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
}
|
||||
36
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/set_dir_command.go
generated
vendored
Normal file
36
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/set_dir_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewSetDirCommand returns the CLI command for "setDir".
|
||||
func NewSetDirCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "setdir",
|
||||
Usage: "create a new directory or update an existing directory TTL",
|
||||
ArgsUsage: "<key>",
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live in seconds"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
mkdirCommandFunc(c, mustNewKeyAPI(c), client.PrevIgnore)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
63
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/update_command.go
generated
vendored
Normal file
63
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/update_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewUpdateCommand returns the CLI command for "update".
|
||||
func NewUpdateCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "update",
|
||||
Usage: "update an existing key with a given value",
|
||||
ArgsUsage: "<key> <value>",
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live in seconds"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
updateCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// updateCommandFunc executes the "update" command.
|
||||
func updateCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
if len(c.Args()) == 0 {
|
||||
handleError(c, ExitBadArgs, errors.New("key required"))
|
||||
}
|
||||
key := c.Args()[0]
|
||||
value, err := argOrStdin(c.Args(), os.Stdin, 1)
|
||||
if err != nil {
|
||||
handleError(c, ExitBadArgs, errors.New("value required"))
|
||||
}
|
||||
|
||||
ttl := c.Int("ttl")
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
resp, err := ki.Set(ctx, key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevExist: client.PrevExist})
|
||||
cancel()
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
}
|
||||
57
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/update_dir_command.go
generated
vendored
Normal file
57
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/update_dir_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewUpdateDirCommand returns the CLI command for "updatedir".
|
||||
func NewUpdateDirCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "updatedir",
|
||||
Usage: "update an existing directory",
|
||||
ArgsUsage: "<key> <value>",
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live in seconds"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
updatedirCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// updatedirCommandFunc executes the "updatedir" command.
|
||||
func updatedirCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
if len(c.Args()) == 0 {
|
||||
handleError(c, ExitBadArgs, errors.New("key required"))
|
||||
}
|
||||
key := c.Args()[0]
|
||||
ttl := c.Int("ttl")
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
resp, err := ki.Set(ctx, key, "", &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: client.PrevExist})
|
||||
cancel()
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
if c.GlobalString("output") != "simple" {
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
}
|
||||
}
|
||||
225
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/user_commands.go
generated
vendored
Normal file
225
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/user_commands.go
generated
vendored
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func NewUserCommands() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "user",
|
||||
Usage: "user add, grant and revoke subcommands",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "add",
|
||||
Usage: "add a new user for the etcd cluster",
|
||||
ArgsUsage: "<user>",
|
||||
Action: actionUserAdd,
|
||||
},
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "get details for a user",
|
||||
ArgsUsage: "<user>",
|
||||
Action: actionUserGet,
|
||||
},
|
||||
{
|
||||
Name: "list",
|
||||
Usage: "list all current users",
|
||||
ArgsUsage: "<user>",
|
||||
Action: actionUserList,
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Usage: "remove a user for the etcd cluster",
|
||||
ArgsUsage: "<user>",
|
||||
Action: actionUserRemove,
|
||||
},
|
||||
{
|
||||
Name: "grant",
|
||||
Usage: "grant roles to an etcd user",
|
||||
ArgsUsage: "<user>",
|
||||
Flags: []cli.Flag{cli.StringSliceFlag{Name: "roles", Value: new(cli.StringSlice), Usage: "List of roles to grant or revoke"}},
|
||||
Action: actionUserGrant,
|
||||
},
|
||||
{
|
||||
Name: "revoke",
|
||||
Usage: "revoke roles for an etcd user",
|
||||
ArgsUsage: "<user>",
|
||||
Flags: []cli.Flag{cli.StringSliceFlag{Name: "roles", Value: new(cli.StringSlice), Usage: "List of roles to grant or revoke"}},
|
||||
Action: actionUserRevoke,
|
||||
},
|
||||
{
|
||||
Name: "passwd",
|
||||
Usage: "change password for a user",
|
||||
ArgsUsage: "<user>",
|
||||
Action: actionUserPasswd,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func mustNewAuthUserAPI(c *cli.Context) client.AuthUserAPI {
|
||||
hc := mustNewClient(c)
|
||||
|
||||
if c.GlobalBool("debug") {
|
||||
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
|
||||
}
|
||||
|
||||
return client.NewAuthUserAPI(hc)
|
||||
}
|
||||
|
||||
func actionUserList(c *cli.Context) error {
|
||||
if len(c.Args()) != 0 {
|
||||
fmt.Fprintln(os.Stderr, "No arguments accepted")
|
||||
os.Exit(1)
|
||||
}
|
||||
u := mustNewAuthUserAPI(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
users, err := u.ListUsers(ctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
fmt.Printf("%s\n", user)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionUserAdd(c *cli.Context) error {
|
||||
api, userarg := mustUserAPIAndName(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
defer cancel()
|
||||
user, _, _ := getUsernamePassword("", userarg+":")
|
||||
|
||||
_, pass, err := getUsernamePassword("New password: ", userarg)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error reading password:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = api.AddUser(ctx, user, pass)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("User %s created\n", user)
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionUserRemove(c *cli.Context) error {
|
||||
api, user := mustUserAPIAndName(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
err := api.RemoveUser(ctx, user)
|
||||
cancel()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("User %s removed\n", user)
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionUserPasswd(c *cli.Context) error {
|
||||
api, user := mustUserAPIAndName(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
defer cancel()
|
||||
pass, err := speakeasy.Ask("New password: ")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error reading password:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = api.ChangePassword(ctx, user, pass)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Password updated\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionUserGrant(c *cli.Context) error {
|
||||
userGrantRevoke(c, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionUserRevoke(c *cli.Context) error {
|
||||
userGrantRevoke(c, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func userGrantRevoke(c *cli.Context, grant bool) {
|
||||
roles := c.StringSlice("roles")
|
||||
if len(roles) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "No roles specified; please use `--roles`")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
defer cancel()
|
||||
|
||||
api, user := mustUserAPIAndName(c)
|
||||
var err error
|
||||
if grant {
|
||||
_, err = api.GrantUser(ctx, user, roles)
|
||||
} else {
|
||||
_, err = api.RevokeUser(ctx, user, roles)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("User %s updated\n", user)
|
||||
}
|
||||
|
||||
func actionUserGet(c *cli.Context) error {
|
||||
api, username := mustUserAPIAndName(c)
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
user, err := api.GetUser(ctx, username)
|
||||
cancel()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("User: %s\n", user.User)
|
||||
fmt.Printf("Roles: %s\n", strings.Join(user.Roles, " "))
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustUserAPIAndName(c *cli.Context) (client.AuthUserAPI, string) {
|
||||
args := c.Args()
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintln(os.Stderr, "Please provide a username")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
api := mustNewAuthUserAPI(c)
|
||||
username := args[0]
|
||||
return api, username
|
||||
}
|
||||
336
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/util.go
generated
vendored
Normal file
336
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoAvailSrc = errors.New("no available argument and stdin")
|
||||
|
||||
// the maximum amount of time a dial will wait for a connection to setup.
|
||||
// 30s is long enough for most of the network conditions.
|
||||
defaultDialTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
func argOrStdin(args []string, stdin io.Reader, i int) (string, error) {
|
||||
if i < len(args) {
|
||||
return args[i], nil
|
||||
}
|
||||
bytes, err := ioutil.ReadAll(stdin)
|
||||
if string(bytes) == "" || err != nil {
|
||||
return "", ErrNoAvailSrc
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func getPeersFlagValue(c *cli.Context) []string {
|
||||
peerstr := c.GlobalString("endpoints")
|
||||
|
||||
if peerstr == "" {
|
||||
peerstr = os.Getenv("ETCDCTL_ENDPOINTS")
|
||||
}
|
||||
|
||||
if peerstr == "" {
|
||||
peerstr = c.GlobalString("endpoint")
|
||||
}
|
||||
|
||||
if peerstr == "" {
|
||||
peerstr = os.Getenv("ETCDCTL_ENDPOINT")
|
||||
}
|
||||
|
||||
if peerstr == "" {
|
||||
peerstr = c.GlobalString("peers")
|
||||
}
|
||||
|
||||
if peerstr == "" {
|
||||
peerstr = os.Getenv("ETCDCTL_PEERS")
|
||||
}
|
||||
|
||||
// If we still don't have peers, use a default
|
||||
if peerstr == "" {
|
||||
peerstr = "http://127.0.0.1:2379,http://127.0.0.1:4001"
|
||||
}
|
||||
|
||||
return strings.Split(peerstr, ",")
|
||||
}
|
||||
|
||||
func getDomainDiscoveryFlagValue(c *cli.Context) ([]string, error) {
|
||||
domainstr, insecure := getDiscoveryDomain(c)
|
||||
|
||||
// If we still don't have domain discovery, return nothing
|
||||
if domainstr == "" {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
discoverer := client.NewSRVDiscover()
|
||||
eps, err := discoverer.Discover(domainstr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if insecure {
|
||||
return eps, err
|
||||
}
|
||||
// strip insecure connections
|
||||
ret := []string{}
|
||||
for _, ep := range eps {
|
||||
if strings.HasPrefix("http://", ep) {
|
||||
fmt.Fprintf(os.Stderr, "ignoring discovered insecure endpoint %q\n", ep)
|
||||
continue
|
||||
}
|
||||
ret = append(ret, ep)
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func getDiscoveryDomain(c *cli.Context) (domainstr string, insecure bool) {
|
||||
domainstr = c.GlobalString("discovery-srv")
|
||||
// Use an environment variable if nothing was supplied on the
|
||||
// command line
|
||||
if domainstr == "" {
|
||||
domainstr = os.Getenv("ETCDCTL_DISCOVERY_SRV")
|
||||
}
|
||||
insecure = c.GlobalBool("insecure-discovery") || (os.Getenv("ETCDCTL_INSECURE_DISCOVERY") != "")
|
||||
return domainstr, insecure
|
||||
}
|
||||
|
||||
func getEndpoints(c *cli.Context) ([]string, error) {
|
||||
eps, err := getDomainDiscoveryFlagValue(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If domain discovery returns no endpoints, check peer flag
|
||||
if len(eps) == 0 {
|
||||
eps = getPeersFlagValue(c)
|
||||
}
|
||||
|
||||
for i, ep := range eps {
|
||||
u, err := url.Parse(ep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.Scheme == "" {
|
||||
u.Scheme = "http"
|
||||
}
|
||||
|
||||
eps[i] = u.String()
|
||||
}
|
||||
|
||||
return eps, nil
|
||||
}
|
||||
|
||||
func getTransport(c *cli.Context) (*http.Transport, error) {
|
||||
cafile := c.GlobalString("ca-file")
|
||||
certfile := c.GlobalString("cert-file")
|
||||
keyfile := c.GlobalString("key-file")
|
||||
|
||||
// Use an environment variable if nothing was supplied on the
|
||||
// command line
|
||||
if cafile == "" {
|
||||
cafile = os.Getenv("ETCDCTL_CA_FILE")
|
||||
}
|
||||
if certfile == "" {
|
||||
certfile = os.Getenv("ETCDCTL_CERT_FILE")
|
||||
}
|
||||
if keyfile == "" {
|
||||
keyfile = os.Getenv("ETCDCTL_KEY_FILE")
|
||||
}
|
||||
|
||||
discoveryDomain, insecure := getDiscoveryDomain(c)
|
||||
if insecure {
|
||||
discoveryDomain = ""
|
||||
}
|
||||
tls := transport.TLSInfo{
|
||||
CAFile: cafile,
|
||||
CertFile: certfile,
|
||||
KeyFile: keyfile,
|
||||
ServerName: discoveryDomain,
|
||||
}
|
||||
|
||||
dialTimeout := defaultDialTimeout
|
||||
totalTimeout := c.GlobalDuration("total-timeout")
|
||||
if totalTimeout != 0 && totalTimeout < dialTimeout {
|
||||
dialTimeout = totalTimeout
|
||||
}
|
||||
return transport.NewTransport(tls, dialTimeout)
|
||||
}
|
||||
|
||||
func getUsernamePasswordFromFlag(usernameFlag string) (username string, password string, err error) {
|
||||
return getUsernamePassword("Password: ", usernameFlag)
|
||||
}
|
||||
|
||||
func getUsernamePassword(prompt, usernameFlag string) (username string, password string, err error) {
|
||||
colon := strings.Index(usernameFlag, ":")
|
||||
if colon == -1 {
|
||||
username = usernameFlag
|
||||
// Prompt for the password.
|
||||
password, err = speakeasy.Ask(prompt)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
} else {
|
||||
username = usernameFlag[:colon]
|
||||
password = usernameFlag[colon+1:]
|
||||
}
|
||||
return username, password, nil
|
||||
}
|
||||
|
||||
func mustNewKeyAPI(c *cli.Context) client.KeysAPI {
|
||||
return client.NewKeysAPI(mustNewClient(c))
|
||||
}
|
||||
|
||||
func mustNewMembersAPI(c *cli.Context) client.MembersAPI {
|
||||
return client.NewMembersAPI(mustNewClient(c))
|
||||
}
|
||||
|
||||
func mustNewClient(c *cli.Context) client.Client {
|
||||
hc, err := newClient(c)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
debug := c.GlobalBool("debug")
|
||||
if debug {
|
||||
client.EnablecURLDebug()
|
||||
}
|
||||
|
||||
if !c.GlobalBool("no-sync") {
|
||||
if debug {
|
||||
fmt.Fprintf(os.Stderr, "start to sync cluster using endpoints(%s)\n", strings.Join(hc.Endpoints(), ","))
|
||||
}
|
||||
ctx, cancel := contextWithTotalTimeout(c)
|
||||
err := hc.Sync(ctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
if err == client.ErrNoEndpoints {
|
||||
fmt.Fprintf(os.Stderr, "etcd cluster has no published client endpoints.\n")
|
||||
fmt.Fprintf(os.Stderr, "Try '--no-sync' if you want to access non-published client endpoints(%s).\n", strings.Join(hc.Endpoints(), ","))
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
if isConnectionError(err) {
|
||||
handleError(c, ExitBadConnection, err)
|
||||
}
|
||||
}
|
||||
if debug {
|
||||
fmt.Fprintf(os.Stderr, "got endpoints(%s) after sync\n", strings.Join(hc.Endpoints(), ","))
|
||||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
|
||||
}
|
||||
|
||||
return hc
|
||||
}
|
||||
|
||||
func isConnectionError(err error) bool {
|
||||
switch t := err.(type) {
|
||||
case *client.ClusterError:
|
||||
for _, cerr := range t.Errors {
|
||||
if !isConnectionError(cerr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case *net.OpError:
|
||||
if t.Op == "dial" || t.Op == "read" {
|
||||
return true
|
||||
}
|
||||
return isConnectionError(t.Err)
|
||||
case net.Error:
|
||||
if t.Timeout() {
|
||||
return true
|
||||
}
|
||||
case syscall.Errno:
|
||||
if t == syscall.ECONNREFUSED {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func mustNewClientNoSync(c *cli.Context) client.Client {
|
||||
hc, err := newClient(c)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if c.GlobalBool("debug") {
|
||||
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
|
||||
client.EnablecURLDebug()
|
||||
}
|
||||
|
||||
return hc
|
||||
}
|
||||
|
||||
func newClient(c *cli.Context) (client.Client, error) {
|
||||
eps, err := getEndpoints(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tr, err := getTransport(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := client.Config{
|
||||
Transport: tr,
|
||||
Endpoints: eps,
|
||||
HeaderTimeoutPerRequest: c.GlobalDuration("timeout"),
|
||||
}
|
||||
|
||||
uFlag := c.GlobalString("username")
|
||||
|
||||
if uFlag == "" {
|
||||
uFlag = os.Getenv("ETCDCTL_USERNAME")
|
||||
}
|
||||
|
||||
if uFlag != "" {
|
||||
username, password, err := getUsernamePasswordFromFlag(uFlag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.Username = username
|
||||
cfg.Password = password
|
||||
}
|
||||
|
||||
return client.New(cfg)
|
||||
}
|
||||
|
||||
func contextWithTotalTimeout(c *cli.Context) (context.Context, context.CancelFunc) {
|
||||
return context.WithTimeout(context.Background(), c.GlobalDuration("total-timeout"))
|
||||
}
|
||||
70
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/util_test.go
generated
vendored
Normal file
70
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/util_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestArgOrStdin(t *testing.T) {
|
||||
tests := []struct {
|
||||
args []string
|
||||
stdin string
|
||||
i int
|
||||
w string
|
||||
we error
|
||||
}{
|
||||
{
|
||||
args: []string{
|
||||
"a",
|
||||
},
|
||||
stdin: "b",
|
||||
i: 0,
|
||||
w: "a",
|
||||
we: nil,
|
||||
},
|
||||
{
|
||||
args: []string{
|
||||
"a",
|
||||
},
|
||||
stdin: "b",
|
||||
i: 1,
|
||||
w: "b",
|
||||
we: nil,
|
||||
},
|
||||
{
|
||||
args: []string{
|
||||
"a",
|
||||
},
|
||||
stdin: "",
|
||||
i: 1,
|
||||
w: "",
|
||||
we: ErrNoAvailSrc,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var b bytes.Buffer
|
||||
b.Write([]byte(tt.stdin))
|
||||
g, ge := argOrStdin(tt.args, &b, tt.i)
|
||||
if g != tt.w {
|
||||
t.Errorf("#%d: expect %v, not %v", i, tt.w, g)
|
||||
}
|
||||
if ge != tt.we {
|
||||
t.Errorf("#%d: expect %v, not %v", i, tt.we, ge)
|
||||
}
|
||||
}
|
||||
}
|
||||
85
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/watch_command.go
generated
vendored
Normal file
85
vendor/github.com/coreos/etcd/etcdctl/ctlv2/command/watch_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// NewWatchCommand returns the CLI command for "watch".
|
||||
func NewWatchCommand() cli.Command {
|
||||
return cli.Command{
|
||||
Name: "watch",
|
||||
Usage: "watch a key for changes",
|
||||
ArgsUsage: "<key>",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "forever, f", Usage: "forever watch a key until CTRL+C"},
|
||||
cli.IntFlag{Name: "after-index", Value: 0, Usage: "watch after the given index"},
|
||||
cli.BoolFlag{Name: "recursive, r", Usage: "returns all values for key and child keys"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
watchCommandFunc(c, mustNewKeyAPI(c))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// watchCommandFunc executes the "watch" command.
|
||||
func watchCommandFunc(c *cli.Context, ki client.KeysAPI) {
|
||||
if len(c.Args()) == 0 {
|
||||
handleError(c, ExitBadArgs, errors.New("key required"))
|
||||
}
|
||||
key := c.Args()[0]
|
||||
recursive := c.Bool("recursive")
|
||||
forever := c.Bool("forever")
|
||||
index := c.Int("after-index")
|
||||
|
||||
stop := false
|
||||
w := ki.Watcher(key, &client.WatcherOptions{AfterIndex: uint64(index), Recursive: recursive})
|
||||
|
||||
sigch := make(chan os.Signal, 1)
|
||||
signal.Notify(sigch, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
<-sigch
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
for !stop {
|
||||
resp, err := w.Next(context.TODO())
|
||||
if err != nil {
|
||||
handleError(c, ExitServerError, err)
|
||||
}
|
||||
if resp.Node.Dir {
|
||||
continue
|
||||
}
|
||||
if recursive {
|
||||
fmt.Printf("[%s] %s\n", resp.Action, resp.Node.Key)
|
||||
}
|
||||
|
||||
printResponseKey(resp, c.GlobalString("output"))
|
||||
|
||||
if !forever {
|
||||
stop = true
|
||||
}
|
||||
}
|
||||
}
|
||||
79
vendor/github.com/coreos/etcd/etcdctl/ctlv2/ctl.go
generated
vendored
Normal file
79
vendor/github.com/coreos/etcd/etcdctl/ctlv2/ctl.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package ctlv2 contains the main entry point for the etcdctl for v2 API.
|
||||
package ctlv2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/etcdctl/ctlv2/command"
|
||||
"github.com/coreos/etcd/version"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func Start() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "etcdctl"
|
||||
app.Version = version.Version
|
||||
cli.VersionPrinter = func(c *cli.Context) {
|
||||
fmt.Fprintf(c.App.Writer, "etcdctl version: %v\n", c.App.Version)
|
||||
fmt.Fprintln(c.App.Writer, "API version: 2")
|
||||
}
|
||||
app.Usage = "A simple command line client for etcd."
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{Name: "debug", Usage: "output cURL commands which can be used to reproduce the request"},
|
||||
cli.BoolFlag{Name: "no-sync", Usage: "don't synchronize cluster information before sending request"},
|
||||
cli.StringFlag{Name: "output, o", Value: "simple", Usage: "output response in the given format (`simple`, `extended` or `json`)"},
|
||||
cli.StringFlag{Name: "discovery-srv, D", Usage: "domain name to query for SRV records describing cluster endpoints"},
|
||||
cli.BoolFlag{Name: "insecure-discovery", Usage: "accept insecure SRV records describing cluster endpoints"},
|
||||
cli.StringFlag{Name: "peers, C", Value: "", Usage: "DEPRECATED - \"--endpoints\" should be used instead"},
|
||||
cli.StringFlag{Name: "endpoint", Value: "", Usage: "DEPRECATED - \"--endpoints\" should be used instead"},
|
||||
cli.StringFlag{Name: "endpoints", Value: "", Usage: "a comma-delimited list of machine addresses in the cluster (default: \"http://127.0.0.1:2379,http://127.0.0.1:4001\")"},
|
||||
cli.StringFlag{Name: "cert-file", Value: "", Usage: "identify HTTPS client using this SSL certificate file"},
|
||||
cli.StringFlag{Name: "key-file", Value: "", Usage: "identify HTTPS client using this SSL key file"},
|
||||
cli.StringFlag{Name: "ca-file", Value: "", Usage: "verify certificates of HTTPS-enabled servers using this CA bundle"},
|
||||
cli.StringFlag{Name: "username, u", Value: "", Usage: "provide username[:password] and prompt if password is not supplied."},
|
||||
cli.DurationFlag{Name: "timeout", Value: 2 * time.Second, Usage: "connection timeout per request"},
|
||||
cli.DurationFlag{Name: "total-timeout", Value: 5 * time.Second, Usage: "timeout for the command execution (except watch)"},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
command.NewBackupCommand(),
|
||||
command.NewClusterHealthCommand(),
|
||||
command.NewMakeCommand(),
|
||||
command.NewMakeDirCommand(),
|
||||
command.NewRemoveCommand(),
|
||||
command.NewRemoveDirCommand(),
|
||||
command.NewGetCommand(),
|
||||
command.NewLsCommand(),
|
||||
command.NewSetCommand(),
|
||||
command.NewSetDirCommand(),
|
||||
command.NewUpdateCommand(),
|
||||
command.NewUpdateDirCommand(),
|
||||
command.NewWatchCommand(),
|
||||
command.NewExecWatchCommand(),
|
||||
command.NewMemberCommand(),
|
||||
command.NewUserCommands(),
|
||||
command.NewRoleCommands(),
|
||||
command.NewAuthCommands(),
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
81
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/alarm_command.go
generated
vendored
Normal file
81
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/alarm_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewAlarmCommand returns the cobra command for "alarm".
|
||||
func NewAlarmCommand() *cobra.Command {
|
||||
ac := &cobra.Command{
|
||||
Use: "alarm <subcommand>",
|
||||
Short: "Alarm related commands",
|
||||
}
|
||||
|
||||
ac.AddCommand(NewAlarmDisarmCommand())
|
||||
ac.AddCommand(NewAlarmListCommand())
|
||||
|
||||
return ac
|
||||
}
|
||||
|
||||
func NewAlarmDisarmCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "disarm",
|
||||
Short: "Disarms all alarms",
|
||||
Run: alarmDisarmCommandFunc,
|
||||
}
|
||||
return &cmd
|
||||
}
|
||||
|
||||
// alarmDisarmCommandFunc executes the "alarm disarm" command.
|
||||
func alarmDisarmCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("alarm disarm command accepts no arguments"))
|
||||
}
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, err := mustClientFromCmd(cmd).AlarmDisarm(ctx, &v3.AlarmMember{})
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
display.Alarm(*resp)
|
||||
}
|
||||
|
||||
func NewAlarmListCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "list",
|
||||
Short: "Lists all alarms",
|
||||
Run: alarmListCommandFunc,
|
||||
}
|
||||
return &cmd
|
||||
}
|
||||
|
||||
// alarmListCommandFunc executes the "alarm list" command.
|
||||
func alarmListCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("alarm list command accepts no arguments"))
|
||||
}
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, err := mustClientFromCmd(cmd).AlarmList(ctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
display.Alarm(*resp)
|
||||
}
|
||||
97
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/auth_command.go
generated
vendored
Normal file
97
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/auth_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewAuthCommand returns the cobra command for "auth".
|
||||
func NewAuthCommand() *cobra.Command {
|
||||
ac := &cobra.Command{
|
||||
Use: "auth <enable or disable>",
|
||||
Short: "Enable or disable authentication",
|
||||
}
|
||||
|
||||
ac.AddCommand(newAuthEnableCommand())
|
||||
ac.AddCommand(newAuthDisableCommand())
|
||||
|
||||
return ac
|
||||
}
|
||||
|
||||
func newAuthEnableCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "enable",
|
||||
Short: "Enables authentication",
|
||||
Run: authEnableCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// authEnableCommandFunc executes the "auth enable" command.
|
||||
func authEnableCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("auth enable command does not accept any arguments."))
|
||||
}
|
||||
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
cli := mustClientFromCmd(cmd)
|
||||
var err error
|
||||
for err == nil {
|
||||
if _, err = cli.AuthEnable(ctx); err == nil {
|
||||
break
|
||||
}
|
||||
if err == rpctypes.ErrRootRoleNotExist {
|
||||
if _, err = cli.RoleAdd(ctx, "root"); err != nil {
|
||||
break
|
||||
}
|
||||
if _, err = cli.UserGrantRole(ctx, "root", "root"); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
fmt.Println("Authentication Enabled")
|
||||
}
|
||||
|
||||
func newAuthDisableCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "disable",
|
||||
Short: "Disables authentication",
|
||||
Run: authDisableCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// authDisableCommandFunc executes the "auth disable" command.
|
||||
func authDisableCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("auth disable command does not accept any arguments."))
|
||||
}
|
||||
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
_, err := mustClientFromCmd(cmd).Auth.AuthDisable(ctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
fmt.Println("Authentication Disabled")
|
||||
}
|
||||
63
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/compaction_command.go
generated
vendored
Normal file
63
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/compaction_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var compactPhysical bool
|
||||
|
||||
// NewCompactionCommand returns the cobra command for "compaction".
|
||||
func NewCompactionCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "compaction [options] <revision>",
|
||||
Short: "Compacts the event history in etcd",
|
||||
Run: compactionCommandFunc,
|
||||
}
|
||||
cmd.Flags().BoolVar(&compactPhysical, "physical", false, "'true' to wait for compaction to physically remove all old revisions")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// compactionCommandFunc executes the "compaction" command.
|
||||
func compactionCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("compaction command needs 1 argument."))
|
||||
}
|
||||
|
||||
rev, err := strconv.ParseInt(args[0], 10, 64)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
var opts []clientv3.CompactOption
|
||||
if compactPhysical {
|
||||
opts = append(opts, clientv3.WithCompactPhysical())
|
||||
}
|
||||
|
||||
c := mustClientFromCmd(cmd)
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
_, cerr := c.Compact(ctx, rev, opts...)
|
||||
cancel()
|
||||
if cerr != nil {
|
||||
ExitWithError(ExitError, cerr)
|
||||
return
|
||||
}
|
||||
fmt.Println("compacted revision", rev)
|
||||
}
|
||||
51
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/defrag_command.go
generated
vendored
Normal file
51
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/defrag_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewDefragCommand returns the cobra command for "Defrag".
|
||||
func NewDefragCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "defrag",
|
||||
Short: "Defragments the storage of the etcd members with given endpoints",
|
||||
Run: defragCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func defragCommandFunc(cmd *cobra.Command, args []string) {
|
||||
failures := 0
|
||||
c := mustClientFromCmd(cmd)
|
||||
for _, ep := range c.Endpoints() {
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
_, err := c.Defragment(ctx, ep)
|
||||
cancel()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to defragment etcd member[%s] (%v)\n", ep, err)
|
||||
failures++
|
||||
} else {
|
||||
fmt.Printf("Finished defragmenting etcd member[%s]\n", ep)
|
||||
}
|
||||
}
|
||||
|
||||
if failures != 0 {
|
||||
os.Exit(ExitError)
|
||||
}
|
||||
}
|
||||
94
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/del_command.go
generated
vendored
Normal file
94
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/del_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
delPrefix bool
|
||||
delPrevKV bool
|
||||
delFromKey bool
|
||||
)
|
||||
|
||||
// NewDelCommand returns the cobra command for "del".
|
||||
func NewDelCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "del [options] <key> [range_end]",
|
||||
Short: "Removes the specified key or range of keys [key, range_end)",
|
||||
Run: delCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&delPrefix, "prefix", false, "delete keys with matching prefix")
|
||||
cmd.Flags().BoolVar(&delPrevKV, "prev-kv", false, "return deleted key-value pairs")
|
||||
cmd.Flags().BoolVar(&delFromKey, "from-key", false, "delete keys that are greater than or equal to the given key using byte compare")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// delCommandFunc executes the "del" command.
|
||||
func delCommandFunc(cmd *cobra.Command, args []string) {
|
||||
key, opts := getDelOp(cmd, args)
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, err := mustClientFromCmd(cmd).Delete(ctx, key, opts...)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
display.Del(*resp)
|
||||
}
|
||||
|
||||
func getDelOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) {
|
||||
if len(args) == 0 || len(args) > 2 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("del command needs one argument as key and an optional argument as range_end."))
|
||||
}
|
||||
|
||||
if delPrefix && delFromKey {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("`--prefix` and `--from-key` cannot be set at the same time, choose one."))
|
||||
}
|
||||
|
||||
opts := []clientv3.OpOption{}
|
||||
key := args[0]
|
||||
if len(args) > 1 {
|
||||
if delPrefix || delFromKey {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("too many arguments, only accept one argument when `--prefix` or `--from-key` is set."))
|
||||
}
|
||||
opts = append(opts, clientv3.WithRange(args[1]))
|
||||
}
|
||||
|
||||
if delPrefix {
|
||||
if len(key) == 0 {
|
||||
key = "\x00"
|
||||
opts = append(opts, clientv3.WithFromKey())
|
||||
} else {
|
||||
opts = append(opts, clientv3.WithPrefix())
|
||||
}
|
||||
}
|
||||
if delPrevKV {
|
||||
opts = append(opts, clientv3.WithPrevKV())
|
||||
}
|
||||
|
||||
if delFromKey {
|
||||
if len(key) == 0 {
|
||||
key = "\x00"
|
||||
}
|
||||
opts = append(opts, clientv3.WithFromKey())
|
||||
}
|
||||
|
||||
return key, opts
|
||||
}
|
||||
16
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/doc.go
generated
vendored
Normal file
16
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package command is a set of libraries for etcd v3 commands.
|
||||
package command
|
||||
135
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/elect_command.go
generated
vendored
Normal file
135
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/elect_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/clientv3/concurrency"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
electListen bool
|
||||
)
|
||||
|
||||
// NewElectCommand returns the cobra command for "elect".
|
||||
func NewElectCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "elect <election-name> [proposal]",
|
||||
Short: "Observes and participates in leader election",
|
||||
Run: electCommandFunc,
|
||||
}
|
||||
cmd.Flags().BoolVarP(&electListen, "listen", "l", false, "observation mode")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func electCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 && len(args) != 2 {
|
||||
ExitWithError(ExitBadArgs, errors.New("elect takes one election name argument and an optional proposal argument."))
|
||||
}
|
||||
c := mustClientFromCmd(cmd)
|
||||
|
||||
var err error
|
||||
if len(args) == 1 {
|
||||
if !electListen {
|
||||
ExitWithError(ExitBadArgs, errors.New("no proposal argument but -l not set"))
|
||||
}
|
||||
err = observe(c, args[0])
|
||||
} else {
|
||||
if electListen {
|
||||
ExitWithError(ExitBadArgs, errors.New("proposal given but -l is set"))
|
||||
}
|
||||
err = campaign(c, args[0], args[1])
|
||||
}
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
}
|
||||
|
||||
func observe(c *clientv3.Client, election string) error {
|
||||
s, err := concurrency.NewSession(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e := concurrency.NewElection(s, election)
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
|
||||
donec := make(chan struct{})
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Notify(sigc, os.Interrupt, os.Kill)
|
||||
go func() {
|
||||
<-sigc
|
||||
cancel()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for resp := range e.Observe(ctx) {
|
||||
display.Get(resp)
|
||||
}
|
||||
close(donec)
|
||||
}()
|
||||
|
||||
<-donec
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
return errors.New("elect: observer lost")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func campaign(c *clientv3.Client, election string, prop string) error {
|
||||
s, err := concurrency.NewSession(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e := concurrency.NewElection(s, election)
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
|
||||
donec := make(chan struct{})
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Notify(sigc, os.Interrupt, os.Kill)
|
||||
go func() {
|
||||
<-sigc
|
||||
cancel()
|
||||
close(donec)
|
||||
}()
|
||||
|
||||
if err = e.Campaign(ctx, prop); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// print key since elected
|
||||
resp, err := c.Get(ctx, e.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
display.Get(*resp)
|
||||
|
||||
select {
|
||||
case <-donec:
|
||||
case <-s.Done():
|
||||
return errors.New("elect: session expired")
|
||||
}
|
||||
|
||||
return e.Resign(context.TODO())
|
||||
}
|
||||
141
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/ep_command.go
generated
vendored
Normal file
141
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/ep_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"github.com/coreos/etcd/pkg/flags"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewEndpointCommand returns the cobra command for "endpoint".
|
||||
func NewEndpointCommand() *cobra.Command {
|
||||
ec := &cobra.Command{
|
||||
Use: "endpoint <subcommand>",
|
||||
Short: "Endpoint related commands",
|
||||
}
|
||||
|
||||
ec.AddCommand(newEpHealthCommand())
|
||||
ec.AddCommand(newEpStatusCommand())
|
||||
|
||||
return ec
|
||||
}
|
||||
|
||||
func newEpHealthCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "health",
|
||||
Short: "Checks the healthiness of endpoints specified in `--endpoints` flag",
|
||||
Run: epHealthCommandFunc,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newEpStatusCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Prints out the status of endpoints specified in `--endpoints` flag",
|
||||
Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint.
|
||||
The items in the lists are endpoint, ID, version, db size, is leader, raft term, raft index.
|
||||
`,
|
||||
Run: epStatusCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// epHealthCommandFunc executes the "endpoint-health" command.
|
||||
func epHealthCommandFunc(cmd *cobra.Command, args []string) {
|
||||
flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags())
|
||||
endpoints, err := cmd.Flags().GetStringSlice("endpoints")
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
sec := secureCfgFromCmd(cmd)
|
||||
dt := dialTimeoutFromCmd(cmd)
|
||||
auth := authCfgFromCmd(cmd)
|
||||
cfgs := []*v3.Config{}
|
||||
for _, ep := range endpoints {
|
||||
cfg, err := newClientCfg([]string{ep}, dt, sec, auth)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
cfgs = append(cfgs, cfg)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, cfg := range cfgs {
|
||||
wg.Add(1)
|
||||
go func(cfg *v3.Config) {
|
||||
defer wg.Done()
|
||||
ep := cfg.Endpoints[0]
|
||||
cli, err := v3.New(*cfg)
|
||||
if err != nil {
|
||||
fmt.Printf("%s is unhealthy: failed to connect: %v\n", ep, err)
|
||||
return
|
||||
}
|
||||
st := time.Now()
|
||||
// get a random key. As long as we can get the response without an error, the
|
||||
// endpoint is health.
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
_, err = cli.Get(ctx, "health")
|
||||
cancel()
|
||||
// permission denied is OK since proposal goes through consensus to get it
|
||||
if err == nil || err == rpctypes.ErrPermissionDenied {
|
||||
fmt.Printf("%s is healthy: successfully committed proposal: took = %v\n", ep, time.Since(st))
|
||||
} else {
|
||||
fmt.Printf("%s is unhealthy: failed to commit proposal: %v\n", ep, err)
|
||||
}
|
||||
}(cfg)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
type epStatus struct {
|
||||
Ep string `json:"Endpoint"`
|
||||
Resp *v3.StatusResponse `json:"Status"`
|
||||
}
|
||||
|
||||
func epStatusCommandFunc(cmd *cobra.Command, args []string) {
|
||||
c := mustClientFromCmd(cmd)
|
||||
|
||||
statusList := []epStatus{}
|
||||
var err error
|
||||
for _, ep := range c.Endpoints() {
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, serr := c.Status(ctx, ep)
|
||||
cancel()
|
||||
if serr != nil {
|
||||
err = serr
|
||||
fmt.Fprintf(os.Stderr, "Failed to get the status of endpoint %s (%v)\n", ep, serr)
|
||||
continue
|
||||
}
|
||||
statusList = append(statusList, epStatus{Ep: ep, Resp: resp})
|
||||
}
|
||||
|
||||
display.EndpointStatus(statusList)
|
||||
|
||||
if err != nil {
|
||||
os.Exit(ExitError)
|
||||
}
|
||||
}
|
||||
42
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/error.go
generated
vendored
Normal file
42
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/error.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
)
|
||||
|
||||
const (
|
||||
// http://tldp.org/LDP/abs/html/exitcodes.html
|
||||
ExitSuccess = iota
|
||||
ExitError
|
||||
ExitBadConnection
|
||||
ExitInvalidInput // for txn, watch command
|
||||
ExitBadFeature // provided a valid flag with an unsupported value
|
||||
ExitInterrupted
|
||||
ExitIO
|
||||
ExitBadArgs = 128
|
||||
)
|
||||
|
||||
func ExitWithError(code int, err error) {
|
||||
fmt.Fprintln(os.Stderr, "Error: ", err)
|
||||
if cerr, ok := err.(*client.ClusterError); ok {
|
||||
fmt.Fprintln(os.Stderr, cerr.Detail())
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
163
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/get_command.go
generated
vendored
Normal file
163
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/get_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
getConsistency string
|
||||
getLimit int64
|
||||
getSortOrder string
|
||||
getSortTarget string
|
||||
getPrefix bool
|
||||
getFromKey bool
|
||||
getRev int64
|
||||
getKeysOnly bool
|
||||
printValueOnly bool
|
||||
)
|
||||
|
||||
// NewGetCommand returns the cobra command for "get".
|
||||
func NewGetCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [options] <key> [range_end]",
|
||||
Short: "Gets the key or a range of keys",
|
||||
Run: getCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&getConsistency, "consistency", "l", "Linearizable(l) or Serializable(s)")
|
||||
cmd.Flags().StringVar(&getSortOrder, "order", "", "Order of results; ASCEND or DESCEND (ASCEND by default)")
|
||||
cmd.Flags().StringVar(&getSortTarget, "sort-by", "", "Sort target; CREATE, KEY, MODIFY, VALUE, or VERSION")
|
||||
cmd.Flags().Int64Var(&getLimit, "limit", 0, "Maximum number of results")
|
||||
cmd.Flags().BoolVar(&getPrefix, "prefix", false, "Get keys with matching prefix")
|
||||
cmd.Flags().BoolVar(&getFromKey, "from-key", false, "Get keys that are greater than or equal to the given key using byte compare")
|
||||
cmd.Flags().Int64Var(&getRev, "rev", 0, "Specify the kv revision")
|
||||
cmd.Flags().BoolVar(&getKeysOnly, "keys-only", false, "Get only the keys")
|
||||
cmd.Flags().BoolVar(&printValueOnly, "print-value-only", false, `Only write values when using the "simple" output format`)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// getCommandFunc executes the "get" command.
|
||||
func getCommandFunc(cmd *cobra.Command, args []string) {
|
||||
key, opts := getGetOp(cmd, args)
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, err := mustClientFromCmd(cmd).Get(ctx, key, opts...)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
if printValueOnly {
|
||||
dp, simple := (display).(*simplePrinter)
|
||||
if !simple {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("print-value-only is only for `--write-out=simple`."))
|
||||
}
|
||||
dp.valueOnly = true
|
||||
}
|
||||
display.Get(*resp)
|
||||
}
|
||||
|
||||
func getGetOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) {
|
||||
if len(args) == 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("range command needs arguments."))
|
||||
}
|
||||
|
||||
if getPrefix && getFromKey {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("`--prefix` and `--from-key` cannot be set at the same time, choose one."))
|
||||
}
|
||||
|
||||
opts := []clientv3.OpOption{}
|
||||
switch getConsistency {
|
||||
case "s":
|
||||
opts = append(opts, clientv3.WithSerializable())
|
||||
case "l":
|
||||
default:
|
||||
ExitWithError(ExitBadFeature, fmt.Errorf("unknown consistency flag %q", getConsistency))
|
||||
}
|
||||
|
||||
key := args[0]
|
||||
if len(args) > 1 {
|
||||
if getPrefix || getFromKey {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("too many arguments, only accept one argument when `--prefix` or `--from-key` is set."))
|
||||
}
|
||||
opts = append(opts, clientv3.WithRange(args[1]))
|
||||
}
|
||||
|
||||
opts = append(opts, clientv3.WithLimit(getLimit))
|
||||
if getRev > 0 {
|
||||
opts = append(opts, clientv3.WithRev(getRev))
|
||||
}
|
||||
|
||||
sortByOrder := clientv3.SortNone
|
||||
sortOrder := strings.ToUpper(getSortOrder)
|
||||
switch {
|
||||
case sortOrder == "ASCEND":
|
||||
sortByOrder = clientv3.SortAscend
|
||||
case sortOrder == "DESCEND":
|
||||
sortByOrder = clientv3.SortDescend
|
||||
case sortOrder == "":
|
||||
// nothing
|
||||
default:
|
||||
ExitWithError(ExitBadFeature, fmt.Errorf("bad sort order %v", getSortOrder))
|
||||
}
|
||||
|
||||
sortByTarget := clientv3.SortByKey
|
||||
sortTarget := strings.ToUpper(getSortTarget)
|
||||
switch {
|
||||
case sortTarget == "CREATE":
|
||||
sortByTarget = clientv3.SortByCreateRevision
|
||||
case sortTarget == "KEY":
|
||||
sortByTarget = clientv3.SortByKey
|
||||
case sortTarget == "MODIFY":
|
||||
sortByTarget = clientv3.SortByModRevision
|
||||
case sortTarget == "VALUE":
|
||||
sortByTarget = clientv3.SortByValue
|
||||
case sortTarget == "VERSION":
|
||||
sortByTarget = clientv3.SortByVersion
|
||||
case sortTarget == "":
|
||||
// nothing
|
||||
default:
|
||||
ExitWithError(ExitBadFeature, fmt.Errorf("bad sort target %v", getSortTarget))
|
||||
}
|
||||
|
||||
opts = append(opts, clientv3.WithSort(sortByTarget, sortByOrder))
|
||||
|
||||
if getPrefix {
|
||||
if len(key) == 0 {
|
||||
key = "\x00"
|
||||
opts = append(opts, clientv3.WithFromKey())
|
||||
} else {
|
||||
opts = append(opts, clientv3.WithPrefix())
|
||||
}
|
||||
}
|
||||
|
||||
if getFromKey {
|
||||
if len(key) == 0 {
|
||||
key = "\x00"
|
||||
}
|
||||
opts = append(opts, clientv3.WithFromKey())
|
||||
}
|
||||
|
||||
if getKeysOnly {
|
||||
opts = append(opts, clientv3.WithKeysOnly())
|
||||
}
|
||||
|
||||
return key, opts
|
||||
}
|
||||
258
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/global.go
generated
vendored
Normal file
258
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/global.go
generated
vendored
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/pkg/flags"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// GlobalFlags are flags that defined globally
|
||||
// and are inherited to all sub-commands.
|
||||
type GlobalFlags struct {
|
||||
Insecure bool
|
||||
InsecureSkipVerify bool
|
||||
Endpoints []string
|
||||
DialTimeout time.Duration
|
||||
CommandTimeOut time.Duration
|
||||
|
||||
TLS transport.TLSInfo
|
||||
|
||||
OutputFormat string
|
||||
IsHex bool
|
||||
|
||||
User string
|
||||
}
|
||||
|
||||
type secureCfg struct {
|
||||
cert string
|
||||
key string
|
||||
cacert string
|
||||
|
||||
insecureTransport bool
|
||||
insecureSkipVerify bool
|
||||
}
|
||||
|
||||
type authCfg struct {
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
var display printer = &simplePrinter{}
|
||||
|
||||
func initDisplayFromCmd(cmd *cobra.Command) {
|
||||
isHex, err := cmd.Flags().GetBool("hex")
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
outputType, err := cmd.Flags().GetString("write-out")
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
if display = NewPrinter(outputType, isHex); display == nil {
|
||||
ExitWithError(ExitBadFeature, errors.New("unsupported output format"))
|
||||
}
|
||||
}
|
||||
|
||||
func mustClientFromCmd(cmd *cobra.Command) *clientv3.Client {
|
||||
flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags())
|
||||
|
||||
endpoints, err := cmd.Flags().GetStringSlice("endpoints")
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
dialTimeout := dialTimeoutFromCmd(cmd)
|
||||
sec := secureCfgFromCmd(cmd)
|
||||
auth := authCfgFromCmd(cmd)
|
||||
|
||||
initDisplayFromCmd(cmd)
|
||||
|
||||
return mustClient(endpoints, dialTimeout, sec, auth)
|
||||
}
|
||||
|
||||
func mustClient(endpoints []string, dialTimeout time.Duration, scfg *secureCfg, acfg *authCfg) *clientv3.Client {
|
||||
cfg, err := newClientCfg(endpoints, dialTimeout, scfg, acfg)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
|
||||
client, err := clientv3.New(*cfg)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadConnection, err)
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func newClientCfg(endpoints []string, dialTimeout time.Duration, scfg *secureCfg, acfg *authCfg) (*clientv3.Config, error) {
|
||||
// set tls if any one tls option set
|
||||
var cfgtls *transport.TLSInfo
|
||||
tlsinfo := transport.TLSInfo{}
|
||||
if scfg.cert != "" {
|
||||
tlsinfo.CertFile = scfg.cert
|
||||
cfgtls = &tlsinfo
|
||||
}
|
||||
|
||||
if scfg.key != "" {
|
||||
tlsinfo.KeyFile = scfg.key
|
||||
cfgtls = &tlsinfo
|
||||
}
|
||||
|
||||
if scfg.cacert != "" {
|
||||
tlsinfo.CAFile = scfg.cacert
|
||||
cfgtls = &tlsinfo
|
||||
}
|
||||
|
||||
cfg := &clientv3.Config{
|
||||
Endpoints: endpoints,
|
||||
DialTimeout: dialTimeout,
|
||||
}
|
||||
if cfgtls != nil {
|
||||
clientTLS, err := cfgtls.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.TLS = clientTLS
|
||||
}
|
||||
// if key/cert is not given but user wants secure connection, we
|
||||
// should still setup an empty tls configuration for gRPC to setup
|
||||
// secure connection.
|
||||
if cfg.TLS == nil && !scfg.insecureTransport {
|
||||
cfg.TLS = &tls.Config{}
|
||||
}
|
||||
|
||||
// If the user wants to skip TLS verification then we should set
|
||||
// the InsecureSkipVerify flag in tls configuration.
|
||||
if scfg.insecureSkipVerify && cfg.TLS != nil {
|
||||
cfg.TLS.InsecureSkipVerify = true
|
||||
}
|
||||
|
||||
if acfg != nil {
|
||||
cfg.Username = acfg.username
|
||||
cfg.Password = acfg.password
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func argOrStdin(args []string, stdin io.Reader, i int) (string, error) {
|
||||
if i < len(args) {
|
||||
return args[i], nil
|
||||
}
|
||||
bytes, err := ioutil.ReadAll(stdin)
|
||||
if string(bytes) == "" || err != nil {
|
||||
return "", errors.New("no available argument and stdin")
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func dialTimeoutFromCmd(cmd *cobra.Command) time.Duration {
|
||||
dialTimeout, err := cmd.Flags().GetDuration("dial-timeout")
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
return dialTimeout
|
||||
}
|
||||
|
||||
func secureCfgFromCmd(cmd *cobra.Command) *secureCfg {
|
||||
cert, key, cacert := keyAndCertFromCmd(cmd)
|
||||
insecureTr := insecureTransportFromCmd(cmd)
|
||||
skipVerify := insecureSkipVerifyFromCmd(cmd)
|
||||
|
||||
return &secureCfg{
|
||||
cert: cert,
|
||||
key: key,
|
||||
cacert: cacert,
|
||||
|
||||
insecureTransport: insecureTr,
|
||||
insecureSkipVerify: skipVerify,
|
||||
}
|
||||
}
|
||||
|
||||
func insecureTransportFromCmd(cmd *cobra.Command) bool {
|
||||
insecureTr, err := cmd.Flags().GetBool("insecure-transport")
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
return insecureTr
|
||||
}
|
||||
|
||||
func insecureSkipVerifyFromCmd(cmd *cobra.Command) bool {
|
||||
skipVerify, err := cmd.Flags().GetBool("insecure-skip-tls-verify")
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
return skipVerify
|
||||
}
|
||||
|
||||
func keyAndCertFromCmd(cmd *cobra.Command) (cert, key, cacert string) {
|
||||
var err error
|
||||
if cert, err = cmd.Flags().GetString("cert"); err != nil {
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
} else if cert == "" && cmd.Flags().Changed("cert") {
|
||||
ExitWithError(ExitBadArgs, errors.New("empty string is passed to --cert option"))
|
||||
}
|
||||
|
||||
if key, err = cmd.Flags().GetString("key"); err != nil {
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
} else if key == "" && cmd.Flags().Changed("key") {
|
||||
ExitWithError(ExitBadArgs, errors.New("empty string is passed to --key option"))
|
||||
}
|
||||
|
||||
if cacert, err = cmd.Flags().GetString("cacert"); err != nil {
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
} else if cacert == "" && cmd.Flags().Changed("cacert") {
|
||||
ExitWithError(ExitBadArgs, errors.New("empty string is passed to --cacert option"))
|
||||
}
|
||||
|
||||
return cert, key, cacert
|
||||
}
|
||||
|
||||
func authCfgFromCmd(cmd *cobra.Command) *authCfg {
|
||||
userFlag, err := cmd.Flags().GetString("user")
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
|
||||
if userFlag == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cfg authCfg
|
||||
|
||||
splitted := strings.SplitN(userFlag, ":", 2)
|
||||
if len(splitted) < 2 {
|
||||
cfg.username = userFlag
|
||||
cfg.password, err = speakeasy.Ask("Password: ")
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
} else {
|
||||
cfg.username = splitted[0]
|
||||
cfg.password = splitted[1]
|
||||
}
|
||||
|
||||
return &cfg
|
||||
}
|
||||
168
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/lease_command.go
generated
vendored
Normal file
168
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/lease_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// NewLeaseCommand returns the cobra command for "lease".
|
||||
func NewLeaseCommand() *cobra.Command {
|
||||
lc := &cobra.Command{
|
||||
Use: "lease <subcommand>",
|
||||
Short: "Lease related commands",
|
||||
}
|
||||
|
||||
lc.AddCommand(NewLeaseGrantCommand())
|
||||
lc.AddCommand(NewLeaseRevokeCommand())
|
||||
lc.AddCommand(NewLeaseTimeToLiveCommand())
|
||||
lc.AddCommand(NewLeaseKeepAliveCommand())
|
||||
|
||||
return lc
|
||||
}
|
||||
|
||||
// NewLeaseGrantCommand returns the cobra command for "lease grant".
|
||||
func NewLeaseGrantCommand() *cobra.Command {
|
||||
lc := &cobra.Command{
|
||||
Use: "grant <ttl>",
|
||||
Short: "Creates leases",
|
||||
|
||||
Run: leaseGrantCommandFunc,
|
||||
}
|
||||
|
||||
return lc
|
||||
}
|
||||
|
||||
// leaseGrantCommandFunc executes the "lease grant" command.
|
||||
func leaseGrantCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("lease grant command needs TTL argument."))
|
||||
}
|
||||
|
||||
ttl, err := strconv.ParseInt(args[0], 10, 64)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("bad TTL (%v)", err))
|
||||
}
|
||||
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, err := mustClientFromCmd(cmd).Grant(ctx, ttl)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, fmt.Errorf("failed to grant lease (%v)\n", err))
|
||||
}
|
||||
fmt.Printf("lease %016x granted with TTL(%ds)\n", resp.ID, resp.TTL)
|
||||
}
|
||||
|
||||
// NewLeaseRevokeCommand returns the cobra command for "lease revoke".
|
||||
func NewLeaseRevokeCommand() *cobra.Command {
|
||||
lc := &cobra.Command{
|
||||
Use: "revoke <leaseID>",
|
||||
Short: "Revokes leases",
|
||||
|
||||
Run: leaseRevokeCommandFunc,
|
||||
}
|
||||
|
||||
return lc
|
||||
}
|
||||
|
||||
// leaseRevokeCommandFunc executes the "lease grant" command.
|
||||
func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("lease revoke command needs 1 argument"))
|
||||
}
|
||||
|
||||
id := leaseFromArgs(args[0])
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
_, err := mustClientFromCmd(cmd).Revoke(ctx, id)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, fmt.Errorf("failed to revoke lease (%v)\n", err))
|
||||
}
|
||||
fmt.Printf("lease %016x revoked\n", id)
|
||||
}
|
||||
|
||||
var timeToLiveKeys bool
|
||||
|
||||
// NewLeaseTimeToLiveCommand returns the cobra command for "lease timetolive".
|
||||
func NewLeaseTimeToLiveCommand() *cobra.Command {
|
||||
lc := &cobra.Command{
|
||||
Use: "timetolive <leaseID> [options]",
|
||||
Short: "Get lease information",
|
||||
|
||||
Run: leaseTimeToLiveCommandFunc,
|
||||
}
|
||||
lc.Flags().BoolVar(&timeToLiveKeys, "keys", false, "Get keys attached to this lease")
|
||||
|
||||
return lc
|
||||
}
|
||||
|
||||
// leaseTimeToLiveCommandFunc executes the "lease timetolive" command.
|
||||
func leaseTimeToLiveCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("lease timetolive command needs lease ID as argument"))
|
||||
}
|
||||
var opts []v3.LeaseOption
|
||||
if timeToLiveKeys {
|
||||
opts = append(opts, v3.WithAttachedKeys())
|
||||
}
|
||||
resp, rerr := mustClientFromCmd(cmd).TimeToLive(context.TODO(), leaseFromArgs(args[0]), opts...)
|
||||
if rerr != nil {
|
||||
ExitWithError(ExitBadConnection, rerr)
|
||||
}
|
||||
display.TimeToLive(*resp, timeToLiveKeys)
|
||||
}
|
||||
|
||||
// NewLeaseKeepAliveCommand returns the cobra command for "lease keep-alive".
|
||||
func NewLeaseKeepAliveCommand() *cobra.Command {
|
||||
lc := &cobra.Command{
|
||||
Use: "keep-alive <leaseID>",
|
||||
Short: "Keeps leases alive (renew)",
|
||||
|
||||
Run: leaseKeepAliveCommandFunc,
|
||||
}
|
||||
|
||||
return lc
|
||||
}
|
||||
|
||||
// leaseKeepAliveCommandFunc executes the "lease keep-alive" command.
|
||||
func leaseKeepAliveCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("lease keep-alive command needs lease ID as argument"))
|
||||
}
|
||||
|
||||
id := leaseFromArgs(args[0])
|
||||
respc, kerr := mustClientFromCmd(cmd).KeepAlive(context.TODO(), id)
|
||||
if kerr != nil {
|
||||
ExitWithError(ExitBadConnection, kerr)
|
||||
}
|
||||
|
||||
for resp := range respc {
|
||||
fmt.Printf("lease %016x keepalived with TTL(%d)\n", resp.ID, resp.TTL)
|
||||
}
|
||||
fmt.Printf("lease %016x expired or revoked.\n", id)
|
||||
}
|
||||
|
||||
func leaseFromArgs(arg string) v3.LeaseID {
|
||||
id, err := strconv.ParseInt(arg, 16, 64)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID arg (%v), expecting ID in Hex", err))
|
||||
}
|
||||
return v3.LeaseID(id)
|
||||
}
|
||||
88
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/lock_command.go
generated
vendored
Normal file
88
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/lock_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/clientv3/concurrency"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// NewLockCommand returns the cobra command for "lock".
|
||||
func NewLockCommand() *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "lock <lockname>",
|
||||
Short: "Acquires a named lock",
|
||||
Run: lockCommandFunc,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func lockCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, errors.New("lock takes one lock name argument."))
|
||||
}
|
||||
c := mustClientFromCmd(cmd)
|
||||
if err := lockUntilSignal(c, args[0]); err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
}
|
||||
|
||||
func lockUntilSignal(c *clientv3.Client, lockname string) error {
|
||||
s, err := concurrency.NewSession(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := concurrency.NewMutex(s, lockname)
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
|
||||
// unlock in case of ordinary shutdown
|
||||
donec := make(chan struct{})
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Notify(sigc, os.Interrupt, os.Kill)
|
||||
go func() {
|
||||
<-sigc
|
||||
cancel()
|
||||
close(donec)
|
||||
}()
|
||||
|
||||
if err := m.Lock(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
k, kerr := c.Get(ctx, m.Key())
|
||||
if kerr != nil {
|
||||
return kerr
|
||||
}
|
||||
if len(k.Kvs) == 0 {
|
||||
return errors.New("lock lost on init")
|
||||
}
|
||||
|
||||
display.Get(*k)
|
||||
|
||||
select {
|
||||
case <-donec:
|
||||
return m.Unlock(context.TODO())
|
||||
case <-s.Done():
|
||||
}
|
||||
|
||||
return errors.New("session expired")
|
||||
}
|
||||
166
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/make_mirror_command.go
generated
vendored
Normal file
166
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/make_mirror_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/clientv3/mirror"
|
||||
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
mminsecureTr bool
|
||||
mmcert string
|
||||
mmkey string
|
||||
mmcacert string
|
||||
mmprefix string
|
||||
mmdestprefix string
|
||||
mmnodestprefix bool
|
||||
)
|
||||
|
||||
// NewMakeMirrorCommand returns the cobra command for "makeMirror".
|
||||
func NewMakeMirrorCommand() *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "make-mirror [options] <destination>",
|
||||
Short: "Makes a mirror at the destination etcd cluster",
|
||||
Run: makeMirrorCommandFunc,
|
||||
}
|
||||
|
||||
c.Flags().StringVar(&mmprefix, "prefix", "", "Key-value prefix to mirror")
|
||||
c.Flags().StringVar(&mmdestprefix, "dest-prefix", "", "destination prefix to mirror a prefix to a different prefix in the destination cluster")
|
||||
c.Flags().BoolVar(&mmnodestprefix, "no-dest-prefix", false, "mirror key-values to the root of the destination cluster")
|
||||
c.Flags().StringVar(&mmcert, "dest-cert", "", "Identify secure client using this TLS certificate file for the destination cluster")
|
||||
c.Flags().StringVar(&mmkey, "dest-key", "", "Identify secure client using this TLS key file")
|
||||
c.Flags().StringVar(&mmcacert, "dest-cacert", "", "Verify certificates of TLS enabled secure servers using this CA bundle")
|
||||
// TODO: secure by default when etcd enables secure gRPC by default.
|
||||
c.Flags().BoolVar(&mminsecureTr, "dest-insecure-transport", true, "Disable transport security for client connections")
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func makeMirrorCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, errors.New("make-mirror takes one destination argument."))
|
||||
}
|
||||
|
||||
dialTimeout := dialTimeoutFromCmd(cmd)
|
||||
sec := &secureCfg{
|
||||
cert: mmcert,
|
||||
key: mmkey,
|
||||
cacert: mmcacert,
|
||||
insecureTransport: mminsecureTr,
|
||||
}
|
||||
|
||||
dc := mustClient([]string{args[0]}, dialTimeout, sec, nil)
|
||||
c := mustClientFromCmd(cmd)
|
||||
|
||||
err := makeMirror(context.TODO(), c, dc)
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
func makeMirror(ctx context.Context, c *clientv3.Client, dc *clientv3.Client) error {
|
||||
total := int64(0)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(30 * time.Second)
|
||||
fmt.Println(atomic.LoadInt64(&total))
|
||||
}
|
||||
}()
|
||||
|
||||
s := mirror.NewSyncer(c, mmprefix, 0)
|
||||
|
||||
rc, errc := s.SyncBase(ctx)
|
||||
|
||||
// if destination prefix is specified and remove destination prefix is true return error
|
||||
if mmnodestprefix && len(mmdestprefix) > 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("`--dest-prefix` and `--no-dest-prefix` cannot be set at the same time, choose one."))
|
||||
}
|
||||
|
||||
// if remove destination prefix is false and destination prefix is empty set the value of destination prefix same as prefix
|
||||
if !mmnodestprefix && len(mmdestprefix) == 0 {
|
||||
mmdestprefix = mmprefix
|
||||
}
|
||||
|
||||
for r := range rc {
|
||||
for _, kv := range r.Kvs {
|
||||
_, err := dc.Put(ctx, modifyPrefix(string(kv.Key)), string(kv.Value))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
atomic.AddInt64(&total, 1)
|
||||
}
|
||||
}
|
||||
|
||||
err := <-errc
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wc := s.SyncUpdates(ctx)
|
||||
|
||||
for wr := range wc {
|
||||
if wr.CompactRevision != 0 {
|
||||
return rpctypes.ErrCompacted
|
||||
}
|
||||
|
||||
var lastRev int64
|
||||
ops := []clientv3.Op{}
|
||||
|
||||
for _, ev := range wr.Events {
|
||||
nextRev := ev.Kv.ModRevision
|
||||
if lastRev != 0 && nextRev > lastRev {
|
||||
_, err := dc.Txn(ctx).Then(ops...).Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ops = []clientv3.Op{}
|
||||
}
|
||||
lastRev = nextRev
|
||||
switch ev.Type {
|
||||
case mvccpb.PUT:
|
||||
ops = append(ops, clientv3.OpPut(modifyPrefix(string(ev.Kv.Key)), string(ev.Kv.Value)))
|
||||
atomic.AddInt64(&total, 1)
|
||||
case mvccpb.DELETE:
|
||||
ops = append(ops, clientv3.OpDelete(modifyPrefix(string(ev.Kv.Key))))
|
||||
atomic.AddInt64(&total, 1)
|
||||
default:
|
||||
panic("unexpected event type")
|
||||
}
|
||||
}
|
||||
|
||||
if len(ops) != 0 {
|
||||
_, err := dc.Txn(ctx).Then(ops...).Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func modifyPrefix(key string) string {
|
||||
return strings.Replace(key, mmprefix, mmdestprefix, 1)
|
||||
}
|
||||
216
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/member_command.go
generated
vendored
Normal file
216
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/member_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var memberPeerURLs string
|
||||
|
||||
// NewMemberCommand returns the cobra command for "member".
|
||||
func NewMemberCommand() *cobra.Command {
|
||||
mc := &cobra.Command{
|
||||
Use: "member <subcommand>",
|
||||
Short: "Membership related commands",
|
||||
}
|
||||
|
||||
mc.AddCommand(NewMemberAddCommand())
|
||||
mc.AddCommand(NewMemberRemoveCommand())
|
||||
mc.AddCommand(NewMemberUpdateCommand())
|
||||
mc.AddCommand(NewMemberListCommand())
|
||||
|
||||
return mc
|
||||
}
|
||||
|
||||
// NewMemberAddCommand returns the cobra command for "member add".
|
||||
func NewMemberAddCommand() *cobra.Command {
|
||||
cc := &cobra.Command{
|
||||
Use: "add <memberName> [options]",
|
||||
Short: "Adds a member into the cluster",
|
||||
|
||||
Run: memberAddCommandFunc,
|
||||
}
|
||||
|
||||
cc.Flags().StringVar(&memberPeerURLs, "peer-urls", "", "comma separated peer URLs for the new member.")
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
// NewMemberRemoveCommand returns the cobra command for "member remove".
|
||||
func NewMemberRemoveCommand() *cobra.Command {
|
||||
cc := &cobra.Command{
|
||||
Use: "remove <memberID>",
|
||||
Short: "Removes a member from the cluster",
|
||||
|
||||
Run: memberRemoveCommandFunc,
|
||||
}
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
// NewMemberUpdateCommand returns the cobra command for "member update".
|
||||
func NewMemberUpdateCommand() *cobra.Command {
|
||||
cc := &cobra.Command{
|
||||
Use: "update <memberID> [options]",
|
||||
Short: "Updates a member in the cluster",
|
||||
|
||||
Run: memberUpdateCommandFunc,
|
||||
}
|
||||
|
||||
cc.Flags().StringVar(&memberPeerURLs, "peer-urls", "", "comma separated peer URLs for the updated member.")
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
// NewMemberListCommand returns the cobra command for "member list".
|
||||
func NewMemberListCommand() *cobra.Command {
|
||||
cc := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "Lists all members in the cluster",
|
||||
Long: `When --write-out is set to simple, this command prints out comma-separated member lists for each endpoint.
|
||||
The items in the lists are ID, Status, Name, Peer Addrs, Client Addrs.
|
||||
`,
|
||||
|
||||
Run: memberListCommandFunc,
|
||||
}
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
// memberAddCommandFunc executes the "member add" command.
|
||||
func memberAddCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("member name not provided."))
|
||||
}
|
||||
newMemberName := args[0]
|
||||
|
||||
if len(memberPeerURLs) == 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("member peer urls not provided."))
|
||||
}
|
||||
|
||||
urls := strings.Split(memberPeerURLs, ",")
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
cli := mustClientFromCmd(cmd)
|
||||
resp, err := cli.MemberAdd(ctx, urls)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
newID := resp.Member.ID
|
||||
|
||||
display.MemberAdd(*resp)
|
||||
|
||||
if _, ok := (display).(*simplePrinter); ok {
|
||||
ctx, cancel = commandCtx(cmd)
|
||||
listResp, err := cli.MemberList(ctx)
|
||||
// get latest member list; if there's failover new member might have outdated list
|
||||
for {
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
if listResp.Header.MemberId == resp.Header.MemberId {
|
||||
break
|
||||
}
|
||||
// quorum get to sync cluster list
|
||||
gresp, gerr := cli.Get(ctx, "_")
|
||||
if gerr != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
resp.Header.MemberId = gresp.Header.MemberId
|
||||
listResp, err = cli.MemberList(ctx)
|
||||
}
|
||||
cancel()
|
||||
|
||||
conf := []string{}
|
||||
for _, memb := range listResp.Members {
|
||||
for _, u := range memb.PeerURLs {
|
||||
n := memb.Name
|
||||
if memb.ID == newID {
|
||||
n = newMemberName
|
||||
}
|
||||
conf = append(conf, fmt.Sprintf("%s=%s", n, u))
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Print("\n")
|
||||
fmt.Printf("ETCD_NAME=%q\n", newMemberName)
|
||||
fmt.Printf("ETCD_INITIAL_CLUSTER=%q\n", strings.Join(conf, ","))
|
||||
fmt.Printf("ETCD_INITIAL_CLUSTER_STATE=\"existing\"\n")
|
||||
}
|
||||
}
|
||||
|
||||
// memberRemoveCommandFunc executes the "member remove" command.
|
||||
func memberRemoveCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("member ID is not provided"))
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(args[0], 16, 64)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("bad member ID arg (%v), expecting ID in Hex", err))
|
||||
}
|
||||
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, err := mustClientFromCmd(cmd).MemberRemove(ctx, id)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
display.MemberRemove(id, *resp)
|
||||
}
|
||||
|
||||
// memberUpdateCommandFunc executes the "member update" command.
|
||||
func memberUpdateCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("member ID is not provided"))
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(args[0], 16, 64)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("bad member ID arg (%v), expecting ID in Hex", err))
|
||||
}
|
||||
|
||||
if len(memberPeerURLs) == 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("member peer urls not provided."))
|
||||
}
|
||||
|
||||
urls := strings.Split(memberPeerURLs, ",")
|
||||
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, err := mustClientFromCmd(cmd).MemberUpdate(ctx, id, urls)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.MemberUpdate(id, *resp)
|
||||
}
|
||||
|
||||
// memberListCommandFunc executes the "member list" command.
|
||||
func memberListCommandFunc(cmd *cobra.Command, args []string) {
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, err := mustClientFromCmd(cmd).MemberList(ctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.MemberList(*resp)
|
||||
}
|
||||
410
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/migrate_command.go
generated
vendored
Normal file
410
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/migrate_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
etcdErr "github.com/coreos/etcd/error"
|
||||
"github.com/coreos/etcd/etcdserver"
|
||||
"github.com/coreos/etcd/etcdserver/api"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"github.com/coreos/etcd/etcdserver/membership"
|
||||
"github.com/coreos/etcd/mvcc"
|
||||
"github.com/coreos/etcd/mvcc/backend"
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
"github.com/coreos/etcd/pkg/pbutil"
|
||||
"github.com/coreos/etcd/pkg/types"
|
||||
"github.com/coreos/etcd/raft/raftpb"
|
||||
"github.com/coreos/etcd/snap"
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/coreos/etcd/wal"
|
||||
"github.com/coreos/etcd/wal/walpb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
migrateExcludeTTLKey bool
|
||||
migrateDatadir string
|
||||
migrateWALdir string
|
||||
migrateTransformer string
|
||||
)
|
||||
|
||||
// NewMigrateCommand returns the cobra command for "migrate".
|
||||
func NewMigrateCommand() *cobra.Command {
|
||||
mc := &cobra.Command{
|
||||
Use: "migrate",
|
||||
Short: "Migrates keys in a v2 store to a mvcc store",
|
||||
Run: migrateCommandFunc,
|
||||
}
|
||||
|
||||
mc.Flags().BoolVar(&migrateExcludeTTLKey, "no-ttl", false, "Do not convert TTL keys")
|
||||
mc.Flags().StringVar(&migrateDatadir, "data-dir", "", "Path to the data directory")
|
||||
mc.Flags().StringVar(&migrateWALdir, "wal-dir", "", "Path to the WAL directory")
|
||||
mc.Flags().StringVar(&migrateTransformer, "transformer", "", "Path to the user-provided transformer program")
|
||||
return mc
|
||||
}
|
||||
|
||||
func migrateCommandFunc(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
writer io.WriteCloser
|
||||
reader io.ReadCloser
|
||||
errc chan error
|
||||
)
|
||||
if migrateTransformer != "" {
|
||||
writer, reader, errc = startTransformer()
|
||||
} else {
|
||||
fmt.Println("using default transformer")
|
||||
writer, reader, errc = defaultTransformer()
|
||||
}
|
||||
|
||||
st, index := rebuildStoreV2()
|
||||
be := prepareBackend()
|
||||
defer be.Close()
|
||||
|
||||
go func() {
|
||||
writeStore(writer, st)
|
||||
writer.Close()
|
||||
}()
|
||||
|
||||
readKeys(reader, be)
|
||||
mvcc.UpdateConsistentIndex(be, index)
|
||||
err := <-errc
|
||||
if err != nil {
|
||||
fmt.Println("failed to transform keys")
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
fmt.Println("finished transforming keys")
|
||||
}
|
||||
|
||||
func prepareBackend() backend.Backend {
|
||||
var be backend.Backend
|
||||
|
||||
bch := make(chan struct{})
|
||||
dbpath := filepath.Join(migrateDatadir, "member", "snap", "db")
|
||||
go func() {
|
||||
defer close(bch)
|
||||
be = backend.New(dbpath, time.Second, 10000)
|
||||
|
||||
}()
|
||||
select {
|
||||
case <-bch:
|
||||
case <-time.After(time.Second):
|
||||
fmt.Fprintf(os.Stderr, "waiting for etcd to close and release its lock on %q\n", dbpath)
|
||||
<-bch
|
||||
}
|
||||
|
||||
tx := be.BatchTx()
|
||||
tx.Lock()
|
||||
tx.UnsafeCreateBucket([]byte("key"))
|
||||
tx.UnsafeCreateBucket([]byte("meta"))
|
||||
tx.Unlock()
|
||||
return be
|
||||
}
|
||||
|
||||
func rebuildStoreV2() (store.Store, uint64) {
|
||||
var index uint64
|
||||
cl := membership.NewCluster("")
|
||||
|
||||
waldir := migrateWALdir
|
||||
if len(waldir) == 0 {
|
||||
waldir = filepath.Join(migrateDatadir, "member", "wal")
|
||||
}
|
||||
snapdir := filepath.Join(migrateDatadir, "member", "snap")
|
||||
|
||||
ss := snap.New(snapdir)
|
||||
snapshot, err := ss.Load()
|
||||
if err != nil && err != snap.ErrNoSnapshot {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
var walsnap walpb.Snapshot
|
||||
if snapshot != nil {
|
||||
walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
|
||||
index = snapshot.Metadata.Index
|
||||
}
|
||||
|
||||
w, err := wal.OpenForRead(waldir, walsnap)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
_, _, ents, err := w.ReadAll()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
st := store.New()
|
||||
if snapshot != nil {
|
||||
err := st.Recovery(snapshot.Data)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
}
|
||||
|
||||
cl.SetStore(st)
|
||||
cl.Recover(api.UpdateCapability)
|
||||
|
||||
applier := etcdserver.NewApplierV2(st, cl)
|
||||
for _, ent := range ents {
|
||||
if ent.Type == raftpb.EntryConfChange {
|
||||
var cc raftpb.ConfChange
|
||||
pbutil.MustUnmarshal(&cc, ent.Data)
|
||||
applyConf(cc, cl)
|
||||
continue
|
||||
}
|
||||
|
||||
var raftReq pb.InternalRaftRequest
|
||||
if !pbutil.MaybeUnmarshal(&raftReq, ent.Data) { // backward compatible
|
||||
var r pb.Request
|
||||
pbutil.MustUnmarshal(&r, ent.Data)
|
||||
applyRequest(&r, applier)
|
||||
} else {
|
||||
if raftReq.V2 != nil {
|
||||
req := raftReq.V2
|
||||
applyRequest(req, applier)
|
||||
}
|
||||
}
|
||||
if ent.Index > index {
|
||||
index = ent.Index
|
||||
}
|
||||
}
|
||||
|
||||
return st, index
|
||||
}
|
||||
|
||||
func applyConf(cc raftpb.ConfChange, cl *membership.RaftCluster) {
|
||||
if err := cl.ValidateConfigurationChange(cc); err != nil {
|
||||
return
|
||||
}
|
||||
switch cc.Type {
|
||||
case raftpb.ConfChangeAddNode:
|
||||
m := new(membership.Member)
|
||||
if err := json.Unmarshal(cc.Context, m); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cl.AddMember(m)
|
||||
case raftpb.ConfChangeRemoveNode:
|
||||
cl.RemoveMember(types.ID(cc.NodeID))
|
||||
case raftpb.ConfChangeUpdateNode:
|
||||
m := new(membership.Member)
|
||||
if err := json.Unmarshal(cc.Context, m); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cl.UpdateRaftAttributes(m.ID, m.RaftAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
func applyRequest(r *pb.Request, applyV2 etcdserver.ApplierV2) {
|
||||
toTTLOptions(r)
|
||||
switch r.Method {
|
||||
case "POST":
|
||||
applyV2.Post(r)
|
||||
case "PUT":
|
||||
applyV2.Put(r)
|
||||
case "DELETE":
|
||||
applyV2.Delete(r)
|
||||
case "QGET":
|
||||
applyV2.QGet(r)
|
||||
case "SYNC":
|
||||
applyV2.Sync(r)
|
||||
default:
|
||||
panic("unknown command")
|
||||
}
|
||||
}
|
||||
|
||||
func toTTLOptions(r *pb.Request) store.TTLOptionSet {
|
||||
refresh, _ := pbutil.GetBool(r.Refresh)
|
||||
ttlOptions := store.TTLOptionSet{Refresh: refresh}
|
||||
if r.Expiration != 0 {
|
||||
ttlOptions.ExpireTime = time.Unix(0, r.Expiration)
|
||||
}
|
||||
return ttlOptions
|
||||
}
|
||||
|
||||
func writeStore(w io.Writer, st store.Store) uint64 {
|
||||
all, err := st.Get("/1", true, true)
|
||||
if err != nil {
|
||||
if eerr, ok := err.(*etcdErr.Error); ok && eerr.ErrorCode == etcdErr.EcodeKeyNotFound {
|
||||
fmt.Println("no v2 keys to migrate")
|
||||
os.Exit(0)
|
||||
}
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
return writeKeys(w, all.Node)
|
||||
}
|
||||
|
||||
func writeKeys(w io.Writer, n *store.NodeExtern) uint64 {
|
||||
maxIndex := n.ModifiedIndex
|
||||
|
||||
nodes := n.Nodes
|
||||
// remove store v2 bucket prefix
|
||||
n.Key = n.Key[2:]
|
||||
if n.Key == "" {
|
||||
n.Key = "/"
|
||||
}
|
||||
if n.Dir {
|
||||
n.Nodes = nil
|
||||
}
|
||||
if !migrateExcludeTTLKey || n.TTL == 0 {
|
||||
b, err := json.Marshal(n)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
fmt.Fprint(w, string(b))
|
||||
}
|
||||
for _, nn := range nodes {
|
||||
max := writeKeys(w, nn)
|
||||
if max > maxIndex {
|
||||
maxIndex = max
|
||||
}
|
||||
}
|
||||
return maxIndex
|
||||
}
|
||||
|
||||
func readKeys(r io.Reader, be backend.Backend) error {
|
||||
for {
|
||||
length64, err := readInt64(r)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
buf := make([]byte, int(length64))
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var kv mvccpb.KeyValue
|
||||
err = proto.Unmarshal(buf, &kv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mvcc.WriteKV(be, kv)
|
||||
}
|
||||
}
|
||||
|
||||
func readInt64(r io.Reader) (int64, error) {
|
||||
var n int64
|
||||
err := binary.Read(r, binary.LittleEndian, &n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func startTransformer() (io.WriteCloser, io.ReadCloser, chan error) {
|
||||
cmd := exec.Command(migrateTransformer)
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
writer, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
reader, rerr := cmd.StdoutPipe()
|
||||
if rerr != nil {
|
||||
ExitWithError(ExitError, rerr)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
errc := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
errc <- cmd.Wait()
|
||||
}()
|
||||
|
||||
return writer, reader, errc
|
||||
}
|
||||
|
||||
func defaultTransformer() (io.WriteCloser, io.ReadCloser, chan error) {
|
||||
// transformer decodes v2 keys from sr
|
||||
sr, sw := io.Pipe()
|
||||
// transformer encodes v3 keys into dw
|
||||
dr, dw := io.Pipe()
|
||||
|
||||
decoder := json.NewDecoder(sr)
|
||||
|
||||
errc := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
sr.Close()
|
||||
dw.Close()
|
||||
}()
|
||||
|
||||
for decoder.More() {
|
||||
node := &client.Node{}
|
||||
if err := decoder.Decode(node); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
kv := transform(node)
|
||||
if kv == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(kv)
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
buf := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(buf, uint64(len(data)))
|
||||
if _, err := dw.Write(buf); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
if _, err := dw.Write(data); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
errc <- nil
|
||||
}()
|
||||
|
||||
return sw, dr, errc
|
||||
}
|
||||
|
||||
func transform(n *client.Node) *mvccpb.KeyValue {
|
||||
const unKnownVersion = 1
|
||||
if n.Dir {
|
||||
return nil
|
||||
}
|
||||
kv := &mvccpb.KeyValue{
|
||||
Key: []byte(n.Key),
|
||||
Value: []byte(n.Value),
|
||||
CreateRevision: int64(n.CreatedIndex),
|
||||
ModRevision: int64(n.ModifiedIndex),
|
||||
Version: unKnownVersion,
|
||||
}
|
||||
return kv
|
||||
}
|
||||
183
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer.go
generated
vendored
Normal file
183
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/dustin/go-humanize"
|
||||
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
)
|
||||
|
||||
type printer interface {
|
||||
Del(v3.DeleteResponse)
|
||||
Get(v3.GetResponse)
|
||||
Put(v3.PutResponse)
|
||||
Txn(v3.TxnResponse)
|
||||
Watch(v3.WatchResponse)
|
||||
|
||||
TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool)
|
||||
|
||||
MemberAdd(v3.MemberAddResponse)
|
||||
MemberRemove(id uint64, r v3.MemberRemoveResponse)
|
||||
MemberUpdate(id uint64, r v3.MemberUpdateResponse)
|
||||
MemberList(v3.MemberListResponse)
|
||||
|
||||
EndpointStatus([]epStatus)
|
||||
|
||||
Alarm(v3.AlarmResponse)
|
||||
DBStatus(dbstatus)
|
||||
|
||||
RoleAdd(role string, r v3.AuthRoleAddResponse)
|
||||
RoleGet(role string, r v3.AuthRoleGetResponse)
|
||||
RoleDelete(role string, r v3.AuthRoleDeleteResponse)
|
||||
RoleList(v3.AuthRoleListResponse)
|
||||
RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse)
|
||||
RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse)
|
||||
|
||||
UserAdd(user string, r v3.AuthUserAddResponse)
|
||||
UserGet(user string, r v3.AuthUserGetResponse)
|
||||
UserList(r v3.AuthUserListResponse)
|
||||
UserChangePassword(v3.AuthUserChangePasswordResponse)
|
||||
UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse)
|
||||
UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse)
|
||||
UserDelete(user string, r v3.AuthUserDeleteResponse)
|
||||
}
|
||||
|
||||
func NewPrinter(printerType string, isHex bool) printer {
|
||||
switch printerType {
|
||||
case "simple":
|
||||
return &simplePrinter{isHex: isHex}
|
||||
case "fields":
|
||||
return &fieldsPrinter{newPrinterUnsupported("fields")}
|
||||
case "json":
|
||||
return newJSONPrinter()
|
||||
case "protobuf":
|
||||
return newPBPrinter()
|
||||
case "table":
|
||||
return &tablePrinter{newPrinterUnsupported("table")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type printerRPC struct {
|
||||
printer
|
||||
p func(interface{})
|
||||
}
|
||||
|
||||
func (p *printerRPC) Del(r v3.DeleteResponse) { p.p((*pb.DeleteRangeResponse)(&r)) }
|
||||
func (p *printerRPC) Get(r v3.GetResponse) { p.p((*pb.RangeResponse)(&r)) }
|
||||
func (p *printerRPC) Put(r v3.PutResponse) { p.p((*pb.PutResponse)(&r)) }
|
||||
func (p *printerRPC) Txn(r v3.TxnResponse) { p.p((*pb.TxnResponse)(&r)) }
|
||||
func (p *printerRPC) Watch(r v3.WatchResponse) { p.p(&r) }
|
||||
func (p *printerRPC) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) { p.p(&r) }
|
||||
func (p *printerRPC) MemberAdd(r v3.MemberAddResponse) { p.p((*pb.MemberAddResponse)(&r)) }
|
||||
func (p *printerRPC) MemberRemove(id uint64, r v3.MemberRemoveResponse) {
|
||||
p.p((*pb.MemberRemoveResponse)(&r))
|
||||
}
|
||||
func (p *printerRPC) MemberUpdate(id uint64, r v3.MemberUpdateResponse) {
|
||||
p.p((*pb.MemberUpdateResponse)(&r))
|
||||
}
|
||||
func (p *printerRPC) MemberList(r v3.MemberListResponse) { p.p((*pb.MemberListResponse)(&r)) }
|
||||
func (p *printerRPC) Alarm(r v3.AlarmResponse) { p.p((*pb.AlarmResponse)(&r)) }
|
||||
|
||||
func (p *printerRPC) RoleAdd(_ string, r v3.AuthRoleAddResponse) { p.p((*pb.AuthRoleAddResponse)(&r)) }
|
||||
func (p *printerRPC) RoleGet(_ string, r v3.AuthRoleGetResponse) { p.p((*pb.AuthRoleGetResponse)(&r)) }
|
||||
func (p *printerRPC) RoleDelete(_ string, r v3.AuthRoleDeleteResponse) {
|
||||
p.p((*pb.AuthRoleDeleteResponse)(&r))
|
||||
}
|
||||
func (p *printerRPC) RoleList(r v3.AuthRoleListResponse) { p.p((*pb.AuthRoleListResponse)(&r)) }
|
||||
func (p *printerRPC) RoleGrantPermission(_ string, r v3.AuthRoleGrantPermissionResponse) {
|
||||
p.p((*pb.AuthRoleGrantPermissionResponse)(&r))
|
||||
}
|
||||
func (p *printerRPC) RoleRevokePermission(_ string, _ string, _ string, r v3.AuthRoleRevokePermissionResponse) {
|
||||
p.p((*pb.AuthRoleRevokePermissionResponse)(&r))
|
||||
}
|
||||
func (p *printerRPC) UserAdd(_ string, r v3.AuthUserAddResponse) { p.p((*pb.AuthUserAddResponse)(&r)) }
|
||||
func (p *printerRPC) UserGet(_ string, r v3.AuthUserGetResponse) { p.p((*pb.AuthUserGetResponse)(&r)) }
|
||||
func (p *printerRPC) UserList(r v3.AuthUserListResponse) { p.p((*pb.AuthUserListResponse)(&r)) }
|
||||
func (p *printerRPC) UserChangePassword(r v3.AuthUserChangePasswordResponse) {
|
||||
p.p((*pb.AuthUserChangePasswordResponse)(&r))
|
||||
}
|
||||
func (p *printerRPC) UserGrantRole(_ string, _ string, r v3.AuthUserGrantRoleResponse) {
|
||||
p.p((*pb.AuthUserGrantRoleResponse)(&r))
|
||||
}
|
||||
func (p *printerRPC) UserRevokeRole(_ string, _ string, r v3.AuthUserRevokeRoleResponse) {
|
||||
p.p((*pb.AuthUserRevokeRoleResponse)(&r))
|
||||
}
|
||||
func (p *printerRPC) UserDelete(_ string, r v3.AuthUserDeleteResponse) {
|
||||
p.p((*pb.AuthUserDeleteResponse)(&r))
|
||||
}
|
||||
|
||||
type printerUnsupported struct{ printerRPC }
|
||||
|
||||
func newPrinterUnsupported(n string) printer {
|
||||
f := func(interface{}) {
|
||||
ExitWithError(ExitBadFeature, errors.New(n+" not supported as output format"))
|
||||
}
|
||||
return &printerUnsupported{printerRPC{nil, f}}
|
||||
}
|
||||
|
||||
func (p *printerUnsupported) EndpointStatus([]epStatus) { p.p(nil) }
|
||||
func (p *printerUnsupported) DBStatus(dbstatus) { p.p(nil) }
|
||||
|
||||
func makeMemberListTable(r v3.MemberListResponse) (hdr []string, rows [][]string) {
|
||||
hdr = []string{"ID", "Status", "Name", "Peer Addrs", "Client Addrs"}
|
||||
for _, m := range r.Members {
|
||||
status := "started"
|
||||
if len(m.Name) == 0 {
|
||||
status = "unstarted"
|
||||
}
|
||||
rows = append(rows, []string{
|
||||
fmt.Sprintf("%x", m.ID),
|
||||
status,
|
||||
m.Name,
|
||||
strings.Join(m.PeerURLs, ","),
|
||||
strings.Join(m.ClientURLs, ","),
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func makeEndpointStatusTable(statusList []epStatus) (hdr []string, rows [][]string) {
|
||||
hdr = []string{"endpoint", "ID", "version", "db size", "is leader", "raft term", "raft index"}
|
||||
for _, status := range statusList {
|
||||
rows = append(rows, []string{
|
||||
fmt.Sprint(status.Ep),
|
||||
fmt.Sprintf("%x", status.Resp.Header.MemberId),
|
||||
fmt.Sprint(status.Resp.Version),
|
||||
fmt.Sprint(humanize.Bytes(uint64(status.Resp.DbSize))),
|
||||
fmt.Sprint(status.Resp.Leader == status.Resp.Header.MemberId),
|
||||
fmt.Sprint(status.Resp.RaftTerm),
|
||||
fmt.Sprint(status.Resp.RaftIndex),
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func makeDBStatusTable(ds dbstatus) (hdr []string, rows [][]string) {
|
||||
hdr = []string{"hash", "revision", "total keys", "total size"}
|
||||
rows = append(rows, []string{
|
||||
fmt.Sprintf("%x", ds.Hash),
|
||||
fmt.Sprint(ds.Revision),
|
||||
fmt.Sprint(ds.TotalKey),
|
||||
humanize.Bytes(uint64(ds.TotalSize)),
|
||||
})
|
||||
return
|
||||
}
|
||||
167
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_fields.go
generated
vendored
Normal file
167
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_fields.go
generated
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
spb "github.com/coreos/etcd/mvcc/mvccpb"
|
||||
)
|
||||
|
||||
type fieldsPrinter struct{ printer }
|
||||
|
||||
func (p *fieldsPrinter) kv(pfx string, kv *spb.KeyValue) {
|
||||
fmt.Printf("\"%sKey\" : %q\n", pfx, string(kv.Key))
|
||||
fmt.Printf("\"%sCreateRevision\" : %d\n", pfx, kv.CreateRevision)
|
||||
fmt.Printf("\"%sModRevision\" : %d\n", pfx, kv.ModRevision)
|
||||
fmt.Printf("\"%sVersion\" : %d\n", pfx, kv.Version)
|
||||
fmt.Printf("\"%sValue\" : %q\n", pfx, string(kv.Value))
|
||||
fmt.Printf("\"%sLease\" : %d\n", pfx, string(kv.Lease))
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) hdr(h *pb.ResponseHeader) {
|
||||
fmt.Println(`"ClusterID" :`, h.ClusterId)
|
||||
fmt.Println(`"MemberID" :`, h.MemberId)
|
||||
fmt.Println(`"Revision" :`, h.Revision)
|
||||
fmt.Println(`"RaftTerm" :`, h.RaftTerm)
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) Del(r v3.DeleteResponse) {
|
||||
p.hdr(r.Header)
|
||||
fmt.Println(`"Deleted" :`, r.Deleted)
|
||||
for _, kv := range r.PrevKvs {
|
||||
p.kv("Prev", kv)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) Get(r v3.GetResponse) {
|
||||
p.hdr(r.Header)
|
||||
for _, kv := range r.Kvs {
|
||||
p.kv("", kv)
|
||||
}
|
||||
fmt.Println(`"More" :`, r.More)
|
||||
fmt.Println(`"Count" :`, r.Count)
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) Put(r v3.PutResponse) {
|
||||
p.hdr(r.Header)
|
||||
if r.PrevKv != nil {
|
||||
p.kv("Prev", r.PrevKv)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) Txn(r v3.TxnResponse) {
|
||||
p.hdr(r.Header)
|
||||
fmt.Println(`"Succeeded" :`, r.Succeeded)
|
||||
for _, resp := range r.Responses {
|
||||
switch v := resp.Response.(type) {
|
||||
case *pb.ResponseOp_ResponseDeleteRange:
|
||||
p.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
|
||||
case *pb.ResponseOp_ResponsePut:
|
||||
p.Put((v3.PutResponse)(*v.ResponsePut))
|
||||
case *pb.ResponseOp_ResponseRange:
|
||||
p.Get((v3.GetResponse)(*v.ResponseRange))
|
||||
default:
|
||||
fmt.Printf("\"Unknown\" : %q\n", fmt.Sprintf("%+v", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) Watch(resp v3.WatchResponse) {
|
||||
p.hdr(&resp.Header)
|
||||
for _, e := range resp.Events {
|
||||
fmt.Println(`"Type" :`, e.Type)
|
||||
if e.PrevKv != nil {
|
||||
p.kv("Prev", e.PrevKv)
|
||||
}
|
||||
p.kv("", e.Kv)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
|
||||
p.hdr(r.ResponseHeader)
|
||||
fmt.Println(`"ID" :`, r.ID)
|
||||
fmt.Println(`"TTL" :`, r.TTL)
|
||||
fmt.Println(`"GrantedTTL" :`, r.GrantedTTL)
|
||||
for _, k := range r.Keys {
|
||||
fmt.Printf("\"Key\" : %q\n", string(k))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) MemberList(r v3.MemberListResponse) {
|
||||
p.hdr(r.Header)
|
||||
for _, m := range r.Members {
|
||||
fmt.Println(`"ID" :`, m.ID)
|
||||
fmt.Printf("\"Name\" : %q\n", m.Name)
|
||||
for _, u := range m.PeerURLs {
|
||||
fmt.Printf("\"PeerURL\" : %q\n", u)
|
||||
}
|
||||
for _, u := range m.ClientURLs {
|
||||
fmt.Printf("\"ClientURL\" : %q\n", u)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) EndpointStatus(eps []epStatus) {
|
||||
for _, ep := range eps {
|
||||
p.hdr(ep.Resp.Header)
|
||||
fmt.Printf("\"Version\" : %q\n", ep.Resp.Version)
|
||||
fmt.Println(`"DBSize" :"`, ep.Resp.DbSize)
|
||||
fmt.Println(`"Leader" :"`, ep.Resp.Leader)
|
||||
fmt.Println(`"RaftIndex" :"`, ep.Resp.RaftIndex)
|
||||
fmt.Println(`"RaftTerm" :"`, ep.Resp.RaftTerm)
|
||||
fmt.Printf("\"Endpoint\" : %q\n", ep.Ep)
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) Alarm(r v3.AlarmResponse) {
|
||||
p.hdr(r.Header)
|
||||
for _, a := range r.Alarms {
|
||||
fmt.Println(`"MemberID" :`, a.MemberID)
|
||||
fmt.Println(`"AlarmType" :`, a.Alarm)
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) DBStatus(r dbstatus) {
|
||||
fmt.Println(`"Hash" :`, r.Hash)
|
||||
fmt.Println(`"Revision" :`, r.Revision)
|
||||
fmt.Println(`"Keys" :`, r.TotalKey)
|
||||
fmt.Println(`"Size" :`, r.TotalSize)
|
||||
}
|
||||
|
||||
func (p *fieldsPrinter) RoleAdd(role string, r v3.AuthRoleAddResponse) { p.hdr(r.Header) }
|
||||
func (p *fieldsPrinter) RoleGet(role string, r v3.AuthRoleGetResponse) { p.hdr(r.Header) }
|
||||
func (p *fieldsPrinter) RoleDelete(role string, r v3.AuthRoleDeleteResponse) { p.hdr(r.Header) }
|
||||
func (p *fieldsPrinter) RoleList(r v3.AuthRoleListResponse) { p.hdr(r.Header) }
|
||||
func (p *fieldsPrinter) RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse) {
|
||||
p.hdr(r.Header)
|
||||
}
|
||||
func (p *fieldsPrinter) RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse) {
|
||||
p.hdr(r.Header)
|
||||
}
|
||||
func (p *fieldsPrinter) UserAdd(user string, r v3.AuthUserAddResponse) { p.hdr(r.Header) }
|
||||
func (p *fieldsPrinter) UserChangePassword(r v3.AuthUserChangePasswordResponse) { p.hdr(r.Header) }
|
||||
func (p *fieldsPrinter) UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse) {
|
||||
p.hdr(r.Header)
|
||||
}
|
||||
func (p *fieldsPrinter) UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse) {
|
||||
p.hdr(r.Header)
|
||||
}
|
||||
func (p *fieldsPrinter) UserDelete(user string, r v3.AuthUserDeleteResponse) { p.hdr(r.Header) }
|
||||
41
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_json.go
generated
vendored
Normal file
41
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_json.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type jsonPrinter struct{ printer }
|
||||
|
||||
func newJSONPrinter() printer {
|
||||
return &jsonPrinter{
|
||||
&printerRPC{newPrinterUnsupported("json"), printJSON},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *jsonPrinter) EndpointStatus(r []epStatus) { printJSON(r) }
|
||||
func (p *jsonPrinter) DBStatus(r dbstatus) { printJSON(r) }
|
||||
|
||||
func printJSON(v interface{}) {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
64
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_protobuf.go
generated
vendored
Normal file
64
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_protobuf.go
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
mvccpb "github.com/coreos/etcd/mvcc/mvccpb"
|
||||
)
|
||||
|
||||
type pbPrinter struct{ printer }
|
||||
|
||||
type pbMarshal interface {
|
||||
Marshal() ([]byte, error)
|
||||
}
|
||||
|
||||
func newPBPrinter() printer {
|
||||
return &pbPrinter{
|
||||
&printerRPC{newPrinterUnsupported("protobuf"), printPB},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pbPrinter) Watch(r v3.WatchResponse) {
|
||||
evs := make([]*mvccpb.Event, len(r.Events))
|
||||
for i, ev := range r.Events {
|
||||
evs[i] = (*mvccpb.Event)(ev)
|
||||
}
|
||||
wr := pb.WatchResponse{
|
||||
Header: &r.Header,
|
||||
Events: evs,
|
||||
CompactRevision: r.CompactRevision,
|
||||
Canceled: r.Canceled,
|
||||
Created: r.Created,
|
||||
}
|
||||
printPB(&wr)
|
||||
}
|
||||
|
||||
func printPB(v interface{}) {
|
||||
m, ok := v.(pbMarshal)
|
||||
if !ok {
|
||||
ExitWithError(ExitBadFeature, fmt.Errorf("marshal unsupported for type %T (%v)", v, v))
|
||||
}
|
||||
b, err := m.Marshal()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf(string(b))
|
||||
}
|
||||
227
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_simple.go
generated
vendored
Normal file
227
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_simple.go
generated
vendored
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
)
|
||||
|
||||
type simplePrinter struct {
|
||||
isHex bool
|
||||
valueOnly bool
|
||||
}
|
||||
|
||||
func (s *simplePrinter) Del(resp v3.DeleteResponse) {
|
||||
fmt.Println(resp.Deleted)
|
||||
for _, kv := range resp.PrevKvs {
|
||||
printKV(s.isHex, s.valueOnly, kv)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) Get(resp v3.GetResponse) {
|
||||
for _, kv := range resp.Kvs {
|
||||
printKV(s.isHex, s.valueOnly, kv)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) Put(r v3.PutResponse) {
|
||||
fmt.Println("OK")
|
||||
if r.PrevKv != nil {
|
||||
printKV(s.isHex, s.valueOnly, r.PrevKv)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) Txn(resp v3.TxnResponse) {
|
||||
if resp.Succeeded {
|
||||
fmt.Println("SUCCESS")
|
||||
} else {
|
||||
fmt.Println("FAILURE")
|
||||
}
|
||||
|
||||
for _, r := range resp.Responses {
|
||||
fmt.Println("")
|
||||
switch v := r.Response.(type) {
|
||||
case *pb.ResponseOp_ResponseDeleteRange:
|
||||
s.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
|
||||
case *pb.ResponseOp_ResponsePut:
|
||||
s.Put((v3.PutResponse)(*v.ResponsePut))
|
||||
case *pb.ResponseOp_ResponseRange:
|
||||
s.Get(((v3.GetResponse)(*v.ResponseRange)))
|
||||
default:
|
||||
fmt.Printf("unexpected response %+v\n", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) Watch(resp v3.WatchResponse) {
|
||||
for _, e := range resp.Events {
|
||||
fmt.Println(e.Type)
|
||||
if e.PrevKv != nil {
|
||||
printKV(s.isHex, s.valueOnly, e.PrevKv)
|
||||
}
|
||||
printKV(s.isHex, s.valueOnly, e.Kv)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) TimeToLive(resp v3.LeaseTimeToLiveResponse, keys bool) {
|
||||
txt := fmt.Sprintf("lease %016x granted with TTL(%ds), remaining(%ds)", resp.ID, resp.GrantedTTL, resp.TTL)
|
||||
if keys {
|
||||
ks := make([]string, len(resp.Keys))
|
||||
for i := range resp.Keys {
|
||||
ks[i] = string(resp.Keys[i])
|
||||
}
|
||||
txt += fmt.Sprintf(", attached keys(%v)", ks)
|
||||
}
|
||||
fmt.Println(txt)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
|
||||
for _, e := range resp.Alarms {
|
||||
fmt.Printf("%+v\n", e)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) MemberAdd(r v3.MemberAddResponse) {
|
||||
fmt.Printf("Member %16x added to cluster %16x\n", r.Member.ID, r.Header.ClusterId)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) MemberRemove(id uint64, r v3.MemberRemoveResponse) {
|
||||
fmt.Printf("Member %16x removed from cluster %16x\n", id, r.Header.ClusterId)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) MemberUpdate(id uint64, r v3.MemberUpdateResponse) {
|
||||
fmt.Printf("Member %16x updated in cluster %16x\n", id, r.Header.ClusterId)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) MemberList(resp v3.MemberListResponse) {
|
||||
_, rows := makeMemberListTable(resp)
|
||||
for _, row := range rows {
|
||||
fmt.Println(strings.Join(row, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) EndpointStatus(statusList []epStatus) {
|
||||
_, rows := makeEndpointStatusTable(statusList)
|
||||
for _, row := range rows {
|
||||
fmt.Println(strings.Join(row, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) DBStatus(ds dbstatus) {
|
||||
_, rows := makeDBStatusTable(ds)
|
||||
for _, row := range rows {
|
||||
fmt.Println(strings.Join(row, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) RoleAdd(role string, r v3.AuthRoleAddResponse) {
|
||||
fmt.Printf("Role %s created\n", role)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) RoleGet(role string, r v3.AuthRoleGetResponse) {
|
||||
fmt.Printf("Role %s\n", role)
|
||||
fmt.Println("KV Read:")
|
||||
|
||||
printRange := func(perm *v3.Permission) {
|
||||
sKey := string(perm.Key)
|
||||
sRangeEnd := string(perm.RangeEnd)
|
||||
fmt.Printf("\t[%s, %s)", sKey, sRangeEnd)
|
||||
if strings.Compare(v3.GetPrefixRangeEnd(sKey), sRangeEnd) == 0 {
|
||||
fmt.Printf(" (prefix %s)", sKey)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
for _, perm := range r.Perm {
|
||||
if perm.PermType == v3.PermRead || perm.PermType == v3.PermReadWrite {
|
||||
if len(perm.RangeEnd) == 0 {
|
||||
fmt.Printf("\t%s\n", string(perm.Key))
|
||||
} else {
|
||||
printRange((*v3.Permission)(perm))
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println("KV Write:")
|
||||
for _, perm := range r.Perm {
|
||||
if perm.PermType == v3.PermWrite || perm.PermType == v3.PermReadWrite {
|
||||
if len(perm.RangeEnd) == 0 {
|
||||
fmt.Printf("\t%s\n", string(perm.Key))
|
||||
} else {
|
||||
printRange((*v3.Permission)(perm))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) RoleList(r v3.AuthRoleListResponse) {
|
||||
for _, role := range r.Roles {
|
||||
fmt.Printf("%s\n", role)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) RoleDelete(role string, r v3.AuthRoleDeleteResponse) {
|
||||
fmt.Printf("Role %s deleted\n", role)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse) {
|
||||
fmt.Printf("Role %s updated\n", role)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse) {
|
||||
if len(end) == 0 {
|
||||
fmt.Printf("Permission of key %s is revoked from role %s\n", key, role)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Permission of range [%s, %s) is revoked from role %s\n", key, end, role)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) UserAdd(name string, r v3.AuthUserAddResponse) {
|
||||
fmt.Printf("User %s created\n", name)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) UserGet(name string, r v3.AuthUserGetResponse) {
|
||||
fmt.Printf("User: %s\n", name)
|
||||
fmt.Printf("Roles:")
|
||||
for _, role := range r.Roles {
|
||||
fmt.Printf(" %s", role)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func (s *simplePrinter) UserChangePassword(v3.AuthUserChangePasswordResponse) {
|
||||
fmt.Println("Password updated")
|
||||
}
|
||||
|
||||
func (s *simplePrinter) UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse) {
|
||||
fmt.Printf("Role %s is granted to user %s\n", role, user)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse) {
|
||||
fmt.Printf("Role %s is revoked from user %s\n", role, user)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) UserDelete(user string, r v3.AuthUserDeleteResponse) {
|
||||
fmt.Printf("User %s deleted\n", user)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) UserList(r v3.AuthUserListResponse) {
|
||||
for _, user := range r.Users {
|
||||
fmt.Printf("%s\n", user)
|
||||
}
|
||||
}
|
||||
53
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_table.go
generated
vendored
Normal file
53
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/printer_table.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
)
|
||||
|
||||
type tablePrinter struct{ printer }
|
||||
|
||||
func (tp *tablePrinter) MemberList(r v3.MemberListResponse) {
|
||||
hdr, rows := makeMemberListTable(r)
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader(hdr)
|
||||
for _, row := range rows {
|
||||
table.Append(row)
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
func (tp *tablePrinter) EndpointStatus(r []epStatus) {
|
||||
hdr, rows := makeEndpointStatusTable(r)
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader(hdr)
|
||||
for _, row := range rows {
|
||||
table.Append(row)
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
func (tp *tablePrinter) DBStatus(r dbstatus) {
|
||||
hdr, rows := makeDBStatusTable(r)
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader(hdr)
|
||||
for _, row := range rows {
|
||||
table.Append(row)
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
95
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/put_command.go
generated
vendored
Normal file
95
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/put_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
leaseStr string
|
||||
putPrevKV bool
|
||||
)
|
||||
|
||||
// NewPutCommand returns the cobra command for "put".
|
||||
func NewPutCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "put [options] <key> <value> (<value> can also be given from stdin)",
|
||||
Short: "Puts the given key into the store",
|
||||
Long: `
|
||||
Puts the given key into the store.
|
||||
|
||||
When <value> begins with '-', <value> is interpreted as a flag.
|
||||
Insert '--' for workaround:
|
||||
|
||||
$ put <key> -- <value>
|
||||
$ put -- <key> <value>
|
||||
|
||||
If <value> isn't given as command line argument, this command tries to read the value from standard input.
|
||||
For example,
|
||||
$ cat file | put <key>
|
||||
will store the content of the file to <key>.
|
||||
`,
|
||||
Run: putCommandFunc,
|
||||
}
|
||||
cmd.Flags().StringVar(&leaseStr, "lease", "0", "lease ID (in hexadecimal) to attach to the key")
|
||||
cmd.Flags().BoolVar(&putPrevKV, "prev-kv", false, "return the previous key-value pair before modification")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// putCommandFunc executes the "put" command.
|
||||
func putCommandFunc(cmd *cobra.Command, args []string) {
|
||||
key, value, opts := getPutOp(cmd, args)
|
||||
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
resp, err := mustClientFromCmd(cmd).Put(ctx, key, value, opts...)
|
||||
cancel()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
display.Put(*resp)
|
||||
}
|
||||
|
||||
func getPutOp(cmd *cobra.Command, args []string) (string, string, []clientv3.OpOption) {
|
||||
if len(args) == 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("put command needs 1 argument and input from stdin or 2 arguments."))
|
||||
}
|
||||
|
||||
key := args[0]
|
||||
value, err := argOrStdin(args, os.Stdin, 1)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("put command needs 1 argument and input from stdin or 2 arguments."))
|
||||
}
|
||||
|
||||
id, err := strconv.ParseInt(leaseStr, 16, 64)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("bad lease ID (%v), expecting ID in Hex", err))
|
||||
}
|
||||
|
||||
opts := []clientv3.OpOption{}
|
||||
if id != 0 {
|
||||
opts = append(opts, clientv3.WithLease(clientv3.LeaseID(id)))
|
||||
}
|
||||
if putPrevKV {
|
||||
opts = append(opts, clientv3.WithPrevKV())
|
||||
}
|
||||
|
||||
return key, value, opts
|
||||
}
|
||||
200
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/role_command.go
generated
vendored
Normal file
200
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/role_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
grantPermissionPrefix bool
|
||||
)
|
||||
|
||||
// NewRoleCommand returns the cobra command for "role".
|
||||
func NewRoleCommand() *cobra.Command {
|
||||
ac := &cobra.Command{
|
||||
Use: "role <subcommand>",
|
||||
Short: "Role related commands",
|
||||
}
|
||||
|
||||
ac.AddCommand(newRoleAddCommand())
|
||||
ac.AddCommand(newRoleDeleteCommand())
|
||||
ac.AddCommand(newRoleGetCommand())
|
||||
ac.AddCommand(newRoleListCommand())
|
||||
ac.AddCommand(newRoleGrantPermissionCommand())
|
||||
ac.AddCommand(newRoleRevokePermissionCommand())
|
||||
|
||||
return ac
|
||||
}
|
||||
|
||||
func newRoleAddCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "add <role name>",
|
||||
Short: "Adds a new role",
|
||||
Run: roleAddCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func newRoleDeleteCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "delete <role name>",
|
||||
Short: "Deletes a role",
|
||||
Run: roleDeleteCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func newRoleGetCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "get <role name>",
|
||||
Short: "Gets detailed information of a role",
|
||||
Run: roleGetCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func newRoleListCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "Lists all roles",
|
||||
Run: roleListCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func newRoleGrantPermissionCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "grant-permission [options] <role name> <permission type> <key> [endkey]",
|
||||
Short: "Grants a key to a role",
|
||||
Run: roleGrantPermissionCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&grantPermissionPrefix, "prefix", false, "grant a prefix permission")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newRoleRevokePermissionCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "revoke-permission <role name> <key> [endkey]",
|
||||
Short: "Revokes a key from a role",
|
||||
Run: roleRevokePermissionCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// roleAddCommandFunc executes the "role add" command.
|
||||
func roleAddCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("role add command requires role name as its argument."))
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.RoleAdd(context.TODO(), args[0])
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.RoleAdd(args[0], *resp)
|
||||
}
|
||||
|
||||
// roleDeleteCommandFunc executes the "role delete" command.
|
||||
func roleDeleteCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("role delete command requires role name as its argument."))
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.RoleDelete(context.TODO(), args[0])
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.RoleDelete(args[0], *resp)
|
||||
}
|
||||
|
||||
// roleGetCommandFunc executes the "role get" command.
|
||||
func roleGetCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("role get command requires role name as its argument."))
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
resp, err := mustClientFromCmd(cmd).Auth.RoleGet(context.TODO(), name)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.RoleGet(name, *resp)
|
||||
}
|
||||
|
||||
// roleListCommandFunc executes the "role list" command.
|
||||
func roleListCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("role list command requires no arguments."))
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.RoleList(context.TODO())
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.RoleList(*resp)
|
||||
}
|
||||
|
||||
// roleGrantPermissionCommandFunc executes the "role grant-permission" command.
|
||||
func roleGrantPermissionCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) < 3 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("role grant command requires role name, permission type, and key [endkey] as its argument."))
|
||||
}
|
||||
|
||||
perm, err := clientv3.StrToPermissionType(args[1])
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
|
||||
rangeEnd := ""
|
||||
if 4 <= len(args) {
|
||||
if grantPermissionPrefix {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("don't pass both of --prefix option and range end to grant permission command"))
|
||||
}
|
||||
rangeEnd = args[3]
|
||||
} else if grantPermissionPrefix {
|
||||
rangeEnd = clientv3.GetPrefixRangeEnd(args[2])
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.RoleGrantPermission(context.TODO(), args[0], args[2], rangeEnd, perm)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.RoleGrantPermission(args[0], *resp)
|
||||
}
|
||||
|
||||
// roleRevokePermissionCommandFunc executes the "role revoke-permission" command.
|
||||
func roleRevokePermissionCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) < 2 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("role revoke-permission command requires role name and key [endkey] as its argument."))
|
||||
}
|
||||
|
||||
rangeEnd := ""
|
||||
if 3 <= len(args) {
|
||||
rangeEnd = args[2]
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.RoleRevokePermission(context.TODO(), args[0], args[1], rangeEnd)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
display.RoleRevokePermission(args[0], args[1], rangeEnd, *resp)
|
||||
}
|
||||
461
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/snapshot_command.go
generated
vendored
Normal file
461
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/snapshot_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/coreos/etcd/etcdserver"
|
||||
"github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"github.com/coreos/etcd/etcdserver/membership"
|
||||
"github.com/coreos/etcd/lease"
|
||||
"github.com/coreos/etcd/mvcc"
|
||||
"github.com/coreos/etcd/mvcc/backend"
|
||||
"github.com/coreos/etcd/pkg/fileutil"
|
||||
"github.com/coreos/etcd/pkg/types"
|
||||
"github.com/coreos/etcd/raft"
|
||||
"github.com/coreos/etcd/raft/raftpb"
|
||||
"github.com/coreos/etcd/snap"
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/coreos/etcd/wal"
|
||||
"github.com/coreos/etcd/wal/walpb"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultName = "default"
|
||||
defaultInitialAdvertisePeerURLs = "http://localhost:2380"
|
||||
)
|
||||
|
||||
var (
|
||||
restoreCluster string
|
||||
restoreClusterToken string
|
||||
restoreDataDir string
|
||||
restorePeerURLs string
|
||||
restoreName string
|
||||
skipHashCheck bool
|
||||
)
|
||||
|
||||
// NewSnapshotCommand returns the cobra command for "snapshot".
|
||||
func NewSnapshotCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "snapshot <subcommand>",
|
||||
Short: "Manages etcd node snapshots",
|
||||
}
|
||||
cmd.AddCommand(NewSnapshotSaveCommand())
|
||||
cmd.AddCommand(NewSnapshotRestoreCommand())
|
||||
cmd.AddCommand(newSnapshotStatusCommand())
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewSnapshotSaveCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "save <filename>",
|
||||
Short: "Stores an etcd node backend snapshot to a given file",
|
||||
Run: snapshotSaveCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func newSnapshotStatusCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "status <filename>",
|
||||
Short: "Gets backend snapshot status of a given file",
|
||||
Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint.
|
||||
The items in the lists are hash, revision, total keys, total size.
|
||||
`,
|
||||
Run: snapshotStatusCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSnapshotRestoreCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "restore <filename> [options]",
|
||||
Short: "Restores an etcd member snapshot to an etcd directory",
|
||||
Run: snapshotRestoreCommandFunc,
|
||||
}
|
||||
cmd.Flags().StringVar(&restoreDataDir, "data-dir", "", "Path to the data directory")
|
||||
cmd.Flags().StringVar(&restoreCluster, "initial-cluster", initialClusterFromName(defaultName), "Initial cluster configuration for restore bootstrap")
|
||||
cmd.Flags().StringVar(&restoreClusterToken, "initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during restore bootstrap")
|
||||
cmd.Flags().StringVar(&restorePeerURLs, "initial-advertise-peer-urls", defaultInitialAdvertisePeerURLs, "List of this member's peer URLs to advertise to the rest of the cluster")
|
||||
cmd.Flags().StringVar(&restoreName, "name", defaultName, "Human-readable name for this member")
|
||||
cmd.Flags().BoolVar(&skipHashCheck, "skip-hash-check", false, "Ignore snapshot integrity hash value (required if copied from data directory)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func snapshotSaveCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
err := fmt.Errorf("snapshot save expects one argument")
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
|
||||
path := args[0]
|
||||
|
||||
partpath := path + ".part"
|
||||
f, err := os.Create(partpath)
|
||||
|
||||
if err != nil {
|
||||
exiterr := fmt.Errorf("could not open %s (%v)", partpath, err)
|
||||
ExitWithError(ExitBadArgs, exiterr)
|
||||
}
|
||||
|
||||
c := mustClientFromCmd(cmd)
|
||||
r, serr := c.Snapshot(context.TODO())
|
||||
if serr != nil {
|
||||
os.RemoveAll(partpath)
|
||||
ExitWithError(ExitInterrupted, serr)
|
||||
}
|
||||
if _, rerr := io.Copy(f, r); rerr != nil {
|
||||
os.RemoveAll(partpath)
|
||||
ExitWithError(ExitInterrupted, rerr)
|
||||
}
|
||||
|
||||
fileutil.Fsync(f)
|
||||
|
||||
f.Close()
|
||||
|
||||
if rerr := os.Rename(partpath, path); rerr != nil {
|
||||
exiterr := fmt.Errorf("could not rename %s to %s (%v)", partpath, path, rerr)
|
||||
ExitWithError(ExitIO, exiterr)
|
||||
}
|
||||
fmt.Printf("Snapshot saved at %s\n", path)
|
||||
}
|
||||
|
||||
func snapshotStatusCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
err := fmt.Errorf("snapshot status requires exactly one argument")
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
initDisplayFromCmd(cmd)
|
||||
ds := dbStatus(args[0])
|
||||
display.DBStatus(ds)
|
||||
}
|
||||
|
||||
func snapshotRestoreCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
err := fmt.Errorf("snapshot restore requires exactly one argument")
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
|
||||
urlmap, uerr := types.NewURLsMap(restoreCluster)
|
||||
if uerr != nil {
|
||||
ExitWithError(ExitBadArgs, uerr)
|
||||
}
|
||||
|
||||
cfg := etcdserver.ServerConfig{
|
||||
InitialClusterToken: restoreClusterToken,
|
||||
InitialPeerURLsMap: urlmap,
|
||||
PeerURLs: types.MustNewURLs(strings.Split(restorePeerURLs, ",")),
|
||||
Name: restoreName,
|
||||
}
|
||||
if err := cfg.VerifyBootstrap(); err != nil {
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
|
||||
cl, cerr := membership.NewClusterFromURLsMap(restoreClusterToken, urlmap)
|
||||
if cerr != nil {
|
||||
ExitWithError(ExitBadArgs, cerr)
|
||||
}
|
||||
|
||||
basedir := restoreDataDir
|
||||
if basedir == "" {
|
||||
basedir = restoreName + ".etcd"
|
||||
}
|
||||
|
||||
waldir := filepath.Join(basedir, "member", "wal")
|
||||
snapdir := filepath.Join(basedir, "member", "snap")
|
||||
|
||||
if _, err := os.Stat(basedir); err == nil {
|
||||
ExitWithError(ExitInvalidInput, fmt.Errorf("data-dir %q exists", basedir))
|
||||
}
|
||||
|
||||
makeDB(snapdir, args[0], len(cl.Members()))
|
||||
makeWALAndSnap(waldir, snapdir, cl)
|
||||
}
|
||||
|
||||
func initialClusterFromName(name string) string {
|
||||
n := name
|
||||
if name == "" {
|
||||
n = defaultName
|
||||
}
|
||||
return fmt.Sprintf("%s=http://localhost:2380", n)
|
||||
}
|
||||
|
||||
// makeWAL creates a WAL for the initial cluster
|
||||
func makeWALAndSnap(waldir, snapdir string, cl *membership.RaftCluster) {
|
||||
if err := fileutil.CreateDirAll(waldir); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
|
||||
// add members again to persist them to the store we create.
|
||||
st := store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix)
|
||||
cl.SetStore(st)
|
||||
for _, m := range cl.Members() {
|
||||
cl.AddMember(m)
|
||||
}
|
||||
|
||||
m := cl.MemberByName(restoreName)
|
||||
md := &etcdserverpb.Metadata{NodeID: uint64(m.ID), ClusterID: uint64(cl.ID())}
|
||||
metadata, merr := md.Marshal()
|
||||
if merr != nil {
|
||||
ExitWithError(ExitInvalidInput, merr)
|
||||
}
|
||||
|
||||
w, walerr := wal.Create(waldir, metadata)
|
||||
if walerr != nil {
|
||||
ExitWithError(ExitIO, walerr)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
peers := make([]raft.Peer, len(cl.MemberIDs()))
|
||||
for i, id := range cl.MemberIDs() {
|
||||
ctx, err := json.Marshal((*cl).Member(id))
|
||||
if err != nil {
|
||||
ExitWithError(ExitInvalidInput, err)
|
||||
}
|
||||
peers[i] = raft.Peer{ID: uint64(id), Context: ctx}
|
||||
}
|
||||
|
||||
ents := make([]raftpb.Entry, len(peers))
|
||||
nodeIDs := make([]uint64, len(peers))
|
||||
for i, p := range peers {
|
||||
nodeIDs[i] = p.ID
|
||||
cc := raftpb.ConfChange{
|
||||
Type: raftpb.ConfChangeAddNode,
|
||||
NodeID: p.ID,
|
||||
Context: p.Context}
|
||||
d, err := cc.Marshal()
|
||||
if err != nil {
|
||||
ExitWithError(ExitInvalidInput, err)
|
||||
}
|
||||
e := raftpb.Entry{
|
||||
Type: raftpb.EntryConfChange,
|
||||
Term: 1,
|
||||
Index: uint64(i + 1),
|
||||
Data: d,
|
||||
}
|
||||
ents[i] = e
|
||||
}
|
||||
|
||||
commit, term := uint64(len(ents)), uint64(1)
|
||||
|
||||
if err := w.Save(raftpb.HardState{
|
||||
Term: term,
|
||||
Vote: peers[0].ID,
|
||||
Commit: commit}, ents); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
|
||||
b, berr := st.Save()
|
||||
if berr != nil {
|
||||
ExitWithError(ExitError, berr)
|
||||
}
|
||||
|
||||
raftSnap := raftpb.Snapshot{
|
||||
Data: b,
|
||||
Metadata: raftpb.SnapshotMetadata{
|
||||
Index: commit,
|
||||
Term: term,
|
||||
ConfState: raftpb.ConfState{
|
||||
Nodes: nodeIDs,
|
||||
},
|
||||
},
|
||||
}
|
||||
snapshotter := snap.New(snapdir)
|
||||
if err := snapshotter.SaveSnap(raftSnap); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := w.SaveSnapshot(walpb.Snapshot{Index: commit, Term: term}); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
}
|
||||
|
||||
// initIndex implements ConsistentIndexGetter so the snapshot won't block
|
||||
// the new raft instance by waiting for a future raft index.
|
||||
type initIndex int
|
||||
|
||||
func (i *initIndex) ConsistentIndex() uint64 { return uint64(*i) }
|
||||
|
||||
// makeDB copies the database snapshot to the snapshot directory
|
||||
func makeDB(snapdir, dbfile string, commit int) {
|
||||
f, ferr := os.OpenFile(dbfile, os.O_RDONLY, 0600)
|
||||
if ferr != nil {
|
||||
ExitWithError(ExitInvalidInput, ferr)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// get snapshot integrity hash
|
||||
if _, err := f.Seek(-sha256.Size, os.SEEK_END); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
sha := make([]byte, sha256.Size)
|
||||
if _, err := f.Read(sha); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
if _, err := f.Seek(0, os.SEEK_SET); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
|
||||
if err := fileutil.CreateDirAll(snapdir); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
|
||||
dbpath := filepath.Join(snapdir, "db")
|
||||
db, dberr := os.OpenFile(dbpath, os.O_RDWR|os.O_CREATE, 0600)
|
||||
if dberr != nil {
|
||||
ExitWithError(ExitIO, dberr)
|
||||
}
|
||||
if _, err := io.Copy(db, f); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
|
||||
// truncate away integrity hash, if any.
|
||||
off, serr := db.Seek(0, os.SEEK_END)
|
||||
if serr != nil {
|
||||
ExitWithError(ExitIO, serr)
|
||||
}
|
||||
hasHash := (off % 512) == sha256.Size
|
||||
if hasHash {
|
||||
if err := db.Truncate(off - sha256.Size); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
}
|
||||
|
||||
if !hasHash && !skipHashCheck {
|
||||
err := fmt.Errorf("snapshot missing hash but --skip-hash-check=false")
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
|
||||
if hasHash && !skipHashCheck {
|
||||
// check for match
|
||||
if _, err := db.Seek(0, os.SEEK_SET); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, db); err != nil {
|
||||
ExitWithError(ExitIO, err)
|
||||
}
|
||||
dbsha := h.Sum(nil)
|
||||
if !reflect.DeepEqual(sha, dbsha) {
|
||||
err := fmt.Errorf("expected sha256 %v, got %v", sha, dbsha)
|
||||
ExitWithError(ExitInvalidInput, err)
|
||||
}
|
||||
}
|
||||
|
||||
// db hash is OK, can now modify DB so it can be part of a new cluster
|
||||
db.Close()
|
||||
|
||||
// update consistentIndex so applies go through on etcdserver despite
|
||||
// having a new raft instance
|
||||
be := backend.NewDefaultBackend(dbpath)
|
||||
// a lessor never timeouts leases
|
||||
lessor := lease.NewLessor(be, math.MaxInt64)
|
||||
|
||||
s := mvcc.NewStore(be, lessor, (*initIndex)(&commit))
|
||||
id := s.TxnBegin()
|
||||
btx := be.BatchTx()
|
||||
del := func(k, v []byte) error {
|
||||
_, _, err := s.TxnDeleteRange(id, k, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// delete stored members from old cluster since using new members
|
||||
btx.UnsafeForEach([]byte("members"), del)
|
||||
// todo: add back new members when we start to deprecate old snap file.
|
||||
btx.UnsafeForEach([]byte("members_removed"), del)
|
||||
// trigger write-out of new consistent index
|
||||
s.TxnEnd(id)
|
||||
s.Commit()
|
||||
s.Close()
|
||||
}
|
||||
|
||||
type dbstatus struct {
|
||||
Hash uint32 `json:"hash"`
|
||||
Revision int64 `json:"revision"`
|
||||
TotalKey int `json:"totalKey"`
|
||||
TotalSize int64 `json:"totalSize"`
|
||||
}
|
||||
|
||||
func dbStatus(p string) dbstatus {
|
||||
if _, err := os.Stat(p); err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
ds := dbstatus{}
|
||||
|
||||
db, err := bolt.Open(p, 0400, nil)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
h := crc32.New(crc32.MakeTable(crc32.Castagnoli))
|
||||
|
||||
err = db.View(func(tx *bolt.Tx) error {
|
||||
ds.TotalSize = tx.Size()
|
||||
c := tx.Cursor()
|
||||
for next, _ := c.First(); next != nil; next, _ = c.Next() {
|
||||
b := tx.Bucket(next)
|
||||
if b == nil {
|
||||
return fmt.Errorf("cannot get hash of bucket %s", string(next))
|
||||
}
|
||||
h.Write(next)
|
||||
iskeyb := (string(next) == "key")
|
||||
b.ForEach(func(k, v []byte) error {
|
||||
h.Write(k)
|
||||
h.Write(v)
|
||||
if iskeyb {
|
||||
rev := bytesToRev(k)
|
||||
ds.Revision = rev.main
|
||||
}
|
||||
ds.TotalKey++
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
ds.Hash = h.Sum32()
|
||||
return ds
|
||||
}
|
||||
|
||||
type revision struct {
|
||||
main int64
|
||||
sub int64
|
||||
}
|
||||
|
||||
func bytesToRev(bytes []byte) revision {
|
||||
return revision{
|
||||
main: int64(binary.BigEndian.Uint64(bytes[0:8])),
|
||||
sub: int64(binary.BigEndian.Uint64(bytes[9:])),
|
||||
}
|
||||
}
|
||||
205
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/txn_command.go
generated
vendored
Normal file
205
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/txn_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
txnInteractive bool
|
||||
)
|
||||
|
||||
// NewTxnCommand returns the cobra command for "txn".
|
||||
func NewTxnCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "txn [options]",
|
||||
Short: "Txn processes all the requests in one transaction",
|
||||
Run: txnCommandFunc,
|
||||
}
|
||||
cmd.Flags().BoolVarP(&txnInteractive, "interactive", "i", false, "Input transaction in interactive mode")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// txnCommandFunc executes the "txn" command.
|
||||
func txnCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("txn command does not accept argument."))
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
txn := mustClientFromCmd(cmd).Txn(context.Background())
|
||||
promptInteractive("compares:")
|
||||
txn.If(readCompares(reader)...)
|
||||
promptInteractive("success requests (get, put, delete):")
|
||||
txn.Then(readOps(reader)...)
|
||||
promptInteractive("failure requests (get, put, delete):")
|
||||
txn.Else(readOps(reader)...)
|
||||
|
||||
resp, err := txn.Commit()
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.Txn(*resp)
|
||||
}
|
||||
|
||||
func promptInteractive(s string) {
|
||||
if txnInteractive {
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
|
||||
func readCompares(r *bufio.Reader) (cmps []clientv3.Cmp) {
|
||||
for {
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
ExitWithError(ExitInvalidInput, err)
|
||||
}
|
||||
|
||||
// remove space from the line
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
cmp, err := parseCompare(line)
|
||||
if err != nil {
|
||||
ExitWithError(ExitInvalidInput, err)
|
||||
}
|
||||
cmps = append(cmps, *cmp)
|
||||
}
|
||||
|
||||
return cmps
|
||||
}
|
||||
|
||||
func readOps(r *bufio.Reader) (ops []clientv3.Op) {
|
||||
for {
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
ExitWithError(ExitInvalidInput, err)
|
||||
}
|
||||
|
||||
// remove space from the line
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
op, err := parseRequestUnion(line)
|
||||
if err != nil {
|
||||
ExitWithError(ExitInvalidInput, err)
|
||||
}
|
||||
ops = append(ops, *op)
|
||||
}
|
||||
|
||||
return ops
|
||||
}
|
||||
|
||||
func parseRequestUnion(line string) (*clientv3.Op, error) {
|
||||
args := argify(line)
|
||||
if len(args) < 2 {
|
||||
return nil, fmt.Errorf("invalid txn compare request: %s", line)
|
||||
}
|
||||
|
||||
opc := make(chan clientv3.Op, 1)
|
||||
|
||||
put := NewPutCommand()
|
||||
put.Run = func(cmd *cobra.Command, args []string) {
|
||||
key, value, opts := getPutOp(cmd, args)
|
||||
opc <- clientv3.OpPut(key, value, opts...)
|
||||
}
|
||||
get := NewGetCommand()
|
||||
get.Run = func(cmd *cobra.Command, args []string) {
|
||||
key, opts := getGetOp(cmd, args)
|
||||
opc <- clientv3.OpGet(key, opts...)
|
||||
}
|
||||
del := NewDelCommand()
|
||||
del.Run = func(cmd *cobra.Command, args []string) {
|
||||
key, opts := getDelOp(cmd, args)
|
||||
opc <- clientv3.OpDelete(key, opts...)
|
||||
}
|
||||
cmds := &cobra.Command{SilenceErrors: true}
|
||||
cmds.AddCommand(put, get, del)
|
||||
|
||||
cmds.SetArgs(args)
|
||||
if err := cmds.Execute(); err != nil {
|
||||
return nil, fmt.Errorf("invalid txn request: %s", line)
|
||||
}
|
||||
|
||||
op := <-opc
|
||||
return &op, nil
|
||||
}
|
||||
|
||||
func parseCompare(line string) (*clientv3.Cmp, error) {
|
||||
var (
|
||||
key string
|
||||
op string
|
||||
val string
|
||||
)
|
||||
|
||||
lparenSplit := strings.SplitN(line, "(", 2)
|
||||
if len(lparenSplit) != 2 {
|
||||
return nil, fmt.Errorf("malformed comparison: %s", line)
|
||||
}
|
||||
|
||||
target := lparenSplit[0]
|
||||
n, serr := fmt.Sscanf(lparenSplit[1], "%q) %s %q", &key, &op, &val)
|
||||
if n != 3 {
|
||||
return nil, fmt.Errorf("malformed comparison: %s; got %s(%q) %s %q", line, target, key, op, val)
|
||||
}
|
||||
if serr != nil {
|
||||
return nil, fmt.Errorf("malformed comparison: %s (%v)", line, serr)
|
||||
}
|
||||
|
||||
var (
|
||||
v int64
|
||||
err error
|
||||
cmp clientv3.Cmp
|
||||
)
|
||||
switch target {
|
||||
case "ver", "version":
|
||||
if v, err = strconv.ParseInt(val, 10, 64); err == nil {
|
||||
cmp = clientv3.Compare(clientv3.Version(key), op, v)
|
||||
}
|
||||
case "c", "create":
|
||||
if v, err = strconv.ParseInt(val, 10, 64); err == nil {
|
||||
cmp = clientv3.Compare(clientv3.CreateRevision(key), op, v)
|
||||
}
|
||||
case "m", "mod":
|
||||
if v, err = strconv.ParseInt(val, 10, 64); err == nil {
|
||||
cmp = clientv3.Compare(clientv3.ModRevision(key), op, v)
|
||||
}
|
||||
case "val", "value":
|
||||
cmp = clientv3.Compare(clientv3.Value(key), op, val)
|
||||
default:
|
||||
return nil, fmt.Errorf("malformed comparison: %s (unknown target %s)", line, target)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid txn compare request: %s", line)
|
||||
}
|
||||
|
||||
return &cmp, nil
|
||||
}
|
||||
280
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/user_command.go
generated
vendored
Normal file
280
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/user_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
userShowDetail bool
|
||||
)
|
||||
|
||||
// NewUserCommand returns the cobra command for "user".
|
||||
func NewUserCommand() *cobra.Command {
|
||||
ac := &cobra.Command{
|
||||
Use: "user <subcommand>",
|
||||
Short: "User related commands",
|
||||
}
|
||||
|
||||
ac.AddCommand(newUserAddCommand())
|
||||
ac.AddCommand(newUserDeleteCommand())
|
||||
ac.AddCommand(newUserGetCommand())
|
||||
ac.AddCommand(newUserListCommand())
|
||||
ac.AddCommand(newUserChangePasswordCommand())
|
||||
ac.AddCommand(newUserGrantRoleCommand())
|
||||
ac.AddCommand(newUserRevokeRoleCommand())
|
||||
|
||||
return ac
|
||||
}
|
||||
|
||||
var (
|
||||
passwordInteractive bool
|
||||
)
|
||||
|
||||
func newUserAddCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "add <user name or user:password> [options]",
|
||||
Short: "Adds a new user",
|
||||
Run: userAddCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&passwordInteractive, "interactive", true, "Read password from stdin instead of interactive terminal")
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
||||
func newUserDeleteCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "delete <user name>",
|
||||
Short: "Deletes a user",
|
||||
Run: userDeleteCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func newUserGetCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "get <user name> [options]",
|
||||
Short: "Gets detailed information of a user",
|
||||
Run: userGetCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&userShowDetail, "detail", false, "Show permissions of roles granted to the user")
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
||||
func newUserListCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "Lists all users",
|
||||
Run: userListCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func newUserChangePasswordCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "passwd <user name> [options]",
|
||||
Short: "Changes password of user",
|
||||
Run: userChangePasswordCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&passwordInteractive, "interactive", true, "If true, read password from stdin instead of interactive terminal")
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
||||
func newUserGrantRoleCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "grant-role <user name> <role name>",
|
||||
Short: "Grants a role to a user",
|
||||
Run: userGrantRoleCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func newUserRevokeRoleCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "revoke-role <user name> <role name>",
|
||||
Short: "Revokes a role from a user",
|
||||
Run: userRevokeRoleCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// userAddCommandFunc executes the "user add" command.
|
||||
func userAddCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("user add command requires user name as its argument."))
|
||||
}
|
||||
|
||||
var password string
|
||||
var user string
|
||||
|
||||
splitted := strings.SplitN(args[0], ":", 2)
|
||||
if len(splitted) < 2 {
|
||||
user = args[0]
|
||||
if !passwordInteractive {
|
||||
fmt.Scanf("%s", &password)
|
||||
} else {
|
||||
password = readPasswordInteractive(args[0])
|
||||
}
|
||||
} else {
|
||||
user = splitted[0]
|
||||
password = splitted[1]
|
||||
if len(user) == 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("empty user name is not allowed."))
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.UserAdd(context.TODO(), user, password)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.UserAdd(user, *resp)
|
||||
}
|
||||
|
||||
// userDeleteCommandFunc executes the "user delete" command.
|
||||
func userDeleteCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("user delete command requires user name as its argument."))
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.UserDelete(context.TODO(), args[0])
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
display.UserDelete(args[0], *resp)
|
||||
}
|
||||
|
||||
// userGetCommandFunc executes the "user get" command.
|
||||
func userGetCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("user get command requires user name as its argument."))
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
client := mustClientFromCmd(cmd)
|
||||
resp, err := client.Auth.UserGet(context.TODO(), name)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
if userShowDetail {
|
||||
fmt.Printf("User: %s\n", name)
|
||||
for _, role := range resp.Roles {
|
||||
fmt.Printf("\n")
|
||||
roleResp, err := client.Auth.RoleGet(context.TODO(), role)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
display.RoleGet(role, *roleResp)
|
||||
}
|
||||
} else {
|
||||
display.UserGet(name, *resp)
|
||||
}
|
||||
}
|
||||
|
||||
// userListCommandFunc executes the "user list" command.
|
||||
func userListCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("user list command requires no arguments."))
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.UserList(context.TODO())
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.UserList(*resp)
|
||||
}
|
||||
|
||||
// userChangePasswordCommandFunc executes the "user passwd" command.
|
||||
func userChangePasswordCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("user passwd command requires user name as its argument."))
|
||||
}
|
||||
|
||||
var password string
|
||||
|
||||
if !passwordInteractive {
|
||||
fmt.Scanf("%s", &password)
|
||||
} else {
|
||||
password = readPasswordInteractive(args[0])
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.UserChangePassword(context.TODO(), args[0], password)
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.UserChangePassword(*resp)
|
||||
}
|
||||
|
||||
// userGrantRoleCommandFunc executes the "user grant-role" command.
|
||||
func userGrantRoleCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("user grant command requires user name and role name as its argument."))
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.UserGrantRole(context.TODO(), args[0], args[1])
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.UserGrantRole(args[0], args[1], *resp)
|
||||
}
|
||||
|
||||
// userRevokeRoleCommandFunc executes the "user revoke-role" command.
|
||||
func userRevokeRoleCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("user revoke-role requires user name and role name as its argument."))
|
||||
}
|
||||
|
||||
resp, err := mustClientFromCmd(cmd).Auth.UserRevokeRole(context.TODO(), args[0], args[1])
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
display.UserRevokeRole(args[0], args[1], *resp)
|
||||
}
|
||||
|
||||
func readPasswordInteractive(name string) string {
|
||||
prompt1 := fmt.Sprintf("Password of %s: ", name)
|
||||
password1, err1 := speakeasy.Ask(prompt1)
|
||||
if err1 != nil {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("failed to ask password: %s.", err1))
|
||||
}
|
||||
|
||||
if len(password1) == 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("empty password"))
|
||||
}
|
||||
|
||||
prompt2 := fmt.Sprintf("Type password of %s again for confirmation: ", name)
|
||||
password2, err2 := speakeasy.Ask(prompt2)
|
||||
if err2 != nil {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("failed to ask password: %s.", err2))
|
||||
}
|
||||
|
||||
if strings.Compare(password1, password2) != 0 {
|
||||
ExitWithError(ExitBadArgs, fmt.Errorf("given passwords are different."))
|
||||
}
|
||||
|
||||
return password1
|
||||
}
|
||||
76
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/util.go
generated
vendored
Normal file
76
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
pb "github.com/coreos/etcd/mvcc/mvccpb"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func printKV(isHex bool, valueOnly bool, kv *pb.KeyValue) {
|
||||
k, v := string(kv.Key), string(kv.Value)
|
||||
if isHex {
|
||||
k = addHexPrefix(hex.EncodeToString(kv.Key))
|
||||
v = addHexPrefix(hex.EncodeToString(kv.Value))
|
||||
}
|
||||
if !valueOnly {
|
||||
fmt.Println(k)
|
||||
}
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
func addHexPrefix(s string) string {
|
||||
ns := make([]byte, len(s)*2)
|
||||
for i := 0; i < len(s); i += 2 {
|
||||
ns[i*2] = '\\'
|
||||
ns[i*2+1] = 'x'
|
||||
ns[i*2+2] = s[i]
|
||||
ns[i*2+3] = s[i+1]
|
||||
}
|
||||
return string(ns)
|
||||
}
|
||||
|
||||
func argify(s string) []string {
|
||||
r := regexp.MustCompile(`"(?:[^"\\]|\\.)*"|'[^']*'|[^'"\s]\S*[^'"\s]?`)
|
||||
args := r.FindAllString(s, -1)
|
||||
for i := range args {
|
||||
if len(args[i]) == 0 {
|
||||
continue
|
||||
}
|
||||
if args[i][0] == '\'' {
|
||||
// 'single-quoted string'
|
||||
args[i] = args[i][1 : len(args)-1]
|
||||
} else if args[i][0] == '"' {
|
||||
// "double quoted string"
|
||||
if _, err := fmt.Sscanf(args[i], "%q", &args[i]); err != nil {
|
||||
ExitWithError(ExitInvalidInput, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func commandCtx(cmd *cobra.Command) (context.Context, context.CancelFunc) {
|
||||
timeOut, err := cmd.Flags().GetDuration("command-timeout")
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
return context.WithTimeout(context.Background(), timeOut)
|
||||
}
|
||||
36
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/version_command.go
generated
vendored
Normal file
36
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/version_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/etcd/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewVersionCommand prints out the version of etcd.
|
||||
func NewVersionCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Prints the version of etcdctl",
|
||||
Run: versionCommandFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func versionCommandFunc(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("etcdctl version:", version.Version)
|
||||
fmt.Println("API version:", version.APIVersion)
|
||||
}
|
||||
134
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/watch_command.go
generated
vendored
Normal file
134
vendor/github.com/coreos/etcd/etcdctl/ctlv3/command/watch_command.go
generated
vendored
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var (
|
||||
watchRev int64
|
||||
watchPrefix bool
|
||||
watchInteractive bool
|
||||
watchPrevKey bool
|
||||
)
|
||||
|
||||
// NewWatchCommand returns the cobra command for "watch".
|
||||
func NewWatchCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "watch [options] [key or prefix] [range_end]",
|
||||
Short: "Watches events stream on keys or prefixes",
|
||||
Run: watchCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVarP(&watchInteractive, "interactive", "i", false, "Interactive mode")
|
||||
cmd.Flags().BoolVar(&watchPrefix, "prefix", false, "Watch on a prefix if prefix is set")
|
||||
cmd.Flags().Int64Var(&watchRev, "rev", 0, "Revision to start watching")
|
||||
cmd.Flags().BoolVar(&watchPrevKey, "prev-kv", false, "get the previous key-value pair before the event happens")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// watchCommandFunc executes the "watch" command.
|
||||
func watchCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if watchInteractive {
|
||||
watchInteractiveFunc(cmd, args)
|
||||
return
|
||||
}
|
||||
|
||||
c := mustClientFromCmd(cmd)
|
||||
wc, err := getWatchChan(c, args)
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadArgs, err)
|
||||
}
|
||||
|
||||
printWatchCh(wc)
|
||||
if err = c.Close(); err != nil {
|
||||
ExitWithError(ExitBadConnection, err)
|
||||
}
|
||||
ExitWithError(ExitInterrupted, fmt.Errorf("watch is canceled by the server"))
|
||||
}
|
||||
|
||||
func watchInteractiveFunc(cmd *cobra.Command, args []string) {
|
||||
c := mustClientFromCmd(cmd)
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
for {
|
||||
l, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
ExitWithError(ExitInvalidInput, fmt.Errorf("Error reading watch request line: %v", err))
|
||||
}
|
||||
l = strings.TrimSuffix(l, "\n")
|
||||
|
||||
args := argify(l)
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintf(os.Stderr, "Invalid command %s (command type or key is not provided)\n", l)
|
||||
continue
|
||||
}
|
||||
|
||||
if args[0] != "watch" {
|
||||
fmt.Fprintf(os.Stderr, "Invalid command %s (only support watch)\n", l)
|
||||
continue
|
||||
}
|
||||
|
||||
flagset := NewWatchCommand().Flags()
|
||||
err = flagset.Parse(args[1:])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Invalid command %s (%v)\n", l, err)
|
||||
continue
|
||||
}
|
||||
ch, err := getWatchChan(c, flagset.Args())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Invalid command %s (%v)\n", l, err)
|
||||
continue
|
||||
}
|
||||
go printWatchCh(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func getWatchChan(c *clientv3.Client, args []string) (clientv3.WatchChan, error) {
|
||||
if len(args) < 1 || len(args) > 2 {
|
||||
return nil, fmt.Errorf("bad number of arguments")
|
||||
}
|
||||
key := args[0]
|
||||
opts := []clientv3.OpOption{clientv3.WithRev(watchRev)}
|
||||
if len(args) == 2 {
|
||||
if watchPrefix {
|
||||
return nil, fmt.Errorf("`range_end` and `--prefix` are mutually exclusive")
|
||||
}
|
||||
opts = append(opts, clientv3.WithRange(args[1]))
|
||||
}
|
||||
if watchPrefix {
|
||||
opts = append(opts, clientv3.WithPrefix())
|
||||
}
|
||||
if watchPrevKey {
|
||||
opts = append(opts, clientv3.WithPrevKV())
|
||||
}
|
||||
return c.Watch(context.TODO(), key, opts...), nil
|
||||
}
|
||||
|
||||
func printWatchCh(ch clientv3.WatchChan) {
|
||||
for resp := range ch {
|
||||
display.Watch(resp)
|
||||
}
|
||||
}
|
||||
99
vendor/github.com/coreos/etcd/etcdctl/ctlv3/ctl.go
generated
vendored
Normal file
99
vendor/github.com/coreos/etcd/etcdctl/ctlv3/ctl.go
generated
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package ctlv3 contains the main entry point for the etcdctl for v3 API.
|
||||
package ctlv3
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/etcdctl/ctlv3/command"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
cliName = "etcdctl"
|
||||
cliDescription = "A simple command line client for etcd3."
|
||||
|
||||
defaultDialTimeout = 2 * time.Second
|
||||
defaultCommandTimeOut = 5 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
globalFlags = command.GlobalFlags{}
|
||||
)
|
||||
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: cliName,
|
||||
Short: cliDescription,
|
||||
SuggestFor: []string{"etcdctl"},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringSliceVar(&globalFlags.Endpoints, "endpoints", []string{"127.0.0.1:2379"}, "gRPC endpoints")
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&globalFlags.OutputFormat, "write-out", "w", "simple", "set the output format (fields, json, protobuf, simple, table)")
|
||||
rootCmd.PersistentFlags().BoolVar(&globalFlags.IsHex, "hex", false, "print byte strings as hex encoded strings")
|
||||
|
||||
rootCmd.PersistentFlags().DurationVar(&globalFlags.DialTimeout, "dial-timeout", defaultDialTimeout, "dial timeout for client connections")
|
||||
rootCmd.PersistentFlags().DurationVar(&globalFlags.CommandTimeOut, "command-timeout", defaultCommandTimeOut, "timeout for short running command (excluding dial timeout)")
|
||||
|
||||
// TODO: secure by default when etcd enables secure gRPC by default.
|
||||
rootCmd.PersistentFlags().BoolVar(&globalFlags.Insecure, "insecure-transport", true, "disable transport security for client connections")
|
||||
rootCmd.PersistentFlags().BoolVar(&globalFlags.InsecureSkipVerify, "insecure-skip-tls-verify", false, "skip server certificate verification")
|
||||
rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CertFile, "cert", "", "identify secure client using this TLS certificate file")
|
||||
rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.KeyFile, "key", "", "identify secure client using this TLS key file")
|
||||
rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CAFile, "cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle")
|
||||
rootCmd.PersistentFlags().StringVar(&globalFlags.User, "user", "", "username[:password] for authentication (prompt if password is not supplied)")
|
||||
|
||||
rootCmd.AddCommand(
|
||||
command.NewGetCommand(),
|
||||
command.NewPutCommand(),
|
||||
command.NewDelCommand(),
|
||||
command.NewTxnCommand(),
|
||||
command.NewCompactionCommand(),
|
||||
command.NewAlarmCommand(),
|
||||
command.NewDefragCommand(),
|
||||
command.NewEndpointCommand(),
|
||||
command.NewWatchCommand(),
|
||||
command.NewVersionCommand(),
|
||||
command.NewLeaseCommand(),
|
||||
command.NewMemberCommand(),
|
||||
command.NewSnapshotCommand(),
|
||||
command.NewMakeMirrorCommand(),
|
||||
command.NewMigrateCommand(),
|
||||
command.NewLockCommand(),
|
||||
command.NewElectCommand(),
|
||||
command.NewAuthCommand(),
|
||||
command.NewUserCommand(),
|
||||
command.NewRoleCommand(),
|
||||
)
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.EnablePrefixMatching = true
|
||||
}
|
||||
|
||||
func Start() {
|
||||
rootCmd.SetUsageFunc(usageFunc)
|
||||
|
||||
// Make help just show the usage
|
||||
rootCmd.SetHelpTemplate(`{{.UsageString}}`)
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
command.ExitWithError(command.ExitError, err)
|
||||
}
|
||||
}
|
||||
174
vendor/github.com/coreos/etcd/etcdctl/ctlv3/help.go
generated
vendored
Normal file
174
vendor/github.com/coreos/etcd/etcdctl/ctlv3/help.go
generated
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// copied from https://github.com/coreos/rkt/blob/master/rkt/help.go
|
||||
|
||||
package ctlv3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
|
||||
"github.com/coreos/etcd/version"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var (
|
||||
commandUsageTemplate *template.Template
|
||||
templFuncs = template.FuncMap{
|
||||
"descToLines": func(s string) []string {
|
||||
// trim leading/trailing whitespace and split into slice of lines
|
||||
return strings.Split(strings.Trim(s, "\n\t "), "\n")
|
||||
},
|
||||
"cmdName": func(cmd *cobra.Command, startCmd *cobra.Command) string {
|
||||
parts := []string{cmd.Name()}
|
||||
for cmd.HasParent() && cmd.Parent().Name() != startCmd.Name() {
|
||||
cmd = cmd.Parent()
|
||||
parts = append([]string{cmd.Name()}, parts...)
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
commandUsage := `
|
||||
{{ $cmd := .Cmd }}\
|
||||
{{ $cmdname := cmdName .Cmd .Cmd.Root }}\
|
||||
NAME:
|
||||
{{ if not .Cmd.HasParent }}\
|
||||
{{printf "\t%s - %s" .Cmd.Name .Cmd.Short}}
|
||||
{{else}}\
|
||||
{{printf "\t%s - %s" $cmdname .Cmd.Short}}
|
||||
{{end}}\
|
||||
|
||||
USAGE:
|
||||
{{printf "\t%s" .Cmd.UseLine}}
|
||||
{{ if not .Cmd.HasParent }}\
|
||||
|
||||
VERSION:
|
||||
{{printf "\t%s" .Version}}
|
||||
{{end}}\
|
||||
{{if .Cmd.HasSubCommands}}\
|
||||
|
||||
API VERSION:
|
||||
{{printf "\t%s" .APIVersion}}
|
||||
{{end}}\
|
||||
{{if .Cmd.HasSubCommands}}\
|
||||
|
||||
|
||||
COMMANDS:
|
||||
{{range .SubCommands}}\
|
||||
{{ $cmdname := cmdName . $cmd }}\
|
||||
{{ if .Runnable }}\
|
||||
{{printf "\t%s\t%s" $cmdname .Short}}
|
||||
{{end}}\
|
||||
{{end}}\
|
||||
{{end}}\
|
||||
{{ if .Cmd.Long }}\
|
||||
|
||||
DESCRIPTION:
|
||||
{{range $line := descToLines .Cmd.Long}}{{printf "\t%s" $line}}
|
||||
{{end}}\
|
||||
{{end}}\
|
||||
{{if .Cmd.HasLocalFlags}}\
|
||||
|
||||
OPTIONS:
|
||||
{{.LocalFlags}}\
|
||||
{{end}}\
|
||||
{{if .Cmd.HasInheritedFlags}}\
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
{{.GlobalFlags}}\
|
||||
{{end}}
|
||||
`[1:]
|
||||
|
||||
commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(strings.Replace(commandUsage, "\\\n", "", -1)))
|
||||
}
|
||||
|
||||
func etcdFlagUsages(flagSet *pflag.FlagSet) string {
|
||||
x := new(bytes.Buffer)
|
||||
|
||||
flagSet.VisitAll(func(flag *pflag.Flag) {
|
||||
if len(flag.Deprecated) > 0 {
|
||||
return
|
||||
}
|
||||
format := ""
|
||||
if len(flag.Shorthand) > 0 {
|
||||
format = " -%s, --%s"
|
||||
} else {
|
||||
format = " %s --%s"
|
||||
}
|
||||
if len(flag.NoOptDefVal) > 0 {
|
||||
format = format + "["
|
||||
}
|
||||
if flag.Value.Type() == "string" {
|
||||
// put quotes on the value
|
||||
format = format + "=%q"
|
||||
} else {
|
||||
format = format + "=%s"
|
||||
}
|
||||
if len(flag.NoOptDefVal) > 0 {
|
||||
format = format + "]"
|
||||
}
|
||||
format = format + "\t%s\n"
|
||||
shorthand := flag.Shorthand
|
||||
fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage)
|
||||
})
|
||||
|
||||
return x.String()
|
||||
}
|
||||
|
||||
func getSubCommands(cmd *cobra.Command) []*cobra.Command {
|
||||
var subCommands []*cobra.Command
|
||||
for _, subCmd := range cmd.Commands() {
|
||||
subCommands = append(subCommands, subCmd)
|
||||
subCommands = append(subCommands, getSubCommands(subCmd)...)
|
||||
}
|
||||
return subCommands
|
||||
}
|
||||
|
||||
func usageFunc(cmd *cobra.Command) error {
|
||||
subCommands := getSubCommands(cmd)
|
||||
tabOut := getTabOutWithWriter(os.Stdout)
|
||||
commandUsageTemplate.Execute(tabOut, struct {
|
||||
Cmd *cobra.Command
|
||||
LocalFlags string
|
||||
GlobalFlags string
|
||||
SubCommands []*cobra.Command
|
||||
Version string
|
||||
APIVersion string
|
||||
}{
|
||||
cmd,
|
||||
etcdFlagUsages(cmd.LocalFlags()),
|
||||
etcdFlagUsages(cmd.InheritedFlags()),
|
||||
subCommands,
|
||||
version.Version,
|
||||
version.APIVersion,
|
||||
})
|
||||
tabOut.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTabOutWithWriter(writer io.Writer) *tabwriter.Writer {
|
||||
aTabOut := new(tabwriter.Writer)
|
||||
aTabOut.Init(writer, 0, 8, 1, '\t', 0)
|
||||
return aTabOut
|
||||
}
|
||||
29
vendor/github.com/coreos/etcd/etcdctl/doc/mirror_maker.md
generated
vendored
Normal file
29
vendor/github.com/coreos/etcd/etcdctl/doc/mirror_maker.md
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
## Mirror Maker
|
||||
|
||||
Mirror maker mirrors a prefix in the key-value space of an etcd cluster into another prefix in another cluster. Mirroring is designed for copying configuration to various clusters distributed around the world. Mirroring usually has very low latency once it completes synchronizing with the initial state. Mirror maker utilizes the etcd watcher facility to immediately inform the mirror of any key modifications. Based on our experiments, the network latency between the mirror maker and the two clusters accounts for most of the latency. If the network is healthy, copying configuration held in etcd to the mirror should take under one second even for a world-wide deployment.
|
||||
|
||||
If the mirror maker fails to connect to one of the clusters, the mirroring will pause. Mirroring can be resumed automatically once connectivity is reestablished.
|
||||
|
||||
The mirroring mechanism is unidirectional. Data under the destination cluster’s mirroring prefix should be treated as read only. The mirror maker only mirrors key-value pairs; metadata, such as version number or modification revision, is discarded. However, mirror maker still attempts to preserve update ordering during normal operation, but there is no ordering guarantee during initial sync nor during failure recovery following network interruption. As a rule of thumb, the ordering of the updates on the mirror should not be considered reliable.
|
||||
|
||||
```
|
||||
+-------------+
|
||||
| |
|
||||
| source | +-----------+
|
||||
| cluster +----> | mirror |
|
||||
| | | maker |
|
||||
+-------------+ +---+-------+
|
||||
|
|
||||
v
|
||||
+-------------+
|
||||
| |
|
||||
| mirror |
|
||||
| cluster |
|
||||
| |
|
||||
+-------------+
|
||||
|
||||
```
|
||||
|
||||
Mirror-maker is a built-in feature of [etcdctl][etcdctl].
|
||||
|
||||
[etcdctl]: ../README.md
|
||||
46
vendor/github.com/coreos/etcd/etcdctl/main.go
generated
vendored
Normal file
46
vendor/github.com/coreos/etcd/etcdctl/main.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// etcdctl is a command line application that controls etcd.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/etcd/etcdctl/ctlv2"
|
||||
"github.com/coreos/etcd/etcdctl/ctlv3"
|
||||
)
|
||||
|
||||
const (
|
||||
apiEnv = "ETCDCTL_API"
|
||||
)
|
||||
|
||||
func main() {
|
||||
apiv := os.Getenv(apiEnv)
|
||||
// unset apiEnv to avoid side-effect for future env and flag parsing.
|
||||
os.Unsetenv(apiEnv)
|
||||
if len(apiv) == 0 || apiv == "2" {
|
||||
ctlv2.Start()
|
||||
return
|
||||
}
|
||||
|
||||
if apiv == "3" {
|
||||
ctlv3.Start()
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "unsupported API version", apiv)
|
||||
os.Exit(1)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue